From d429f810a9ea3fda9efed2ff51483d25a288ecc9 Mon Sep 17 00:00:00 2001 From: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com> Date: Thu, 13 Apr 2023 00:31:22 +0900 Subject: [PATCH 001/501] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41353c346b..df2265727d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,9 @@ ## 13.11.2 +### Note +- 13.11.0または13.11.1から13.11.2以降にアップデートする場合、Redisのカスタム絵文字のキャッシュを削除する必要があります(https://github.com/misskey-dev/misskey/issues/10502#issuecomment-1502790755 参照) + ### General - チャンネルの検索用ページの追加 From e1b22165db643f49363b4acebc87ebf9892feac2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 19:42:42 +0900 Subject: [PATCH 002/501] =?UTF-8?q?feat:=20=E7=AE=A1=E7=90=86=E8=80=85?= =?UTF-8?q?=E6=A8=A9=E9=99=90=E3=82=92=E6=8C=81=E3=81=A3=E3=81=A6=E3=82=8B?= =?UTF-8?q?=E4=BA=BA=E3=81=AF=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=81=AE?= =?UTF-8?q?=E9=9D=9E=E5=85=AC=E9=96=8B=E6=8A=95=E7=A8=BF(=E3=83=9B?= =?UTF-8?q?=E3=83=BC=E3=83=A0)=E3=82=82=E8=A6=8B=E3=82=8C=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AATL=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/ServerModule.ts | 2 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../endpoints/notes/hybrid-all-timeline.ts | 148 ++++++++++++++++++ .../src/server/api/stream/ChannelsService.ts | 4 +- .../stream/channels/hybrid-all-timeline.ts | 140 +++++++++++++++++ .../frontend/src/components/MkTimeline.vue | 9 ++ packages/frontend/src/pages/timeline.vue | 8 +- 8 files changed, 315 insertions(+), 2 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts create mode 100644 packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index fa81380f01..ed2c981136 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -34,6 +34,7 @@ import { GlobalTimelineChannelService } from './api/stream/channels/global-timel import { HashtagChannelService } from './api/stream/channels/hashtag.js'; import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js'; import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js'; +import { HybridAllTimelineChannelService } from './api/stream/channels/hybrid-all-timeline.js'; import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js'; import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; @@ -79,6 +80,7 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; RoleTimelineChannelService, HomeTimelineChannelService, HybridTimelineChannelService, + HybridAllTimelineChannelService, LocalTimelineChannelService, QueueStatsChannelService, ServerStatsChannelService, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 799ba4498d..e4b6ad9c4f 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -262,6 +262,7 @@ import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; +import * as ep___notes_hybrid_All_Timeline from './endpoints/notes/hybrid-all-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; @@ -608,6 +609,7 @@ const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete' const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }; const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }; +const $notes_hybridAllTimeline: Provider = { provide: 'ep:notes/hybrid-all-timeline', useClass: ep___notes_hybrid_All_Timeline.default }; const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }; const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; @@ -958,6 +960,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_featured, $notes_globalTimeline, $notes_hybridTimeline, + $notes_hybridAllTimeline, $notes_localTimeline, $notes_mentions, $notes_polls_recommendation, @@ -1302,6 +1305,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_featured, $notes_globalTimeline, $notes_hybridTimeline, + $notes_hybridAllTimeline, $notes_localTimeline, $notes_mentions, $notes_polls_recommendation, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 3924b43d16..347b6973a8 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -262,6 +262,7 @@ import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; +import * as ep___notes_hybrid_All_Timeline from './endpoints/notes/hybrid-all-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; @@ -606,6 +607,7 @@ const eps = [ ['notes/featured', ep___notes_featured], ['notes/global-timeline', ep___notes_globalTimeline], ['notes/hybrid-timeline', ep___notes_hybridTimeline], + ['notes/hybrid-all-timeline', ep___notes_hybrid_All_Timeline], ['notes/local-timeline', ep___notes_localTimeline], ['notes/mentions', ep___notes_mentions], ['notes/polls/recommendation', ep___notes_polls_recommendation], diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts new file mode 100644 index 0000000000..b00df57043 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts @@ -0,0 +1,148 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Brackets } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { QueryService } from '@/core/QueryService.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; +import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, + + errors: { + stlDisabled: { + message: 'Hybrid All timeline has been disabled.', + code: 'STL_DISABLED', + id: '620763f4-f621-4533-ab33-0577a1a3c342', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + includeMyRenotes: { type: 'boolean', default: true }, + includeRenotedMyNotes: { type: 'boolean', default: true }, + includeLocalRenotes: { type: 'boolean', default: true }, + withFiles: { type: 'boolean', default: false }, + withReplies: { type: 'boolean', default: false }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + private noteEntityService: NoteEntityService, + private queryService: QueryService, + private roleService: RoleService, + private activeUsersChart: ActiveUsersChart, + private idService: IdService, + ) { + super(meta, paramDef, async (ps, me) => { + if (!me?.isRoot) throw new Error('access denied'); + + //#region Construct query + const followingQuery = this.followingsRepository.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: me.id }); + + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで + .andWhere(new Brackets(qb => { + qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) + .orWhere('(note.visibility = \'public\' OR note.visibility = \'home\') AND (note.userHost IS NULL)'); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .setParameters(followingQuery.getParameters()); + + this.queryService.generateChannelQuery(query, me); + this.queryService.generateRepliesQuery(query, ps.withReplies, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateMutedNoteQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :meId', { meId: me.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeRenotedMyNotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeLocalRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserHost IS NOT NULL'); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + //#endregion + + const timeline = await query.limit(ps.limit).getMany(); + + process.nextTick(() => { + this.activeUsersChart.read(me); + }); + + return await this.noteEntityService.packMany(timeline, me); + }); + } +} diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 8fd106c10c..c418d314a6 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -19,7 +19,7 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; - +import { HybridAllTimelineChannelService } from './channels/hybrid-all-timeline.js'; @Injectable() export class ChannelsService { constructor( @@ -27,6 +27,7 @@ export class ChannelsService { private homeTimelineChannelService: HomeTimelineChannelService, private localTimelineChannelService: LocalTimelineChannelService, private hybridTimelineChannelService: HybridTimelineChannelService, + private hybridAllTimelineChannelService: HybridAllTimelineChannelService, private globalTimelineChannelService: GlobalTimelineChannelService, private userListChannelService: UserListChannelService, private hashtagChannelService: HashtagChannelService, @@ -47,6 +48,7 @@ export class ChannelsService { case 'homeTimeline': return this.homeTimelineChannelService; case 'localTimeline': return this.localTimelineChannelService; case 'hybridTimeline': return this.hybridTimelineChannelService; + case 'hybridAllTimeline': return this.hybridAllTimelineChannelService; case 'globalTimeline': return this.globalTimelineChannelService; case 'userList': return this.userListChannelService; case 'hashtag': return this.hashtagChannelService; diff --git a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts new file mode 100644 index 0000000000..20c99bfcbf --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { checkWordMute } from '@/misc/check-word-mute.js'; +import { isUserRelated } from '@/misc/is-user-related.js'; +import { isInstanceMuted } from '@/misc/is-instance-muted.js'; +import type { Packed } from '@/misc/json-schema.js'; +import { MetaService } from '@/core/MetaService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { bindThis } from '@/decorators.js'; +import { RoleService } from '@/core/RoleService.js'; +import Channel from '../channel.js'; + +class HybridAllTimelineChannel extends Channel { + public readonly chName = 'hybridAllTimeline'; + public static shouldShare = true; + public static requireCredential = true; + private withReplies: boolean; + + constructor( + private metaService: MetaService, + private roleService: RoleService, + private noteEntityService: NoteEntityService, + + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + //this.onNote = this.onNote.bind(this); + } + + @bindThis + public async init(params: any): Promise<void> { + if (this.user == null || !this.user.isRoot ) return; + this.withReplies = params.withReplies as boolean; + + // Subscribe events + this.subscriber.on('notesStream', this.onNote); + } + + @bindThis + private async onNote(note: Packed<'Note'>) { + // チャンネルの投稿ではなく、自分自身の投稿 または + // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または + // チャンネルの投稿ではなく、全体公開のローカルの投稿 または + // チャンネルの投稿ではなく、ホームのローカルの投稿 または + // フォローしているチャンネルの投稿 の場合だけ + if (!( + (note.channelId == null && this.user!.id === note.userId) || + (note.channelId == null && this.following.has(note.userId)) || + (note.channelId == null && (note.user.host == null && note.visibility === 'public')) || + (note.channelId == null && (note.user.host == null && note.visibility === 'home')) || + (note.channelId != null && this.followingChannels.has(note.channelId)) + )) return; + + if (['followers', 'specified'].includes(note.visibility)) { + note = await this.noteEntityService.pack(note.id, this.user!, { + detail: true, + }); + + if (note.isHidden) { + return; + } + } else { + // リプライなら再pack + if (note.replyId != null) { + note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { + detail: true, + }); + } + // Renoteなら再pack + if (note.renoteId != null) { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { + detail: true, + }); + } + } + + // Ignore notes from instances the user has muted + if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances ?? []))) return; + + // 関係ない返信は除外 + if (note.reply && !this.withReplies) { + const reply = note.reply; + // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 + if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; + } + + // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する + if (isUserRelated(note, this.userIdsWhoMeMuting)) return; + // 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する + if (isUserRelated(note, this.userIdsWhoBlockingMe)) return; + + if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return; + + // 流れてきたNoteがミュートすべきNoteだったら無視する + // TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある) + // 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、 + // レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。 + // そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる + if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return; + + this.connection.cacheNote(note); + + this.send('note', note); + } + + @bindThis + public dispose(): void { + // Unsubscribe events + this.subscriber.off('notesStream', this.onNote); + } +} + +@Injectable() +export class HybridAllTimelineChannelService { + public readonly shouldShare = HybridAllTimelineChannel.shouldShare; + public readonly requireCredential = HybridAllTimelineChannel.requireCredential; + + constructor( + private metaService: MetaService, + private roleService: RoleService, + private noteEntityService: NoteEntityService, + ) { + } + + @bindThis + public create(id: string, connection: Channel['connection']): HybridAllTimelineChannel { + return new HybridAllTimelineChannel( + this.metaService, + this.roleService, + this.noteEntityService, + id, + connection, + ); + } +} diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index cb0aaf085c..2e09a75626 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -88,6 +88,15 @@ if (props.src === 'antenna') { withReplies: defaultStore.state.showTimelineReplies, }); connection.on('note', prepend); +} else if (props.src === 'all') { + endpoint = 'notes/hybrid-all-timeline'; + query = { + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel(' ', { + withReplies: defaultStore.state.showTimelineReplies, + }); + connection.on('note', prepend); } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f5fadb3899..860f732b08 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -45,6 +45,7 @@ const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); +const isAdmin = ($i != null && $i.isAdmin); const keymap = { 't': focus, }; @@ -100,7 +101,7 @@ async function chooseChannel(ev: MouseEvent): Promise<void> { os.popupMenu(items, ev.currentTarget ?? ev.target); } -function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global'): void { +function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | 'all'): void { defaultStore.set('tl', { ...defaultStore.state.tl, src: newSrc, @@ -143,6 +144,11 @@ const headerTabs = $computed(() => [{ title: i18n.ts._timelines.global, icon: 'ti ti-whirl', iconOnly: true, +}] : []), ...(isAdmin ? [{ + key: 'all', + title: 'all', + icon: 'ti ti-whirl', + iconOnly: true, }] : []), { icon: 'ti ti-list', title: i18n.ts.lists, From 2f37f89f5ac0086933906504e93c60ce7a677d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC?= <56515516+mattyatea@users.noreply.github.com> Date: Sun, 3 Sep 2023 19:54:32 +0900 Subject: [PATCH 003/501] mattyaski-2023.9.0-beta.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c6d089e93..e84ef6b023 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.2", + "version": "mattyaski-2023.9.0-beta.2", "codename": "nasubi", "repository": { "type": "git", From 3e958d5bd480d36b5474de082f4c479a36f8ce78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC?= <56515516+mattyatea@users.noreply.github.com> Date: Sun, 3 Sep 2023 20:08:34 +0900 Subject: [PATCH 004/501] Update hybrid-all-timeline.ts --- .../src/server/api/endpoints/notes/hybrid-all-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts index b00df57043..dd21438823 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts @@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - if (!me?.isRoot) throw new Error('access denied'); + if (!me?.isAdmin) throw new Error('access denied'); //#region Construct query const followingQuery = this.followingsRepository.createQueryBuilder('following') From a488e58edb6e9a966a5db5b97130c3412afb958f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 20:50:36 +0900 Subject: [PATCH 005/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../endpoints/notes/hybrid-all-timeline.ts | 102 +++++++----------- .../frontend/src/components/MkTimeline.vue | 2 +- 2 files changed, 41 insertions(+), 63 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts index b00df57043..0899aa5279 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts @@ -5,11 +5,11 @@ import { Brackets } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository, FollowingsRepository } from '@/models/index.js'; +import type { NotesRepository } from '@/models/index.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { QueryService } from '@/core/QueryService.js'; -import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; import { IdService } from '@/core/IdService.js'; @@ -18,8 +18,6 @@ import { ApiError } from '../../error.js'; export const meta = { tags: ['notes'], - requireCredential: true, - res: { type: 'array', optional: false, nullable: false, @@ -31,10 +29,10 @@ export const meta = { }, errors: { - stlDisabled: { - message: 'Hybrid All timeline has been disabled.', - code: 'STL_DISABLED', - id: '620763f4-f621-4533-ab33-0577a1a3c342', + ltlDisabled: { + message: 'hybrid Local timeline has been disabled.', + code: 'LTL_DISABLED', + id: '45a6eb02-7695-4393-b023-dd3be9aaaefd', }, }, } as const; @@ -42,16 +40,17 @@ export const meta = { export const paramDef = { type: 'object', properties: { + withFiles: { type: 'boolean', default: false }, + withReplies: { type: 'boolean', default: false }, + fileType: { type: 'array', items: { + type: 'string', + } }, + excludeNsfw: { type: 'boolean', default: false }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, sinceDate: { type: 'integer' }, untilDate: { type: 'integer' }, - includeMyRenotes: { type: 'boolean', default: true }, - includeRenotedMyNotes: { type: 'boolean', default: true }, - includeLocalRenotes: { type: 'boolean', default: true }, - withFiles: { type: 'boolean', default: false }, - withReplies: { type: 'boolean', default: false }, }, required: [], } as const; @@ -62,9 +61,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - private noteEntityService: NoteEntityService, private queryService: QueryService, private roleService: RoleService, @@ -72,74 +68,56 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { - if (!me?.isRoot) throw new Error('access denied'); + const policies = await this.roleService.getUserPolicies(me ? me.id : null); + if (!policies.ltlAvailable) { + throw new ApiError(meta.errors.ltlDisabled); + } //#region Construct query - const followingQuery = this.followingsRepository.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで - .andWhere(new Brackets(qb => { - qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) - .orWhere('(note.visibility = \'public\' OR note.visibility = \'home\') AND (note.userHost IS NULL)'); - })) + .andWhere('(note.visibility = \'public\' OR note.visibility = \'home\') AND (note.userHost IS NULL)') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .setParameters(followingQuery.getParameters()); + .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateChannelQuery(query, me); this.queryService.generateRepliesQuery(query, ps.withReplies, me); this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateMutedNoteQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateMutedNoteQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } + + if (ps.fileType != null) { + query.andWhere('note.fileIds != \'{}\''); + query.andWhere(new Brackets(qb => { + for (const type of ps.fileType!) { + const i = ps.fileType!.indexOf(type); + qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); + } + })); + + if (ps.excludeNsfw) { + query.andWhere('note.cw IS NULL'); + query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); + } + } //#endregion const timeline = await query.limit(ps.limit).getMany(); process.nextTick(() => { - this.activeUsersChart.read(me); + if (me) { + this.activeUsersChart.read(me); + } }); return await this.noteEntityService.packMany(timeline, me); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 2e09a75626..52640e7315 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -93,7 +93,7 @@ if (props.src === 'antenna') { query = { withReplies: defaultStore.state.showTimelineReplies, }; - connection = stream.useChannel(' ', { + connection = stream.useChannel('hybridAllTimeline', { withReplies: defaultStore.state.showTimelineReplies, }); connection.on('note', prepend); From 70c8f8f3a65366d5f04b7e1487869db9e24f3405 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 21:07:33 +0900 Subject: [PATCH 006/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../stream/channels/hybrid-all-timeline.ts | 61 ++++++------------- 1 file changed, 20 insertions(+), 41 deletions(-) diff --git a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts index 20c99bfcbf..9ab0e2fcf6 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts @@ -6,7 +6,6 @@ import { Injectable } from '@nestjs/common'; import { checkWordMute } from '@/misc/check-word-mute.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import type { Packed } from '@/misc/json-schema.js'; import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; @@ -17,7 +16,7 @@ import Channel from '../channel.js'; class HybridAllTimelineChannel extends Channel { public readonly chName = 'hybridAllTimeline'; public static shouldShare = true; - public static requireCredential = true; + public static requireCredential = false; private withReplies: boolean; constructor( @@ -33,8 +32,10 @@ class HybridAllTimelineChannel extends Channel { } @bindThis - public async init(params: any): Promise<void> { - if (this.user == null || !this.user.isRoot ) return; + public async init(params: any) { + const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); + if (!policies.ltlAvailable) return; + this.withReplies = params.withReplies as boolean; // Subscribe events @@ -43,50 +44,28 @@ class HybridAllTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { - // チャンネルの投稿ではなく、自分自身の投稿 または - // チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または - // チャンネルの投稿ではなく、全体公開のローカルの投稿 または - // チャンネルの投稿ではなく、ホームのローカルの投稿 または - // フォローしているチャンネルの投稿 の場合だけ - if (!( - (note.channelId == null && this.user!.id === note.userId) || - (note.channelId == null && this.following.has(note.userId)) || - (note.channelId == null && (note.user.host == null && note.visibility === 'public')) || - (note.channelId == null && (note.user.host == null && note.visibility === 'home')) || - (note.channelId != null && this.followingChannels.has(note.channelId)) - )) return; + if (note.user.host !== null) return; + if (!['public', 'home'].includes(note.visibility)) return; + if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; - if (['followers', 'specified'].includes(note.visibility)) { - note = await this.noteEntityService.pack(note.id, this.user!, { + // リプライなら再pack + if (note.replyId != null) { + note.reply = await this.noteEntityService.pack(note.replyId, this.user, { + detail: true, + }); + } + // Renoteなら再pack + if (note.renoteId != null) { + note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { detail: true, }); - - if (note.isHidden) { - return; - } - } else { - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user!, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user!, { - detail: true, - }); - } } - // Ignore notes from instances the user has muted - if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances ?? []))) return; - // 関係ない返信は除外 - if (note.reply && !this.withReplies) { + if (note.reply && this.user && !this.withReplies) { const reply = note.reply; // 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合 - if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return; + if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return; } // 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する @@ -109,7 +88,7 @@ class HybridAllTimelineChannel extends Channel { } @bindThis - public dispose(): void { + public dispose() { // Unsubscribe events this.subscriber.off('notesStream', this.onNote); } From c4af6fb51bf2c3ff98535edef7cfe25143be13ea Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 21:18:50 +0900 Subject: [PATCH 007/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/stream/channels/hybrid-all-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts index 9ab0e2fcf6..0d21468e30 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts @@ -45,7 +45,7 @@ class HybridAllTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { if (note.user.host !== null) return; - if (!['public', 'home'].includes(note.visibility)) return; + if (note.visibility !== "home") return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; // リプライなら再pack From c99c9f7ce107898ae15088bb9bec5bcd5b654cd4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 21:24:15 +0900 Subject: [PATCH 008/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/stream/channels/hybrid-all-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts index 0d21468e30..101fc3337f 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts @@ -45,7 +45,7 @@ class HybridAllTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { if (note.user.host !== null) return; - if (note.visibility !== "home") return; + if (note.visibility === "home") return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; // リプライなら再pack From 734880275b8f8f7758fb9e1c189a28117a9c8979 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 21:28:23 +0900 Subject: [PATCH 009/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/stream/channels/hybrid-all-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts index 101fc3337f..bca26eb36a 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-all-timeline.ts @@ -45,7 +45,7 @@ class HybridAllTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { if (note.user.host !== null) return; - if (note.visibility === "home") return; + if (note.visibility === "public") return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; // リプライなら再pack From 06f411ab668c5b5aa038a66cdad1d37066642118 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 3 Sep 2023 21:32:31 +0900 Subject: [PATCH 010/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/notes/hybrid-all-timeline.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts index 0899aa5279..d3d9fa346a 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts @@ -77,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで - .andWhere('(note.visibility = \'public\' OR note.visibility = \'home\') AND (note.userHost IS NULL)') + .andWhere('(note.visibility = \'home\') AND (note.userHost IS NULL)') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') From a97c4d25140ce1062f99265d4957a4b318cc6f7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC?= <56515516+mattyatea@users.noreply.github.com> Date: Fri, 8 Sep 2023 09:49:45 +0900 Subject: [PATCH 011/501] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e84ef6b023..40f3058509 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "mattyaski-2023.9.0-beta.2", + "version": "2023.9.0-beta.2-mattyaski", "codename": "nasubi", "repository": { "type": "git", From 77fabe66260bde6ce53d83fc1ce844e03d9326f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC?= <56515516+mattyatea@users.noreply.github.com> Date: Mon, 11 Sep 2023 18:58:48 +0900 Subject: [PATCH 012/501] Update package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 40f3058509..6b3c65fac0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.2-mattyaski", + "version": "2023.9.0-beta.5-mattyaski", "codename": "nasubi", "repository": { "type": "git", From b64a06b9622e3a5a9721f1263d3e54483b447806 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 11 Sep 2023 19:51:09 +0900 Subject: [PATCH 013/501] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=82=92=E3=83=9B=E3=83=BC=E3=83=A0=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/notes/show.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/show.ts b/packages/backend/src/server/api/endpoints/notes/show.ts index 5bb8196543..d5f3c420d6 100644 --- a/packages/backend/src/server/api/endpoints/notes/show.ts +++ b/packages/backend/src/server/api/endpoints/notes/show.ts @@ -48,7 +48,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); - return await this.noteEntityService.pack(note, me, { detail: true, }); From 0f1e562a40c9ff168e1e1ede509b3fef9b1b1559 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 11 Sep 2023 19:51:31 +0900 Subject: [PATCH 014/501] 2023.9.0-beta.5-mattyaski --- package.json | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 6b3c65fac0..b403cee8d3 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@8.6.10", + "packageManager": "pnpm@8.7.4", "workspaces": [ "packages/frontend", "packages/backend", @@ -15,7 +15,8 @@ "private": true, "scripts": { "build-pre": "node ./scripts/build-pre.js", - "build": "pnpm build-pre && pnpm -r build && pnpm gulp", + "build-assets": "node ./scripts/build-assets.mjs", + "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/index.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js", @@ -23,7 +24,6 @@ "migrate": "cd packages/backend && pnpm migrate", "check:connect": "cd packages/backend && pnpm check:connect", "migrateandstart": "pnpm migrate && pnpm start", - "gulp": "pnpm exec gulp build", "watch": "pnpm dev", "dev": "node ./scripts/dev.mjs", "lint": "pnpm -r lint", @@ -34,7 +34,6 @@ "jest-and-coverage": "cd packages/backend && pnpm jest-and-coverage", "test": "pnpm -r test", "test-and-coverage": "pnpm -r test-and-coverage", - "format": "pnpm exec gulp format", "clean": "node ./scripts/clean.js", "clean-all": "node ./scripts/clean-all.js", "cleanall": "pnpm clean-all" @@ -44,23 +43,19 @@ "lodash": "4.17.21" }, "dependencies": { - "execa": "7.2.0", - "gulp": "4.0.2", - "gulp-cssnano": "2.1.3", - "gulp-rename": "2.0.0", - "gulp-replace": "1.1.4", - "gulp-terser": "2.1.0", + "execa": "8.0.1", + "cssnano": "6.0.1", "js-yaml": "4.1.0", - "typescript": "5.1.6" + "postcss": "8.4.27", + "terser": "5.19.2", + "typescript": "5.2.2" }, "devDependencies": { - "@types/gulp": "4.0.13", - "@types/gulp-rename": "2.0.2", - "@typescript-eslint/eslint-plugin": "6.2.0", - "@typescript-eslint/parser": "6.2.0", + "@typescript-eslint/eslint-plugin": "6.6.0", + "@typescript-eslint/parser": "6.6.0", "cross-env": "7.0.3", - "cypress": "12.17.2", - "eslint": "8.46.0", + "cypress": "13.1.0", + "eslint": "8.48.0", "start-server-and-test": "2.0.0" }, "optionalDependencies": { From c29824f39be3869c1fdf608c24134fb1b7c57ab6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 11 Sep 2023 19:54:36 +0900 Subject: [PATCH 015/501] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=82=92=E3=83=9B=E3=83=BC=E3=83=A0=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/notes/create.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 22e772d1ab..bd249e81c8 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -17,6 +17,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; +import {noteVisibilities} from "@/types.js"; export const meta = { tags: ['notes'], @@ -232,7 +233,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } } - + let visibility = ps.visibility; let reply: MiNote | null = null; if (ps.replyId != null) { // Fetch reply @@ -243,7 +244,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) { throw new ApiError(meta.errors.cannotReplyToPureRenote); } - + // ノートがリプライでパブリック投稿の場合はホームにする + if (ps.visibility != 'home' && ps.visibility!== 'followers' && ps.visibility!=='specified' ){ + visibility = 'home'; + } // Check blocking if (reply.userId !== me.id) { const blockExist = await this.blockingsRepository.exist({ @@ -292,7 +296,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- cw: ps.cw, localOnly: ps.localOnly, reactionAcceptance: ps.reactionAcceptance, - visibility: ps.visibility, + visibility, visibleUsers, channel, apMentions: ps.noExtractMentions ? [] : undefined, From 382daa61ae1a5579e41821fe8be2560a293c3914 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 14:50:03 +0900 Subject: [PATCH 016/501] 2023.9.0-beta.6-mattyaski --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 431edce395..a05fcf563c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.6", + "version": "2023.9.0-beta.6-mattyaski", "codename": "nasubi", "repository": { "type": "git", From b36609bc74d64b82063dcfddae8a0fba70f26ddc Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 18:46:07 +0900 Subject: [PATCH 017/501] =?UTF-8?q?feat:=20=E9=87=8D=E8=A4=87=E3=81=97?= =?UTF-8?q?=E3=81=9F=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AF=E7=99=BB=E9=8C=B2?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/api/endpoints/admin/emoji/add.ts | 21 +++++++++++++++++-- .../server/api/endpoints/admin/emoji/copy.ts | 15 +++++++++++++ .../api/endpoints/admin/emoji/update.ts | 17 +++++++++++++-- 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 7bd920c312..afad538925 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/_.js'; +import type { DriveFilesRepository, EmojisRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -24,6 +24,11 @@ export const meta = { code: 'NO_SUCH_FILE', id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', }, + duplicationEmojiAdd: { + message: 'This emoji is already added.', + code: 'DUPLICATION_EMOJI_ADD', + id: 'mattyaski_emoji_duplication_error', + } }, } as const; @@ -57,7 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - + private emojisRepository: EmojisRepository, private customEmojiService: CustomEmojiService, private emojiEntityService: EmojiEntityService, @@ -67,6 +72,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + const existEmoji = await this.emojisRepository.exist({ + where: { + name: ps.name, + }, + }); + + if (existEmoji) { + throw new ApiError(meta.errors.duplicationEmojiAdd); + } + const emoji = await this.customEmojiService.add({ driveFile, name: ps.name, @@ -79,6 +94,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }); + + this.moderationLogService.insertModerationLog(me, 'addEmoji', { emojiId: emoji.id, }); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index f374b31303..5a592b989c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -26,6 +26,11 @@ export const meta = { code: 'NO_SUCH_EMOJI', id: 'e2785b66-dca3-4087-9cac-b93c541cc425', }, + duplicationEmojiAdd: { + message: 'This emoji is already added.', + code: 'DUPLICATION_EMOJI_ADD', + id: 'mattyaski_emoji_duplication_error', + } }, res: { @@ -57,6 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, + private emojiEntityService: EmojiEntityService, private idService: IdService, private globalEventService: GlobalEventService, @@ -77,6 +83,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } catch (e) { throw new ApiError(); } + const existEmoji = await this.emojisRepository.exist({ + where: { + name: emoji.name, + }, + }); + + if (existEmoji) { + throw new ApiError(meta.errors.duplicationEmojiAdd); + } const copied = await this.emojisRepository.insert({ id: this.idService.genId(), diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index f01be9e27a..c8fb2a2a2d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -6,7 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository } from '@/models/_.js'; +import type { DriveFilesRepository , EmojisRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -32,6 +32,11 @@ export const meta = { code: 'SAME_NAME_EMOJI_EXISTS', id: '7180fe9d-1ee3-bff9-647d-fe9896d2ffb8', }, + duplicationEmojiAdd: { + message: 'This emoji is already added.', + code: 'DUPLICATION_EMOJI_ADD', + id: 'mattyaski_emoji_duplication_error', + } }, } as const; @@ -64,7 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - + private emojisRepository: EmojisRepository, private customEmojiService: CustomEmojiService, ) { super(meta, paramDef, async (ps, me) => { @@ -74,7 +79,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } + const existEmoji = await this.emojisRepository.exist({ + where: { + name: ps.name, + }, + }); + if (existEmoji) { + throw new ApiError(meta.errors.duplicationEmojiAdd); + } await this.customEmojiService.update(ps.id, { driveFile, name: ps.name, From eddf29ea1ab6ed844c4e631a178add14321235ab Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 18:46:21 +0900 Subject: [PATCH 018/501] 2023.9.0-beta.6-mattyaski.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a05fcf563c..98a0194782 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.6-mattyaski", + "version": "2023.9.0-beta.6-mattyaski.2", "codename": "nasubi", "repository": { "type": "git", From 0312fcccfae41abf85be13f89d24e2c49141c10a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 18:54:47 +0900 Subject: [PATCH 019/501] =?UTF-8?q?fix:=20repository=E3=81=AFinject?= =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=81=A8=E3=81=84=E3=81=91=E3=81=AA?= =?UTF-8?q?=E3=81=84=E8=A6=9A=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/admin/emoji/add.ts | 1 + packages/backend/src/server/api/endpoints/admin/emoji/update.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index afad538925..61b1b0c5db 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -62,6 +62,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, private customEmojiService: CustomEmojiService, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index c8fb2a2a2d..0dc061e790 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -69,6 +69,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, + @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, private customEmojiService: CustomEmojiService, ) { From 365b5eaa701d39f512721223a6f545b9d178dfc9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 19:18:44 +0900 Subject: [PATCH 020/501] =?UTF-8?q?fix:=20=E3=81=AA=E3=82=93=E3=81=8B?= =?UTF-8?q?=E3=82=A2=E3=83=9B=E3=81=BF=E3=81=9F=E3=81=84=E3=81=AA=E3=83=90?= =?UTF-8?q?=E3=82=B0fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/admin/emoji/update.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 0dc061e790..dfe5266382 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -69,8 +69,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.emojisRepository) - private emojisRepository: EmojisRepository, private customEmojiService: CustomEmojiService, ) { super(meta, paramDef, async (ps, me) => { @@ -80,15 +78,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } - const existEmoji = await this.emojisRepository.exist({ - where: { - name: ps.name, - }, - }); - if (existEmoji) { - throw new ApiError(meta.errors.duplicationEmojiAdd); - } await this.customEmojiService.update(ps.id, { driveFile, name: ps.name, From 0e5d164acc4688826a70419163113efcb9424329 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 19:48:30 +0900 Subject: [PATCH 021/501] =?UTF-8?q?fix:=20=E3=81=AA=E3=82=93=E3=81=8B?= =?UTF-8?q?=E3=82=A2=E3=83=9B=E3=81=BF=E3=81=9F=E3=81=84=E3=81=AA=E3=83=90?= =?UTF-8?q?=E3=82=B0fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/api/endpoints/admin/emoji/copy.ts | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 5a592b989c..57cd7f712a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -74,15 +74,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (emoji == null) { throw new ApiError(meta.errors.noSuchEmoji); } - - let driveFile: MiDriveFile; - - try { - // Create file - driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true }); - } catch (e) { - throw new ApiError(); - } const existEmoji = await this.emojisRepository.exist({ where: { name: emoji.name, @@ -92,6 +83,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (existEmoji) { throw new ApiError(meta.errors.duplicationEmojiAdd); } + let driveFile: MiDriveFile; + + try { + // Create file + driveFile = await this.driveService.uploadFromUrl({ url: emoji.originalUrl, user: null, force: true }); + } catch (e) { + throw new ApiError(); + } const copied = await this.emojisRepository.insert({ id: this.idService.genId(), From 272c5a1dfe3a40b4563e1cbc4e2f96efb2bc8e7b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 15 Sep 2023 19:53:04 +0900 Subject: [PATCH 022/501] =?UTF-8?q?fix:=20=E3=81=AA=E3=82=93=E3=81=8B?= =?UTF-8?q?=E3=82=A2=E3=83=9B=E3=81=BF=E3=81=9F=E3=81=84=E3=81=AA=E3=83=90?= =?UTF-8?q?=E3=82=B0fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/admin/emoji/copy.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 57cd7f712a..f9a6016a6d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -74,15 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (emoji == null) { throw new ApiError(meta.errors.noSuchEmoji); } - const existEmoji = await this.emojisRepository.exist({ - where: { - name: emoji.name, - }, - }); - if (existEmoji) { - throw new ApiError(meta.errors.duplicationEmojiAdd); - } let driveFile: MiDriveFile; try { From 3a20dfb245a1a2fb8e1b38a5cd3ddfba5f488ad7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 17:08:03 +0900 Subject: [PATCH 023/501] =?UTF-8?q?fix:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E9=87=8D=E8=A4=87=E5=91=A8=E3=82=8A=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/admin/emoji/add.ts | 12 ++++++++---- .../src/server/api/endpoints/admin/emoji/copy.ts | 14 ++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 61b1b0c5db..4ae1b8aa11 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -73,15 +73,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const existEmoji = await this.emojisRepository.exist({ + const duplicationEmoji = await this.emojisRepository.find({ where: { name: ps.name, }, }); - if (existEmoji) { - throw new ApiError(meta.errors.duplicationEmojiAdd); - } + duplicationEmoji.forEach( + (emoji) => { + if (emoji.name === ps.name) { + throw new ApiError(meta.errors.duplicationEmojiAdd); + } + } + ) const emoji = await this.customEmojiService.add({ driveFile, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index f9a6016a6d..40f9f9e1e3 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -75,6 +75,20 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.noSuchEmoji); } + const duplicationEmoji = await this.emojisRepository.find({ + where: { + name: emoji.name, + }, + }); + + duplicationEmoji.forEach( + (_emoji) => { + if (_emoji.name === emoji.name) { + throw new ApiError(meta.errors.duplicationEmojiAdd); + } + } + ) + let driveFile: MiDriveFile; try { From 0111f99b326ef7fb549ed3190d4bb33a53d31c3d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 18:01:03 +0900 Subject: [PATCH 024/501] =?UTF-8?q?feat:=20=E5=BD=93=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=BC=E3=82=AF=E3=81=AE=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=B3?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=95=E3=81=9B?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/pages/about-misskey.vue | 5 +++++ 3 files changed, 7 insertions(+) diff --git a/locales/en-US.yml b/locales/en-US.yml index e0358a8468..9ce6ee5cdb 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1551,6 +1551,7 @@ _aboutMisskey: contributors: "Main contributors" allContributors: "All contributors" source: "Source code" + forksource: "Source code for this fork" translation: "Translate Misskey" donate: "Donate to Misskey" morePatrons: "We also appreciate the support of many other helpers not listed here. Thank you! 🥰" diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index d97b09f63c..71c04873ad 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1573,6 +1573,7 @@ _aboutMisskey: contributors: "主なコントリビューター" allContributors: "全てのコントリビューター" source: "ソースコード" + forksource: "当フォークのソースコード" translation: "Misskeyを翻訳" donate: "Misskeyに寄付" morePatrons: "他にも多くの方が支援してくれています。ありがとうございます🥰" diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 7cc6040faa..a7cbf0da05 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -34,6 +34,11 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._aboutMisskey.source }} <template #suffix>GitHub</template> </FormLink> + <FormLink to="https://github.com/mattyatea/misskey" external> + <template #icon><i class="ti ti-code"></i></template> + {{ i18n.ts._aboutMisskey.forksource }} + <template #suffix>GitHub</template> + </FormLink> <FormLink to="https://crowdin.com/project/misskey" external> <template #icon><i class="ti ti-language-hiragana"></i></template> {{ i18n.ts._aboutMisskey.translation }} From dd564466770c692ebe1ceb2253042b94a2108d03 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 18:01:25 +0900 Subject: [PATCH 025/501] 2023.9.0-beta.8-mattyaski.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index ffb4596e90..8c318695ec 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,10 @@ { "name": "misskey", - "version": "2023.9.0-beta.8", + "version": "2023.9.0-beta.8-mattyaski.1", "codename": "nasubi", "repository": { "type": "git", - "url": "https://github.com/misskey-dev/misskey.git" + "url": "https://github.com/mattyatea/misskey.git" }, "packageManager": "pnpm@8.7.5", "workspaces": [ From 61dad2ce3548b64a618134dfb3602afe592252bb Mon Sep 17 00:00:00 2001 From: Fairy-Phy <phy.public@gmail.com> Date: Sat, 16 Sep 2023 22:16:47 +0900 Subject: [PATCH 026/501] =?UTF-8?q?emojiKitchen=E3=83=99=E3=83=BC=E3=82=B9?= =?UTF-8?q?=E3=81=AEmix=E9=96=A2=E6=95=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/global/MkEmojiKitchen.vue | 46 + .../global/MkMisskeyFlavoredMarkdown.ts | 155 +- .../src/scripts/emojiKitchen/emojiData.ts | 37031 ++++++++++++++++ .../src/scripts/emojiKitchen/emojiMixer.ts | 60 + packages/frontend/src/scripts/mfm-tags.ts | 7 +- 5 files changed, 37278 insertions(+), 21 deletions(-) create mode 100644 packages/frontend/src/components/global/MkEmojiKitchen.vue create mode 100644 packages/frontend/src/scripts/emojiKitchen/emojiData.ts create mode 100644 packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts diff --git a/packages/frontend/src/components/global/MkEmojiKitchen.vue b/packages/frontend/src/components/global/MkEmojiKitchen.vue new file mode 100644 index 0000000000..1ce71b36b2 --- /dev/null +++ b/packages/frontend/src/components/global/MkEmojiKitchen.vue @@ -0,0 +1,46 @@ +<template> +<span v-if="errored">{{ alt }}</span> +<img v-else :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" :src="url" :alt="alt" :title="alt" decoding="async" @error="errored = true" @load="errored = false"/> +</template> + +<script lang="ts" setup> +import { computed } from 'vue'; + +const props = defineProps<{ + name: string; + normal?: boolean; + url: string; +}>(); + +const rawUrl = computed(() => props.url); + +const url = computed(() => rawUrl.value); + +const alt = computed(() => props.name); +let errored = $ref(url.value == null); +</script> + +<style lang="scss" module> +.root { + height: 2em; + vertical-align: middle; + transition: transform 0.2s ease; + + &:hover { + transform: scale(1.2); + } +} + +.normal { + height: 1.25em; + vertical-align: -0.25em; + + &:hover { + transform: none; + } +} + +.noStyle { + height: auto !important; +} +</style> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index f9e502dc44..25262030d6 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -1,8 +1,3 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - import { VNode, h } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; @@ -11,12 +6,14 @@ import MkLink from '@/components/MkLink.vue'; import MkMention from '@/components/MkMention.vue'; import MkEmoji from '@/components/global/MkEmoji.vue'; import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue'; +import MkEmojiKitchen from '@/components/global/MkEmojiKitchen.vue'; import MkCode from '@/components/MkCode.vue'; import MkGoogle from '@/components/MkGoogle.vue'; import MkSparkle from '@/components/MkSparkle.vue'; import MkA from '@/components/global/MkA.vue'; import { host } from '@/config'; import { defaultStore } from '@/store'; +import { mixEmoji } from '@/scripts/emojiKitchen/emojiMixer'; const QUOTE_STYLE = ` display: block; @@ -27,6 +24,38 @@ border-left: solid 3px var(--fg); opacity: 0.7; `.split('\n').join(' '); +const colorRegexp = /^([0-9a-f]{3,4}?|[0-9a-f]{6}?|[0-9a-f]{8}?)$/i; +function checkColorHex(text: string) { + return colorRegexp.test(text); +} + +const gradientCounterRegExp = /^(color|step)(\d+)/; + +function toGradientText(args: Record<string, string>) { + const colors: { index: number; step?: string, color?: string }[] = []; + for (const k in args) { + const matches = k.match(gradientCounterRegExp); + if (matches == null) continue; + const mindex = parseInt(matches[2]); + let i = colors.findIndex(v => v.index === mindex); + if (i === -1) { + i = colors.length; + colors.push({ index: mindex }); + } + colors[i][matches[1]] = args[k]; + } + let deg = parseFloat(args.deg || '90'); + let res = `linear-gradient(${deg}deg`; + for (const colorProp of colors.sort((a, b) => a.index - b.index)) { + let color = colorProp.color; + if (!color || !checkColorHex(color)) color = 'f00'; + let step = parseFloat(colorProp.step ?? ''); + let stepText = isNaN(step) ? '' : ` ${step}%`; + res += `, #${color}${stepText}`; + } + return res + ')'; +} + export default function(props: { text: string; plain?: boolean; @@ -44,7 +73,7 @@ export default function(props: { const ast = (props.plain ? mfm.parseSimple : mfm.parse)(props.text); const validTime = (t: string | null | undefined) => { - if (t == null) return null; + if (t == null || typeof t === 'boolean') return null; return t.match(/^[0-9.]+s$/) ? t : null; }; @@ -170,18 +199,15 @@ export default function(props: { break; } case 'blur': { + const radius = parseFloat(token.props.args.rad ?? '6'); return h('span', { class: '_mfm_blur_', + style: `--blur-px: ${radius}px;` }, genEl(token.children, scale)); } case 'rainbow': { - if (!useAnim) { - return h('span', { - class: '_mfm_rainbow_fallback_', - }, genEl(token.children, scale)); - } const speed = validTime(token.props.args.speed) ?? '1s'; - style = `animation: mfm-rainbow ${speed} linear infinite;`; + style = useAnim ? `animation: mfm-rainbow ${speed} linear infinite;` : ''; break; } case 'sparkle': { @@ -192,7 +218,23 @@ export default function(props: { } case 'rotate': { const degrees = parseFloat(token.props.args.deg ?? '90'); - style = `transform: rotate(${degrees}deg); transform-origin: center center;`; + let rotateText = `rotate(${degrees}deg)`; + if (!token.props.args.deg && (token.props.args.x || token.props.args.y || token.props.args.z)) { + rotateText = ''; + } + if (token.props.args.x) { + const degrees = parseFloat(token.props.args.x ?? '0'); + rotateText += ` rotateX(${degrees}deg)`; + } + if (token.props.args.y) { + const degrees = parseFloat(token.props.args.y ?? '0'); + rotateText += ` rotateY(${degrees}deg)`; + } + if (token.props.args.z) { + const degrees = parseFloat(token.props.args.z ?? '0'); + rotateText += ` rotateZ(${degrees}deg)`; + } + style = `transform: ${rotateText}; transform-origin: center center;`; break; } case 'position': { @@ -213,18 +255,101 @@ export default function(props: { scale = scale * Math.max(x, y); break; } + case 'skew': { + if (!defaultStore.state.advancedMfm) { + style = ''; + break; + } + const x = parseFloat(token.props.args.x ?? '0'); + const y = parseFloat(token.props.args.y ?? '0'); + style = `transform: skew(${x}deg, ${y}deg);`; + break; + } + case 'fgg': { + if (!defaultStore.state.advancedMfm) break; + style = `-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: ${toGradientText(token.props.args)};` + break; + } case 'fg': { let color = token.props.args.color; - if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00'; + if (!checkColorHex(color)) color = 'f00'; style = `color: #${color};`; break; } + case 'bgg': { + if (!defaultStore.state.advancedMfm) break; + style = `background-image: ${toGradientText(token.props.args)};` + break; + } case 'bg': { let color = token.props.args.color; - if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00'; + if (!checkColorHex(color)) color = 'f00'; style = `background-color: #${color};`; break; } + case 'clip': { + if (!defaultStore.state.advancedMfm) break; + + let path = ''; + if (token.props.args.circle) { + const percent = parseFloat(token.props.args.circle ?? ''); + const percentText = isNaN(percent) ? '' : `${percent}%`; + path = `circle(${percentText})`; + } + else { + const top = parseFloat(token.props.args.t ?? '0'); + const bottom = parseFloat(token.props.args.b ?? '0'); + const left = parseFloat(token.props.args.l ?? '0'); + const right = parseFloat(token.props.args.r ?? '0'); + path = `inset(${top}% ${right}% ${bottom}% ${left}%)`; + } + style = `clip-path: ${path};`; + break; + } + case 'move': { + const speed = validTime(token.props.args.speed) ?? '1s'; + const fromX = parseFloat(token.props.args.fromx ?? '0'); + const fromY = parseFloat(token.props.args.fromy ?? '0'); + const toX = parseFloat(token.props.args.tox ?? '0'); + const toY = parseFloat(token.props.args.toy ?? '0'); + const ease = + token.props.args.ease ? 'ease' : + token.props.args.easein ? 'ease-in' : + token.props.args.easeout ? 'ease-out' : + token.props.args.easeinout ? 'ease-in-out' : + 'linear'; + const delay = validTime(token.props.args.delay) ?? '0s'; + const direction = + token.props.args.rev && token.props.args.once ? 'reverse' : + token.props.args.rev ? 'alternate-reverse' : + token.props.args.once ? 'normal' : + 'alternate'; + style = useAnim ? `--move-fromX: ${fromX}em; --move-fromY: ${fromY}em; --move-toX: ${toX}em; --move-toY: ${toY}em; animation: ${speed} ${ease} ${delay} infinite ${direction} mfm-move;` : ''; + break; + } + case 'mix': { + const ch = token.children; + if (ch.length != 2 || ch.some(c => c.type !== 'unicodeEmoji')) { + style = null; + break; + } + + const emoji1 = ch[0].props.emoji; + const emoji2 = ch[1].props.emoji; + + const mixedEmojiUrl = mixEmoji(emoji1, emoji2); + if (!mixedEmojiUrl) { + style = null; + break; + } + + return h(MkEmojiKitchen, { + key: Math.random(), + name: emoji1 + emoji2, + normal: props.plain, + url: mixedEmojiUrl + }); + } } if (style == null) { return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']); diff --git a/packages/frontend/src/scripts/emojiKitchen/emojiData.ts b/packages/frontend/src/scripts/emojiKitchen/emojiData.ts new file mode 100644 index 0000000000..e11ead82ff --- /dev/null +++ b/packages/frontend/src/scripts/emojiKitchen/emojiData.ts @@ -0,0 +1,37031 @@ +// original Source: https://github.com/alcor/emoji-supply/blob/main/docs/kitchen/emojidata.js + +export const points = +['2764-fe0f','1f9e1','1f49b','1f49a','1fa75','1f499','1f49c','1fa77','1f90e','1fa76','1f5a4','1f90d','1fa84','1f600','1f603','1f604','1f601','1f606','1f605','1f923','1f602','1f642','1f643','1fae0','1f609','1f60a','1f607','1f970','1f60d','1f929','1f618','1f617','263a-fe0f','1f61a','1f619','1f972','1f60b','1f61b','1f61c','1f92a','1f61d','1f911','1f917','1f92d','1fae2','1fae3','1f92b','1f914','1fae1','1f910','1f928','1f610','1f611','1f636','1fae5','1f636-200d-1f32b-fe0f','1f60f','1f612','1f644','1f62c','1f62e-200d-1f4a8','1f925','1f60c','1f614','1f62a','1f924','1f634','1f637','1f912','1f915','1f922','1f92e','1f927','1f975','1f976','1f974','1f635','1f92f','1f920','1f973','1f978','1f60e','1f913','1f9d0','1f615','1fae4','1f61f','1f641','2639-fe0f','1f62e','1f62f','1f632','1f633','1f97a','1f979','1f626','1f627','1f628','1f630','1f625','1f622','1f62d','1f631','1f616','1f623','1f61e','1f613','1f629','1f62b','1f971','1f624','1f621','1f620','1f92c','1f608','1f47f','1f480','1f4a9','1f921','1f47b','1f47d','1f916','1f648','1f48c','1f498','1f49d','1f496','1f497','1f493','1f49e','1f495','1f49f','2763-fe0f','1f494','2764-fe0f-200d-1fa79','1f48b','1f4af','1f4a5','1f4ab','1f573-fe0f','1f4ac','1f5ef-fe0f','1f44d','1f9e0','1fac0','1fac1','1f9b7','1f9b4','1f440','1f441-fe0f','1fae6','1f937','1fac2','1f435','1f436','1f429','1f43a','1f98a','1f99d','1f431','1f981','1f42f','1f984','1f98c','1f437','1f410','1f999','1f42d','1f430','1f994','1f987','1f43b','1f428','1f43c','1f9a5','1f43e','1f414','1f426','1f427','1f54a-fe0f','1f989','1f9a9','1fabf','1f422','1f433','1f41f','1f988','1f419','1f41a','1fab8','1f40c','1f98b','1f41d','1fab2','1f41e','1f997','1fab3','1f577-fe0f','1f982','1f9a0','1f490','1f338','1f4ae','1fab7','1f3f5-fe0f','1f339','1f940','1f33a','1f33b','1f33c','1f337','1fabb','1f331','1fab4','1f332','1f333','1f334','1f335','1f33f','1f340','1f341','1f343','1fab9','1f344','1f347','1f348','1f349','1f34a','1f34b','1f34c','1f34d','1f96d','1f350','1f352','1f353','1fad0','1f95d','1f345','1fad2','1f965','1f951','1f954','1f955','1f33d','1f336-fe0f','1fad1','1f952','1f96c','1f966','1f9c4','1f9c5','1f95c','1fad8','1f330','1fada','1fadb','1f35e','1f950','1fad3','1f968','1f96f','1f95e','1f9c7','1f9c0','1f356','1f969','1f953','1f354','1f35f','1f32d','1f96a','1f32e','1f32f','1fad4','1f959','1f9c6','1f373','1f958','1f372','1fad5','1f963','1f957','1f37f','1f9c2','1f96b','1f371','1f358','1f359','1f35a','1f35b','1f35c','1f35d','1f360','1f363','1f364','1f365','1f960','1f961','1f980','1f99e','1f9aa','1f366','1f367','1f368','1f369','1f36a','1f382','1f370','1f9c1','1f967','1f36b','1f36c','1f37c','2615','1f375','1f376','1f379','1f964','1f9cb','1f9c9','1f962','1f37d-fe0f','1f30d','1f30b','1f3d5-fe0f','1f3d6-fe0f','1f3dd-fe0f','1f3de-fe0f','1f3df-fe0f','1f3db-fe0f','1faa8','1fab5','1f3e0','1f307','1f3a1','1f3a2','1f3aa','1f682','1f68c','1f695','1f697','1f3ce-fe0f','1f6f9','1f6fc','1f6a8','1f6d1','2693','1f6df','1f6f6','2708-fe0f','1f680','1f6f8','1f9f3','231b','23f3','23f0','1f31a','1f31b','1f31c','1f321-fe0f','1f31d','1f31e','1fa90','2b50','1f31f','1f30c','2601-fe0f','26c5','1f327-fe0f','1f329-fe0f','1f32a-fe0f','1f32c-fe0f','1f300','1f308','2602-fe0f','26a1','26c4','2604-fe0f','1f525','1f4a7','1f30a','1f383','1f386','1f388','1f38a','1f380','1f381','1f397-fe0f','1f39f-fe0f','1f396-fe0f','1f3c6','1f3c5','1f947','1f948','1f949','26bd','26be','1f94e','1f3c0','1f3d0','1f3c8','1f3c9','1f3be','1f94f','1f3b3','1f3cf','1f3d1','1f3d2','1f94d','1f3d3','1f3f8','1f94a','1f94b','1f945','26f3','26f8-fe0f','1f93f','1f3bd','1f3bf','1f6f7','1f94c','1f3af','1fa80','1fa81','1f3b1','1f52e','1f3ae','1f3b0','1f3b2','1f9e9','1faa9','2660-fe0f','2665-fe0f','265f-fe0f','1f0cf','1f004','1f3b4','1f3ad','1f3a8','1f9f5','1faa1','1f9f6','1f9e6','1f460','1fa70','1faae','1f451','1f393','1f48d','1f48e','1f508','1f4e3','1f514','1f399-fe0f','1f39a-fe0f','1f39b-fe0f','1f3a4','1f3a7','1f4fb','1f3b7','1fa97','1f3b8','1f3b9','1f3ba','1f3bb','1fa95','1f941','1fa98','1f4f1','260e-fe0f','1f4df','1f4e0','1f50b','1faab','1f50c','1f4bb','1f5a8-fe0f','1f4be','1f4bf','1f39e-fe0f','1f3ac','1f4fa','1f4f7','1f4fc','1f50e','1f4a1','1f4da','1f4f0','1f4b8','270f-fe0f','2712-fe0f','1f58a-fe0f','1f58c-fe0f','1f58d-fe0f','1f4c8','1f4c9','1f4ca','1f587-fe0f','1f4cf','2702-fe0f','1f5c3-fe0f','1f5d1-fe0f','1f512','1f5dd-fe0f','26cf-fe0f','1f6e0-fe0f','1fa83','1f3f9','2696-fe0f','26d3-fe0f','1f9f2','1f9ea','1f9ec','1f52c','1f52d','1fa78','1fa7a','1f6cb-fe0f','1faa4','1fa92','1f9f9','1f9fc','1fae7','1f9ff','1f6ae','26a0-fe0f','262f-fe0f','262e-fe0f','2648','2649','264a','264b','264c','264d','264e','264f','2650','2651','2652','2653','26ce','1f4f4','2716-fe0f','2795','2796','2797','267e-fe0f','2049-fe0f','2753','2757','3030-fe0f','267b-fe0f','2705','27b0','27bf','a9-fe0f','ae-fe0f','2122-fe0f','1f192','1f193','1f195','1f197','1f198','1f199'] + +export const revisions = +[20201001,20230418,20211115,20230301,20220815,20230127,20220203,20221101,20220506,20210831,20220406,20210218,20230126,20230216,20220110,20221107,20210521,20230426,20230405,20220823,20230421,20230221,20230118,20230613] + +export const pairs = ` +0.0.0. +0.0.1. +0.0.2. +0.0.3. +7.4.0. +0.0.5. +0.0.6. +0.0.6. +7.7.0. +0.0.8. +7.9.0. +0.0.a. +0.0.b. +g.c.0. +0.r.0. +0.s.0. +0.t.0. +0.u.0. +0.1f.0. +0.1h.0. +0.1i.0. +0.1C.0. +0.1S.0. +0.1T.0. +0.0.1X. +0.0.1Y. +0.0.1Z. +0.0.1+. +0.0.1/. +0.0.20. +0.0.21. +0.0.22. +0.0.23. +0.0.24. +0.0.25. +b.26.0. +0.0.27. +0.0.28. +0.0.2a. +0.0.2b. +3.0.2k. +0.0.2l. +6.2m.0. +4.2n.0. +2.2r.0. +9.2y.0. +9.2N.0. +b.36.0. +0.0.38. +5.39.0. +0.0.3d. +0.0.3h. +0.0.3i. +3.0.4O. +0.0.4Q. +5.4T.0. +0.0.5B. +0.0.5C. +0.0.5H. +0.0.5I. +3.0.5R. +0.0.5W. +1.5Y.0. +h.5+.0. +0.0.5/. +0.0.60. +2.62.0. +0.0.63. +0.0.6M. +0.0.6+. +0.0.71. +h.86.0. +0.1.1. +0.1.2. +0.1.3. +7.4.1. +0.1.5. +0.1.6. +7.7.1. +0.1.8. +7.9.1. +0.1.a. +0.1.b. +g.c.1. +0.r.1. +0.s.1. +0.t.1. +0.u.1. +0.1f.1. +0.1h.1. +0.1i.1. +0.1C.1. +0.1S.1. +0.1T.1. +0.1.1X. +0.1.1Y. +0.1.1Z. +0.1.1+. +0.1.1/. +0.1.20. +0.1.21. +0.1.22. +0.1.23. +0.1.24. +0.1.25. +b.26.1. +0.1.27. +0.1.28. +0.1.2a. +0.1.2b. +3.1.2k. +0.1.2l. +6.2m.1. +4.2n.1. +2.2r.1. +9.2y.1. +9.2N.1. +b.36.1. +0.1.38. +5.39.1. +0.1.3d. +0.1.3h. +0.1.3i. +3.1.4O. +0.1.4Q. +5.4T.1. +0.1.5B. +0.1.5C. +0.1.5H. +0.1.5I. +3.1.5R. +0.1.5W. +1.5Y.1. +0.1.5/. +0.1.60. +2.62.1. +0.1.63. +0.1.6M. +0.1.6+. +0.1.71. +0.2.2. +0.2.3. +7.4.2. +0.2.5. +0.2.6. +7.7.2. +0.2.8. +7.9.2. +0.2.a. +0.2.b. +g.c.2. +0.r.2. +0.s.2. +0.t.2. +0.u.2. +3.1f.2. +0.1h.2. +3.1i.2. +3.1C.2. +3.1S.2. +0.1T.2. +0.2.1X. +0.2.1Y. +0.2.1Z. +0.2.1+. +0.2.1/. +0.2.20. +0.2.21. +0.2.22. +0.2.23. +0.2.24. +0.2.25. +b.26.2. +0.2.27. +0.2.28. +0.2.2a. +0.2.2b. +d.2.2k. +0.2.2l. +6.2m.2. +4.2n.2. +2.2r.2. +9.2y.2. +9.2N.2. +b.36.2. +0.2.38. +5.39.2. +0.2.3d. +0.2.3h. +0.2.3i. +a.3y.2. +a.3F.2. +0.2.4O. +0.2.4Q. +5.4T.2. +0.2.5B. +0.2.5C. +0.2.5H. +0.2.5I. +d.2.5R. +0.2.5W. +1.5Y.2. +0.2.5/. +d.2.60. +2.62.2. +0.2.63. +0.2.6M. +0.2.6+. +0.2.71. +0.3.3. +7.4.3. +0.3.5. +0.3.6. +7.7.3. +0.3.8. +7.9.3. +0.3.a. +0.3.b. +g.c.3. +0.r.3. +0.s.3. +0.t.3. +0.u.3. +0.1f.3. +0.1h.3. +0.1i.3. +0.1C.3. +0.1S.3. +0.1T.3. +0.3.1X. +0.3.1Y. +0.3.1Z. +0.3.1+. +0.3.1/. +0.3.20. +0.3.21. +0.3.22. +0.3.23. +0.3.24. +0.3.25. +b.26.3. +0.3.27. +0.3.28. +0.3.2a. +0.3.2b. +d.3.2k. +0.3.2l. +6.2m.3. +4.2n.3. +2.2r.3. +9.2y.3. +9.2N.3. +b.36.3. +0.3.38. +5.39.3. +0.3.3d. +0.3.3h. +0.3.3i. +5.3A.3. +0.3.4O. +0.3.4Q. +5.4T.3. +0.3.4V. +0.3.5B. +0.3.5C. +0.3.5H. +0.3.5I. +d.3.5R. +0.3.5W. +1.5Y.3. +0.3.5/. +0.3.60. +2.62.3. +0.3.63. +0.3.6M. +0.3.6+. +0.3.71. +7.4.4. +7.4.5. +7.4.6. +7.7.4. +7.4.8. +7.9.4. +3.4.a. +7.4.b. +7.4.c. +7.4.r. +7.4.s. +7.4.t. +7.4.u. +7.4.1e. +7.4.1f. +7.4.1h. +7.4.1i. +7.4.1S. +7.4.1X. +3.4.1Y. +3.4.1Z. +3.4.1+. +3.4.1/. +3.4.20. +3.4.21. +7.4.22. +7.4.23. +7.4.24. +7.4.25. +3.4.26. +3.4.27. +7.4.28. +7.4.2a. +7.4.2b. +3.4.2k. +7.4.2l. +7.4.2m. +7.4.2r. +7.4.2y. +3.4.2N. +7.4.38. +3.4.39. +7.4.3d. +7.4.3h. +7.4.3i. +7.4.4O. +7.4.4Q. +7.4.4T. +7.4.4V. +7.4.5B. +7.4.5C. +7.4.5H. +7.4.5I. +7.4.5R. +7.4.5W. +1.5Y.4. +7.4.5/. +7.4.60. +3.4.62. +7.4.63. +3.4.6M. +7.4.6+. +7.4.71. +7.4.7J. +0.5.5. +0.5.6. +7.7.5. +0.5.8. +7.9.5. +0.5.a. +0.5.b. +g.c.5. +0.r.5. +0.s.5. +0.t.5. +0.u.5. +0.1f.5. +0.1h.5. +0.1i.5. +0.1C.5. +0.1S.5. +0.1T.5. +0.5.1X. +0.5.1Y. +0.5.1Z. +0.5.1+. +0.5.1/. +0.5.20. +0.5.21. +0.5.22. +0.5.23. +0.5.24. +0.5.25. +b.26.5. +0.5.27. +0.5.28. +0.5.2a. +0.5.2b. +d.5.2k. +0.5.2l. +6.2m.5. +4.2n.5. +2.2r.5. +9.2y.5. +9.2N.5. +b.36.5. +0.5.38. +5.39.5. +0.5.3d. +0.5.3h. +0.5.3i. +d.5.4O. +0.5.4Q. +5.4T.5. +0.5.5B. +0.5.5C. +0.5.5H. +0.5.5I. +d.5.5R. +0.5.5W. +1.5Y.5. +0.5.5/. +0.5.60. +2.62.5. +0.5.63. +0.5.6M. +0.5.6+. +0.5.71. +0.6.6. +7.7.6. +0.6.8. +7.9.6. +0.6.a. +0.6.b. +g.c.6. +0.r.6. +0.s.6. +0.t.6. +0.u.6. +0.1f.6. +0.1h.6. +0.1i.6. +0.1C.6. +0.1S.6. +0.1T.6. +0.6.1X. +0.6.1Y. +0.6.1Z. +0.6.1+. +0.6.1/. +0.6.20. +0.6.21. +0.6.22. +0.6.23. +0.6.24. +0.6.25. +b.26.6. +0.6.27. +0.6.28. +0.6.2a. +0.6.2b. +d.6.2k. +0.6.2l. +6.2m.6. +4.2n.6. +2.2r.6. +9.2y.6. +9.2N.6. +b.36.6. +0.6.38. +5.39.6. +0.6.3d. +0.6.3h. +0.6.3i. +0.6.4O. +0.6.4Q. +5.4T.6. +0.6.5B. +0.6.5C. +0.6.5H. +0.6.5I. +d.6.5R. +0.6.5W. +1.5Y.6. +0.6.5/. +0.6.60. +2.62.6. +0.6.63. +0.6.6M. +0.6.6+. +0.6.71. +7.7.7. +7.7.8. +7.9.7. +m.7.a. +7.7.b. +7.7.c. +7.7.r. +7.7.s. +7.7.t. +7.7.u. +7.7.1e. +7.7.1f. +7.7.1h. +7.7.1i. +7.7.1S. +7.7.1X. +3.7.1Y. +7.7.1Z. +3.7.1+. +7.7.1/. +7.7.20. +3.7.21. +3.7.22. +7.7.23. +3.7.24. +7.7.25. +7.7.26. +7.7.27. +7.7.28. +3.7.2a. +7.7.2b. +7.7.2k. +3.7.2l. +7.7.2m. +7.7.2r. +3.7.2y. +3.7.2N. +3.7.38. +7.7.39. +7.7.3d. +7.7.3h. +7.7.3i. +7.7.4O. +3.7.4Q. +7.7.4T. +7.7.4V. +7.7.5B. +7.7.5C. +7.7.5H. +3.7.5I. +7.7.5R. +3.7.5W. +1.5Y.7. +7.7.5/. +3.7.60. +7.7.62. +7.7.63. +7.7.6M. +7.7.6+. +7.7.71. +3.7.7J. +0.8.8. +7.9.8. +0.8.a. +0.8.b. +g.c.8. +0.r.8. +0.s.8. +0.t.8. +0.u.8. +0.1f.8. +0.1h.8. +0.1i.8. +0.1C.8. +0.1S.8. +0.1T.8. +0.8.1X. +0.8.1Y. +0.8.1Z. +0.8.1+. +0.8.1/. +0.8.20. +0.8.21. +0.8.22. +0.8.23. +0.8.24. +0.8.25. +b.26.8. +0.8.27. +0.8.28. +0.8.2a. +0.8.2b. +3.8.2k. +0.8.2l. +6.2m.8. +4.2n.8. +2.2r.8. +9.2y.8. +9.2N.8. +b.36.8. +0.8.38. +5.39.8. +0.8.3d. +0.8.3h. +0.8.3i. +a.3v.8. +0.8.4O. +0.8.4Q. +5.4T.8. +3.8.4V. +0.8.5B. +0.8.5C. +0.8.5H. +0.8.5I. +3.8.5R. +0.8.5W. +1.5Y.8. +0.8.5/. +0.8.60. +2.62.8. +0.8.63. +2.66.8. +0.8.6M. +0.8.6+. +0.8.71. +7.9.9. +3.9.a. +7.9.b. +7.9.c. +7.9.r. +7.9.s. +7.9.t. +7.9.u. +7.9.1e. +7.9.1f. +7.9.1h. +7.9.1i. +7.9.1S. +7.9.1X. +3.9.1Y. +7.9.1Z. +3.9.1+. +7.9.1/. +7.9.20. +3.9.21. +3.9.22. +7.9.23. +3.9.24. +7.9.25. +7.9.26. +7.9.27. +7.9.28. +3.9.2a. +7.9.2b. +7.9.2k. +3.9.2l. +7.9.2m. +7.9.2r. +3.9.2y. +3.9.2N. +3.9.38. +7.9.39. +7.9.3d. +7.9.3h. +7.9.3i. +7.9.4O. +3.9.4Q. +7.9.4T. +7.9.4V. +7.9.5B. +7.9.5C. +7.9.5H. +3.9.5I. +7.9.5R. +3.9.5W. +1.5Y.9. +7.9.5/. +3.9.60. +7.9.62. +7.9.63. +7.9.6M. +7.9.6+. +7.9.71. +3.9.7J. +0.a.a. +0.b.a. +g.c.a. +0.r.a. +0.s.a. +0.t.a. +0.u.a. +0.1f.a. +0.1h.a. +3.1i.a. +0.1C.a. +3.1S.a. +0.1T.a. +0.a.1X. +0.a.1Y. +0.a.1Z. +0.a.1+. +0.a.1/. +0.a.20. +0.a.21. +0.a.22. +0.a.23. +0.a.24. +0.a.25. +b.26.a. +0.a.27. +0.a.28. +0.a.2a. +0.a.2b. +3.a.2k. +0.a.2l. +6.2m.a. +4.2n.a. +2.2r.a. +9.2y.a. +9.2N.a. +b.36.a. +0.a.38. +5.39.a. +0.a.3d. +0.a.3h. +0.a.3i. +3.a.4O. +3.a.4Q. +5.4T.a. +3.a.4V. +0.a.5B. +0.a.5C. +0.a.5H. +0.a.5I. +3.a.5R. +0.a.5W. +1.5Y.a. +0.a.5/. +0.a.60. +2.62.a. +0.a.63. +2.66.a. +0.a.6M. +0.a.6+. +0.a.71. +0.b.b. +g.c.b. +0.r.b. +0.s.b. +0.t.b. +0.u.b. +0.1f.b. +0.1h.b. +0.1i.b. +0.1C.b. +0.1S.b. +d.1T.b. +0.b.1X. +0.b.1Y. +0.b.1Z. +0.b.1+. +0.b.1/. +3.b.20. +0.b.21. +0.b.22. +0.b.23. +0.b.24. +0.b.25. +b.26.b. +3.b.27. +0.b.28. +0.b.2a. +0.b.2b. +3.b.2k. +0.b.2l. +6.2m.b. +4.2n.b. +2.2r.b. +e.2x.b. +9.2y.b. +9.2N.b. +b.36.b. +0.b.38. +5.39.b. +0.b.3d. +3.b.3h. +0.b.3i. +0.b.4O. +0.b.4Q. +5.4T.b. +0.b.5B. +0.b.5C. +3.b.5H. +0.b.5I. +3.b.5R. +0.b.5W. +1.5Y.b. +0.b.5/. +3.b.60. +2.62.b. +0.b.63. +2.66.b. +0.b.6M. +0.b.6+. +0.b.71. +g.c.c. +g.c.d. +g.c.e. +g.c.f. +g.c.g. +g.c.h. +g.c.i. +g.c.j. +g.c.k. +g.c.l. +g.c.m. +2.n.c. +g.c.o. +g.c.p. +g.c.q. +g.c.r. +g.c.s. +g.c.t. +g.c.u. +g.c.v. +g.c.w. +g.c.x. +g.c.y. +g.c.z. +g.c.A. +g.c.B. +g.c.C. +g.c.D. +g.c.E. +g.c.F. +g.c.G. +3.c.H. +2.I.c. +2.J.c. +g.c.K. +g.c.L. +2.M.c. +g.c.N. +g.c.O. +g.c.P. +g.c.Q. +g.c.R. +3.S.c. +g.c.T. +g.c.U. +g.c.V. +g.c.W. +g.c.X. +g.c.Y. +g.c.Z. +g.c.+. +g.c./. +g.c.10. +g.c.11. +g.c.12. +g.c.13. +g.c.14. +g.c.15. +g.c.16. +g.c.17. +g.c.18. +g.c.19. +g.c.1a. +g.c.1b. +g.c.1c. +g.c.1d. +g.c.1e. +g.c.1f. +g.c.1g. +g.c.1h. +g.c.1i. +g.c.1j. +g.c.1k. +2.1l.c. +g.c.1m. +g.c.1n. +g.c.1o. +g.c.1p. +g.c.1q. +g.c.1r. +g.c.1s. +g.c.1t. +2.1u.c. +g.c.1v. +g.c.1w. +g.c.1x. +g.c.1y. +g.c.1z. +g.c.1A. +g.c.1B. +g.c.1C. +g.c.1D. +g.c.1E. +g.c.1F. +g.c.1G. +g.c.1H. +g.c.1I. +g.c.1J. +g.c.1K. +g.c.1L. +g.c.1M. +g.c.1N. +g.c.1O. +g.c.1P. +g.c.1Q. +g.c.1R. +g.c.1S. +g.c.1T. +g.c.1U. +g.c.1V. +g.c.1W. +g.c.1X. +g.c.1Y. +g.c.1Z. +g.c.1+. +g.c.1/. +g.c.20. +g.c.21. +g.c.22. +g.c.23. +g.c.24. +g.c.25. +g.c.26. +g.c.27. +g.c.28. +4.c.29. +g.c.2a. +g.c.2b. +4.c.2c. +4.c.2d. +4.c.2f. +4.c.2g. +4.c.2h. +4.c.2i. +4.c.2j. +g.c.2l. +6.2m.c. +4.2n.c. +1.c.2o. +g.c.2p. +2.2q.c. +2.2r.c. +7.2s.c. +7.2t.c. +2.2u.c. +g.c.2v. +g.c.2w. +d.2x.c. +9.2y.c. +g.c.2z. +g.c.2A. +9.2B.c. +g.c.2C. +g.c.2D. +g.c.2E. +g.c.2F. +g.c.2G. +9.2H.c. +g.c.2I. +g.c.2J. +g.c.2K. +4.c.2L. +c.2M.c. +9.2N.c. +2.2O.c. +4.c.2P. +9.2Q.c. +8.c.2R. +7.2S.c. +g.c.2T. +1.2U.c. +9.2V.c. +1.2W.c. +g.c.2X. +8.c.2Y. +8.c.2Z. +g.c.2+. +8.c.2/. +g.c.30. +8.c.31. +3.c.32. +8.c.33. +8.c.34. +g.c.35. +g.c.36. +g.c.37. +g.c.38. +g.c.39. +4.c.3a. +4.c.3c. +g.c.3d. +1.c.3e. +4.c.3f. +8.c.3g. +g.c.3h. +g.c.3i. +1.c.3j. +8.c.3k. +4.c.3l. +g.c.3m. +8.c.3n. +3.c.3o. +g.c.3p. +8.c.3q. +4.c.3r. +4.c.3s. +8.c.3t. +8.c.3u. +a.3v.c. +4.c.3w. +4.c.3x. +a.3y.c. +2.3z.c. +5.3A.c. +2.3B.c. +g.c.3C. +4.c.3D. +4.c.3E. +a.3F.c. +5.3G.c. +4.c.3H. +4.c.3I. +4.c.3J. +4.c.3K. +4.c.3L. +g.c.3M. +4.c.3N. +4.c.3O. +4.c.3P. +g.c.3Q. +4.c.3R. +4.c.3S. +1.c.3T. +4.c.3U. +4.c.3V. +4.c.3W. +4.c.3X. +4.c.3Y. +4.c.3Z. +1.c.3+. +1.c.3/. +5.40.c. +4.c.41. +4.c.42. +4.c.43. +4.c.44. +4.c.45. +g.c.47. +4.c.48. +4.c.49. +1.c.4a. +4.c.4b. +4.c.4c. +g.c.4d. +4.c.4e. +4.c.4f. +4.c.4g. +4.c.4h. +4.c.4i. +4.c.4j. +8.c.4k. +4.c.4l. +4.c.4m. +4.c.4n. +4.c.4o. +4.c.4p. +4.c.4q. +4.c.4r. +4.c.4s. +4.c.4t. +4.c.4u. +4.c.4v. +4.c.4w. +4.c.4x. +4.c.4y. +4.c.4z. +4.c.4A. +4.c.4B. +4.c.4C. +4.c.4D. +8.c.4E. +1.c.4F. +8.c.4G. +8.c.4H. +1.c.4I. +3.c.4J. +3.c.4K. +3.c.4L. +4.c.4M. +4.c.4N. +g.c.4O. +4.c.4P. +g.c.4Q. +1.c.4R. +4.c.4S. +4.c.4T. +8.c.4U. +g.c.4V. +4.c.4W. +8.c.4Y. +1.c.4Z. +4.c.4+. +4.c.4/. +1.c.50. +g.c.51. +g.c.52. +8.c.53. +8.c.54. +8.c.55. +8.c.56. +1.c.57. +8.c.58. +8.c.59. +a.5a.c. +2.5b.c. +4.c.5c. +5.5d.c. +8.c.5e. +8.c.5f. +4.c.5g. +8.c.5h. +4.c.5i. +4.c.5j. +4.c.5k. +4.c.5l. +4.c.5m. +4.c.5n. +4.c.5o. +4.c.5p. +4.c.5q. +4.c.5r. +1.c.5s. +4.c.5t. +4.c.5u. +4.c.5v. +1.c.5w. +1.c.5y. +1.c.5z. +8.c.5A. +g.c.5B. +g.c.5C. +1.c.5D. +8.c.5E. +g.c.5F. +8.c.5G. +g.c.5H. +g.c.5I. +f.c.5K. +3.c.5L. +8.c.5M. +8.c.5N. +g.c.5O. +8.c.5P. +4.c.5Q. +g.c.5R. +4.c.5S. +4.c.5T. +g.c.5U. +8.c.5V. +g.c.5W. +8.c.5X. +8.c.5Y. +g.c.5Z. +h.5+.c. +g.c.5/. +g.c.60. +4.c.61. +2.62.c. +4.c.64. +4.c.65. +2.66.c. +4.c.67. +4.c.68. +4.c.69. +4.c.6a. +a.6b.c. +4.c.6c. +4.c.6d. +4.c.6e. +4.c.6f. +4.c.6g. +4.c.6h. +4.c.6i. +4.c.6j. +4.c.6k. +4.c.6l. +4.c.6m. +4.c.6n. +4.c.6o. +4.c.6p. +4.c.6q. +4.c.6r. +4.c.6s. +4.c.6t. +4.c.6u. +4.c.6v. +4.c.6w. +4.c.6x. +4.c.6y. +4.c.6z. +4.c.6A. +4.c.6B. +4.c.6C. +4.c.6D. +4.c.6E. +g.c.6F. +4.c.6G. +4.c.6H. +4.c.6I. +4.c.6J. +4.c.6K. +8.c.6L. +g.c.6M. +4.c.6N. +4.c.6O. +4.c.6P. +4.c.6Q. +4.c.6R. +4.c.6S. +4.c.6T. +4.c.6U. +4.c.6V. +4.c.6W. +1.c.6X. +4.c.6Y. +1.c.6Z. +g.c.6+. +1.c.6/. +1.c.70. +g.c.71. +4.c.72. +4.c.73. +1.c.74. +4.c.75. +1.c.76. +1.c.77. +4.c.78. +5.79.c. +1.c.7a. +4.c.7b. +4.c.7c. +4.c.7d. +4.c.7e. +4.c.7f. +4.c.7g. +4.c.7h. +4.c.7i. +4.c.7j. +4.c.7k. +4.c.7l. +1.c.7m. +4.c.7o. +4.c.7p. +1.c.7r. +1.c.7s. +8.c.7t. +4.c.7u. +4.c.7v. +8.c.7w. +4.c.7x. +4.c.7y. +1.c.7z. +4.c.7A. +8.c.7B. +4.c.7C. +g.c.7D. +4.c.7E. +4.c.7F. +4.c.7G. +4.c.7H. +4.c.7I. +4.c.7J. +1.c.7K. +1.c.7L. +1.c.7M. +1.c.7N. +1.c.7O. +1.c.7P. +4.c.7Q. +8.c.7R. +4.c.7S. +4.c.7T. +1.c.7U. +8.c.7V. +4.c.7W. +4.c.7X. +4.c.7Y. +4.c.7Z. +1.c.7+. +1.c.7/. +1.c.80. +1.c.81. +1.c.82. +4.c.83. +1.c.84. +1.c.85. +h.86.c. +1.c.87. +1.c.88. +8.c.89. +4.c.8a. +3.c.8b. +4.c.8c. +8.c.8d. +4.c.8e. +4.c.8f. +4.c.8g. +4.c.8h. +4.c.8i. +4.c.8j. +4.c.8k. +4.c.8l. +4.c.8m. +4.c.8n. +4.c.8o. +4.c.8p. +4.c.8q. +4.c.8r. +4.c.8s. +4.c.8t. +8.c.8u. +8.c.8v. +8.c.8w. +8.c.8x. +4.c.8y. +4.c.8z. +3.c.8A. +3.c.8B. +8.c.8C. +4.c.8D. +4.c.8E. +4.c.8F. +4.c.8G. +3.c.8H. +3.c.8I. +4.c.8J. +4.c.8K. +4.c.8L. +4.c.8M. +4.c.8N. +4.c.8O. +4.c.8P. +g.c.2p. +g.c.2p. +8.c.3b. +1.c.46. +1.c.4X. +1.c.5x. +1.c.5J. +1.c.7n. +1.c.7q. +0.d.d. +0.d.e. +0.d.f. +0.d.g. +0.d.h. +0.d.i. +0.d.j. +0.d.k. +0.d.l. +0.d.m. +2.n.d. +0.d.o. +0.d.p. +0.d.q. +0.d.r. +0.d.s. +3.d.t. +0.d.u. +0.d.v. +0.d.w. +3.d.x. +0.d.y. +0.d.z. +0.d.A. +0.d.B. +0.d.C. +0.d.D. +0.d.E. +3.d.F. +0.d.G. +0.d.H. +2.I.d. +2.J.d. +0.d.K. +0.d.L. +2.M.d. +0.d.N. +0.d.O. +0.d.P. +0.d.Q. +0.d.R. +3.S.d. +b.T.d. +0.d.U. +0.d.V. +0.d.W. +0.d.X. +b.Y.d. +3.d.Z. +0.d.+. +0.d./. +0.d.10. +0.d.11. +0.d.12. +0.d.13. +0.d.14. +0.d.15. +0.d.16. +0.d.17. +3.d.18. +0.d.19. +3.d.1a. +0.d.1b. +0.d.1c. +3.d.1d. +0.d.1e. +3.d.1f. +0.d.1g. +3.d.1h. +3.d.1i. +3.d.1j. +0.d.1k. +2.1l.d. +0.d.1m. +0.d.1n. +0.d.1o. +0.d.1p. +3.d.1q. +3.d.1r. +3.d.1s. +0.d.1t. +2.1u.d. +0.d.1v. +3.d.1w. +3.d.1x. +3.d.1y. +0.d.1z. +0.d.1A. +0.d.1B. +0.d.1C. +0.d.1D. +0.d.1E. +0.d.1F. +0.d.1G. +0.d.1H. +0.d.1I. +0.d.1J. +0.d.1K. +0.d.1L. +0.d.1M. +0.d.1N. +0.d.1O. +0.d.1P. +3.d.1Q. +0.d.1R. +0.d.1S. +0.d.1T. +3.d.1U. +0.d.1V. +0.d.1W. +0.d.1X. +0.d.1Y. +0.d.1Z. +0.d.1+. +0.d.1/. +0.d.20. +0.d.21. +0.d.22. +0.d.23. +0.d.24. +0.d.25. +b.26.d. +0.d.27. +4.d.28. +0.d.2a. +0.d.2b. +0.d.2l. +6.2m.d. +0.d.2p. +2.2q.d. +2.2r.d. +7.2s.d. +7.2t.d. +2.2u.d. +3.d.2v. +0.d.2w. +e.2x.d. +9.2y.d. +0.d.2z. +3.d.2A. +9.2B.d. +0.d.2C. +0.d.2D. +3.d.2E. +0.d.2F. +0.d.2G. +9.2H.d. +0.d.2I. +3.d.2J. +0.d.2K. +c.2M.d. +9.2N.d. +2.2O.d. +9.2Q.d. +7.2S.d. +0.d.2T. +1.2U.d. +9.2V.d. +1.2W.d. +0.d.2X. +b.2+.d. +0.d.30. +0.d.35. +b.36.d. +0.d.37. +3.d.38. +5.39.d. +0.d.3d. +3.d.3h. +3.d.3i. +0.d.3m. +0.d.3p. +a.3v.d. +a.3y.d. +2.3z.d. +5.3A.d. +2.3B.d. +0.d.3C. +a.3F.d. +5.3G.d. +0.d.3M. +0.d.3Q. +5.40.d. +0.d.47. +0.d.4d. +0.d.4O. +3.d.4Q. +5.4T.d. +3.d.4V. +0.d.51. +0.d.52. +a.5a.d. +2.5b.d. +5.5d.d. +0.d.5B. +0.d.5C. +0.d.5F. +3.d.5H. +0.d.5I. +f.d.5K. +7.5M.d. +0.d.5O. +3.d.5R. +0.d.5U. +0.d.5W. +1.5Y.d. +0.d.5Z. +0.d.5/. +0.d.60. +2.62.d. +2.66.d. +a.6b.d. +c.6e.d. +0.d.6F. +0.d.6M. +0.d.6+. +3.d.71. +5.79.d. +3.d.7D. +0.d.2p. +0.d.2p. +0.e.e. +0.e.f. +0.e.g. +0.e.h. +0.e.i. +0.e.j. +0.e.k. +0.e.l. +3.e.m. +2.n.e. +0.e.o. +0.e.p. +0.e.q. +0.e.r. +0.e.s. +3.e.t. +0.e.u. +0.e.v. +0.e.w. +3.e.x. +0.e.y. +0.e.z. +0.e.A. +0.e.B. +0.e.C. +0.e.D. +0.e.E. +0.e.F. +0.e.G. +0.e.H. +2.I.e. +2.J.e. +0.e.K. +0.e.L. +2.M.e. +0.e.N. +0.e.O. +0.e.P. +0.e.Q. +0.e.R. +3.S.e. +b.T.e. +0.e.U. +0.e.V. +0.e.W. +0.e.X. +b.Y.e. +3.e.Z. +0.e.+. +0.e./. +0.e.10. +0.e.11. +0.e.12. +0.e.13. +0.e.14. +0.e.15. +0.e.16. +0.e.17. +3.e.18. +0.e.19. +0.e.1a. +0.e.1b. +0.e.1c. +3.e.1d. +0.e.1e. +3.e.1f. +0.e.1g. +3.e.1h. +3.e.1i. +3.e.1j. +0.e.1k. +2.1l.e. +0.e.1m. +0.e.1n. +0.e.1o. +0.e.1p. +0.e.1q. +0.e.1r. +0.e.1s. +3.e.1t. +2.1u.e. +0.e.1v. +3.e.1w. +0.e.1x. +0.e.1y. +0.e.1z. +0.e.1A. +0.e.1B. +0.e.1C. +0.e.1D. +0.e.1E. +0.e.1F. +0.e.1G. +0.e.1H. +0.e.1I. +0.e.1J. +0.e.1K. +0.e.1L. +0.e.1M. +0.e.1N. +0.e.1O. +0.e.1P. +3.e.1Q. +0.e.1R. +0.e.1S. +0.e.1T. +3.e.1U. +0.e.1V. +0.e.1W. +0.e.1X. +0.e.1Y. +0.e.1Z. +0.e.1+. +0.e.1/. +0.e.20. +0.e.21. +0.e.22. +0.e.23. +0.e.24. +0.e.25. +b.26.e. +0.e.27. +4.e.28. +0.e.2a. +0.e.2b. +0.e.2l. +6.2m.e. +0.e.2p. +2.2q.e. +2.2r.e. +7.2s.e. +7.2t.e. +2.2u.e. +3.e.2v. +0.e.2w. +e.2x.e. +9.2y.e. +0.e.2z. +3.e.2A. +9.2B.e. +0.e.2C. +0.e.2D. +3.e.2E. +0.e.2F. +0.e.2G. +9.2H.e. +0.e.2I. +3.e.2J. +0.e.2K. +c.2M.e. +9.2N.e. +2.2O.e. +9.2Q.e. +7.2S.e. +0.e.2T. +1.2U.e. +9.2V.e. +1.2W.e. +0.e.2X. +b.2+.e. +0.e.30. +0.e.35. +b.36.e. +0.e.37. +3.e.38. +5.39.e. +0.e.3d. +3.e.3h. +3.e.3i. +0.e.3m. +0.e.3p. +a.3v.e. +a.3y.e. +2.3z.e. +5.3A.e. +2.3B.e. +0.e.3C. +a.3F.e. +5.3G.e. +0.e.3M. +0.e.3Q. +5.40.e. +0.e.47. +0.e.4d. +0.e.4O. +3.e.4Q. +5.4T.e. +3.e.4V. +0.e.51. +0.e.52. +a.5a.e. +2.5b.e. +5.5d.e. +0.e.5B. +0.e.5C. +0.e.5F. +3.e.5H. +0.e.5I. +f.e.5K. +7.5M.e. +0.e.5O. +3.e.5R. +0.e.5U. +0.e.5W. +1.5Y.e. +0.e.5Z. +0.e.5/. +0.e.60. +2.62.e. +2.66.e. +a.6b.e. +c.6e.e. +0.e.6F. +0.e.6M. +0.e.6+. +3.e.71. +5.79.e. +3.e.7D. +0.e.2p. +0.e.2p. +0.f.f. +0.f.g. +0.f.h. +0.f.i. +0.f.j. +0.f.k. +0.f.l. +0.f.m. +2.n.f. +0.f.o. +0.f.p. +0.f.q. +0.f.r. +0.f.s. +3.f.t. +0.f.u. +0.f.v. +0.f.w. +3.f.x. +0.f.y. +0.f.z. +0.f.A. +0.f.B. +0.f.C. +0.f.D. +0.f.E. +0.f.F. +0.f.G. +0.f.H. +2.I.f. +2.J.f. +0.f.K. +0.f.L. +2.M.f. +0.f.N. +0.f.O. +0.f.P. +0.f.Q. +0.f.R. +3.S.f. +b.T.f. +0.f.U. +0.f.V. +0.f.W. +0.f.X. +b.Y.f. +0.f.Z. +0.f.+. +0.f./. +0.f.10. +0.f.11. +0.f.12. +0.f.13. +0.f.14. +0.f.15. +0.f.16. +0.f.17. +3.f.18. +0.f.19. +3.f.1a. +0.f.1b. +0.f.1c. +3.f.1d. +0.f.1e. +3.f.1f. +0.f.1g. +3.f.1h. +3.f.1i. +3.f.1j. +0.f.1k. +2.1l.f. +0.f.1m. +0.f.1n. +0.f.1o. +0.f.1p. +0.f.1q. +0.f.1r. +0.f.1s. +0.f.1t. +2.1u.f. +0.f.1v. +3.f.1w. +0.f.1x. +0.f.1y. +0.f.1z. +0.f.1A. +0.f.1B. +0.f.1C. +0.f.1D. +0.f.1E. +0.f.1F. +0.f.1G. +0.f.1H. +0.f.1I. +0.f.1J. +0.f.1K. +0.f.1L. +0.f.1M. +0.f.1N. +0.f.1O. +0.f.1P. +3.f.1Q. +0.f.1R. +0.f.1S. +0.f.1T. +0.f.1U. +0.f.1V. +0.f.1W. +0.f.1X. +0.f.1Y. +0.f.1Z. +0.f.1+. +0.f.1/. +0.f.20. +0.f.21. +0.f.22. +0.f.23. +0.f.24. +0.f.25. +b.26.f. +0.f.27. +4.f.28. +0.f.2a. +0.f.2b. +0.f.2l. +6.2m.f. +0.f.2p. +2.2q.f. +2.2r.f. +7.2s.f. +7.2t.f. +2.2u.f. +3.f.2v. +0.f.2w. +e.2x.f. +9.2y.f. +0.f.2z. +3.f.2A. +9.2B.f. +0.f.2C. +0.f.2D. +3.f.2E. +0.f.2F. +0.f.2G. +9.2H.f. +0.f.2I. +3.f.2J. +0.f.2K. +c.2M.f. +9.2N.f. +2.2O.f. +9.2Q.f. +7.2S.f. +0.f.2T. +1.2U.f. +9.2V.f. +1.2W.f. +0.f.2X. +d.2+.f. +0.f.30. +0.f.35. +b.36.f. +0.f.37. +3.f.38. +5.39.f. +0.f.3d. +3.f.3h. +0.f.3i. +0.f.3m. +0.f.3p. +a.3v.f. +a.3y.f. +2.3z.f. +5.3A.f. +2.3B.f. +0.f.3C. +a.3F.f. +5.3G.f. +0.f.3M. +0.f.3Q. +5.40.f. +0.f.47. +0.f.4d. +3.f.4O. +3.f.4Q. +5.4T.f. +3.f.4V. +0.f.51. +0.f.52. +a.5a.f. +2.5b.f. +5.5d.f. +0.f.5B. +0.f.5C. +0.f.5F. +3.f.5H. +0.f.5I. +f.f.5K. +7.5M.f. +0.f.5O. +3.f.5R. +0.f.5U. +0.f.5W. +1.5Y.f. +0.f.5Z. +0.f.5/. +0.f.60. +2.62.f. +2.66.f. +a.6b.f. +c.6e.f. +0.f.6F. +0.f.6M. +0.f.6+. +3.f.71. +5.79.f. +3.f.7D. +0.f.2p. +0.f.2p. +0.g.g. +0.g.h. +0.g.i. +0.g.j. +0.g.k. +0.g.l. +0.g.m. +2.n.g. +0.g.o. +0.g.p. +0.g.q. +0.g.r. +0.g.s. +0.g.t. +0.g.u. +0.g.v. +0.g.w. +3.g.x. +0.g.y. +0.g.z. +0.g.A. +0.g.B. +0.g.C. +0.g.D. +0.g.E. +0.g.F. +0.g.G. +0.g.H. +2.I.g. +2.J.g. +0.g.K. +0.g.L. +2.M.g. +0.g.N. +0.g.O. +0.g.P. +0.g.Q. +0.g.R. +3.S.g. +b.T.g. +0.g.U. +0.g.V. +0.g.W. +0.g.X. +b.Y.g. +0.g.Z. +0.g.+. +0.g./. +0.g.10. +0.g.11. +0.g.12. +0.g.13. +0.g.14. +0.g.15. +0.g.16. +0.g.17. +0.g.18. +0.g.19. +0.g.1a. +0.g.1b. +0.g.1c. +3.g.1d. +0.g.1e. +0.g.1f. +0.g.1g. +3.g.1h. +3.g.1i. +3.g.1j. +0.g.1k. +2.1l.g. +0.g.1m. +0.g.1n. +0.g.1o. +0.g.1p. +0.g.1q. +0.g.1r. +0.g.1s. +0.g.1t. +2.1u.g. +0.g.1v. +3.g.1w. +0.g.1x. +0.g.1y. +0.g.1z. +0.g.1A. +0.g.1B. +0.g.1C. +0.g.1D. +0.g.1E. +0.g.1F. +0.g.1G. +0.g.1H. +0.g.1I. +0.g.1J. +0.g.1K. +0.g.1L. +0.g.1M. +0.g.1N. +0.g.1O. +0.g.1P. +3.g.1Q. +0.g.1R. +0.g.1S. +0.g.1T. +3.g.1U. +0.g.1V. +0.g.1W. +0.g.1X. +0.g.1Y. +0.g.1Z. +0.g.1+. +0.g.1/. +0.g.20. +0.g.21. +0.g.22. +0.g.23. +0.g.24. +0.g.25. +b.26.g. +0.g.27. +4.g.28. +0.g.2a. +0.g.2b. +0.g.2l. +6.2m.g. +0.g.2p. +2.2q.g. +2.2r.g. +7.2s.g. +7.2t.g. +2.2u.g. +3.g.2v. +0.g.2w. +e.2x.g. +9.2y.g. +0.g.2z. +3.g.2A. +9.2B.g. +0.g.2C. +0.g.2D. +3.g.2E. +0.g.2F. +0.g.2G. +9.2H.g. +0.g.2I. +3.g.2J. +0.g.2K. +c.2M.g. +9.2N.g. +2.2O.g. +9.2Q.g. +7.2S.g. +0.g.2T. +1.2U.g. +9.2V.g. +1.2W.g. +0.g.2X. +d.2+.g. +0.g.30. +0.g.35. +b.36.g. +0.g.37. +0.g.38. +5.39.g. +0.g.3d. +3.g.3h. +0.g.3i. +0.g.3m. +0.g.3p. +a.3v.g. +a.3y.g. +2.3z.g. +5.3A.g. +2.3B.g. +0.g.3C. +a.3F.g. +5.3G.g. +0.g.3M. +0.g.3Q. +5.40.g. +0.g.47. +0.g.4d. +0.g.4O. +3.g.4Q. +5.4T.g. +3.g.4V. +0.g.51. +0.g.52. +a.5a.g. +2.5b.g. +5.5d.g. +0.g.5B. +0.g.5C. +0.g.5F. +3.g.5H. +0.g.5I. +f.g.5K. +7.5M.g. +0.g.5O. +0.g.5R. +0.g.5U. +0.g.5W. +1.5Y.g. +0.g.5Z. +0.g.5/. +0.g.60. +2.62.g. +2.66.g. +a.6b.g. +c.6e.g. +0.g.6F. +0.g.6M. +0.g.6+. +3.g.71. +5.79.g. +3.g.7D. +0.g.2p. +0.g.2p. +3.h.h. +3.h.i. +3.h.j. +3.h.k. +0.h.l. +0.h.m. +2.n.h. +0.h.o. +0.h.p. +3.h.q. +0.h.r. +0.h.s. +0.h.t. +0.h.u. +0.h.v. +0.h.w. +0.h.x. +0.h.y. +3.h.z. +0.h.A. +0.h.B. +0.h.C. +0.h.D. +0.h.E. +0.h.F. +0.h.G. +0.h.H. +2.I.h. +2.J.h. +0.h.K. +3.h.L. +2.M.h. +0.h.N. +3.h.O. +0.h.P. +0.h.Q. +0.h.R. +3.S.h. +b.T.h. +3.h.U. +3.h.V. +3.h.W. +0.h.X. +b.Y.h. +0.h.Z. +3.h.+. +3.h./. +0.h.10. +0.h.11. +0.h.12. +0.h.13. +0.h.14. +0.h.15. +0.h.16. +0.h.17. +0.h.18. +0.h.19. +0.h.1a. +0.h.1b. +0.h.1c. +0.h.1d. +3.h.1e. +0.h.1f. +3.h.1g. +0.h.1h. +0.h.1i. +3.h.1j. +0.h.1k. +2.1l.h. +0.h.1m. +0.h.1n. +0.h.1o. +0.h.1p. +0.h.1q. +0.h.1r. +0.h.1s. +0.h.1t. +2.1u.h. +0.h.1v. +0.h.1w. +0.h.1x. +0.h.1y. +0.h.1z. +0.h.1A. +0.h.1B. +0.h.1C. +0.h.1D. +3.h.1E. +0.h.1F. +0.h.1G. +0.h.1H. +0.h.1I. +0.h.1J. +0.h.1K. +0.h.1L. +0.h.1M. +0.h.1N. +0.h.1O. +0.h.1P. +0.h.1Q. +0.h.1R. +3.h.1S. +3.h.1T. +0.h.1U. +0.h.1V. +0.h.1W. +0.h.1X. +3.h.1Y. +0.h.1Z. +3.h.1+. +0.h.1/. +0.h.20. +3.h.21. +0.h.22. +0.h.23. +0.h.24. +0.h.25. +b.26.h. +3.h.27. +4.h.28. +0.h.2a. +0.h.2b. +0.h.2l. +6.2m.h. +0.h.2p. +2.2q.h. +2.2r.h. +7.2s.h. +7.2t.h. +2.2u.h. +3.h.2v. +0.h.2w. +e.2x.h. +9.2y.h. +0.h.2z. +3.h.2A. +9.2B.h. +0.h.2C. +0.h.2D. +0.h.2E. +0.h.2F. +0.h.2G. +9.2H.h. +0.h.2I. +3.h.2J. +3.h.2K. +c.2M.h. +9.2N.h. +2.2O.h. +9.2Q.h. +7.2S.h. +0.h.2T. +1.2U.h. +9.2V.h. +1.2W.h. +0.h.2X. +b.2+.h. +0.h.30. +3.h.35. +b.36.h. +0.h.37. +3.h.38. +5.39.h. +0.h.3d. +3.h.3h. +0.h.3i. +0.h.3m. +0.h.3p. +a.3v.h. +a.3y.h. +2.3z.h. +5.3A.h. +2.3B.h. +0.h.3C. +a.3F.h. +5.3G.h. +0.h.3M. +0.h.3Q. +5.40.h. +0.h.47. +0.h.4d. +0.h.4O. +3.h.4Q. +5.4T.h. +3.h.4V. +3.h.51. +0.h.52. +a.5a.h. +2.5b.h. +5.5d.h. +0.h.5B. +0.h.5C. +0.h.5F. +3.h.5H. +0.h.5I. +f.h.5K. +7.5M.h. +3.h.5O. +0.h.5R. +0.h.5U. +0.h.5W. +1.5Y.h. +0.h.5Z. +0.h.5/. +0.h.60. +2.62.h. +2.66.h. +a.6b.h. +c.6e.h. +0.h.6F. +0.h.6M. +0.h.6+. +3.h.71. +5.79.h. +3.h.7D. +0.h.2p. +0.h.2p. +0.i.i. +0.i.j. +0.i.k. +0.i.l. +3.i.m. +2.n.i. +0.i.o. +0.i.p. +0.i.q. +0.i.r. +0.i.s. +3.i.t. +0.i.u. +0.i.v. +0.i.w. +3.i.x. +0.i.y. +0.i.z. +0.i.A. +0.i.B. +0.i.C. +0.i.D. +0.i.E. +0.i.F. +0.i.G. +0.i.H. +2.I.i. +2.J.i. +0.i.K. +0.i.L. +2.M.i. +0.i.N. +0.i.O. +0.i.P. +0.i.Q. +0.i.R. +3.S.i. +b.T.i. +0.i.U. +0.i.V. +0.i.W. +0.i.X. +b.Y.i. +3.i.Z. +0.i.+. +0.i./. +0.i.10. +0.i.11. +0.i.12. +0.i.13. +0.i.14. +0.i.15. +0.i.16. +0.i.17. +3.i.18. +0.i.19. +3.i.1a. +0.i.1b. +0.i.1c. +3.i.1d. +0.i.1e. +0.i.1f. +0.i.1g. +3.i.1h. +3.i.1i. +3.i.1j. +0.i.1k. +2.1l.i. +0.i.1m. +0.i.1n. +0.i.1o. +0.i.1p. +0.i.1q. +0.i.1r. +3.i.1s. +0.i.1t. +2.1u.i. +0.i.1v. +3.i.1w. +0.i.1x. +0.i.1y. +0.i.1z. +0.i.1A. +0.i.1B. +0.i.1C. +0.i.1D. +0.i.1E. +0.i.1F. +0.i.1G. +0.i.1H. +0.i.1I. +0.i.1J. +0.i.1K. +0.i.1L. +0.i.1M. +0.i.1N. +0.i.1O. +0.i.1P. +0.i.1Q. +0.i.1R. +0.i.1S. +3.i.1T. +0.i.1U. +0.i.1V. +0.i.1W. +0.i.1X. +0.i.1Y. +0.i.1Z. +0.i.1+. +0.i.1/. +0.i.20. +0.i.21. +0.i.22. +0.i.23. +0.i.24. +0.i.25. +b.26.i. +0.i.27. +4.i.28. +0.i.2a. +0.i.2b. +0.i.2l. +6.2m.i. +0.i.2p. +2.2q.i. +2.2r.i. +7.2s.i. +7.2t.i. +2.2u.i. +3.i.2v. +0.i.2w. +e.2x.i. +9.2y.i. +0.i.2z. +3.i.2A. +9.2B.i. +0.i.2C. +0.i.2D. +3.i.2E. +0.i.2F. +0.i.2G. +9.2H.i. +0.i.2I. +3.i.2J. +0.i.2K. +c.2M.i. +9.2N.i. +2.2O.i. +9.2Q.i. +7.2S.i. +0.i.2T. +1.2U.i. +9.2V.i. +1.2W.i. +0.i.2X. +d.2+.i. +0.i.30. +0.i.35. +b.36.i. +0.i.37. +3.i.38. +5.39.i. +0.i.3d. +3.i.3h. +3.i.3i. +0.i.3m. +0.i.3p. +a.3v.i. +a.3y.i. +2.3z.i. +5.3A.i. +2.3B.i. +0.i.3C. +a.3F.i. +5.3G.i. +0.i.3M. +0.i.3Q. +5.40.i. +0.i.47. +0.i.4d. +3.i.4O. +3.i.4Q. +5.4T.i. +3.i.4V. +0.i.51. +0.i.52. +a.5a.i. +2.5b.i. +5.5d.i. +0.i.5B. +0.i.5C. +0.i.5F. +3.i.5H. +0.i.5I. +f.i.5K. +7.5M.i. +0.i.5O. +3.i.5R. +0.i.5U. +0.i.5W. +1.5Y.i. +0.i.5Z. +0.i.5/. +0.i.60. +2.62.i. +2.66.i. +a.6b.i. +c.6e.i. +0.i.6F. +0.i.6M. +0.i.6+. +3.i.71. +5.79.i. +3.i.7D. +0.i.2p. +0.i.2p. +0.j.j. +0.k.j. +0.j.l. +3.j.m. +2.n.j. +0.j.o. +0.j.p. +0.j.q. +3.j.r. +0.j.s. +3.j.t. +0.j.u. +0.j.v. +3.j.w. +3.j.x. +0.j.y. +0.j.z. +0.j.A. +0.j.B. +0.j.C. +0.j.D. +0.j.E. +0.j.F. +3.j.G. +0.j.H. +2.I.j. +2.J.j. +0.j.K. +0.j.L. +2.M.j. +0.j.N. +0.j.O. +0.j.P. +3.j.Q. +0.j.R. +3.S.j. +b.T.j. +3.j.U. +0.j.V. +0.j.W. +0.j.X. +b.Y.j. +3.j.Z. +0.j.+. +0.j./. +0.j.10. +0.j.11. +0.j.12. +0.j.13. +0.j.14. +0.j.15. +0.j.16. +0.j.17. +3.j.18. +0.j.19. +3.j.1a. +3.j.1b. +0.j.1c. +3.j.1d. +0.j.1e. +3.j.1f. +0.j.1g. +3.j.1h. +3.j.1i. +3.j.1j. +3.j.1k. +2.1l.j. +0.j.1m. +0.j.1n. +0.j.1o. +3.j.1p. +0.j.1q. +0.j.1r. +3.j.1s. +0.j.1t. +2.1u.j. +0.j.1v. +3.j.1w. +3.j.1x. +0.j.1y. +0.j.1z. +0.j.1A. +0.j.1B. +0.j.1C. +0.j.1D. +0.j.1E. +0.j.1F. +0.j.1G. +0.j.1H. +0.j.1I. +0.j.1J. +0.j.1K. +0.j.1L. +0.j.1M. +0.j.1N. +0.j.1O. +0.j.1P. +3.j.1Q. +0.j.1R. +3.j.1S. +3.j.1T. +3.j.1U. +0.j.1V. +0.j.1W. +0.j.1X. +0.j.1Y. +0.j.1Z. +0.j.1+. +0.j.1/. +0.j.20. +0.j.21. +0.j.22. +0.j.23. +0.j.24. +0.j.25. +b.26.j. +3.j.27. +4.j.28. +0.j.2a. +0.j.2b. +0.j.2l. +6.2m.j. +0.j.2p. +2.2q.j. +2.2r.j. +7.2s.j. +7.2t.j. +2.2u.j. +0.j.2v. +0.j.2w. +e.2x.j. +9.2y.j. +0.j.2z. +3.j.2A. +9.2B.j. +0.j.2C. +0.j.2D. +3.j.2E. +3.j.2F. +0.j.2G. +9.2H.j. +3.j.2I. +3.j.2J. +0.j.2K. +c.2M.j. +9.2N.j. +2.2O.j. +9.2Q.j. +7.2S.j. +0.j.2T. +1.2U.j. +9.2V.j. +1.2W.j. +0.j.2X. +d.2+.j. +0.j.30. +0.j.35. +3.36.j. +0.j.37. +3.j.38. +5.39.j. +0.j.3d. +3.j.3h. +3.j.3i. +0.j.3m. +0.j.3p. +a.3v.j. +a.3y.j. +2.3z.j. +5.3A.j. +2.3B.j. +0.j.3C. +a.3F.j. +5.3G.j. +0.j.3M. +0.j.3Q. +5.40.j. +0.j.47. +0.j.4d. +3.j.4O. +3.j.4Q. +5.4T.j. +3.j.4V. +0.j.51. +0.j.52. +a.5a.j. +2.5b.j. +5.5d.j. +0.j.5B. +0.j.5C. +0.j.5F. +3.j.5H. +0.j.5I. +f.j.5K. +7.5M.j. +0.j.5O. +3.j.5R. +0.j.5U. +3.j.5W. +1.5Y.j. +0.j.5Z. +0.j.5/. +0.j.60. +2.62.j. +2.66.j. +a.6b.j. +c.6e.j. +0.j.6F. +0.j.6M. +0.j.6+. +3.j.71. +5.79.j. +3.j.7D. +0.j.2p. +0.j.2p. +0.k.k. +0.k.l. +3.k.m. +2.n.k. +0.k.o. +0.k.p. +0.k.q. +3.k.r. +0.k.s. +3.k.t. +0.k.u. +0.k.v. +0.k.w. +3.k.x. +0.k.y. +0.k.z. +0.k.A. +0.k.B. +0.k.C. +0.k.D. +0.k.E. +0.k.F. +0.k.G. +0.k.H. +2.I.k. +2.J.k. +0.k.K. +0.k.L. +2.M.k. +0.k.N. +0.k.O. +0.k.P. +0.k.Q. +0.k.R. +3.S.k. +b.T.k. +0.k.U. +0.k.V. +0.k.W. +0.k.X. +b.Y.k. +0.k.Z. +0.k.+. +0.k./. +0.k.10. +0.k.11. +0.k.12. +0.k.13. +0.k.14. +0.k.15. +0.k.16. +0.k.17. +3.k.18. +0.k.19. +3.k.1a. +0.k.1b. +0.k.1c. +3.k.1d. +0.k.1e. +3.k.1f. +0.k.1g. +3.k.1h. +3.k.1i. +3.k.1j. +0.k.1k. +2.1l.k. +0.k.1m. +0.k.1n. +0.k.1o. +0.k.1p. +0.k.1q. +0.k.1r. +3.k.1s. +0.k.1t. +2.1u.k. +0.k.1v. +3.k.1w. +0.k.1x. +0.k.1y. +0.k.1z. +0.k.1A. +0.k.1B. +0.k.1C. +0.k.1D. +0.k.1E. +0.k.1F. +0.k.1G. +0.k.1H. +0.k.1I. +0.k.1J. +0.k.1K. +0.k.1L. +0.k.1M. +3.k.1N. +0.k.1O. +0.k.1P. +3.k.1Q. +0.k.1R. +0.k.1S. +3.k.1T. +3.k.1U. +0.k.1V. +0.k.1W. +0.k.1X. +0.k.1Y. +0.k.1Z. +0.k.1+. +0.k.1/. +0.k.20. +0.k.21. +0.k.22. +0.k.23. +0.k.24. +0.k.25. +b.26.k. +0.k.27. +0.k.28. +4.k.29. +0.k.2a. +0.k.2b. +4.k.2c. +4.k.2d. +4.k.2f. +4.k.2g. +4.k.2h. +4.k.2i. +4.k.2j. +0.k.2l. +6.2m.k. +1.k.2o. +0.k.2p. +2.2q.k. +2.2r.k. +7.2s.k. +7.2t.k. +2.2u.k. +3.k.2v. +0.k.2w. +e.2x.k. +9.2y.k. +0.k.2z. +3.k.2A. +9.2B.k. +0.k.2C. +0.k.2D. +3.k.2E. +3.k.2F. +0.k.2G. +9.2H.k. +3.k.2I. +3.k.2J. +0.k.2K. +4.k.2L. +c.2M.k. +9.2N.k. +2.2O.k. +4.k.2P. +9.2Q.k. +8.k.2R. +7.2S.k. +0.k.2T. +1.2U.k. +9.2V.k. +1.2W.k. +0.k.2X. +8.k.2Y. +8.k.2Z. +d.2+.k. +8.k.2/. +0.k.30. +3.k.31. +8.k.32. +8.k.33. +8.k.34. +0.k.35. +b.36.k. +0.k.37. +3.k.38. +5.39.k. +4.k.3a. +4.k.3c. +0.k.3d. +1.k.3e. +4.k.3f. +3.k.3g. +3.k.3h. +3.k.3i. +1.k.3j. +3.k.3k. +4.k.3l. +0.k.3m. +3.k.3n. +3.k.3o. +0.k.3p. +3.k.3q. +4.k.3r. +4.k.3s. +8.k.3t. +8.k.3u. +a.3v.k. +4.k.3w. +4.k.3x. +a.3y.k. +2.3z.k. +5.3A.k. +2.3B.k. +0.k.3C. +4.k.3D. +4.k.3E. +a.3F.k. +5.3G.k. +4.k.3H. +4.k.3I. +4.k.3J. +4.k.3K. +4.k.3L. +0.k.3M. +4.k.3N. +4.k.3O. +4.k.3P. +0.k.3Q. +4.k.3R. +4.k.3S. +1.k.3T. +4.k.3U. +4.k.3V. +4.k.3W. +4.k.3X. +4.k.3Y. +4.k.3Z. +1.k.3+. +1.k.3/. +5.40.k. +4.k.41. +4.k.42. +4.k.43. +4.k.44. +4.k.45. +0.k.47. +4.k.48. +4.k.49. +1.k.4a. +4.k.4b. +4.k.4c. +0.k.4d. +4.k.4e. +4.k.4f. +4.k.4g. +4.k.4h. +4.k.4i. +4.k.4j. +8.k.4k. +4.k.4l. +4.k.4m. +4.k.4n. +4.k.4o. +4.k.4p. +4.k.4q. +4.k.4r. +4.k.4s. +4.k.4t. +4.k.4u. +4.k.4v. +4.k.4w. +4.k.4x. +4.k.4y. +4.k.4z. +4.k.4A. +4.k.4B. +4.k.4C. +4.k.4D. +4.k.4E. +1.k.4F. +8.k.4G. +8.k.4H. +1.k.4I. +8.k.4J. +8.k.4K. +8.k.4L. +4.k.4M. +4.k.4N. +3.k.4O. +4.k.4P. +3.k.4Q. +1.k.4R. +4.k.4S. +4.k.4T. +8.k.4U. +3.k.4V. +4.k.4W. +3.k.4Y. +1.k.4Z. +4.k.4+. +4.k.4/. +1.k.50. +0.k.51. +0.k.52. +8.k.53. +3.k.54. +8.k.55. +8.k.56. +1.k.57. +3.k.58. +8.k.59. +a.5a.k. +2.5b.k. +4.k.5c. +5.5d.k. +3.k.5e. +8.k.5f. +4.k.5g. +3.k.5h. +4.k.5i. +4.k.5j. +4.k.5k. +4.k.5l. +4.k.5m. +4.k.5n. +4.k.5o. +4.k.5p. +4.k.5q. +4.k.5r. +1.k.5s. +4.k.5t. +4.k.5u. +4.k.5v. +1.k.5w. +1.k.5y. +1.k.5z. +8.k.5A. +0.k.5B. +0.k.5C. +1.k.5D. +8.k.5E. +0.k.5F. +8.k.5G. +3.k.5H. +0.k.5I. +f.k.5K. +8.k.5L. +8.k.5M. +8.k.5N. +0.k.5O. +8.k.5P. +4.k.5Q. +3.k.5R. +4.k.5S. +4.k.5T. +0.k.5U. +8.k.5V. +0.k.5W. +8.k.5X. +8.k.5Y. +0.k.5Z. +0.k.5/. +0.k.60. +4.k.61. +2.62.k. +4.k.64. +4.k.65. +2.66.k. +4.k.67. +4.k.68. +4.k.69. +4.k.6a. +a.6b.k. +4.k.6c. +4.k.6d. +4.k.6e. +4.k.6f. +4.k.6g. +4.k.6h. +4.k.6i. +4.k.6j. +4.k.6k. +4.k.6l. +4.k.6m. +4.k.6n. +4.k.6o. +4.k.6p. +4.k.6q. +4.k.6r. +4.k.6s. +4.k.6t. +4.k.6u. +4.k.6v. +4.k.6w. +4.k.6x. +4.k.6y. +4.k.6z. +4.k.6A. +4.k.6B. +4.k.6C. +4.k.6D. +4.k.6E. +0.k.6F. +4.k.6G. +4.k.6H. +4.k.6I. +4.k.6J. +4.k.6K. +8.k.6L. +0.k.6M. +4.k.6N. +4.k.6O. +4.k.6P. +4.k.6Q. +4.k.6R. +4.k.6S. +4.k.6T. +4.k.6U. +4.k.6V. +4.k.6W. +1.k.6X. +4.k.6Y. +1.k.6Z. +0.k.6+. +1.k.6/. +1.k.70. +3.k.71. +4.k.72. +4.k.73. +1.k.74. +4.k.75. +1.k.76. +1.k.77. +4.k.78. +5.79.k. +1.k.7a. +4.k.7b. +4.k.7c. +4.k.7d. +4.k.7e. +4.k.7f. +4.k.7g. +4.k.7h. +4.k.7i. +4.k.7j. +4.k.7k. +4.k.7l. +1.k.7m. +4.k.7o. +4.k.7p. +1.k.7r. +1.k.7s. +3.k.7t. +4.k.7u. +4.k.7v. +4.k.7w. +4.k.7x. +4.k.7y. +1.k.7z. +4.k.7A. +8.k.7B. +4.k.7C. +3.k.7D. +4.k.7E. +4.k.7F. +4.k.7G. +4.k.7H. +4.k.7I. +4.k.7J. +1.k.7K. +1.k.7L. +1.k.7M. +1.k.7N. +1.k.7O. +1.k.7P. +4.k.7Q. +3.k.7R. +4.k.7S. +4.k.7T. +1.k.7U. +3.k.7V. +4.k.7W. +4.k.7X. +4.k.7Y. +4.k.7Z. +1.k.7+. +1.k.7/. +1.k.80. +1.k.81. +1.k.82. +4.k.83. +1.k.84. +1.k.85. +1.k.87. +1.k.88. +8.k.89. +4.k.8a. +3.k.8b. +4.k.8c. +8.k.8d. +4.k.8e. +4.k.8f. +4.k.8g. +4.k.8h. +4.k.8i. +4.k.8j. +4.k.8k. +4.k.8l. +4.k.8m. +4.k.8n. +4.k.8o. +4.k.8p. +4.k.8q. +4.k.8r. +4.k.8s. +4.k.8t. +4.k.8u. +4.k.8v. +4.k.8w. +4.k.8x. +4.k.8y. +4.k.8z. +4.k.8A. +4.k.8B. +4.k.8C. +4.k.8D. +4.k.8E. +4.k.8F. +4.k.8G. +4.k.8H. +4.k.8I. +4.k.8J. +4.k.8K. +4.k.8L. +4.k.8M. +4.k.8N. +4.k.8O. +4.k.8P. +0.k.2p. +0.k.2p. +8.k.3b. +1.k.46. +1.k.4X. +1.k.5x. +1.k.5J. +1.k.7n. +1.k.7q. +0.l.l. +3.l.m. +2.n.l. +0.l.o. +0.p.l. +0.l.q. +0.r.l. +0.s.l. +0.t.l. +0.u.l. +0.v.l. +0.w.l. +0.x.l. +0.y.l. +0.l.z. +0.l.A. +0.l.B. +0.l.C. +0.l.D. +0.l.E. +0.l.F. +0.G.l. +0.l.H. +2.I.l. +2.J.l. +0.l.K. +0.l.L. +2.M.l. +0.l.N. +0.l.O. +0.l.P. +0.l.Q. +0.l.R. +3.S.l. +b.T.l. +0.l.U. +0.l.V. +0.l.W. +0.l.X. +b.Y.l. +0.l.Z. +0.l.+. +0.l./. +0.l.10. +0.l.11. +0.l.12. +0.l.13. +0.l.14. +0.l.15. +0.l.16. +0.l.17. +0.l.18. +0.l.19. +0.l.1a. +0.l.1b. +0.l.1c. +3.l.1d. +0.l.1e. +0.l.1f. +0.l.1g. +3.l.1h. +3.l.1i. +3.l.1j. +0.l.1k. +2.1l.l. +0.l.1m. +0.l.1n. +0.l.1o. +0.l.1p. +0.l.1q. +0.l.1r. +0.l.1s. +0.l.1t. +2.1u.l. +0.l.1v. +3.l.1w. +0.l.1x. +0.l.1y. +0.l.1z. +0.l.1A. +0.1B.l. +0.l.1C. +0.l.1D. +0.l.1E. +0.l.1F. +0.l.1G. +0.l.1H. +0.l.1I. +0.l.1J. +0.l.1K. +0.l.1L. +0.l.1M. +0.l.1N. +0.l.1O. +0.l.1P. +0.l.1Q. +0.l.1R. +0.l.1S. +0.l.1T. +3.l.1U. +0.l.1V. +0.l.1W. +0.l.1X. +0.l.1Y. +0.l.1Z. +0.l.1+. +0.l.1/. +0.l.20. +0.l.21. +0.l.22. +0.l.23. +0.l.24. +0.l.25. +b.26.l. +0.l.27. +4.l.28. +0.l.2a. +0.l.2b. +0.l.2l. +6.2m.l. +0.l.2p. +2.2q.l. +2.2r.l. +7.2s.l. +7.2t.l. +2.2u.l. +0.l.2v. +0.l.2w. +e.2x.l. +9.2y.l. +0.l.2z. +3.l.2A. +9.2B.l. +0.l.2C. +0.l.2D. +3.l.2E. +0.l.2F. +0.l.2G. +9.2H.l. +0.l.2I. +3.l.2J. +0.l.2K. +c.2M.l. +9.2N.l. +2.2O.l. +9.2Q.l. +7.2S.l. +0.l.2T. +1.2U.l. +9.2V.l. +1.2W.l. +0.l.2X. +d.2+.l. +0.l.30. +0.l.35. +b.36.l. +0.l.37. +0.l.38. +5.39.l. +0.l.3d. +3.l.3h. +0.l.3i. +0.l.3m. +0.l.3p. +a.3v.l. +a.3y.l. +2.3z.l. +5.3A.l. +2.3B.l. +0.l.3C. +a.3F.l. +5.3G.l. +0.l.3M. +0.l.3Q. +5.40.l. +0.l.47. +0.l.4d. +0.l.4O. +3.l.4Q. +5.4T.l. +3.l.4V. +0.l.51. +0.l.52. +a.5a.l. +2.5b.l. +5.5d.l. +0.l.5B. +0.l.5C. +0.l.5F. +3.l.5H. +0.l.5I. +f.l.5K. +7.5M.l. +0.l.5O. +3.l.5R. +0.l.5U. +0.l.5W. +1.5Y.l. +0.l.5Z. +0.l.5/. +0.l.60. +2.62.l. +2.66.l. +a.6b.l. +c.6e.l. +0.l.6F. +0.l.6M. +0.l.6+. +3.l.71. +5.79.l. +3.l.7D. +0.l.2p. +0.l.2p. +3.m.m. +2.n.m. +0.m.o. +0.p.m. +0.m.q. +0.r.m. +0.s.m. +0.t.m. +0.u.m. +0.v.m. +0.w.m. +0.x.m. +0.y.m. +0.m.z. +0.m.A. +0.m.B. +0.m.C. +0.m.D. +0.m.E. +3.m.F. +0.G.m. +0.m.H. +2.I.m. +2.J.m. +0.m.K. +0.m.L. +2.M.m. +0.m.N. +0.m.O. +0.m.P. +0.m.Q. +0.m.R. +3.S.m. +b.T.m. +0.m.U. +0.m.V. +0.m.W. +0.m.X. +b.Y.m. +0.m.Z. +0.m.+. +0.m./. +0.m.10. +0.m.11. +0.m.12. +0.m.13. +0.m.14. +0.m.15. +0.m.16. +0.m.17. +3.m.18. +0.m.19. +0.m.1a. +0.m.1b. +0.m.1c. +3.m.1d. +0.m.1e. +0.m.1f. +0.m.1g. +3.m.1h. +3.m.1i. +3.m.1j. +0.m.1k. +2.1l.m. +0.m.1m. +0.m.1n. +0.m.1o. +0.m.1p. +0.m.1q. +0.m.1r. +0.m.1s. +0.m.1t. +2.1u.m. +0.m.1v. +3.m.1w. +0.m.1x. +0.m.1y. +0.m.1z. +0.m.1A. +0.1B.m. +0.m.1C. +0.m.1D. +0.m.1E. +0.m.1F. +0.m.1G. +0.m.1H. +0.m.1I. +0.m.1J. +0.m.1K. +0.m.1L. +0.m.1M. +0.m.1N. +0.m.1O. +0.m.1P. +0.m.1Q. +0.m.1R. +0.m.1S. +0.m.1T. +0.m.1U. +0.m.1V. +0.m.1W. +0.m.1X. +0.m.1Y. +0.m.1Z. +0.m.1+. +0.m.1/. +0.m.20. +0.m.21. +0.m.22. +0.m.23. +0.m.24. +0.m.25. +b.26.m. +0.m.27. +0.m.28. +0.m.2a. +0.m.2b. +0.m.2l. +6.2m.m. +0.m.2p. +2.2q.m. +2.2r.m. +7.2s.m. +7.2t.m. +2.2u.m. +0.m.2v. +0.m.2w. +e.2x.m. +9.2y.m. +0.m.2z. +3.m.2A. +9.2B.m. +0.m.2C. +0.m.2D. +0.m.2E. +0.m.2F. +0.m.2G. +9.2H.m. +0.m.2I. +3.m.2J. +0.m.2K. +c.2M.m. +9.2N.m. +2.2O.m. +9.2Q.m. +7.2S.m. +0.m.2T. +1.2U.m. +9.2V.m. +1.2W.m. +0.m.2X. +d.2+.m. +0.m.30. +0.m.35. +b.36.m. +0.m.37. +0.m.38. +5.39.m. +0.m.3d. +3.m.3h. +0.m.3i. +0.m.3m. +0.m.3p. +a.3v.m. +a.3y.m. +2.3z.m. +5.3A.m. +2.3B.m. +0.m.3C. +a.3F.m. +5.3G.m. +0.m.3M. +0.m.3Q. +5.40.m. +0.m.47. +0.m.4d. +0.m.4O. +3.m.4Q. +5.4T.m. +3.m.4V. +0.m.51. +0.m.52. +a.5a.m. +2.5b.m. +5.5d.m. +0.m.5B. +0.m.5C. +0.m.5F. +3.m.5H. +0.m.5I. +f.m.5K. +7.5M.m. +0.m.5O. +3.m.5R. +0.m.5U. +0.m.5W. +1.5Y.m. +0.m.5Z. +0.m.5/. +0.m.60. +2.62.m. +2.66.m. +a.6b.m. +c.6e.m. +0.m.6F. +0.m.6M. +0.m.6+. +3.m.71. +5.79.m. +3.m.7D. +0.m.2p. +0.m.2p. +2.n.n. +2.n.o. +2.n.p. +2.n.q. +2.n.r. +2.n.s. +2.n.t. +2.n.u. +2.n.v. +2.n.w. +2.n.x. +2.n.y. +2.n.z. +2.n.A. +2.n.B. +2.n.C. +2.n.D. +2.n.E. +2.n.F. +2.n.G. +2.n.H. +2.I.n. +2.J.n. +2.n.K. +2.n.L. +2.n.M. +2.n.N. +2.n.O. +2.n.P. +2.n.Q. +2.n.R. +3.n.S. +2.n.T. +2.n.U. +2.n.V. +2.n.W. +2.n.X. +2.n.Y. +2.n.Z. +2.n.+. +2.n./. +2.n.10. +2.n.11. +2.n.12. +2.n.13. +2.n.14. +2.n.15. +2.n.16. +2.n.17. +2.n.18. +2.n.19. +2.n.1a. +2.n.1b. +2.n.1c. +2.n.1d. +2.n.1e. +2.n.1f. +2.n.1g. +2.n.1h. +2.n.1i. +2.n.1j. +2.n.1k. +2.1l.n. +2.n.1m. +2.n.1n. +2.n.1o. +2.n.1p. +2.n.1q. +2.n.1r. +2.n.1s. +2.n.1t. +2.1u.n. +2.n.1v. +2.n.1w. +2.n.1x. +2.n.1y. +2.n.1z. +2.n.1A. +2.n.1B. +2.n.1C. +2.n.1D. +2.n.1E. +2.n.1F. +2.n.1G. +2.n.1H. +2.n.1I. +2.n.1J. +2.n.1K. +2.n.1L. +2.n.1M. +2.n.1N. +2.n.1O. +2.n.1P. +2.n.1Q. +2.n.1R. +2.n.1S. +2.n.1T. +2.n.1U. +2.n.1V. +2.n.1W. +2.n.1X. +2.n.1Y. +2.n.1Z. +2.n.1+. +2.n.1/. +2.n.20. +2.n.21. +2.n.22. +2.n.23. +2.n.24. +2.n.25. +2.n.26. +2.n.27. +2.n.28. +2.n.2a. +2.n.2b. +2.n.2l. +6.2m.n. +2.n.2p. +2.n.2q. +2.n.2r. +7.2s.n. +7.2t.n. +2.2u.n. +2.n.2v. +2.n.2w. +e.2x.n. +2.n.2y. +2.n.2z. +2.n.2A. +2.n.2B. +2.n.2C. +2.n.2D. +2.n.2E. +2.n.2F. +2.n.2G. +2.n.2H. +2.n.2I. +2.n.2J. +2.n.2K. +c.2M.n. +2.n.2N. +2.n.2O. +2.n.2Q. +7.2S.n. +2.n.2T. +1.2U.n. +2.n.2V. +1.2W.n. +2.n.2X. +2.n.2+. +2.n.30. +2.n.35. +2.n.36. +2.n.37. +2.n.38. +2.n.39. +2.n.3d. +2.n.3h. +2.n.3i. +2.n.3m. +2.n.3p. +a.3v.n. +a.3y.n. +2.3z.n. +2.n.3A. +2.3B.n. +2.n.3C. +a.3F.n. +2.n.3G. +2.n.3M. +2.n.3Q. +2.n.40. +2.n.47. +2.n.4d. +2.n.4O. +2.n.4Q. +5.4T.n. +2.n.4V. +2.n.51. +2.n.52. +a.5a.n. +2.5b.n. +2.n.5d. +2.n.5B. +2.n.5C. +2.n.5F. +2.n.5H. +2.n.5I. +f.n.5K. +7.5M.n. +2.n.5O. +2.n.5R. +2.n.5U. +2.n.5W. +1.5Y.n. +2.n.5Z. +2.n.5/. +2.n.60. +2.n.62. +2.n.66. +a.6b.n. +c.6e.n. +2.n.6F. +2.n.6M. +2.n.6+. +2.n.71. +2.n.79. +2.n.7D. +2.n.2p. +2.n.2p. +3.o.o. +0.p.o. +0.o.q. +0.r.o. +0.s.o. +0.t.o. +0.u.o. +0.v.o. +0.w.o. +0.x.o. +0.y.o. +0.o.z. +0.o.A. +0.o.B. +0.o.C. +0.o.D. +0.o.E. +0.o.F. +0.G.o. +0.o.H. +2.I.o. +2.J.o. +0.o.K. +0.o.L. +2.M.o. +0.o.N. +0.o.O. +0.o.P. +0.o.Q. +0.o.R. +3.S.o. +b.T.o. +0.o.U. +0.o.V. +0.o.W. +0.o.X. +b.Y.o. +0.o.Z. +0.o.+. +0.o./. +0.o.10. +0.o.11. +0.o.12. +0.o.13. +0.o.14. +0.o.15. +0.o.16. +0.o.17. +3.o.18. +0.o.19. +0.o.1a. +0.o.1b. +0.o.1c. +3.o.1d. +0.o.1e. +0.o.1f. +0.o.1g. +3.o.1h. +3.o.1i. +3.o.1j. +0.o.1k. +2.1l.o. +0.o.1m. +0.o.1n. +0.o.1o. +0.o.1p. +0.o.1q. +0.o.1r. +3.o.1s. +0.o.1t. +2.1u.o. +0.o.1v. +3.o.1w. +0.o.1x. +0.o.1y. +0.o.1z. +0.o.1A. +0.1B.o. +0.o.1C. +0.o.1D. +0.o.1E. +0.o.1F. +0.o.1G. +0.o.1H. +0.o.1I. +0.o.1J. +0.o.1K. +0.o.1L. +0.o.1M. +0.o.1N. +0.o.1O. +0.o.1P. +3.o.1Q. +0.o.1R. +0.o.1S. +0.o.1T. +3.o.1U. +0.o.1V. +0.o.1W. +0.o.1X. +0.o.1Y. +0.o.1Z. +0.o.1+. +0.o.1/. +0.o.20. +0.o.21. +0.o.22. +0.o.23. +0.o.24. +0.o.25. +b.26.o. +0.o.27. +4.o.28. +0.o.2a. +0.o.2b. +0.o.2l. +6.2m.o. +0.o.2p. +2.2q.o. +2.2r.o. +7.2s.o. +7.2t.o. +2.2u.o. +3.o.2v. +0.o.2w. +e.2x.o. +9.2y.o. +0.o.2z. +3.o.2A. +9.2B.o. +0.o.2C. +0.o.2D. +3.o.2E. +3.o.2F. +0.o.2G. +9.2H.o. +0.o.2I. +3.o.2J. +0.o.2K. +c.2M.o. +9.2N.o. +2.2O.o. +9.2Q.o. +7.2S.o. +0.o.2T. +1.2U.o. +9.2V.o. +1.2W.o. +0.o.2X. +b.2+.o. +0.o.30. +0.o.35. +b.36.o. +0.o.37. +0.o.38. +5.39.o. +0.o.3d. +3.o.3h. +3.o.3i. +0.o.3m. +0.o.3p. +a.3v.o. +a.3y.o. +2.3z.o. +5.3A.o. +2.3B.o. +0.o.3C. +a.3F.o. +5.3G.o. +0.o.3M. +0.o.3Q. +5.40.o. +0.o.47. +0.o.4d. +3.o.4O. +3.o.4Q. +5.4T.o. +3.o.4V. +0.o.51. +0.o.52. +a.5a.o. +2.5b.o. +5.5d.o. +0.o.5B. +0.o.5C. +0.o.5F. +3.o.5H. +0.o.5I. +f.o.5K. +7.5M.o. +0.o.5O. +3.o.5R. +0.o.5U. +0.o.5W. +1.5Y.o. +0.o.5Z. +0.o.5/. +0.o.60. +2.62.o. +2.66.o. +a.6b.o. +c.6e.o. +0.o.6F. +0.o.6M. +0.o.6+. +3.o.71. +5.79.o. +3.o.7D. +0.o.2p. +0.o.2p. +0.p.p. +0.p.q. +0.p.r. +0.p.s. +0.p.t. +0.u.p. +0.v.p. +0.w.p. +0.x.p. +0.y.p. +0.p.z. +0.p.A. +0.p.B. +0.p.C. +0.p.D. +0.p.E. +0.p.F. +0.p.G. +0.p.H. +2.I.p. +2.J.p. +0.p.K. +0.p.L. +2.M.p. +0.p.N. +0.p.O. +0.p.P. +0.p.Q. +0.p.R. +3.S.p. +b.T.p. +0.p.U. +0.p.V. +0.p.W. +0.p.X. +b.Y.p. +0.p.Z. +0.p.+. +0.p./. +0.p.10. +0.p.11. +0.p.12. +0.p.13. +0.p.14. +0.p.15. +0.p.16. +0.p.17. +0.p.18. +0.p.19. +0.p.1a. +0.p.1b. +0.p.1c. +3.p.1d. +0.p.1e. +0.p.1f. +0.p.1g. +3.p.1h. +3.p.1i. +3.p.1j. +0.p.1k. +2.1l.p. +0.p.1m. +0.p.1n. +0.p.1o. +0.p.1p. +0.p.1q. +0.p.1r. +0.p.1s. +0.p.1t. +2.1u.p. +0.p.1v. +0.p.1w. +0.p.1x. +0.p.1y. +0.p.1z. +0.p.1A. +0.1B.p. +0.p.1C. +0.p.1D. +0.p.1E. +0.p.1F. +0.p.1G. +0.p.1H. +0.p.1I. +0.p.1J. +0.p.1K. +0.p.1L. +0.p.1M. +0.p.1N. +0.p.1O. +0.p.1P. +0.p.1Q. +0.p.1R. +0.p.1S. +0.p.1T. +0.p.1U. +0.p.1V. +0.p.1W. +0.p.1X. +0.p.1Y. +0.p.1Z. +0.p.1+. +0.p.1/. +0.p.20. +0.p.21. +0.p.22. +0.p.23. +0.p.24. +0.p.25. +b.26.p. +0.p.27. +4.p.28. +0.p.2a. +0.p.2b. +0.p.2l. +6.2m.p. +0.p.2p. +2.2q.p. +2.2r.p. +7.2s.p. +7.2t.p. +2.2u.p. +0.p.2v. +0.p.2w. +e.2x.p. +9.2y.p. +0.p.2z. +3.p.2A. +9.2B.p. +0.p.2C. +0.p.2D. +3.p.2E. +0.p.2F. +0.p.2G. +9.2H.p. +0.p.2I. +3.p.2J. +0.p.2K. +c.2M.p. +9.2N.p. +2.2O.p. +9.2Q.p. +7.2S.p. +0.p.2T. +1.2U.p. +9.2V.p. +1.2W.p. +0.p.2X. +b.2+.p. +0.p.30. +0.p.35. +b.36.p. +0.p.37. +0.p.38. +5.39.p. +0.p.3d. +3.p.3h. +0.p.3i. +0.p.3m. +0.p.3p. +a.3v.p. +a.3y.p. +2.3z.p. +5.3A.p. +2.3B.p. +0.p.3C. +a.3F.p. +5.3G.p. +0.p.3M. +0.p.3Q. +5.40.p. +0.p.47. +0.p.4d. +0.p.4O. +3.p.4Q. +5.4T.p. +3.p.4V. +0.p.51. +0.p.52. +a.5a.p. +2.5b.p. +5.5d.p. +0.p.5B. +0.p.5C. +0.p.5F. +3.p.5H. +0.p.5I. +f.p.5K. +7.5M.p. +0.p.5O. +0.p.5R. +0.p.5U. +0.p.5W. +1.5Y.p. +0.p.5Z. +0.p.5/. +0.p.60. +2.62.p. +2.66.p. +a.6b.p. +c.6e.p. +0.p.6F. +0.p.6M. +0.p.6+. +3.p.71. +5.79.p. +3.p.7D. +0.p.2p. +0.p.2p. +0.q.q. +0.r.q. +0.s.q. +0.t.q. +0.u.q. +0.v.q. +0.w.q. +0.x.q. +0.y.q. +0.q.z. +0.A.q. +0.B.q. +0.C.q. +0.D.q. +0.E.q. +3.q.F. +0.G.q. +0.H.q. +2.I.q. +2.J.q. +0.K.q. +0.L.q. +2.M.q. +0.N.q. +0.O.q. +0.P.q. +0.Q.q. +0.R.q. +3.S.q. +b.T.q. +0.U.q. +0.V.q. +0.W.q. +0.X.q. +b.Y.q. +0.q.Z. +0.+.q. +0./.q. +0.10.q. +0.11.q. +3.12.q. +0.13.q. +0.14.q. +0.15.q. +0.16.q. +0.17.q. +0.18.q. +0.19.q. +0.1a.q. +0.1b.q. +0.1c.q. +0.1d.q. +0.q.1e. +0.q.1f. +0.q.1g. +3.q.1h. +3.q.1i. +0.1j.q. +0.1k.q. +2.1l.q. +0.1m.q. +0.1n.q. +0.1o.q. +0.1p.q. +0.1q.q. +0.1r.q. +0.1s.q. +0.1t.q. +2.1u.q. +0.1v.q. +0.1w.q. +0.1x.q. +0.1y.q. +0.1z.q. +0.1A.q. +0.1B.q. +0.1C.q. +0.1D.q. +0.1E.q. +0.1F.q. +0.1G.q. +0.1H.q. +0.1I.q. +0.1J.q. +0.1K.q. +0.1L.q. +0.1M.q. +0.1N.q. +0.1O.q. +0.1P.q. +3.q.1Q. +d.1R.q. +0.q.1S. +0.q.1T. +3.q.1U. +0.q.1V. +0.q.1W. +0.q.1X. +0.q.1Y. +0.q.1Z. +0.q.1+. +0.q.1/. +0.q.20. +0.q.21. +0.q.22. +0.q.23. +0.q.24. +0.q.25. +b.26.q. +0.q.27. +4.q.28. +0.q.2a. +0.q.2b. +0.q.2l. +6.2m.q. +0.q.2p. +2.2q.q. +2.2r.q. +7.2s.q. +7.2t.q. +2.2u.q. +3.q.2v. +0.q.2w. +e.2x.q. +9.2y.q. +0.q.2z. +3.q.2A. +9.2B.q. +0.q.2C. +0.q.2D. +3.q.2E. +3.q.2F. +0.q.2G. +9.2H.q. +0.q.2I. +3.q.2J. +0.q.2K. +c.2M.q. +9.2N.q. +2.2O.q. +9.2Q.q. +7.2S.q. +0.q.2T. +1.2U.q. +9.2V.q. +1.2W.q. +0.q.2X. +d.2+.q. +0.q.30. +0.q.35. +b.36.q. +0.q.37. +0.q.38. +5.39.q. +0.q.3d. +3.q.3h. +0.q.3i. +0.q.3m. +0.q.3p. +a.3v.q. +a.3y.q. +2.3z.q. +5.3A.q. +2.3B.q. +0.q.3C. +a.3F.q. +5.3G.q. +0.q.3M. +0.q.3Q. +5.40.q. +0.q.47. +0.q.4d. +3.q.4O. +3.q.4Q. +5.4T.q. +3.q.4V. +0.q.51. +0.q.52. +a.5a.q. +2.5b.q. +5.5d.q. +0.q.5B. +0.q.5C. +5.5F.q. +3.q.5H. +0.q.5I. +f.q.5K. +7.5M.q. +0.q.5O. +3.q.5R. +0.q.5U. +3.q.5W. +1.5Y.q. +0.q.5Z. +0.q.5/. +0.q.60. +2.62.q. +2.66.q. +a.6b.q. +c.6e.q. +0.q.6F. +0.q.6M. +0.q.6+. +3.q.71. +5.79.q. +3.q.7D. +0.q.2p. +0.q.2p. +0.r.r. +0.r.s. +0.r.t. +0.u.r. +0.v.r. +0.w.r. +0.x.r. +0.y.r. +0.r.z. +0.r.A. +0.r.B. +0.r.C. +0.r.D. +0.r.E. +0.r.F. +0.r.G. +0.r.H. +2.I.r. +2.J.r. +0.r.K. +0.r.L. +2.M.r. +0.r.N. +0.r.O. +0.r.P. +0.r.Q. +0.r.R. +3.S.r. +b.T.r. +0.r.U. +0.r.V. +0.r.W. +0.r.X. +b.Y.r. +0.r.Z. +0.r.+. +0.r./. +0.r.10. +0.r.11. +0.r.12. +0.r.13. +0.r.14. +0.r.15. +0.r.16. +0.r.17. +0.r.18. +0.r.19. +0.r.1a. +0.r.1b. +0.r.1c. +0.r.1d. +0.r.1e. +0.r.1f. +0.r.1g. +0.r.1h. +0.r.1i. +0.r.1j. +0.r.1k. +2.1l.r. +0.r.1m. +0.r.1n. +0.r.1o. +0.r.1p. +0.r.1q. +0.r.1r. +0.r.1s. +0.r.1t. +2.1u.r. +0.r.1v. +0.r.1w. +0.r.1x. +0.r.1y. +0.r.1z. +0.r.1A. +0.1B.r. +0.r.1C. +0.r.1D. +0.r.1E. +0.r.1F. +0.r.1G. +0.r.1H. +0.r.1I. +0.r.1J. +0.r.1K. +0.r.1L. +0.r.1M. +0.r.1N. +0.r.1O. +0.r.1P. +0.r.1Q. +0.r.1R. +3.r.1S. +0.r.1T. +0.r.1U. +0.r.1V. +0.r.1W. +0.r.1X. +0.r.1Y. +0.r.1Z. +0.r.1+. +0.r.1/. +0.r.20. +0.r.21. +0.r.22. +0.r.23. +0.r.24. +0.r.25. +b.26.r. +0.r.27. +0.r.28. +6.r.29. +0.r.2a. +0.r.2b. +3.r.2c. +3.r.2d. +6.r.2f. +6.r.2g. +3.r.2h. +6.r.2i. +3.r.2j. +0.r.2l. +6.2m.r. +1.r.2o. +0.r.2p. +2.2q.r. +2.2r.r. +7.2s.r. +7.2t.r. +2.2u.r. +0.r.2v. +0.r.2w. +e.2x.r. +9.2y.r. +0.r.2z. +0.r.2A. +9.2B.r. +0.r.2C. +0.r.2D. +0.r.2E. +0.r.2F. +0.r.2G. +9.2H.r. +0.r.2I. +0.r.2J. +0.r.2K. +3.r.2L. +c.2M.r. +9.2N.r. +2.2O.r. +3.r.2P. +9.2Q.r. +8.r.2R. +7.2S.r. +0.r.2T. +1.2U.r. +9.2V.r. +1.2W.r. +0.r.2X. +3.r.2Y. +8.r.2Z. +b.2+.r. +8.r.2/. +0.r.30. +3.r.31. +3.r.32. +8.r.33. +3.r.34. +0.r.35. +b.36.r. +0.r.37. +0.r.38. +5.39.r. +6.r.3a. +6.r.3c. +0.r.3d. +1.r.3e. +6.r.3f. +3.r.3g. +0.r.3h. +0.r.3i. +1.r.3j. +3.r.3k. +6.r.3l. +0.r.3m. +8.r.3n. +8.r.3o. +0.r.3p. +3.r.3q. +6.r.3r. +4.r.3s. +8.r.3t. +8.r.3u. +6.r.3v. +6.r.3w. +6.r.3x. +a.3y.r. +2.3z.r. +5.3A.r. +2.3B.r. +0.r.3C. +6.r.3D. +6.r.3E. +a.3F.r. +5.3G.r. +6.r.3H. +6.r.3I. +3.r.3J. +3.r.3K. +6.r.3L. +0.r.3M. +6.r.3N. +3.r.3O. +6.r.3P. +0.r.3Q. +6.r.3R. +6.r.3S. +1.r.3T. +6.r.3U. +6.r.3V. +6.r.3W. +6.r.3X. +3.r.3Y. +6.r.3Z. +1.r.3+. +1.r.3/. +5.40.r. +6.r.41. +6.r.42. +6.r.43. +6.r.44. +3.r.45. +0.r.47. +6.r.48. +6.r.49. +1.r.4a. +6.r.4b. +6.r.4c. +0.r.4d. +6.r.4e. +6.r.4f. +6.r.4g. +6.r.4h. +6.r.4i. +6.r.4j. +8.r.4k. +6.r.4l. +6.r.4m. +6.r.4n. +6.r.4o. +6.r.4p. +6.r.4q. +6.r.4r. +6.r.4s. +6.r.4t. +6.r.4u. +6.r.4v. +6.r.4w. +6.r.4x. +3.r.4y. +6.r.4z. +6.r.4A. +6.r.4B. +3.r.4C. +6.r.4D. +6.r.4E. +1.r.4F. +8.r.4G. +8.r.4H. +1.r.4I. +8.r.4J. +8.r.4K. +8.r.4L. +6.r.4M. +6.r.4N. +0.r.4O. +6.r.4P. +0.r.4Q. +1.r.4R. +6.r.4S. +5.4T.r. +8.r.4U. +0.r.4V. +6.r.4W. +3.r.4Y. +1.r.4Z. +6.r.4+. +6.r.4/. +1.r.50. +0.r.51. +0.r.52. +8.r.53. +3.r.54. +8.r.55. +8.r.56. +1.r.57. +3.r.58. +8.r.59. +a.5a.r. +2.5b.r. +6.r.5c. +5.5d.r. +3.r.5e. +8.r.5f. +6.r.5g. +8.r.5h. +6.r.5i. +3.r.5j. +6.r.5k. +3.r.5l. +6.r.5m. +6.r.5n. +6.r.5o. +3.r.5p. +6.r.5q. +6.r.5r. +1.r.5s. +6.r.5t. +3.r.5u. +6.r.5v. +1.r.5w. +1.r.5y. +1.r.5z. +8.r.5A. +0.r.5B. +0.r.5C. +1.r.5D. +3.r.5E. +0.r.5F. +8.r.5G. +0.r.5H. +0.r.5I. +f.r.5K. +3.r.5L. +8.r.5M. +3.r.5N. +0.r.5O. +8.r.5P. +6.r.5Q. +0.r.5R. +6.r.5S. +3.r.5T. +0.r.5U. +8.r.5V. +0.r.5W. +8.r.5X. +8.r.5Y. +0.r.5Z. +h.5+.r. +0.r.5/. +0.r.60. +6.r.61. +2.62.r. +6.r.64. +6.r.65. +2.66.r. +6.r.67. +3.r.68. +6.r.69. +6.r.6a. +6.r.6b. +6.r.6c. +6.r.6d. +6.r.6e. +6.r.6f. +6.r.6g. +6.r.6h. +6.r.6i. +6.r.6j. +6.r.6k. +3.r.6l. +3.r.6m. +3.r.6n. +3.r.6o. +3.r.6p. +3.r.6q. +6.r.6r. +6.r.6s. +6.r.6t. +6.r.6u. +6.r.6v. +6.r.6w. +6.r.6x. +3.r.6y. +6.r.6z. +6.r.6A. +6.r.6B. +6.r.6C. +6.r.6D. +6.r.6E. +0.r.6F. +6.r.6G. +3.r.6H. +6.r.6I. +6.r.6J. +6.r.6K. +8.r.6L. +0.r.6M. +6.r.6N. +3.r.6O. +6.r.6P. +6.r.6Q. +3.r.6R. +3.r.6S. +6.r.6T. +6.r.6U. +3.r.6V. +6.r.6W. +1.r.6X. +3.r.6Y. +1.r.6Z. +0.r.6+. +1.r.6/. +1.r.70. +0.r.71. +6.r.72. +6.r.73. +1.r.74. +6.r.75. +1.r.76. +1.r.77. +6.r.78. +5.79.r. +1.r.7a. +3.r.7b. +6.r.7c. +6.r.7d. +6.r.7e. +3.r.7f. +6.r.7g. +6.r.7h. +6.r.7i. +6.r.7j. +6.r.7k. +6.r.7l. +1.r.7m. +6.r.7o. +6.r.7p. +1.r.7r. +1.r.7s. +3.r.7t. +6.r.7u. +6.r.7v. +6.r.7w. +3.r.7x. +6.r.7y. +1.r.7z. +6.r.7A. +3.r.7B. +6.r.7C. +0.r.7D. +6.r.7E. +6.r.7F. +3.r.7G. +6.r.7H. +3.r.7I. +3.r.7J. +1.r.7K. +1.r.7L. +1.r.7M. +1.r.7N. +1.r.7O. +1.r.7P. +6.r.7Q. +3.r.7R. +6.r.7S. +6.r.7T. +1.r.7U. +8.r.7V. +6.r.7W. +6.r.7X. +6.r.7Y. +3.r.7Z. +1.r.7+. +1.r.7/. +1.r.80. +1.r.81. +1.r.82. +3.r.83. +1.r.84. +1.r.85. +h.86.r. +1.r.87. +1.r.88. +8.r.89. +3.r.8a. +8.r.8b. +6.r.8c. +8.r.8d. +6.r.8e. +6.r.8f. +6.r.8g. +6.r.8h. +6.r.8i. +6.r.8j. +6.r.8k. +6.r.8l. +6.r.8m. +6.r.8n. +6.r.8o. +6.r.8p. +6.r.8q. +6.r.8r. +6.r.8s. +6.r.8t. +6.r.8u. +6.r.8v. +6.r.8w. +6.r.8x. +6.r.8y. +6.r.8z. +6.r.8A. +6.r.8B. +6.r.8C. +6.r.8D. +6.r.8E. +6.r.8F. +6.r.8G. +4.r.8H. +4.r.8I. +6.r.8J. +6.r.8K. +6.r.8L. +6.r.8M. +6.r.8N. +6.r.8O. +6.r.8P. +0.r.2p. +0.r.2p. +8.r.3b. +1.r.46. +1.r.4X. +1.r.5x. +1.r.5J. +1.r.7n. +1.r.7q. +0.s.s. +3.s.t. +0.u.s. +0.v.s. +0.w.s. +0.x.s. +0.y.s. +0.s.z. +0.s.A. +0.s.B. +0.s.C. +0.s.D. +0.s.E. +0.s.F. +0.s.G. +0.s.H. +2.I.s. +2.J.s. +0.s.K. +0.s.L. +2.M.s. +0.s.N. +0.s.O. +0.s.P. +0.s.Q. +0.s.R. +3.S.s. +b.T.s. +0.s.U. +0.s.V. +0.s.W. +0.s.X. +b.Y.s. +0.s.Z. +0.s.+. +0.s./. +0.s.10. +0.s.11. +0.s.12. +0.s.13. +0.s.14. +0.s.15. +0.s.16. +0.s.17. +3.s.18. +0.s.19. +0.s.1a. +0.s.1b. +0.s.1c. +0.s.1d. +0.s.1e. +0.s.1f. +0.s.1g. +3.s.1h. +3.s.1i. +3.s.1j. +0.s.1k. +2.1l.s. +0.s.1m. +0.s.1n. +0.s.1o. +0.s.1p. +0.s.1q. +0.s.1r. +0.s.1s. +0.s.1t. +2.1u.s. +0.s.1v. +3.s.1w. +0.s.1x. +0.s.1y. +0.s.1z. +0.s.1A. +0.1B.s. +0.s.1C. +0.s.1D. +0.s.1E. +0.s.1F. +0.s.1G. +0.s.1H. +0.s.1I. +0.s.1J. +0.s.1K. +0.s.1L. +0.s.1M. +0.s.1N. +0.s.1O. +0.s.1P. +0.s.1Q. +0.s.1R. +0.s.1S. +0.s.1T. +0.s.1U. +0.s.1V. +0.s.1W. +0.s.1X. +0.s.1Y. +0.s.1Z. +0.s.1+. +0.s.1/. +0.s.20. +0.s.21. +0.s.22. +0.s.23. +0.s.24. +0.s.25. +b.26.s. +0.s.27. +0.s.28. +6.s.29. +0.s.2a. +0.s.2b. +3.s.2c. +3.s.2d. +6.s.2f. +3.s.2g. +3.s.2h. +6.s.2i. +3.s.2j. +0.s.2l. +6.2m.s. +1.s.2o. +0.s.2p. +2.2q.s. +2.2r.s. +7.2s.s. +7.2t.s. +2.2u.s. +0.s.2v. +0.s.2w. +e.2x.s. +9.2y.s. +0.s.2z. +3.s.2A. +9.2B.s. +0.s.2C. +0.s.2D. +3.s.2E. +0.s.2F. +0.s.2G. +9.2H.s. +0.s.2I. +3.s.2J. +0.s.2K. +6.s.2L. +c.2M.s. +9.2N.s. +2.2O.s. +6.s.2P. +9.2Q.s. +8.s.2R. +7.2S.s. +0.s.2T. +1.2U.s. +9.2V.s. +1.2W.s. +0.s.2X. +8.s.2Y. +8.s.2Z. +d.2+.s. +8.s.2/. +0.s.30. +3.s.31. +8.s.32. +8.s.33. +3.s.34. +0.s.35. +b.36.s. +0.s.37. +0.s.38. +5.39.s. +6.s.3a. +6.s.3c. +0.s.3d. +1.s.3e. +3.s.3f. +8.s.3g. +3.s.3h. +0.s.3i. +1.s.3j. +3.s.3k. +6.s.3l. +0.s.3m. +3.s.3n. +3.s.3o. +0.s.3p. +8.s.3q. +6.s.3r. +4.s.3s. +8.s.3t. +8.s.3u. +6.s.3v. +3.s.3w. +6.s.3x. +a.3y.s. +2.3z.s. +5.3A.s. +2.3B.s. +0.s.3C. +3.s.3D. +6.s.3E. +5.3F.s. +5.3G.s. +3.s.3H. +3.s.3I. +3.s.3J. +6.s.3K. +3.s.3L. +0.s.3M. +6.s.3N. +6.s.3O. +6.s.3P. +0.s.3Q. +6.s.3R. +3.s.3S. +1.s.3T. +3.s.3U. +3.s.3V. +6.s.3W. +6.s.3X. +3.s.3Y. +6.s.3Z. +1.s.3+. +1.s.3/. +5.40.s. +6.s.41. +6.s.42. +6.s.43. +6.s.44. +6.s.45. +0.s.47. +6.s.48. +3.s.49. +1.s.4a. +6.s.4b. +6.s.4c. +0.s.4d. +6.s.4e. +6.s.4f. +3.s.4g. +3.s.4h. +6.s.4i. +6.s.4j. +8.s.4k. +6.s.4l. +3.s.4m. +6.s.4n. +6.s.4o. +6.s.4p. +6.s.4q. +6.s.4r. +6.s.4s. +6.s.4t. +6.s.4u. +6.s.4v. +6.s.4w. +6.s.4x. +3.s.4y. +6.s.4z. +3.s.4A. +6.s.4B. +3.s.4C. +6.s.4D. +6.s.4E. +1.s.4F. +8.s.4G. +8.s.4H. +1.s.4I. +8.s.4J. +8.s.4K. +8.s.4L. +6.s.4M. +6.s.4N. +3.s.4O. +6.s.4P. +3.s.4Q. +1.s.4R. +6.s.4S. +5.4T.s. +8.s.4U. +3.s.4V. +3.s.4W. +3.s.4Y. +1.s.4Z. +6.s.4+. +6.s.4/. +1.s.50. +0.s.51. +0.s.52. +8.s.53. +3.s.54. +8.s.55. +8.s.56. +1.s.57. +3.s.58. +8.s.59. +a.5a.s. +2.5b.s. +6.s.5c. +5.5d.s. +8.s.5e. +8.s.5f. +6.s.5g. +8.s.5h. +3.s.5i. +3.s.5j. +3.s.5k. +3.s.5l. +3.s.5m. +6.s.5n. +6.s.5o. +3.s.5p. +6.s.5q. +6.s.5r. +1.s.5s. +3.s.5t. +3.s.5u. +6.s.5v. +1.s.5w. +1.s.5y. +1.s.5z. +8.s.5A. +0.s.5B. +0.s.5C. +1.s.5D. +8.s.5E. +0.s.5F. +8.s.5G. +3.s.5H. +0.s.5I. +f.s.5K. +8.s.5L. +8.s.5M. +8.s.5N. +0.s.5O. +8.s.5P. +6.s.5Q. +0.s.5R. +6.s.5S. +3.s.5T. +0.s.5U. +8.s.5V. +0.s.5W. +8.s.5X. +8.s.5Y. +0.s.5Z. +h.5+.s. +0.s.5/. +0.s.60. +6.s.61. +2.62.s. +6.s.64. +3.s.65. +2.66.s. +6.s.67. +6.s.68. +3.s.69. +6.s.6a. +6.s.6b. +6.s.6c. +3.s.6d. +6.s.6e. +6.s.6f. +6.s.6g. +3.s.6h. +6.s.6i. +3.s.6j. +3.s.6k. +6.s.6l. +3.s.6m. +3.s.6n. +3.s.6o. +3.s.6p. +3.s.6q. +3.s.6r. +6.s.6s. +6.s.6t. +3.s.6u. +6.s.6v. +6.s.6w. +6.s.6x. +3.s.6y. +6.s.6z. +6.s.6A. +3.s.6B. +6.s.6C. +6.s.6D. +6.s.6E. +0.s.6F. +6.s.6G. +3.s.6H. +6.s.6I. +6.s.6J. +6.s.6K. +3.s.6L. +0.s.6M. +6.s.6N. +3.s.6O. +6.s.6P. +3.s.6Q. +6.s.6R. +3.s.6S. +6.s.6T. +3.s.6U. +3.s.6V. +6.s.6W. +1.s.6X. +3.s.6Y. +1.s.6Z. +0.s.6+. +1.s.6/. +1.s.70. +3.s.71. +3.s.72. +6.s.73. +1.s.74. +6.s.75. +1.s.76. +1.s.77. +3.s.78. +5.79.s. +1.s.7a. +3.s.7b. +6.s.7c. +6.s.7d. +6.s.7e. +3.s.7f. +3.s.7g. +3.s.7h. +6.s.7i. +3.s.7j. +6.s.7k. +6.s.7l. +1.s.7m. +6.s.7o. +6.s.7p. +1.s.7r. +1.s.7s. +8.s.7t. +6.s.7u. +6.s.7v. +6.s.7w. +3.s.7x. +6.s.7y. +1.s.7z. +6.s.7A. +8.s.7B. +3.s.7C. +3.s.7D. +6.s.7E. +3.s.7F. +3.s.7G. +3.s.7H. +3.s.7I. +3.s.7J. +1.s.7K. +1.s.7L. +1.s.7M. +1.s.7N. +1.s.7O. +1.s.7P. +6.s.7Q. +3.s.7R. +6.s.7S. +3.s.7T. +1.s.7U. +8.s.7V. +3.s.7W. +3.s.7X. +3.s.7Y. +3.s.7Z. +1.s.7+. +1.s.7/. +1.s.80. +1.s.81. +1.s.82. +6.s.83. +1.s.84. +1.s.85. +h.86.s. +1.s.87. +1.s.88. +3.s.89. +6.s.8a. +8.s.8b. +6.s.8c. +8.s.8d. +6.s.8e. +6.s.8f. +6.s.8g. +6.s.8h. +6.s.8i. +6.s.8j. +6.s.8k. +6.s.8l. +6.s.8m. +6.s.8n. +6.s.8o. +6.s.8p. +6.s.8q. +6.s.8r. +6.s.8s. +6.s.8t. +6.s.8u. +6.s.8v. +6.s.8w. +6.s.8x. +6.s.8y. +6.s.8z. +6.s.8A. +6.s.8B. +3.s.8C. +6.s.8D. +6.s.8E. +6.s.8F. +6.s.8G. +4.s.8H. +4.s.8I. +6.s.8J. +6.s.8K. +6.s.8L. +6.s.8M. +6.s.8N. +6.s.8O. +6.s.8P. +0.s.2p. +0.s.2p. +8.s.3b. +1.s.46. +1.s.4X. +1.s.5x. +1.s.5J. +1.s.7n. +1.s.7q. +0.t.t. +0.u.t. +0.v.t. +0.w.t. +0.x.t. +0.y.t. +3.t.z. +0.t.A. +0.t.B. +0.t.C. +0.t.D. +0.t.E. +0.t.F. +0.t.G. +0.t.H. +2.I.t. +2.J.t. +0.t.K. +0.t.L. +2.M.t. +0.t.N. +0.t.O. +0.t.P. +0.t.Q. +0.t.R. +3.S.t. +b.T.t. +3.t.U. +0.t.V. +3.t.W. +0.t.X. +b.Y.t. +0.t.Z. +0.t.+. +3.t./. +0.t.10. +0.t.11. +0.t.12. +0.t.13. +0.t.14. +0.t.15. +0.t.16. +0.t.17. +0.t.18. +0.t.19. +0.t.1a. +0.t.1b. +3.t.1c. +0.t.1d. +0.t.1e. +0.t.1f. +3.t.1g. +0.t.1h. +0.t.1i. +3.t.1j. +0.t.1k. +2.1l.t. +0.t.1m. +0.t.1n. +0.t.1o. +0.t.1p. +0.t.1q. +0.t.1r. +3.t.1s. +0.t.1t. +2.1u.t. +0.t.1v. +0.t.1w. +0.t.1x. +0.t.1y. +0.t.1z. +0.t.1A. +0.1B.t. +0.t.1C. +0.t.1D. +0.t.1E. +0.t.1F. +0.t.1G. +0.t.1H. +0.t.1I. +0.t.1J. +0.t.1K. +0.t.1L. +0.t.1M. +0.t.1N. +0.t.1O. +0.t.1P. +0.t.1Q. +0.t.1R. +0.t.1S. +3.t.1T. +0.t.1U. +0.t.1V. +0.t.1W. +0.t.1X. +3.t.1Y. +3.t.1Z. +3.t.1+. +0.t.1/. +3.t.20. +3.t.21. +0.t.22. +0.t.23. +0.t.24. +0.t.25. +b.26.t. +3.t.27. +0.t.28. +0.t.2a. +0.t.2b. +0.t.2l. +6.2m.t. +0.t.2p. +2.2q.t. +2.2r.t. +7.2s.t. +7.2t.t. +2.2u.t. +0.t.2v. +0.t.2w. +e.2x.t. +9.2y.t. +0.t.2z. +3.t.2A. +9.2B.t. +0.t.2C. +0.t.2D. +0.t.2E. +0.t.2F. +0.t.2G. +9.2H.t. +0.t.2I. +3.t.2J. +0.t.2K. +c.2M.t. +9.2N.t. +2.2O.t. +9.2Q.t. +7.2S.t. +0.t.2T. +1.2U.t. +9.2V.t. +1.2W.t. +0.t.2X. +d.2+.t. +0.t.30. +3.t.35. +b.36.t. +0.t.37. +3.t.38. +5.39.t. +0.t.3d. +3.t.3h. +0.t.3i. +0.t.3m. +0.t.3p. +a.3v.t. +a.3y.t. +2.3z.t. +5.3A.t. +2.3B.t. +0.t.3C. +5.3F.t. +5.3G.t. +0.t.3M. +0.t.3Q. +5.40.t. +0.t.47. +0.t.4d. +0.t.4O. +0.t.4Q. +5.4T.t. +3.t.4V. +3.t.51. +0.t.52. +a.5a.t. +2.5b.t. +5.5d.t. +0.t.5B. +0.t.5C. +0.t.5F. +0.t.5H. +0.t.5I. +f.t.5K. +7.5M.t. +3.t.5O. +0.t.5R. +0.t.5U. +0.t.5W. +1.5Y.t. +0.t.5Z. +0.t.5/. +0.t.60. +2.62.t. +2.66.t. +a.6b.t. +c.6e.t. +0.t.6F. +0.t.6M. +0.t.6+. +3.t.71. +5.79.t. +0.t.7D. +0.t.2p. +0.t.2p. +0.u.u. +0.v.u. +0.u.w. +0.x.u. +0.y.u. +3.u.z. +0.u.A. +0.u.B. +0.u.C. +0.u.D. +0.u.E. +0.u.F. +0.u.G. +0.u.H. +2.I.u. +2.J.u. +0.u.K. +3.u.L. +2.M.u. +0.u.N. +3.u.O. +0.u.P. +0.u.Q. +0.u.R. +3.S.u. +b.T.u. +3.u.U. +3.u.V. +0.u.W. +0.u.X. +b.Y.u. +0.u.Z. +3.u.+. +3.u./. +0.u.10. +0.u.11. +0.u.12. +0.u.13. +0.u.14. +0.u.15. +0.u.16. +0.u.17. +0.u.18. +0.u.19. +0.u.1a. +0.u.1b. +3.u.1c. +0.u.1d. +3.u.1e. +0.u.1f. +3.u.1g. +0.u.1h. +0.u.1i. +3.u.1j. +0.u.1k. +2.1l.u. +0.u.1m. +0.u.1n. +0.u.1o. +0.u.1p. +0.u.1q. +0.u.1r. +0.u.1s. +0.u.1t. +2.1u.u. +0.u.1v. +0.u.1w. +0.u.1x. +0.u.1y. +0.u.1z. +0.u.1A. +0.1B.u. +0.u.1C. +0.u.1D. +3.u.1E. +0.u.1F. +0.u.1G. +0.u.1H. +0.u.1I. +0.u.1J. +0.u.1K. +0.u.1L. +0.u.1M. +0.u.1N. +0.u.1O. +0.u.1P. +0.u.1Q. +0.u.1R. +0.u.1S. +3.u.1T. +0.u.1U. +0.u.1V. +0.u.1W. +0.u.1X. +3.u.1Y. +0.u.1Z. +0.u.1+. +0.u.1/. +0.u.20. +0.u.21. +0.u.22. +0.u.23. +0.u.24. +0.u.25. +b.26.u. +3.u.27. +0.u.28. +3.u.29. +0.u.2a. +0.u.2b. +3.u.2c. +3.u.2d. +6.u.2f. +6.u.2g. +6.u.2h. +6.u.2i. +6.u.2j. +0.u.2l. +6.2m.u. +1.u.2o. +0.u.2p. +2.2q.u. +2.2r.u. +7.2s.u. +7.2t.u. +2.2u.u. +0.u.2v. +0.u.2w. +e.2x.u. +9.2y.u. +0.u.2z. +3.u.2A. +9.2B.u. +0.u.2C. +0.u.2D. +0.u.2E. +0.u.2F. +0.u.2G. +9.2H.u. +0.u.2I. +3.u.2J. +0.u.2K. +3.u.2L. +c.2M.u. +9.2N.u. +2.2O.u. +3.u.2P. +9.2Q.u. +8.u.2R. +7.2S.u. +0.u.2T. +1.2U.u. +9.2V.u. +1.2W.u. +0.u.2X. +3.u.2Y. +3.u.2Z. +d.2+.u. +8.u.2/. +0.u.30. +3.u.31. +3.u.32. +8.u.33. +3.u.34. +3.u.35. +b.36.u. +0.u.37. +3.u.38. +5.39.u. +6.u.3a. +6.u.3c. +0.u.3d. +1.u.3e. +3.u.3f. +8.u.3g. +3.u.3h. +0.u.3i. +1.u.3j. +3.u.3k. +3.u.3l. +0.u.3m. +3.u.3n. +3.u.3o. +0.u.3p. +8.u.3q. +6.u.3r. +4.u.3s. +8.u.3t. +8.u.3u. +6.u.3v. +6.u.3w. +6.u.3x. +a.3y.u. +2.3z.u. +5.3A.u. +2.3B.u. +0.u.3C. +6.u.3D. +6.u.3E. +a.3F.u. +5.3G.u. +3.u.3H. +6.u.3I. +3.u.3J. +3.u.3K. +6.u.3L. +0.u.3M. +6.u.3N. +3.u.3O. +3.u.3P. +0.u.3Q. +6.u.3R. +3.u.3S. +1.u.3T. +6.u.3U. +3.u.3V. +6.u.3W. +6.u.3X. +3.u.3Y. +6.u.3Z. +1.u.3+. +1.u.3/. +5.40.u. +6.u.41. +6.u.42. +6.u.43. +6.u.44. +3.u.45. +0.u.47. +6.u.48. +6.u.49. +1.u.4a. +6.u.4b. +6.u.4c. +0.u.4d. +6.u.4e. +6.u.4f. +6.u.4g. +3.u.4h. +6.u.4i. +6.u.4j. +8.u.4k. +6.u.4l. +3.u.4m. +6.u.4n. +6.u.4o. +6.u.4p. +6.u.4q. +6.u.4r. +6.u.4s. +6.u.4t. +6.u.4u. +6.u.4v. +6.u.4w. +6.u.4x. +3.u.4y. +6.u.4z. +3.u.4A. +6.u.4B. +6.u.4C. +6.u.4D. +6.u.4E. +1.u.4F. +8.u.4G. +3.u.4H. +1.u.4I. +8.u.4J. +8.u.4K. +8.u.4L. +6.u.4M. +6.u.4N. +0.u.4O. +6.u.4P. +3.u.4Q. +1.u.4R. +6.u.4S. +5.4T.u. +8.u.4U. +3.u.4V. +3.u.4W. +3.u.4Y. +1.u.4Z. +3.u.4+. +3.u.4/. +1.u.50. +3.u.51. +0.u.52. +8.u.53. +3.u.54. +3.u.55. +3.u.56. +1.u.57. +3.u.58. +8.u.59. +a.5a.u. +2.5b.u. +6.u.5c. +5.5d.u. +3.u.5e. +8.u.5f. +6.u.5g. +8.u.5h. +3.u.5i. +3.u.5j. +3.u.5k. +3.u.5l. +3.u.5m. +3.u.5n. +6.u.5o. +3.u.5p. +6.u.5q. +6.u.5r. +1.u.5s. +3.u.5t. +3.u.5u. +3.u.5v. +1.u.5w. +1.u.5y. +1.u.5z. +8.u.5A. +0.u.5B. +0.u.5C. +1.u.5D. +8.u.5E. +0.u.5F. +3.u.5G. +0.u.5H. +0.u.5I. +f.u.5K. +8.u.5L. +8.u.5M. +8.u.5N. +3.u.5O. +8.u.5P. +6.u.5Q. +0.u.5R. +3.u.5S. +6.u.5T. +0.u.5U. +3.u.5V. +0.u.5W. +8.u.5X. +8.u.5Y. +0.u.5Z. +h.5+.u. +0.u.5/. +0.u.60. +6.u.61. +2.62.u. +6.u.64. +6.u.65. +2.66.u. +3.u.67. +3.u.68. +3.u.69. +3.u.6a. +6.u.6b. +6.u.6c. +6.u.6d. +6.u.6e. +6.u.6f. +6.u.6g. +6.u.6h. +3.u.6i. +6.u.6j. +3.u.6k. +6.u.6l. +3.u.6m. +3.u.6n. +3.u.6o. +3.u.6p. +3.u.6q. +6.u.6r. +6.u.6s. +6.u.6t. +6.u.6u. +3.u.6v. +3.u.6w. +6.u.6x. +3.u.6y. +3.u.6z. +6.u.6A. +6.u.6B. +6.u.6C. +6.u.6D. +6.u.6E. +0.u.6F. +6.u.6G. +3.u.6H. +6.u.6I. +6.u.6J. +6.u.6K. +8.u.6L. +0.u.6M. +6.u.6N. +6.u.6O. +6.u.6P. +6.u.6Q. +3.u.6R. +3.u.6S. +6.u.6T. +6.u.6U. +3.u.6V. +6.u.6W. +1.u.6X. +6.u.6Y. +1.u.6Z. +0.u.6+. +1.u.6/. +1.u.70. +3.u.71. +3.u.72. +6.u.73. +1.u.74. +6.u.75. +1.u.76. +1.u.77. +3.u.78. +5.79.u. +1.u.7a. +3.u.7b. +6.u.7c. +3.u.7d. +6.u.7e. +3.u.7f. +3.u.7g. +3.u.7h. +6.u.7i. +3.u.7j. +6.u.7k. +6.u.7l. +1.u.7m. +6.u.7o. +6.u.7p. +1.u.7r. +1.u.7s. +8.u.7t. +6.u.7u. +6.u.7v. +6.u.7w. +3.u.7x. +6.u.7y. +1.u.7z. +6.u.7A. +3.u.7B. +3.u.7C. +0.u.7D. +6.u.7E. +3.u.7F. +3.u.7G. +3.u.7H. +3.u.7I. +3.u.7J. +1.u.7K. +1.u.7L. +1.u.7M. +1.u.7N. +1.u.7O. +1.u.7P. +6.u.7Q. +3.u.7R. +6.u.7S. +6.u.7T. +1.u.7U. +3.u.7V. +3.u.7W. +6.u.7X. +3.u.7Y. +3.u.7Z. +1.u.7+. +1.u.7/. +1.u.80. +1.u.81. +1.u.82. +3.u.83. +1.u.84. +1.u.85. +h.86.u. +1.u.87. +1.u.88. +8.u.89. +3.u.8a. +3.u.8b. +6.u.8c. +8.u.8d. +6.u.8e. +6.u.8f. +6.u.8g. +6.u.8h. +6.u.8i. +6.u.8j. +6.u.8k. +6.u.8l. +6.u.8m. +6.u.8n. +6.u.8o. +6.u.8p. +6.u.8q. +6.u.8r. +6.u.8s. +6.u.8t. +6.u.8u. +6.u.8v. +6.u.8w. +6.u.8x. +6.u.8y. +6.u.8z. +6.u.8A. +6.u.8B. +6.u.8C. +6.u.8D. +6.u.8E. +6.u.8F. +6.u.8G. +4.u.8H. +4.u.8I. +6.u.8J. +6.u.8K. +6.u.8L. +6.u.8M. +6.u.8N. +6.u.8O. +6.u.8P. +0.u.2p. +0.u.2p. +3.u.3b. +1.u.46. +1.u.4X. +1.u.5x. +1.u.5J. +1.u.7n. +1.u.7q. +0.v.v. +0.v.w. +0.v.x. +0.v.y. +3.v.z. +0.v.A. +0.v.B. +0.v.C. +0.v.D. +0.v.E. +0.v.F. +0.v.G. +0.v.H. +2.I.v. +2.J.v. +0.v.K. +0.v.L. +2.M.v. +0.v.N. +3.v.O. +0.v.P. +0.v.Q. +0.v.R. +3.S.v. +b.T.v. +3.v.U. +3.v.V. +0.v.W. +0.v.X. +b.Y.v. +0.v.Z. +0.v.+. +3.v./. +0.v.10. +0.v.11. +0.v.12. +0.v.13. +0.v.14. +0.v.15. +0.v.16. +0.v.17. +0.v.18. +0.v.19. +0.v.1a. +0.v.1b. +0.v.1c. +0.v.1d. +0.v.1e. +0.v.1f. +3.v.1g. +0.v.1h. +0.v.1i. +3.v.1j. +0.v.1k. +2.1l.v. +0.v.1m. +0.v.1n. +0.v.1o. +0.v.1p. +0.v.1q. +0.v.1r. +0.v.1s. +0.v.1t. +2.1u.v. +0.v.1v. +0.v.1w. +0.v.1x. +0.v.1y. +0.v.1z. +0.v.1A. +0.1B.v. +0.v.1C. +0.v.1D. +3.v.1E. +0.v.1F. +0.v.1G. +0.v.1H. +0.v.1I. +0.v.1J. +0.v.1K. +0.v.1L. +0.v.1M. +0.v.1N. +0.v.1O. +0.v.1P. +0.v.1Q. +0.v.1R. +0.v.1S. +3.v.1T. +0.v.1U. +0.v.1V. +0.v.1W. +0.v.1X. +0.v.1Y. +0.v.1Z. +0.v.1+. +0.v.1/. +0.v.20. +0.v.21. +0.v.22. +0.v.23. +0.v.24. +0.v.25. +b.26.v. +0.v.27. +4.v.28. +0.v.2a. +0.v.2b. +0.v.2l. +6.2m.v. +0.v.2p. +2.2q.v. +2.2r.v. +7.2s.v. +7.2t.v. +2.2u.v. +0.v.2v. +3.v.2w. +e.2x.v. +9.2y.v. +0.v.2z. +3.v.2A. +9.2B.v. +0.v.2C. +0.v.2D. +0.v.2E. +0.v.2F. +0.v.2G. +9.2H.v. +0.v.2I. +3.v.2J. +0.v.2K. +c.2M.v. +9.2N.v. +2.2O.v. +9.2Q.v. +7.2S.v. +0.v.2T. +1.2U.v. +9.2V.v. +1.2W.v. +0.v.2X. +d.2+.v. +0.v.30. +3.v.35. +b.36.v. +0.v.37. +0.v.38. +5.39.v. +0.v.3d. +3.v.3h. +0.v.3i. +0.v.3m. +0.v.3p. +a.3v.v. +a.3y.v. +2.3z.v. +5.3A.v. +2.3B.v. +0.v.3C. +a.3F.v. +5.3G.v. +0.v.3M. +0.v.3Q. +5.40.v. +0.v.47. +0.v.4d. +0.v.4O. +0.v.4Q. +5.4T.v. +3.v.4V. +3.v.51. +0.v.52. +a.5a.v. +2.5b.v. +5.5d.v. +0.v.5B. +0.v.5C. +0.v.5F. +0.v.5H. +0.v.5I. +f.v.5K. +7.5M.v. +3.v.5O. +0.v.5R. +0.v.5U. +0.v.5W. +1.5Y.v. +0.v.5Z. +0.v.5/. +0.v.60. +2.62.v. +2.66.v. +a.6b.v. +c.6e.v. +0.v.6F. +0.v.6M. +0.v.6+. +0.v.71. +5.79.v. +0.v.7D. +0.v.2p. +0.v.2p. +3.w.w. +0.x.w. +0.y.w. +0.w.z. +0.w.A. +0.w.B. +0.w.C. +0.w.D. +0.w.E. +0.w.F. +0.w.G. +0.w.H. +2.I.w. +2.J.w. +0.w.K. +3.w.L. +2.M.w. +0.w.N. +3.w.O. +0.w.P. +0.w.Q. +0.w.R. +3.S.w. +b.T.w. +3.w.U. +3.w.V. +0.w.W. +0.w.X. +b.Y.w. +0.w.Z. +3.w.+. +3.w./. +0.w.10. +0.w.11. +0.w.12. +0.w.13. +0.w.14. +0.w.15. +0.w.16. +0.w.17. +0.w.18. +0.w.19. +0.w.1a. +0.w.1b. +0.w.1c. +0.w.1d. +0.w.1e. +0.w.1f. +0.w.1g. +3.w.1h. +0.w.1i. +3.w.1j. +0.w.1k. +2.1l.w. +0.w.1m. +0.w.1n. +0.w.1o. +0.w.1p. +0.w.1q. +0.w.1r. +0.w.1s. +0.w.1t. +2.1u.w. +0.w.1v. +0.w.1w. +0.w.1x. +0.w.1y. +0.w.1z. +0.w.1A. +0.1B.w. +0.w.1C. +0.w.1D. +3.w.1E. +0.w.1F. +0.w.1G. +0.w.1H. +0.w.1I. +0.w.1J. +0.w.1K. +0.w.1L. +0.w.1M. +0.w.1N. +0.w.1O. +0.w.1P. +0.w.1Q. +0.w.1R. +3.w.1S. +0.w.1T. +0.w.1U. +0.w.1V. +0.w.1W. +0.w.1X. +3.w.1Y. +0.w.1Z. +0.w.1+. +0.w.1/. +0.w.20. +0.w.21. +0.w.22. +0.w.23. +0.w.24. +0.w.25. +b.26.w. +3.w.27. +4.w.28. +0.w.2a. +0.w.2b. +0.w.2l. +6.2m.w. +0.w.2p. +2.2q.w. +2.2r.w. +7.2s.w. +7.2t.w. +2.2u.w. +0.w.2v. +0.w.2w. +e.2x.w. +9.2y.w. +0.w.2z. +3.w.2A. +9.2B.w. +0.w.2C. +0.w.2D. +0.w.2E. +0.w.2F. +0.w.2G. +9.2H.w. +0.w.2I. +3.w.2J. +0.w.2K. +c.2M.w. +9.2N.w. +2.2O.w. +9.2Q.w. +7.2S.w. +0.w.2T. +1.2U.w. +9.2V.w. +1.2W.w. +0.w.2X. +b.2+.w. +0.w.30. +0.w.35. +b.36.w. +0.w.37. +0.w.38. +5.39.w. +0.w.3d. +3.w.3h. +0.w.3i. +0.w.3m. +0.w.3p. +a.3v.w. +a.3y.w. +2.3z.w. +5.3A.w. +2.3B.w. +0.w.3C. +a.3F.w. +5.3G.w. +0.w.3M. +0.w.3Q. +5.40.w. +0.w.47. +0.w.4d. +0.w.4O. +3.w.4Q. +5.4T.w. +3.w.4V. +3.w.51. +0.w.52. +a.5a.w. +2.5b.w. +5.5d.w. +0.w.5B. +0.w.5C. +0.w.5F. +3.w.5H. +0.w.5I. +f.w.5K. +7.5M.w. +0.w.5O. +0.w.5R. +0.w.5U. +0.w.5W. +1.5Y.w. +0.w.5Z. +0.w.5/. +0.w.60. +2.62.w. +2.66.w. +a.6b.w. +c.6e.w. +0.w.6F. +0.w.6M. +0.w.6+. +3.w.71. +5.79.w. +0.w.7D. +0.w.2p. +0.w.2p. +0.x.x. +0.y.x. +3.x.z. +0.x.A. +0.x.B. +0.x.C. +0.x.D. +0.x.E. +0.x.F. +0.x.G. +0.x.H. +2.I.x. +2.J.x. +0.x.K. +3.x.L. +2.M.x. +0.x.N. +3.x.O. +0.x.P. +0.x.Q. +0.x.R. +3.S.x. +b.T.x. +3.x.U. +0.x.V. +0.x.W. +0.x.X. +b.Y.x. +0.x.Z. +0.x.+. +3.x./. +0.x.10. +0.x.11. +0.x.12. +0.x.13. +0.x.14. +0.x.15. +0.x.16. +0.x.17. +0.x.18. +0.x.19. +0.x.1a. +0.x.1b. +3.x.1c. +0.x.1d. +0.x.1e. +0.x.1f. +3.x.1g. +0.x.1h. +0.x.1i. +3.x.1j. +0.x.1k. +2.1l.x. +0.x.1m. +0.x.1n. +0.x.1o. +0.x.1p. +0.x.1q. +0.x.1r. +0.x.1s. +0.x.1t. +2.1u.x. +0.x.1v. +0.x.1w. +0.x.1x. +0.x.1y. +0.x.1z. +0.x.1A. +0.1B.x. +0.x.1C. +0.x.1D. +3.x.1E. +0.x.1F. +0.x.1G. +0.x.1H. +0.x.1I. +0.x.1J. +0.x.1K. +0.x.1L. +0.x.1M. +0.x.1N. +0.x.1O. +0.x.1P. +0.x.1Q. +0.x.1R. +3.x.1S. +3.x.1T. +0.x.1U. +0.x.1V. +0.x.1W. +0.x.1X. +3.x.1Y. +0.x.1Z. +0.x.1+. +0.x.1/. +0.x.20. +0.x.21. +0.x.22. +0.x.23. +0.x.24. +0.x.25. +b.26.x. +3.x.27. +4.x.28. +0.x.2a. +0.x.2b. +0.x.2l. +6.2m.x. +0.x.2p. +2.2q.x. +2.2r.x. +7.2s.x. +7.2t.x. +2.2u.x. +0.x.2v. +0.x.2w. +e.2x.x. +9.2y.x. +0.x.2z. +3.x.2A. +9.2B.x. +0.x.2C. +0.x.2D. +0.x.2E. +0.x.2F. +0.x.2G. +9.2H.x. +0.x.2I. +3.x.2J. +0.x.2K. +c.2M.x. +9.2N.x. +2.2O.x. +9.2Q.x. +7.2S.x. +0.x.2T. +1.2U.x. +9.2V.x. +1.2W.x. +0.x.2X. +d.2+.x. +0.x.30. +3.x.35. +b.36.x. +0.x.37. +3.x.38. +5.39.x. +0.x.3d. +3.x.3h. +0.x.3i. +0.x.3m. +0.x.3p. +a.3v.x. +a.3y.x. +2.3z.x. +5.3A.x. +2.3B.x. +0.x.3C. +a.3F.x. +5.3G.x. +0.x.3M. +0.x.3Q. +5.40.x. +0.x.47. +0.x.4d. +0.x.4O. +3.x.4Q. +5.4T.x. +3.x.4V. +3.x.51. +0.x.52. +a.5a.x. +2.5b.x. +5.5d.x. +0.x.5B. +0.x.5C. +0.x.5F. +0.x.5H. +0.x.5I. +0.x.5K. +7.5M.x. +3.x.5O. +0.x.5R. +0.x.5U. +0.x.5W. +1.5Y.x. +0.x.5Z. +0.x.5/. +0.x.60. +2.62.x. +2.66.x. +a.6b.x. +c.6e.x. +0.x.6F. +0.x.6M. +0.x.6+. +3.x.71. +5.79.x. +0.x.7D. +0.x.2p. +0.x.2p. +0.y.y. +3.y.z. +0.y.A. +0.y.B. +0.y.C. +0.y.D. +0.y.E. +0.y.F. +0.y.G. +0.y.H. +2.I.y. +2.J.y. +0.y.K. +3.y.L. +2.M.y. +0.y.N. +3.y.O. +0.y.P. +0.y.Q. +0.y.R. +3.S.y. +b.T.y. +0.y.U. +0.y.V. +0.y.W. +0.y.X. +b.Y.y. +0.y.Z. +0.y.+. +3.y./. +0.y.10. +0.y.11. +0.y.12. +0.y.13. +0.y.14. +0.y.15. +0.y.16. +0.y.17. +0.y.18. +0.y.19. +0.y.1a. +0.y.1b. +0.y.1c. +0.y.1d. +0.y.1e. +0.y.1f. +3.y.1g. +0.y.1h. +0.y.1i. +3.y.1j. +0.y.1k. +2.1l.y. +0.y.1m. +0.y.1n. +0.y.1o. +0.y.1p. +0.y.1q. +0.y.1r. +0.y.1s. +0.y.1t. +2.1u.y. +0.y.1v. +0.y.1w. +0.y.1x. +0.y.1y. +0.y.1z. +0.y.1A. +0.1B.y. +0.y.1C. +0.y.1D. +0.y.1E. +0.y.1F. +0.y.1G. +0.y.1H. +0.y.1I. +0.y.1J. +0.y.1K. +0.y.1L. +0.y.1M. +0.y.1N. +0.y.1O. +0.y.1P. +0.y.1Q. +0.y.1R. +0.y.1S. +3.y.1T. +0.y.1U. +0.y.1V. +0.y.1W. +0.y.1X. +0.y.1Y. +0.y.1Z. +0.y.1+. +0.y.1/. +0.y.20. +0.y.21. +0.y.22. +0.y.23. +0.y.24. +0.y.25. +b.26.y. +0.y.27. +4.y.28. +0.y.2a. +0.y.2b. +0.y.2l. +6.2m.y. +0.y.2p. +2.2q.y. +2.2r.y. +7.2s.y. +7.2t.y. +2.2u.y. +0.y.2v. +0.y.2w. +e.2x.y. +9.2y.y. +0.y.2z. +3.y.2A. +9.2B.y. +0.y.2C. +0.y.2D. +0.y.2E. +0.y.2F. +0.y.2G. +9.2H.y. +0.y.2I. +3.y.2J. +0.y.2K. +c.2M.y. +9.2N.y. +2.2O.y. +9.2Q.y. +7.2S.y. +0.y.2T. +1.2U.y. +9.2V.y. +1.2W.y. +0.y.2X. +d.2+.y. +0.y.30. +3.y.35. +b.36.y. +0.y.37. +0.y.38. +5.39.y. +0.y.3d. +3.y.3h. +0.y.3i. +0.y.3m. +0.y.3p. +a.3v.y. +a.3y.y. +2.3z.y. +5.3A.y. +2.3B.y. +0.y.3C. +a.3F.y. +5.3G.y. +0.y.3M. +0.y.3Q. +5.40.y. +0.y.47. +0.y.4d. +0.y.4O. +0.y.4Q. +5.4T.y. +3.y.4V. +3.y.51. +0.y.52. +a.5a.y. +2.5b.y. +5.5d.y. +0.y.5B. +0.y.5C. +0.y.5F. +0.y.5H. +0.y.5I. +f.y.5K. +7.5M.y. +3.y.5O. +0.y.5R. +0.y.5U. +0.y.5W. +1.5Y.y. +0.y.5Z. +0.y.5/. +0.y.60. +2.62.y. +2.66.y. +a.6b.y. +c.6e.y. +0.y.6F. +0.y.6M. +0.y.6+. +0.y.71. +5.79.y. +0.y.7D. +0.y.2p. +0.y.2p. +0.z.z. +0.A.z. +0.B.z. +0.C.z. +0.D.z. +0.E.z. +0.F.z. +3.G.z. +0.H.z. +2.I.z. +2.J.z. +0.K.z. +0.L.z. +2.M.z. +0.N.z. +0.O.z. +0.P.z. +0.Q.z. +0.R.z. +3.S.z. +b.T.z. +0.U.z. +0.V.z. +0.W.z. +0.X.z. +b.Y.z. +3.Z.z. +0.+.z. +0./.z. +0.10.z. +0.11.z. +0.12.z. +0.13.z. +0.14.z. +0.15.z. +0.16.z. +0.17.z. +0.18.z. +0.19.z. +0.1a.z. +0.1b.z. +0.1c.z. +0.1d.z. +0.1e.z. +0.1f.z. +f.1g.z. +0.1h.z. +0.1i.z. +0.1j.z. +0.1k.z. +2.1l.z. +0.1m.z. +0.1n.z. +0.1o.z. +0.1p.z. +0.1q.z. +0.1r.z. +0.1s.z. +0.1t.z. +2.1u.z. +0.1v.z. +0.1w.z. +0.1x.z. +0.1y.z. +0.1z.z. +0.1A.z. +3.1B.z. +0.1C.z. +0.1D.z. +0.1E.z. +0.1F.z. +0.1G.z. +0.1H.z. +0.1I.z. +0.1J.z. +0.1K.z. +0.1L.z. +0.1M.z. +0.1N.z. +0.1O.z. +0.1P.z. +0.1Q.z. +0.1R.z. +0.1S.z. +d.1T.z. +0.1U.z. +0.1V.z. +0.z.1W. +0.1X.z. +0.1Y.z. +0.1Z.z. +d.1+.z. +0.1/.z. +0.20.z. +d.21.z. +0.22.z. +0.23.z. +0.24.z. +0.25.z. +b.26.z. +0.27.z. +4.28.z. +0.2a.z. +3.2b.z. +0.2l.z. +6.2m.z. +d.2p.z. +2.2q.z. +2.2r.z. +7.2s.z. +7.2t.z. +2.2u.z. +d.2v.z. +0.2w.z. +e.2x.z. +9.2y.z. +3.2z.z. +d.2A.z. +9.2B.z. +3.2C.z. +d.2D.z. +d.2E.z. +0.2F.z. +0.2G.z. +9.2H.z. +d.2I.z. +d.2J.z. +0.2K.z. +c.2M.z. +9.2N.z. +2.2O.z. +9.2Q.z. +7.2S.z. +d.2T.z. +1.2U.z. +9.2V.z. +1.2W.z. +0.2X.z. +b.2+.z. +d.30.z. +0.35.z. +b.36.z. +0.z.37. +d.38.z. +5.39.z. +5.3d.z. +0.3h.z. +5.3i.z. +0.z.3m. +5.3p.z. +a.3v.z. +a.3y.z. +2.3z.z. +5.3A.z. +2.3B.z. +5.3C.z. +a.3F.z. +5.3G.z. +0.z.3M. +0.z.3Q. +5.40.z. +0.z.47. +5.4d.z. +0.z.4O. +3.4Q.z. +5.4T.z. +3.z.4V. +5.51.z. +0.52.z. +a.5a.z. +2.5b.z. +5.5d.z. +5.5B.z. +5.5C.z. +5.5F.z. +0.5H.z. +0.5I.z. +f.z.5K. +7.5M.z. +5.5O.z. +5.5R.z. +0.z.5U. +3.5W.z. +1.5Y.z. +5.5Z.z. +5.5/.z. +0.60.z. +2.62.z. +2.66.z. +a.6b.z. +c.6e.z. +0.z.6F. +0.6M.z. +d.6+.z. +d.71.z. +5.79.z. +3.z.7D. +d.2p.z. +d.2p.z. +0.A.A. +0.A.B. +0.A.C. +0.A.D. +0.A.E. +0.A.F. +0.G.A. +0.A.H. +2.I.A. +2.J.A. +0.A.K. +0.A.L. +2.M.A. +0.A.N. +0.A.O. +0.A.P. +0.A.Q. +0.A.R. +3.S.A. +b.T.A. +0.A.U. +0.A.V. +0.A.W. +0.A.X. +b.Y.A. +0.A.Z. +0.A.+. +0.A./. +0.A.10. +0.A.11. +0.A.12. +0.A.13. +0.A.14. +0.A.15. +0.A.16. +0.A.17. +3.A.18. +0.A.19. +0.A.1a. +0.A.1b. +0.A.1c. +3.A.1d. +0.A.1e. +0.A.1f. +0.A.1g. +3.A.1h. +3.A.1i. +3.A.1j. +0.A.1k. +2.1l.A. +0.A.1m. +0.A.1n. +0.A.1o. +0.A.1p. +0.A.1q. +0.A.1r. +0.A.1s. +0.A.1t. +2.1u.A. +0.A.1v. +3.A.1w. +0.A.1x. +0.A.1y. +0.A.1z. +0.A.1A. +0.1B.A. +0.A.1C. +0.A.1D. +0.A.1E. +0.A.1F. +0.A.1G. +0.A.1H. +0.A.1I. +0.A.1J. +0.A.1K. +0.A.1L. +0.A.1M. +0.A.1N. +0.A.1O. +0.A.1P. +3.A.1Q. +0.A.1R. +0.A.1S. +0.A.1T. +0.A.1U. +0.A.1V. +0.A.1W. +0.A.1X. +0.A.1Y. +0.A.1Z. +0.A.1+. +0.A.1/. +0.A.20. +0.A.21. +0.A.22. +0.A.23. +0.A.24. +0.A.25. +b.26.A. +0.A.27. +4.A.28. +0.A.2a. +0.A.2b. +0.A.2l. +6.2m.A. +0.A.2p. +2.2q.A. +2.2r.A. +7.2s.A. +7.2t.A. +2.2u.A. +3.A.2v. +0.A.2w. +e.2x.A. +9.2y.A. +0.A.2z. +3.A.2A. +9.2B.A. +0.A.2C. +0.A.2D. +3.A.2E. +0.A.2F. +0.A.2G. +9.2H.A. +0.A.2I. +3.A.2J. +0.A.2K. +c.2M.A. +9.2N.A. +2.2O.A. +9.2Q.A. +7.2S.A. +0.A.2T. +1.2U.A. +9.2V.A. +1.2W.A. +0.A.2X. +d.2+.A. +0.A.30. +0.A.35. +b.36.A. +0.A.37. +3.A.38. +5.39.A. +0.A.3d. +3.A.3h. +0.A.3i. +0.A.3m. +0.A.3p. +a.3v.A. +a.3y.A. +2.3z.A. +5.3A.A. +2.3B.A. +0.A.3C. +a.3F.A. +5.3G.A. +0.A.3M. +0.A.3Q. +5.40.A. +0.A.47. +0.A.4d. +3.A.4O. +3.A.4Q. +5.4T.A. +3.A.4V. +0.A.51. +0.A.52. +a.5a.A. +2.5b.A. +5.5d.A. +0.A.5B. +0.A.5C. +0.A.5F. +3.A.5H. +0.A.5I. +f.A.5K. +7.5M.A. +0.A.5O. +3.A.5R. +0.A.5U. +0.A.5W. +1.5Y.A. +0.A.5Z. +0.A.5/. +0.A.60. +2.62.A. +2.66.A. +a.6b.A. +c.6e.A. +0.A.6F. +0.A.6M. +0.A.6+. +3.A.71. +5.79.A. +3.A.7D. +0.A.2p. +0.A.2p. +0.B.B. +0.B.C. +0.B.D. +0.B.E. +0.B.F. +0.G.B. +0.B.H. +2.I.B. +2.J.B. +0.B.K. +0.B.L. +2.M.B. +0.B.N. +0.B.O. +0.B.P. +0.B.Q. +0.B.R. +3.S.B. +b.T.B. +0.B.U. +0.B.V. +0.B.W. +0.B.X. +b.Y.B. +0.B.Z. +0.B.+. +0.B./. +0.B.10. +0.B.11. +0.B.12. +0.B.13. +0.B.14. +0.B.15. +0.B.16. +0.B.17. +0.B.18. +0.B.19. +0.B.1a. +0.B.1b. +0.B.1c. +3.B.1d. +0.B.1e. +0.B.1f. +0.B.1g. +3.B.1h. +3.B.1i. +3.B.1j. +0.B.1k. +2.1l.B. +0.B.1m. +0.B.1n. +0.B.1o. +0.B.1p. +0.B.1q. +0.B.1r. +0.B.1s. +0.B.1t. +2.1u.B. +0.B.1v. +3.B.1w. +0.B.1x. +0.B.1y. +0.B.1z. +0.B.1A. +0.1B.B. +0.B.1C. +0.B.1D. +0.B.1E. +0.B.1F. +0.B.1G. +0.B.1H. +0.B.1I. +0.B.1J. +0.B.1K. +0.B.1L. +0.B.1M. +0.B.1N. +0.B.1O. +0.B.1P. +0.B.1Q. +0.B.1R. +0.B.1S. +0.B.1T. +0.B.1U. +0.B.1V. +0.B.1W. +0.B.1X. +0.B.1Y. +0.B.1Z. +0.B.1+. +0.B.1/. +0.B.20. +0.B.21. +0.B.22. +0.B.23. +0.B.24. +0.B.25. +b.26.B. +0.B.27. +4.B.28. +0.B.2a. +0.B.2b. +0.B.2l. +6.2m.B. +0.B.2p. +2.2q.B. +2.2r.B. +7.2s.B. +7.2t.B. +2.2u.B. +0.B.2v. +0.B.2w. +e.2x.B. +9.2y.B. +0.B.2z. +3.B.2A. +9.2B.B. +0.B.2C. +0.B.2D. +3.B.2E. +0.B.2F. +0.B.2G. +9.2H.B. +0.B.2I. +3.B.2J. +0.B.2K. +c.2M.B. +9.2N.B. +2.2O.B. +9.2Q.B. +7.2S.B. +0.B.2T. +1.2U.B. +9.2V.B. +1.2W.B. +0.B.2X. +b.2+.B. +0.B.30. +0.B.35. +b.36.B. +0.B.37. +0.B.38. +5.39.B. +0.B.3d. +3.B.3h. +0.B.3i. +0.B.3m. +0.B.3p. +a.3v.B. +a.3y.B. +2.3z.B. +5.3A.B. +2.3B.B. +0.B.3C. +a.3F.B. +5.3G.B. +0.B.3M. +0.B.3Q. +5.40.B. +0.B.47. +0.B.4d. +0.B.4O. +3.B.4Q. +5.4T.B. +3.B.4V. +0.B.51. +0.B.52. +a.5a.B. +2.5b.B. +5.5d.B. +0.B.5B. +0.B.5C. +0.B.5F. +3.B.5H. +0.B.5I. +f.B.5K. +7.5M.B. +0.B.5O. +3.B.5R. +0.B.5U. +0.B.5W. +1.5Y.B. +0.B.5Z. +0.B.5/. +0.B.60. +2.62.B. +2.66.B. +a.6b.B. +c.6e.B. +0.B.6F. +0.B.6M. +0.B.6+. +3.B.71. +5.79.B. +3.B.7D. +0.B.2p. +0.B.2p. +0.C.C. +0.C.D. +0.E.C. +0.C.F. +3.G.C. +0.C.H. +2.I.C. +2.J.C. +0.C.K. +0.C.L. +2.M.C. +0.C.N. +0.C.O. +0.C.P. +0.C.Q. +0.C.R. +3.S.C. +b.T.C. +0.C.U. +0.C.V. +0.C.W. +0.C.X. +b.Y.C. +0.C.Z. +0.C.+. +0.C./. +0.C.10. +0.C.11. +0.C.12. +0.C.13. +0.C.14. +0.C.15. +0.C.16. +0.C.17. +3.C.18. +0.C.19. +3.C.1a. +0.C.1b. +0.C.1c. +3.C.1d. +0.C.1e. +0.C.1f. +0.C.1g. +3.C.1h. +3.C.1i. +3.C.1j. +0.C.1k. +2.1l.C. +0.C.1m. +0.C.1n. +0.C.1o. +0.C.1p. +0.C.1q. +0.C.1r. +3.C.1s. +0.C.1t. +2.1u.C. +0.C.1v. +3.C.1w. +0.C.1x. +0.C.1y. +0.C.1z. +0.C.1A. +0.1B.C. +0.C.1C. +0.C.1D. +0.C.1E. +0.C.1F. +0.C.1G. +0.C.1H. +0.C.1I. +0.C.1J. +0.C.1K. +0.C.1L. +0.C.1M. +0.C.1N. +0.C.1O. +0.C.1P. +0.C.1Q. +0.C.1R. +0.C.1S. +0.C.1T. +3.C.1U. +0.C.1V. +0.C.1W. +0.C.1X. +0.C.1Y. +0.C.1Z. +0.C.1+. +0.C.1/. +0.C.20. +0.C.21. +0.C.22. +0.C.23. +0.C.24. +0.C.25. +b.26.C. +0.C.27. +4.C.28. +0.C.2a. +0.C.2b. +0.C.2l. +6.2m.C. +0.C.2p. +2.2q.C. +2.2r.C. +7.2s.C. +7.2t.C. +2.2u.C. +3.C.2v. +0.C.2w. +e.2x.C. +9.2y.C. +0.C.2z. +3.C.2A. +9.2B.C. +0.C.2C. +0.C.2D. +3.C.2E. +3.C.2F. +0.C.2G. +9.2H.C. +0.C.2I. +3.C.2J. +0.C.2K. +c.2M.C. +9.2N.C. +2.2O.C. +9.2Q.C. +7.2S.C. +0.C.2T. +1.2U.C. +9.2V.C. +1.2W.C. +0.C.2X. +b.2+.C. +0.C.30. +0.C.35. +b.36.C. +0.C.37. +3.C.38. +5.39.C. +0.C.3d. +3.C.3h. +3.C.3i. +0.C.3m. +0.C.3p. +a.3v.C. +a.3y.C. +2.3z.C. +5.3A.C. +2.3B.C. +0.C.3C. +a.3F.C. +5.3G.C. +0.C.3M. +0.C.3Q. +5.40.C. +0.C.47. +0.C.4d. +0.C.4O. +3.C.4Q. +5.4T.C. +3.C.4V. +0.C.51. +0.C.52. +a.5a.C. +2.5b.C. +5.5d.C. +0.C.5B. +0.C.5C. +0.C.5F. +3.C.5H. +0.C.5I. +f.C.5K. +7.5M.C. +0.C.5O. +3.C.5R. +0.C.5U. +0.C.5W. +1.5Y.C. +0.C.5Z. +0.C.5/. +0.C.60. +2.62.C. +2.66.C. +a.6b.C. +c.6e.C. +0.C.6F. +0.C.6M. +0.C.6+. +3.C.71. +5.79.C. +3.C.7D. +0.C.2p. +0.C.2p. +0.D.D. +3.E.D. +0.D.F. +0.G.D. +0.D.H. +2.I.D. +2.J.D. +0.D.K. +0.D.L. +2.M.D. +0.D.N. +0.D.O. +0.D.P. +0.D.Q. +0.D.R. +3.S.D. +b.T.D. +0.D.U. +0.D.V. +0.D.W. +0.D.X. +b.Y.D. +3.D.Z. +0.D.+. +0.D./. +0.D.10. +0.D.11. +0.D.12. +0.D.13. +0.D.14. +0.D.15. +0.D.16. +0.D.17. +3.D.18. +0.D.19. +3.D.1a. +0.D.1b. +0.D.1c. +3.D.1d. +0.D.1e. +0.D.1f. +0.D.1g. +3.D.1h. +3.D.1i. +3.D.1j. +0.D.1k. +2.1l.D. +0.D.1m. +0.D.1n. +0.D.1o. +0.D.1p. +0.D.1q. +0.D.1r. +0.D.1s. +0.D.1t. +2.1u.D. +0.D.1v. +3.D.1w. +0.D.1x. +0.D.1y. +0.D.1z. +0.D.1A. +0.1B.D. +0.D.1C. +0.D.1D. +0.D.1E. +0.D.1F. +0.D.1G. +0.D.1H. +0.D.1I. +0.D.1J. +0.D.1K. +0.D.1L. +0.D.1M. +0.D.1N. +0.D.1O. +0.D.1P. +0.D.1Q. +0.D.1R. +0.D.1S. +0.D.1T. +3.D.1U. +0.D.1V. +0.D.1W. +0.D.1X. +0.D.1Y. +0.D.1Z. +0.D.1+. +0.D.1/. +0.D.20. +0.D.21. +0.D.22. +0.D.23. +0.D.24. +0.D.25. +b.26.D. +0.D.27. +4.D.28. +0.D.2a. +0.D.2b. +0.D.2l. +6.2m.D. +0.D.2p. +2.2q.D. +2.2r.D. +7.2s.D. +7.2t.D. +2.2u.D. +0.D.2v. +0.D.2w. +e.2x.D. +9.2y.D. +0.D.2z. +3.D.2A. +9.2B.D. +0.D.2C. +0.D.2D. +3.D.2E. +3.D.2F. +0.D.2G. +9.2H.D. +0.D.2I. +3.D.2J. +0.D.2K. +c.2M.D. +9.2N.D. +2.2O.D. +9.2Q.D. +7.2S.D. +0.D.2T. +1.2U.D. +9.2V.D. +1.2W.D. +0.D.2X. +b.2+.D. +0.D.30. +0.D.35. +b.36.D. +0.D.37. +0.D.38. +5.39.D. +0.D.3d. +3.D.3h. +3.D.3i. +0.D.3m. +0.D.3p. +a.3v.D. +a.3y.D. +2.3z.D. +5.3A.D. +2.3B.D. +0.D.3C. +a.3F.D. +5.3G.D. +0.D.3M. +0.D.3Q. +5.40.D. +0.D.47. +0.D.4d. +0.D.4O. +3.D.4Q. +5.4T.D. +3.D.4V. +0.D.51. +0.D.52. +a.5a.D. +2.5b.D. +5.5d.D. +0.D.5B. +0.D.5C. +0.D.5F. +3.D.5H. +0.D.5I. +f.D.5K. +7.5M.D. +0.D.5O. +3.D.5R. +0.D.5U. +0.D.5W. +1.5Y.D. +0.D.5Z. +0.D.5/. +0.D.60. +2.62.D. +2.66.D. +a.6b.D. +c.6e.D. +0.D.6F. +0.D.6M. +0.D.6+. +3.D.71. +5.79.D. +3.D.7D. +0.D.2p. +0.D.2p. +0.E.E. +0.E.F. +0.G.E. +0.E.H. +2.I.E. +2.J.E. +0.E.K. +0.E.L. +2.M.E. +0.E.N. +0.E.O. +0.E.P. +0.E.Q. +0.E.R. +3.S.E. +b.T.E. +0.E.U. +0.E.V. +0.E.W. +0.E.X. +b.Y.E. +3.E.Z. +0.E.+. +0.E./. +0.E.10. +0.E.11. +0.E.12. +0.E.13. +0.E.14. +0.E.15. +0.E.16. +0.E.17. +3.E.18. +0.E.19. +0.E.1a. +0.E.1b. +0.E.1c. +3.E.1d. +0.E.1e. +0.E.1f. +0.E.1g. +3.E.1h. +3.E.1i. +3.E.1j. +0.E.1k. +2.1l.E. +0.E.1m. +0.E.1n. +0.E.1o. +0.E.1p. +0.E.1q. +3.E.1r. +3.E.1s. +0.E.1t. +2.1u.E. +0.E.1v. +3.E.1w. +0.E.1x. +0.E.1y. +0.E.1z. +0.E.1A. +0.1B.E. +0.E.1C. +0.E.1D. +0.E.1E. +0.E.1F. +0.E.1G. +0.E.1H. +0.E.1I. +0.E.1J. +0.E.1K. +0.E.1L. +0.E.1M. +0.E.1N. +0.E.1O. +0.E.1P. +0.E.1Q. +0.E.1R. +0.E.1S. +3.E.1T. +3.E.1U. +0.E.1V. +0.E.1W. +0.E.1X. +0.E.1Y. +0.E.1Z. +0.E.1+. +0.E.1/. +0.E.20. +0.E.21. +0.E.22. +0.E.23. +0.E.24. +0.E.25. +b.26.E. +0.E.27. +4.E.28. +0.E.2a. +0.E.2b. +0.E.2l. +6.2m.E. +0.E.2p. +2.2q.E. +2.2r.E. +7.2s.E. +7.2t.E. +2.2u.E. +3.E.2v. +0.E.2w. +e.2x.E. +9.2y.E. +0.E.2z. +3.E.2A. +9.2B.E. +0.E.2C. +0.E.2D. +3.E.2E. +3.E.2F. +0.E.2G. +9.2H.E. +0.E.2I. +3.E.2J. +0.E.2K. +c.2M.E. +9.2N.E. +2.2O.E. +9.2Q.E. +7.2S.E. +0.E.2T. +1.2U.E. +9.2V.E. +1.2W.E. +0.E.2X. +b.2+.E. +0.E.30. +0.E.35. +b.36.E. +0.E.37. +3.E.38. +5.39.E. +0.E.3d. +3.E.3h. +3.E.3i. +0.E.3m. +0.E.3p. +a.3v.E. +a.3y.E. +2.3z.E. +5.3A.E. +2.3B.E. +0.E.3C. +a.3F.E. +5.3G.E. +0.E.3M. +0.E.3Q. +5.40.E. +0.E.47. +0.E.4d. +3.E.4O. +3.E.4Q. +5.4T.E. +3.E.4V. +0.E.51. +0.E.52. +a.5a.E. +2.5b.E. +5.5d.E. +0.E.5B. +0.E.5C. +0.E.5F. +3.E.5H. +0.E.5I. +f.E.5K. +7.5M.E. +0.E.5O. +3.E.5R. +0.E.5U. +0.E.5W. +1.5Y.E. +0.E.5Z. +0.E.5/. +0.E.60. +2.62.E. +2.66.E. +a.6b.E. +c.6e.E. +0.E.6F. +0.E.6M. +0.E.6+. +3.E.71. +5.79.E. +3.E.7D. +0.E.2p. +0.E.2p. +3.F.F. +3.G.F. +0.H.F. +2.I.F. +2.J.F. +0.K.F. +0.L.F. +2.M.F. +0.N.F. +0.O.F. +0.P.F. +0.Q.F. +0.R.F. +3.S.F. +b.T.F. +0.U.F. +0.V.F. +0.W.F. +0.X.F. +b.Y.F. +3.F.Z. +0.+.F. +0./.F. +0.10.F. +0.11.F. +0.12.F. +0.13.F. +0.14.F. +3.15.F. +0.16.F. +0.17.F. +0.18.F. +0.19.F. +0.1a.F. +0.1b.F. +3.1c.F. +0.1d.F. +0.1e.F. +0.F.1f. +3.F.1g. +3.F.1h. +3.F.1i. +0.1j.F. +0.1k.F. +2.1l.F. +0.1m.F. +0.1n.F. +0.1o.F. +0.1p.F. +0.1q.F. +0.1r.F. +0.1s.F. +3.1t.F. +2.1u.F. +0.1v.F. +0.1w.F. +0.1x.F. +0.1y.F. +0.1z.F. +0.1A.F. +0.1B.F. +0.1C.F. +0.1D.F. +3.1E.F. +0.1F.F. +0.1G.F. +0.1H.F. +0.1I.F. +0.1J.F. +3.1K.F. +0.1L.F. +0.1M.F. +3.1N.F. +0.1O.F. +0.1P.F. +3.F.1Q. +0.1R.F. +3.1S.F. +3.F.1T. +3.F.1U. +0.F.1V. +0.F.1W. +0.F.1X. +0.F.1Y. +0.F.1Z. +0.F.1+. +0.F.1/. +0.F.20. +0.F.21. +0.F.22. +0.F.23. +0.F.24. +0.F.25. +b.26.F. +0.F.27. +4.F.28. +0.F.2a. +0.F.2b. +0.F.2l. +6.2m.F. +3.F.2p. +2.2q.F. +2.2r.F. +7.2s.F. +7.2t.F. +2.2u.F. +3.F.2v. +3.F.2w. +e.2x.F. +9.2y.F. +0.F.2z. +3.F.2A. +9.2B.F. +0.F.2C. +0.F.2D. +3.F.2E. +3.F.2F. +0.F.2G. +9.2H.F. +0.F.2I. +3.F.2J. +0.F.2K. +c.2M.F. +9.2N.F. +2.2O.F. +9.2Q.F. +7.2S.F. +0.F.2T. +1.2U.F. +9.2V.F. +1.2W.F. +0.F.2X. +d.2+.F. +0.F.30. +0.F.35. +b.36.F. +0.F.37. +3.F.38. +5.39.F. +0.F.3d. +3.F.3h. +3.F.3i. +0.F.3m. +0.F.3p. +a.3v.F. +a.3y.F. +2.3z.F. +5.3A.F. +2.3B.F. +0.F.3C. +a.3F.F. +5.3G.F. +0.F.3M. +0.F.3Q. +5.40.F. +0.F.47. +0.F.4d. +3.F.4O. +3.F.4Q. +5.4T.F. +3.F.4V. +0.F.51. +0.F.52. +a.5a.F. +2.5b.F. +5.5d.F. +0.F.5B. +0.F.5C. +5.5F.F. +3.F.5H. +0.F.5I. +f.F.5K. +7.5M.F. +0.F.5O. +3.F.5R. +0.F.5U. +0.F.5W. +1.5Y.F. +0.F.5Z. +0.F.5/. +0.F.60. +2.62.F. +2.66.F. +a.6b.F. +c.6e.F. +0.F.6F. +0.F.6M. +0.F.6+. +3.F.71. +5.79.F. +3.F.7D. +3.F.2p. +3.F.2p. +0.G.G. +0.G.H. +2.I.G. +2.J.G. +0.G.K. +3.G.L. +2.M.G. +0.G.N. +3.G.O. +0.G.P. +0.G.Q. +0.G.R. +3.S.G. +b.T.G. +3.G.U. +3.G.V. +3.G.W. +0.G.X. +b.Y.G. +0.G.Z. +3.G.+. +3.G./. +0.G.10. +0.G.11. +0.G.12. +0.G.13. +0.G.14. +0.G.15. +0.G.16. +0.G.17. +0.G.18. +0.G.19. +0.G.1a. +0.G.1b. +3.G.1c. +0.G.1d. +3.G.1e. +0.G.1f. +3.G.1g. +0.G.1h. +0.G.1i. +3.G.1j. +0.G.1k. +2.1l.G. +0.G.1m. +0.G.1n. +0.G.1o. +0.G.1p. +0.G.1q. +0.G.1r. +3.G.1s. +0.G.1t. +2.1u.G. +0.G.1v. +0.G.1w. +0.G.1x. +0.G.1y. +0.G.1z. +0.G.1A. +0.1B.G. +0.G.1C. +0.G.1D. +3.G.1E. +0.G.1F. +0.G.1G. +0.G.1H. +0.G.1I. +0.G.1J. +0.G.1K. +0.G.1L. +0.G.1M. +0.G.1N. +0.G.1O. +0.G.1P. +3.G.1Q. +0.G.1R. +3.G.1S. +3.G.1T. +0.G.1U. +0.G.1V. +0.G.1W. +0.G.1X. +3.G.1Y. +3.G.1Z. +0.G.1+. +0.G.1/. +0.G.20. +0.G.21. +0.G.22. +0.G.23. +0.G.24. +0.G.25. +b.26.G. +3.G.27. +4.G.28. +0.G.2a. +0.G.2b. +0.G.2l. +6.2m.G. +0.G.2p. +2.2q.G. +2.2r.G. +7.2s.G. +7.2t.G. +2.2u.G. +0.G.2v. +3.G.2w. +e.2x.G. +9.2y.G. +0.G.2z. +3.G.2A. +9.2B.G. +0.G.2C. +0.G.2D. +3.G.2E. +0.G.2F. +0.G.2G. +9.2H.G. +0.G.2I. +3.G.2J. +0.G.2K. +c.2M.G. +9.2N.G. +2.2O.G. +9.2Q.G. +7.2S.G. +0.G.2T. +1.2U.G. +9.2V.G. +1.2W.G. +3.G.2X. +b.2+.G. +0.G.30. +3.G.35. +b.36.G. +0.G.37. +3.G.38. +5.39.G. +0.G.3d. +3.G.3h. +0.G.3i. +0.G.3m. +0.G.3p. +a.3v.G. +a.3y.G. +2.3z.G. +5.3A.G. +2.3B.G. +0.G.3C. +a.3F.G. +5.3G.G. +0.G.3M. +0.G.3Q. +5.40.G. +0.G.47. +0.G.4d. +0.G.4O. +3.G.4Q. +5.4T.G. +3.G.4V. +0.G.51. +0.G.52. +a.5a.G. +2.5b.G. +5.5d.G. +0.G.5B. +0.G.5C. +0.G.5F. +0.G.5H. +0.G.5I. +f.G.5K. +7.5M.G. +3.G.5O. +0.G.5R. +0.G.5U. +0.G.5W. +1.5Y.G. +0.G.5Z. +0.G.5/. +0.G.60. +2.62.G. +2.66.G. +a.6b.G. +c.6e.G. +0.G.6F. +0.G.6M. +0.G.6+. +3.G.71. +5.79.G. +3.G.7D. +0.G.2p. +0.G.2p. +0.H.H. +2.I.H. +2.J.H. +0.K.H. +0.L.H. +2.M.H. +0.N.H. +0.O.H. +0.H.P. +0.H.Q. +0.H.R. +3.S.H. +b.T.H. +0.U.H. +0.V.H. +0.W.H. +0.X.H. +b.Y.H. +0.H.Z. +0.+.H. +0./.H. +0.H.10. +0.H.11. +0.H.12. +0.H.13. +0.H.14. +0.H.15. +0.H.16. +0.H.17. +3.H.18. +0.H.19. +0.H.1a. +0.H.1b. +0.H.1c. +3.H.1d. +0.H.1e. +0.H.1f. +0.H.1g. +3.H.1h. +3.H.1i. +0.1j.H. +0.1k.H. +2.1l.H. +0.1m.H. +0.1n.H. +0.1o.H. +0.H.1p. +3.H.1q. +0.H.1r. +0.H.1s. +0.1t.H. +2.1u.H. +0.H.1v. +3.H.1w. +0.H.1x. +0.H.1y. +0.H.1z. +0.H.1A. +0.1B.H. +0.H.1C. +0.H.1D. +0.1E.H. +0.H.1F. +0.H.1G. +0.H.1H. +0.H.1I. +0.H.1J. +0.H.1K. +0.H.1L. +0.H.1M. +0.H.1N. +0.H.1O. +0.H.1P. +3.H.1Q. +0.H.1R. +0.H.1S. +0.H.1T. +3.H.1U. +0.H.1V. +0.H.1W. +0.H.1X. +0.H.1Y. +0.H.1Z. +0.H.1+. +0.H.1/. +0.H.20. +0.H.21. +0.H.22. +0.H.23. +0.H.24. +0.H.25. +b.26.H. +0.H.27. +4.H.28. +0.H.2a. +0.H.2b. +0.H.2l. +6.2m.H. +0.H.2p. +2.2q.H. +2.2r.H. +7.2s.H. +7.2t.H. +2.2u.H. +0.H.2v. +0.H.2w. +e.2x.H. +9.2y.H. +0.H.2z. +3.H.2A. +9.2B.H. +0.H.2C. +0.H.2D. +3.H.2E. +0.H.2F. +0.H.2G. +9.2H.H. +0.H.2I. +3.H.2J. +0.H.2K. +c.2M.H. +9.2N.H. +2.2O.H. +9.2Q.H. +7.2S.H. +0.H.2T. +1.2U.H. +9.2V.H. +1.2W.H. +0.H.2X. +d.2+.H. +0.H.30. +0.H.35. +3.36.H. +0.H.37. +0.H.38. +5.39.H. +0.H.3d. +3.H.3h. +0.H.3i. +0.H.3m. +0.H.3p. +a.3v.H. +a.3y.H. +2.3z.H. +5.3A.H. +2.3B.H. +0.H.3C. +a.3F.H. +5.3G.H. +0.H.3M. +0.H.3Q. +5.40.H. +0.H.47. +0.H.4d. +3.H.4O. +3.H.4Q. +5.4T.H. +3.H.4V. +0.H.51. +0.H.52. +a.5a.H. +2.5b.H. +5.5d.H. +0.H.5B. +0.H.5C. +0.H.5F. +3.H.5H. +0.H.5I. +f.H.5K. +7.5M.H. +0.H.5O. +0.H.5R. +0.H.5U. +0.H.5W. +1.5Y.H. +0.H.5Z. +0.H.5/. +0.H.60. +2.62.H. +2.66.H. +a.6b.H. +c.6e.H. +0.H.6F. +0.H.6M. +0.H.6+. +3.H.71. +5.79.H. +3.H.7D. +0.H.2p. +0.H.2p. +2.I.I. +2.J.I. +2.I.K. +2.I.L. +2.I.M. +2.I.N. +2.I.O. +2.I.P. +2.I.Q. +2.I.R. +3.I.S. +2.I.T. +2.I.U. +2.I.V. +2.I.W. +2.I.X. +2.I.Y. +2.I.Z. +2.I.+. +2.I./. +2.I.10. +2.I.11. +2.I.12. +2.I.13. +2.I.14. +2.I.15. +2.I.16. +2.I.17. +2.I.18. +2.I.19. +2.I.1a. +2.I.1b. +2.I.1c. +2.I.1d. +2.I.1e. +2.I.1f. +2.I.1g. +2.I.1h. +2.I.1i. +2.I.1j. +2.I.1k. +2.1l.I. +2.I.1m. +2.I.1n. +2.I.1o. +2.I.1p. +2.I.1q. +2.I.1r. +2.I.1s. +2.I.1t. +2.1u.I. +2.I.1v. +2.I.1w. +2.I.1x. +2.I.1y. +2.I.1z. +2.I.1A. +2.I.1B. +2.I.1C. +2.I.1D. +2.I.1E. +2.I.1F. +2.I.1G. +2.I.1H. +2.I.1I. +2.I.1J. +2.I.1K. +2.I.1L. +2.I.1M. +2.I.1N. +2.I.1O. +2.I.1P. +2.I.1Q. +2.I.1R. +2.I.1S. +2.I.1T. +2.I.1U. +2.I.1V. +2.I.1W. +2.I.1X. +2.I.1Y. +2.I.1Z. +2.I.1+. +2.I.1/. +2.I.20. +2.I.21. +2.I.22. +2.I.23. +2.I.24. +2.I.25. +2.I.26. +2.I.27. +2.I.28. +2.I.2a. +2.I.2b. +2.I.2l. +6.2m.I. +2.I.2p. +2.I.2q. +2.I.2r. +7.2s.I. +7.2t.I. +2.2u.I. +2.I.2v. +2.I.2w. +e.2x.I. +2.I.2y. +2.I.2z. +2.I.2A. +2.I.2B. +2.I.2C. +2.I.2D. +2.I.2E. +2.I.2F. +2.I.2G. +2.I.2H. +2.I.2I. +2.I.2J. +2.I.2K. +c.2M.I. +2.I.2N. +2.I.2O. +2.I.2Q. +7.2S.I. +2.I.2T. +1.2U.I. +2.I.2V. +1.2W.I. +2.I.2X. +2.I.2+. +2.I.30. +2.I.35. +2.I.36. +2.I.37. +2.I.38. +2.I.39. +2.I.3d. +2.I.3h. +2.I.3i. +2.I.3m. +2.I.3p. +a.3v.I. +a.3y.I. +2.3z.I. +2.I.3A. +2.3B.I. +2.I.3C. +a.3F.I. +2.I.3G. +2.I.3M. +2.I.3Q. +2.I.40. +2.I.47. +2.I.4d. +2.I.4O. +2.I.4Q. +5.4T.I. +2.I.4V. +2.I.51. +2.I.52. +a.5a.I. +2.5b.I. +2.I.5d. +2.I.5B. +2.I.5C. +2.I.5F. +2.I.5H. +2.I.5I. +f.I.5K. +7.5M.I. +2.I.5O. +2.I.5R. +2.I.5U. +2.I.5W. +1.5Y.I. +2.I.5Z. +2.I.5/. +2.I.60. +2.I.62. +2.I.66. +a.6b.I. +c.6e.I. +2.I.6F. +2.I.6M. +2.I.6+. +2.I.71. +2.I.79. +2.I.7D. +2.I.2p. +2.I.2p. +2.J.J. +2.J.K. +2.J.L. +2.J.M. +2.J.N. +2.J.O. +2.J.P. +2.J.Q. +2.J.R. +3.J.S. +2.J.T. +2.J.U. +2.J.V. +2.J.W. +2.J.X. +2.J.Y. +2.J.Z. +2.J.+. +2.J./. +2.J.10. +2.J.11. +2.J.12. +2.J.13. +2.J.14. +2.J.15. +2.J.16. +2.J.17. +2.J.18. +2.J.19. +2.J.1a. +2.J.1b. +2.J.1c. +2.J.1d. +2.J.1e. +2.J.1f. +2.J.1g. +2.J.1h. +2.J.1i. +2.J.1j. +2.J.1k. +2.1l.J. +2.J.1m. +2.J.1n. +2.J.1o. +2.J.1p. +2.J.1q. +2.J.1r. +2.J.1s. +2.J.1t. +2.1u.J. +2.J.1v. +2.J.1w. +2.J.1x. +2.J.1y. +2.J.1z. +2.J.1A. +2.J.1B. +2.J.1C. +2.J.1D. +2.J.1E. +2.J.1F. +2.J.1G. +2.J.1H. +2.J.1I. +2.J.1J. +2.J.1K. +2.J.1L. +2.J.1M. +2.J.1N. +2.J.1O. +2.J.1P. +2.J.1Q. +2.J.1R. +2.J.1S. +2.J.1T. +2.J.1U. +2.J.1V. +2.J.1W. +2.J.1X. +2.J.1Y. +2.J.1Z. +2.J.1+. +2.J.1/. +2.J.20. +2.J.21. +2.J.22. +2.J.23. +2.J.24. +2.J.25. +2.J.26. +2.J.27. +4.J.28. +2.J.2a. +2.J.2b. +2.J.2l. +6.2m.J. +2.J.2p. +2.J.2q. +2.J.2r. +7.2s.J. +7.2t.J. +2.2u.J. +2.J.2v. +2.J.2w. +e.2x.J. +2.J.2y. +2.J.2z. +2.J.2A. +2.J.2B. +2.J.2C. +2.J.2D. +2.J.2E. +2.J.2F. +2.J.2G. +2.J.2H. +2.J.2I. +2.J.2J. +2.J.2K. +c.2M.J. +2.J.2N. +2.J.2O. +2.J.2Q. +7.2S.J. +2.J.2T. +1.2U.J. +2.J.2V. +1.2W.J. +2.J.2X. +2.J.2+. +2.J.30. +2.J.35. +2.J.36. +2.J.37. +2.J.38. +2.J.39. +2.J.3d. +2.J.3h. +2.J.3i. +2.J.3m. +2.J.3p. +a.3v.J. +a.3y.J. +2.3z.J. +2.J.3A. +2.3B.J. +2.J.3C. +a.3F.J. +2.J.3G. +2.J.3M. +2.J.3Q. +2.J.40. +2.J.47. +2.J.4d. +2.J.4O. +2.J.4Q. +5.4T.J. +2.J.4V. +2.J.51. +2.J.52. +a.5a.J. +2.5b.J. +2.J.5d. +2.J.5B. +2.J.5C. +2.J.5F. +2.J.5H. +2.J.5I. +f.J.5K. +7.5M.J. +2.J.5O. +2.J.5R. +2.J.5U. +2.J.5W. +1.5Y.J. +2.J.5Z. +2.J.5/. +2.J.60. +2.J.62. +2.J.66. +a.6b.J. +c.6e.J. +2.J.6F. +2.J.6M. +2.J.6+. +2.J.71. +2.J.79. +2.J.7D. +2.J.2p. +2.J.2p. +0.K.K. +0.L.K. +2.M.K. +0.N.K. +0.O.K. +0.K.P. +0.K.Q. +0.K.R. +3.S.K. +b.T.K. +0.U.K. +0.V.K. +0.W.K. +0.X.K. +b.Y.K. +0.K.Z. +0.+.K. +0./.K. +0.K.10. +0.K.11. +0.K.12. +0.K.13. +0.K.14. +0.K.15. +0.K.16. +0.K.17. +3.K.18. +0.K.19. +0.K.1a. +0.K.1b. +0.K.1c. +3.K.1d. +0.K.1e. +0.K.1f. +0.K.1g. +3.K.1h. +3.K.1i. +0.1j.K. +0.1k.K. +2.1l.K. +0.1m.K. +0.1n.K. +0.1o.K. +0.K.1p. +0.K.1q. +0.K.1r. +0.K.1s. +0.1t.K. +2.1u.K. +0.K.1v. +3.K.1w. +0.K.1x. +0.K.1y. +0.K.1z. +0.K.1A. +0.1B.K. +0.K.1C. +0.K.1D. +0.1E.K. +0.K.1F. +0.K.1G. +0.K.1H. +0.K.1I. +0.K.1J. +0.K.1K. +0.K.1L. +0.K.1M. +0.K.1N. +0.K.1O. +0.K.1P. +3.K.1Q. +0.K.1R. +0.K.1S. +0.K.1T. +0.K.1U. +0.K.1V. +0.K.1W. +0.K.1X. +0.K.1Y. +0.K.1Z. +0.K.1+. +0.K.1/. +0.K.20. +0.K.21. +0.K.22. +0.K.23. +0.K.24. +0.K.25. +b.26.K. +0.K.27. +4.K.28. +0.K.2a. +0.K.2b. +0.K.2l. +6.2m.K. +0.K.2p. +2.2q.K. +2.2r.K. +7.2s.K. +7.2t.K. +2.2u.K. +0.K.2v. +0.K.2w. +e.2x.K. +9.2y.K. +0.K.2z. +3.K.2A. +9.2B.K. +0.K.2C. +0.K.2D. +0.K.2E. +0.K.2F. +0.K.2G. +9.2H.K. +0.K.2I. +3.K.2J. +0.K.2K. +c.2M.K. +9.2N.K. +2.2O.K. +9.2Q.K. +7.2S.K. +3.K.2T. +1.2U.K. +9.2V.K. +1.2W.K. +0.K.2X. +b.2+.K. +0.K.30. +0.K.35. +3.36.K. +0.K.37. +0.K.38. +5.39.K. +0.K.3d. +3.K.3h. +0.K.3i. +0.K.3m. +0.K.3p. +a.3v.K. +a.3y.K. +2.3z.K. +5.3A.K. +2.3B.K. +0.K.3C. +a.3F.K. +5.3G.K. +0.K.3M. +0.K.3Q. +5.40.K. +0.K.47. +0.K.4d. +0.K.4O. +3.K.4Q. +5.4T.K. +3.K.4V. +0.K.51. +0.K.52. +a.5a.K. +2.5b.K. +5.5d.K. +0.K.5B. +0.K.5C. +0.K.5F. +3.K.5H. +0.K.5I. +f.K.5K. +7.5M.K. +0.K.5O. +0.K.5R. +0.K.5U. +0.K.5W. +1.5Y.K. +0.K.5Z. +0.K.5/. +0.K.60. +2.62.K. +2.66.K. +a.6b.K. +c.6e.K. +0.K.6F. +0.K.6M. +0.K.6+. +3.K.71. +5.79.K. +3.K.7D. +0.K.2p. +0.K.2p. +0.L.L. +2.M.L. +0.L.N. +0.L.O. +0.L.P. +0.L.Q. +0.L.R. +3.S.L. +b.T.L. +0.L.U. +0.L.V. +0.L.W. +0.L.X. +b.Y.L. +0.L.Z. +0.L.+. +0.L./. +0.L.10. +0.L.11. +0.L.12. +0.L.13. +0.L.14. +0.L.15. +0.L.16. +0.L.17. +0.L.18. +0.L.19. +0.L.1a. +0.L.1b. +0.L.1c. +0.L.1d. +0.L.1e. +0.L.1f. +0.L.1g. +0.L.1h. +0.L.1i. +0.L.1j. +0.L.1k. +2.1l.L. +0.L.1m. +0.L.1n. +0.L.1o. +0.L.1p. +0.L.1q. +0.L.1r. +0.L.1s. +0.L.1t. +2.1u.L. +0.L.1v. +0.L.1w. +0.L.1x. +0.L.1y. +0.L.1z. +0.L.1A. +0.1B.L. +0.L.1C. +0.L.1D. +0.L.1E. +0.L.1F. +0.L.1G. +0.L.1H. +0.L.1I. +0.L.1J. +0.L.1K. +0.L.1L. +0.L.1M. +0.L.1N. +0.L.1O. +0.L.1P. +0.L.1Q. +0.L.1R. +0.L.1S. +0.L.1T. +0.L.1U. +0.L.1V. +0.L.1W. +0.L.1X. +0.L.1Y. +0.L.1Z. +0.L.1+. +0.L.1/. +0.L.20. +0.L.21. +0.L.22. +0.L.23. +0.L.24. +0.L.25. +b.26.L. +0.L.27. +0.L.28. +0.L.2a. +0.L.2b. +0.L.2l. +6.2m.L. +0.L.2p. +2.2q.L. +2.2r.L. +7.2s.L. +7.2t.L. +2.2u.L. +0.L.2v. +0.L.2w. +e.2x.L. +9.2y.L. +0.L.2z. +0.L.2A. +9.2B.L. +0.L.2C. +0.L.2D. +0.L.2E. +0.L.2F. +0.L.2G. +9.2H.L. +0.L.2I. +0.L.2J. +0.L.2K. +c.2M.L. +9.2N.L. +2.2O.L. +9.2Q.L. +7.2S.L. +0.L.2T. +1.2U.L. +9.2V.L. +1.2W.L. +0.L.2X. +b.2+.L. +0.L.30. +0.L.35. +b.36.L. +0.L.37. +0.L.38. +5.39.L. +0.L.3d. +0.L.3h. +0.L.3i. +0.L.3m. +0.L.3p. +a.3v.L. +a.3y.L. +2.3z.L. +5.3A.L. +2.3B.L. +0.L.3C. +a.3F.L. +5.3G.L. +0.L.3M. +0.L.3Q. +5.40.L. +0.L.47. +0.L.4d. +0.L.4O. +0.L.4Q. +5.4T.L. +0.L.4V. +0.L.51. +0.L.52. +a.5a.L. +2.5b.L. +5.5d.L. +0.L.5B. +0.L.5C. +0.L.5F. +0.L.5H. +0.L.5I. +f.L.5K. +7.5M.L. +0.L.5O. +0.L.5R. +0.L.5U. +0.L.5W. +1.5Y.L. +0.L.5Z. +0.L.5/. +0.L.60. +2.62.L. +2.66.L. +a.6b.L. +c.6e.L. +0.L.6F. +0.L.6M. +0.L.6+. +0.L.71. +5.79.L. +0.L.7D. +0.L.2p. +0.L.2p. +2.M.M. +2.M.N. +2.M.O. +2.M.P. +2.M.Q. +2.M.R. +3.S.M. +2.M.T. +2.M.U. +2.M.V. +2.M.W. +2.M.X. +2.M.Y. +2.M.Z. +2.M.+. +2.M./. +2.M.10. +2.M.11. +2.M.12. +2.M.13. +2.M.14. +2.M.15. +2.M.16. +2.M.17. +2.M.18. +2.M.19. +2.M.1a. +2.M.1b. +2.M.1c. +2.M.1d. +2.M.1e. +2.M.1f. +2.M.1g. +2.M.1h. +2.M.1i. +2.M.1j. +2.M.1k. +2.1l.M. +2.M.1m. +2.M.1n. +2.M.1o. +2.M.1p. +2.M.1q. +2.M.1r. +2.M.1s. +2.M.1t. +2.1u.M. +2.M.1v. +2.M.1w. +2.M.1x. +2.M.1y. +2.M.1z. +2.M.1A. +2.M.1B. +2.M.1C. +2.M.1D. +2.M.1E. +2.M.1F. +2.M.1G. +2.M.1H. +2.M.1I. +2.M.1J. +2.M.1K. +2.M.1L. +2.M.1M. +2.M.1N. +2.M.1O. +2.M.1P. +2.M.1Q. +2.M.1R. +2.M.1S. +2.M.1T. +2.M.1U. +2.M.1V. +2.M.1W. +2.M.1X. +2.M.1Y. +2.M.1Z. +2.M.1+. +2.M.1/. +2.M.20. +2.M.21. +2.M.22. +2.M.23. +2.M.24. +2.M.25. +2.M.26. +2.M.27. +2.M.28. +2.M.2a. +2.M.2b. +2.M.2l. +6.2m.M. +2.M.2p. +2.M.2q. +2.M.2r. +7.2s.M. +7.2t.M. +2.2u.M. +2.M.2v. +2.M.2w. +e.2x.M. +2.M.2y. +2.M.2z. +2.M.2A. +2.M.2B. +2.M.2C. +2.M.2D. +2.M.2E. +2.M.2F. +2.M.2G. +2.M.2H. +2.M.2I. +2.M.2J. +2.M.2K. +c.2M.M. +2.M.2N. +2.M.2O. +2.M.2Q. +7.2S.M. +2.M.2T. +1.2U.M. +2.M.2V. +1.2W.M. +2.M.2X. +2.M.2+. +2.M.30. +2.M.35. +2.M.36. +2.M.37. +2.M.38. +2.M.39. +2.M.3d. +2.M.3h. +2.M.3i. +2.M.3m. +2.M.3p. +a.3v.M. +a.3y.M. +2.3z.M. +2.M.3A. +2.3B.M. +2.M.3C. +a.3F.M. +2.M.3G. +2.M.3M. +2.M.3Q. +2.M.40. +2.M.47. +2.M.4d. +2.M.4O. +2.M.4Q. +5.4T.M. +2.M.4V. +2.M.51. +2.M.52. +a.5a.M. +2.5b.M. +2.M.5d. +2.M.5B. +2.M.5C. +2.M.5F. +2.M.5H. +2.M.5I. +2.M.5K. +7.5M.M. +2.M.5O. +2.M.5R. +2.M.5U. +2.M.5W. +1.5Y.M. +2.M.5Z. +2.M.5/. +2.M.60. +2.M.62. +2.M.66. +a.6b.M. +c.6e.M. +2.M.6F. +2.M.6M. +2.M.6+. +2.M.71. +2.M.79. +2.M.7D. +2.M.2p. +2.M.2p. +0.N.N. +0.O.N. +0.N.P. +0.N.Q. +0.N.R. +3.S.N. +b.T.N. +0.U.N. +0.V.N. +0.W.N. +0.X.N. +b.Y.N. +0.N.Z. +0.+.N. +0./.N. +0.N.10. +0.N.11. +0.N.12. +0.N.13. +0.N.14. +0.N.15. +0.N.16. +0.N.17. +0.N.18. +0.N.19. +0.N.1a. +0.N.1b. +0.N.1c. +3.N.1d. +0.N.1e. +0.N.1f. +0.N.1g. +3.N.1h. +3.N.1i. +0.1j.N. +0.1k.N. +2.1l.N. +0.1m.N. +0.1n.N. +0.1o.N. +0.N.1p. +0.N.1q. +0.N.1r. +0.N.1s. +0.1t.N. +2.1u.N. +0.N.1v. +3.N.1w. +0.N.1x. +0.N.1y. +0.N.1z. +0.N.1A. +0.1B.N. +0.N.1C. +0.N.1D. +0.1E.N. +0.N.1F. +0.N.1G. +0.N.1H. +0.N.1I. +0.N.1J. +0.N.1K. +0.N.1L. +0.N.1M. +0.N.1N. +0.N.1O. +0.N.1P. +0.N.1Q. +0.N.1R. +0.N.1S. +0.N.1T. +0.N.1U. +0.N.1V. +0.N.1W. +0.N.1X. +0.N.1Y. +0.N.1Z. +0.N.1+. +0.N.1/. +0.N.20. +0.N.21. +0.N.22. +0.N.23. +0.N.24. +0.N.25. +b.26.N. +0.N.27. +4.N.28. +0.N.2a. +0.N.2b. +0.N.2l. +6.2m.N. +0.N.2p. +2.2q.N. +2.2r.N. +7.2s.N. +7.2t.N. +2.2u.N. +0.N.2v. +0.N.2w. +e.2x.N. +9.2y.N. +0.N.2z. +3.N.2A. +9.2B.N. +0.N.2C. +0.N.2D. +3.N.2E. +0.N.2F. +0.N.2G. +9.2H.N. +0.N.2I. +3.N.2J. +0.N.2K. +c.2M.N. +9.2N.N. +2.2O.N. +9.2Q.N. +7.2S.N. +0.N.2T. +1.2U.N. +9.2V.N. +1.2W.N. +0.N.2X. +b.2+.N. +0.N.30. +0.N.35. +b.36.N. +0.N.37. +3.N.38. +5.39.N. +0.N.3d. +3.N.3h. +3.N.3i. +0.N.3m. +0.N.3p. +a.3v.N. +a.3y.N. +2.3z.N. +5.3A.N. +2.3B.N. +0.N.3C. +a.3F.N. +5.3G.N. +0.N.3M. +0.N.3Q. +5.40.N. +0.N.47. +0.N.4d. +0.N.4O. +3.N.4Q. +5.4T.N. +3.N.4V. +0.N.51. +0.N.52. +a.5a.N. +2.5b.N. +5.5d.N. +0.N.5B. +0.N.5C. +0.N.5F. +3.N.5H. +0.N.5I. +f.N.5K. +7.5M.N. +0.N.5O. +3.N.5R. +0.N.5U. +0.N.5W. +1.5Y.N. +0.N.5Z. +0.N.5/. +0.N.60. +2.62.N. +2.66.N. +a.6b.N. +c.6e.N. +0.N.6F. +0.N.6M. +0.N.6+. +3.N.71. +5.79.N. +3.N.7D. +0.N.2p. +0.N.2p. +0.O.O. +0.O.P. +0.O.Q. +0.O.R. +3.S.O. +b.T.O. +0.O.U. +0.O.V. +0.O.W. +0.O.X. +b.Y.O. +0.O.Z. +0.O.+. +0.O./. +0.O.10. +0.O.11. +0.O.12. +0.O.13. +0.O.14. +0.O.15. +0.O.16. +0.O.17. +3.O.18. +0.O.19. +0.O.1a. +0.O.1b. +0.O.1c. +3.O.1d. +0.O.1e. +0.O.1f. +0.O.1g. +3.O.1h. +3.O.1i. +3.O.1j. +0.O.1k. +2.1l.O. +0.O.1m. +0.O.1n. +0.O.1o. +3.O.1p. +0.O.1q. +0.O.1r. +0.O.1s. +0.O.1t. +2.1u.O. +0.O.1v. +3.O.1w. +0.O.1x. +0.O.1y. +0.O.1z. +0.O.1A. +0.1B.O. +0.O.1C. +0.O.1D. +0.O.1E. +0.O.1F. +0.O.1G. +0.O.1H. +0.O.1I. +0.O.1J. +0.O.1K. +0.O.1L. +0.O.1M. +0.O.1N. +0.O.1O. +0.O.1P. +3.O.1Q. +0.O.1R. +0.O.1S. +0.O.1T. +3.O.1U. +0.O.1V. +0.O.1W. +0.O.1X. +0.O.1Y. +0.O.1Z. +0.O.1+. +0.O.1/. +0.O.20. +0.O.21. +0.O.22. +0.O.23. +0.O.24. +0.O.25. +b.26.O. +0.O.27. +4.O.28. +0.O.2a. +0.O.2b. +0.O.2l. +6.2m.O. +0.O.2p. +2.2q.O. +2.2r.O. +7.2s.O. +7.2t.O. +2.2u.O. +3.O.2v. +0.O.2w. +e.2x.O. +9.2y.O. +0.O.2z. +3.O.2A. +9.2B.O. +0.O.2C. +0.O.2D. +3.O.2E. +3.O.2F. +0.O.2G. +9.2H.O. +0.O.2I. +3.O.2J. +0.O.2K. +c.2M.O. +9.2N.O. +2.2O.O. +9.2Q.O. +7.2S.O. +0.O.2T. +1.2U.O. +9.2V.O. +1.2W.O. +0.O.2X. +b.2+.O. +0.O.30. +0.O.35. +b.36.O. +0.O.37. +3.O.38. +5.39.O. +0.O.3d. +3.O.3h. +3.O.3i. +0.O.3m. +0.O.3p. +a.3v.O. +a.3y.O. +2.3z.O. +5.3A.O. +2.3B.O. +0.O.3C. +a.3F.O. +5.3G.O. +0.O.3M. +0.O.3Q. +5.40.O. +0.O.47. +0.O.4d. +3.O.4O. +3.O.4Q. +5.4T.O. +3.O.4V. +0.O.51. +0.O.52. +a.5a.O. +2.5b.O. +5.5d.O. +0.O.5B. +0.O.5C. +0.O.5F. +3.O.5H. +0.O.5I. +f.O.5K. +7.5M.O. +0.O.5O. +3.O.5R. +0.O.5U. +0.O.5W. +1.5Y.O. +0.O.5Z. +0.O.5/. +0.O.60. +2.62.O. +2.66.O. +a.6b.O. +c.6e.O. +0.O.6F. +0.O.6M. +0.O.6+. +3.O.71. +5.79.O. +3.O.7D. +0.O.2p. +0.O.2p. +0.P.P. +0.P.Q. +0.R.P. +3.S.P. +b.T.P. +0.U.P. +0.V.P. +0.W.P. +0.X.P. +b.Y.P. +0.P.Z. +0.+.P. +0./.P. +0.P.10. +0.P.11. +0.P.12. +0.13.P. +0.14.P. +0.15.P. +0.16.P. +0.17.P. +0.18.P. +0.19.P. +0.1a.P. +0.1b.P. +0.1c.P. +0.1d.P. +0.P.1e. +0.P.1f. +0.P.1g. +3.P.1h. +3.P.1i. +0.1j.P. +0.1k.P. +2.1l.P. +0.1m.P. +0.1n.P. +0.1o.P. +0.1p.P. +0.1q.P. +0.1r.P. +0.1s.P. +0.1t.P. +2.1u.P. +0.1v.P. +0.1w.P. +0.1x.P. +0.1y.P. +0.1z.P. +0.1A.P. +0.1B.P. +0.1C.P. +0.1D.P. +0.1E.P. +0.1F.P. +0.1G.P. +0.1H.P. +0.1I.P. +0.P.1J. +0.P.1K. +0.P.1L. +0.P.1M. +0.P.1N. +0.P.1O. +0.P.1P. +3.P.1Q. +0.P.1R. +0.P.1S. +0.P.1T. +3.P.1U. +0.P.1V. +0.P.1W. +0.P.1X. +0.P.1Y. +0.P.1Z. +0.P.1+. +0.P.1/. +0.P.20. +0.P.21. +0.P.22. +0.P.23. +0.P.24. +0.P.25. +b.26.P. +0.P.27. +4.P.28. +0.P.2a. +0.P.2b. +0.P.2l. +6.2m.P. +0.P.2p. +2.2q.P. +2.2r.P. +7.2s.P. +7.2t.P. +2.2u.P. +0.P.2v. +0.P.2w. +e.2x.P. +9.2y.P. +0.P.2z. +3.P.2A. +9.2B.P. +0.P.2C. +0.P.2D. +3.P.2E. +0.P.2F. +0.P.2G. +9.2H.P. +0.P.2I. +3.P.2J. +0.P.2K. +c.2M.P. +9.2N.P. +2.2O.P. +9.2Q.P. +7.2S.P. +0.P.2T. +1.2U.P. +9.2V.P. +1.2W.P. +0.P.2X. +b.2+.P. +0.P.30. +0.P.35. +b.36.P. +0.P.37. +0.P.38. +5.39.P. +0.P.3d. +3.P.3h. +0.P.3i. +0.P.3m. +0.P.3p. +a.3v.P. +a.3y.P. +2.3z.P. +5.3A.P. +2.3B.P. +0.P.3C. +a.3F.P. +5.3G.P. +0.P.3M. +0.P.3Q. +5.40.P. +0.P.47. +0.P.4d. +0.P.4O. +3.P.4Q. +5.4T.P. +3.P.4V. +0.P.51. +0.P.52. +a.5a.P. +2.5b.P. +5.5d.P. +0.P.5B. +0.P.5C. +0.P.5F. +3.P.5H. +0.P.5I. +f.P.5K. +7.5M.P. +0.P.5O. +3.P.5R. +0.P.5U. +0.P.5W. +1.5Y.P. +0.P.5Z. +0.P.5/. +0.P.60. +2.62.P. +2.66.P. +a.6b.P. +c.6e.P. +0.P.6F. +0.P.6M. +0.P.6+. +3.P.71. +5.79.P. +3.P.7D. +0.P.2p. +0.P.2p. +0.Q.Q. +0.R.Q. +3.S.Q. +b.T.Q. +0.U.Q. +0.V.Q. +0.W.Q. +0.X.Q. +b.Y.Q. +0.Q.Z. +0.+.Q. +0./.Q. +0.Q.10. +0.Q.11. +0.Q.12. +0.13.Q. +0.14.Q. +0.15.Q. +0.16.Q. +0.17.Q. +0.18.Q. +0.19.Q. +0.1a.Q. +0.1b.Q. +0.1c.Q. +0.1d.Q. +0.Q.1e. +0.Q.1f. +0.Q.1g. +3.Q.1h. +3.Q.1i. +0.1j.Q. +0.1k.Q. +2.1l.Q. +0.1m.Q. +0.1n.Q. +0.1o.Q. +0.1p.Q. +0.1q.Q. +0.1r.Q. +0.1s.Q. +3.1t.Q. +2.1u.Q. +0.1v.Q. +0.1w.Q. +0.1x.Q. +0.1y.Q. +0.1z.Q. +0.1A.Q. +0.1B.Q. +0.1C.Q. +0.1D.Q. +0.1E.Q. +0.1F.Q. +0.1G.Q. +0.1H.Q. +0.1I.Q. +0.Q.1J. +0.Q.1K. +0.Q.1L. +0.Q.1M. +0.Q.1N. +0.Q.1O. +0.Q.1P. +0.Q.1Q. +0.Q.1R. +0.Q.1S. +0.Q.1T. +0.Q.1U. +0.Q.1V. +0.Q.1W. +0.Q.1X. +0.Q.1Y. +0.Q.1Z. +0.Q.1+. +0.Q.1/. +0.Q.20. +0.Q.21. +0.Q.22. +0.Q.23. +0.Q.24. +0.Q.25. +b.26.Q. +0.Q.27. +4.Q.28. +0.Q.2a. +0.Q.2b. +0.Q.2l. +6.2m.Q. +0.Q.2p. +2.2q.Q. +2.2r.Q. +7.2s.Q. +7.2t.Q. +2.2u.Q. +3.Q.2v. +0.Q.2w. +e.2x.Q. +9.2y.Q. +0.Q.2z. +3.Q.2A. +9.2B.Q. +0.Q.2C. +0.Q.2D. +3.Q.2E. +0.Q.2F. +0.Q.2G. +9.2H.Q. +0.Q.2I. +3.Q.2J. +0.Q.2K. +c.2M.Q. +9.2N.Q. +2.2O.Q. +9.2Q.Q. +7.2S.Q. +0.Q.2T. +1.2U.Q. +9.2V.Q. +1.2W.Q. +0.Q.2X. +b.2+.Q. +0.Q.30. +0.Q.35. +b.36.Q. +0.Q.37. +0.Q.38. +5.39.Q. +0.Q.3d. +3.Q.3h. +0.Q.3i. +0.Q.3m. +0.Q.3p. +a.3v.Q. +a.3y.Q. +2.3z.Q. +5.3A.Q. +2.3B.Q. +0.Q.3C. +a.3F.Q. +5.3G.Q. +0.Q.3M. +0.Q.3Q. +5.40.Q. +0.Q.47. +0.Q.4d. +0.Q.4O. +3.Q.4Q. +5.4T.Q. +3.Q.4V. +0.Q.51. +0.Q.52. +a.5a.Q. +2.5b.Q. +5.5d.Q. +0.Q.5B. +0.Q.5C. +0.Q.5F. +3.Q.5H. +0.Q.5I. +f.Q.5K. +7.5M.Q. +0.Q.5O. +3.Q.5R. +0.Q.5U. +0.Q.5W. +1.5Y.Q. +0.Q.5Z. +0.Q.5/. +0.Q.60. +2.62.Q. +2.66.Q. +a.6b.Q. +c.6e.Q. +0.Q.6F. +0.Q.6M. +0.Q.6+. +3.Q.71. +5.79.Q. +3.Q.7D. +0.Q.2p. +0.Q.2p. +0.R.R. +3.S.R. +b.T.R. +0.U.R. +0.V.R. +0.W.R. +0.X.R. +b.Y.R. +0.R.Z. +0.+.R. +0./.R. +0.R.10. +0.R.11. +0.R.12. +0.13.R. +0.14.R. +0.15.R. +0.16.R. +0.17.R. +0.18.R. +0.19.R. +0.1a.R. +0.1b.R. +0.1c.R. +0.1d.R. +0.R.1e. +0.R.1f. +0.R.1g. +0.R.1h. +3.R.1i. +0.1j.R. +0.1k.R. +2.1l.R. +0.1m.R. +0.1n.R. +0.1o.R. +0.1p.R. +0.1q.R. +0.1r.R. +0.1s.R. +0.1t.R. +2.1u.R. +0.1v.R. +0.1w.R. +0.1x.R. +0.1y.R. +0.1z.R. +0.1A.R. +0.1B.R. +0.1C.R. +0.1D.R. +0.1E.R. +0.1F.R. +0.1G.R. +0.1H.R. +0.1I.R. +0.R.1J. +0.R.1K. +0.R.1L. +0.R.1M. +0.R.1N. +0.R.1O. +0.R.1P. +0.R.1Q. +0.R.1R. +0.R.1S. +0.R.1T. +0.R.1U. +0.R.1V. +0.R.1W. +0.R.1X. +0.R.1Y. +0.R.1Z. +0.R.1+. +0.R.1/. +0.R.20. +0.R.21. +0.R.22. +0.R.23. +0.R.24. +0.R.25. +b.26.R. +0.R.27. +4.R.28. +0.R.2a. +0.R.2b. +0.R.2l. +6.2m.R. +0.R.2p. +2.2q.R. +2.2r.R. +7.2s.R. +7.2t.R. +2.2u.R. +0.R.2v. +0.R.2w. +e.2x.R. +9.2y.R. +0.R.2z. +3.R.2A. +9.2B.R. +0.R.2C. +0.R.2D. +0.R.2E. +0.R.2F. +0.R.2G. +9.2H.R. +0.R.2I. +3.R.2J. +0.R.2K. +c.2M.R. +9.2N.R. +2.2O.R. +9.2Q.R. +7.2S.R. +0.R.2T. +1.2U.R. +9.2V.R. +1.2W.R. +0.R.2X. +b.2+.R. +0.R.30. +0.R.35. +b.36.R. +0.R.37. +0.R.38. +5.39.R. +0.R.3d. +3.R.3h. +0.R.3i. +0.R.3m. +0.R.3p. +a.3v.R. +a.3y.R. +2.3z.R. +5.3A.R. +2.3B.R. +0.R.3C. +a.3F.R. +5.3G.R. +0.R.3M. +0.R.3Q. +5.40.R. +0.R.47. +0.R.4d. +0.R.4O. +3.R.4Q. +5.4T.R. +3.R.4V. +0.R.51. +0.R.52. +a.5a.R. +2.5b.R. +5.5d.R. +0.R.5B. +0.R.5C. +0.R.5F. +3.R.5H. +0.R.5I. +f.R.5K. +7.5M.R. +0.R.5O. +0.R.5R. +0.R.5U. +0.R.5W. +1.5Y.R. +0.R.5Z. +0.R.5/. +0.R.60. +2.62.R. +2.66.R. +a.6b.R. +c.6e.R. +0.R.6F. +0.R.6M. +0.R.6+. +0.R.71. +5.79.R. +3.R.7D. +0.R.2p. +0.R.2p. +3.S.S. +3.S.T. +3.S.U. +3.S.V. +3.S.W. +3.S.X. +3.S.Y. +3.S.Z. +3.S.+. +3.S./. +3.S.10. +3.S.11. +3.S.12. +3.S.13. +3.S.14. +3.S.15. +2.S.16. +3.S.17. +3.S.18. +2.S.19. +2.S.1a. +3.S.1b. +3.S.1c. +3.S.1d. +3.S.1e. +3.S.1f. +3.S.1g. +3.S.1h. +3.S.1i. +3.S.1j. +3.S.1k. +3.1l.S. +3.S.1m. +3.S.1n. +3.S.1o. +3.S.1p. +3.S.1q. +3.S.1r. +3.S.1s. +3.S.1t. +3.1u.S. +3.S.1v. +3.S.1w. +3.S.1x. +3.S.1y. +3.S.1z. +3.S.1A. +3.S.1B. +3.S.1C. +3.S.1D. +3.S.1E. +3.S.1F. +3.S.1G. +3.S.1H. +3.S.1I. +3.S.1J. +3.S.1K. +2.S.1L. +3.S.1M. +2.S.1N. +2.S.1O. +2.S.1P. +3.S.1Q. +2.S.1R. +3.S.1S. +2.S.1T. +3.S.1U. +3.S.1V. +2.S.1W. +2.S.1X. +3.S.1Y. +2.S.1Z. +2.S.1+. +2.S.1/. +3.S.20. +3.S.21. +3.S.22. +3.S.23. +2.S.24. +2.S.25. +2.S.26. +3.S.27. +2.S.28. +3.S.2a. +2.S.2b. +2.S.2l. +6.2m.S. +2.S.2p. +2.S.2q. +2.S.2r. +7.2s.S. +7.2t.S. +2.2u.S. +2.S.2v. +2.S.2w. +e.2x.S. +2.S.2y. +2.S.2z. +2.S.2A. +2.S.2B. +2.S.2C. +2.S.2D. +2.S.2E. +2.S.2F. +2.S.2G. +2.S.2H. +2.S.2I. +2.S.2J. +2.S.2K. +c.2M.S. +2.S.2N. +2.S.2O. +2.S.2Q. +7.2S.S. +2.S.2T. +1.2U.S. +2.S.2V. +1.2W.S. +2.S.2X. +2.S.2+. +2.S.30. +2.S.35. +2.S.36. +2.S.37. +2.S.38. +2.S.39. +2.S.3d. +2.S.3h. +3.S.3i. +2.S.3m. +2.S.3p. +a.3v.S. +a.3y.S. +2.3z.S. +2.S.3A. +2.3B.S. +2.S.3C. +a.3F.S. +2.S.3G. +2.S.3M. +2.S.3Q. +2.S.40. +2.S.47. +2.S.4d. +2.S.4O. +2.S.4Q. +5.4T.S. +2.S.4V. +2.S.51. +2.S.52. +a.5a.S. +2.5b.S. +2.S.5d. +2.S.5B. +2.S.5C. +3.S.5F. +2.S.5H. +2.S.5I. +2.S.5K. +7.5M.S. +2.S.5O. +2.S.5R. +2.S.5U. +2.S.5W. +1.5Y.S. +2.S.5Z. +2.S.5/. +2.S.60. +2.S.62. +2.S.66. +a.6b.S. +c.6e.S. +2.S.6F. +2.S.6M. +2.S.6+. +2.S.71. +3.S.79. +2.S.7D. +2.S.2p. +2.S.2p. +b.T.T. +b.T.U. +b.T.V. +b.T.W. +b.T.X. +b.T.Y. +b.T.Z. +b.T.+. +b.T./. +b.T.10. +b.T.11. +b.T.12. +b.T.13. +b.T.14. +b.T.15. +b.T.16. +b.T.17. +b.T.18. +b.T.19. +b.T.1a. +b.T.1b. +b.T.1c. +b.T.1d. +b.T.1e. +b.T.1f. +b.T.1g. +b.T.1h. +b.T.1i. +b.T.1j. +b.T.1k. +2.1l.T. +b.T.1m. +b.T.1n. +b.T.1o. +b.T.1p. +b.T.1q. +b.T.1r. +b.T.1s. +b.T.1t. +2.1u.T. +b.T.1v. +b.T.1w. +b.T.1x. +b.T.1y. +b.T.1z. +b.T.1A. +b.T.1B. +b.T.1C. +b.T.1D. +b.T.1E. +b.T.1F. +b.T.1G. +b.T.1H. +b.T.1I. +b.T.1J. +b.T.1K. +b.T.1L. +b.T.1M. +b.T.1N. +b.T.1O. +b.T.1P. +b.T.1Q. +b.T.1R. +b.T.1S. +b.T.1T. +b.T.1U. +b.T.1V. +b.T.1W. +b.T.1X. +b.T.1Y. +b.T.1Z. +b.T.1+. +b.T.1/. +b.T.20. +b.T.21. +b.T.22. +b.T.23. +b.T.24. +b.T.25. +b.26.T. +b.T.27. +b.T.28. +b.T.2a. +b.T.2b. +b.T.2l. +6.2m.T. +b.T.2p. +2.2q.T. +2.2r.T. +7.2s.T. +7.2t.T. +2.2u.T. +b.T.2v. +b.T.2w. +e.2x.T. +9.2y.T. +b.T.2z. +b.T.2A. +9.2B.T. +b.T.2C. +3.T.2D. +b.T.2E. +b.T.2F. +b.T.2G. +9.2H.T. +b.T.2I. +b.T.2J. +b.T.2K. +c.2M.T. +9.2N.T. +2.2O.T. +9.2Q.T. +7.2S.T. +b.T.2T. +1.2U.T. +9.2V.T. +1.2W.T. +b.T.2X. +b.2+.T. +b.T.30. +b.T.35. +b.36.T. +b.T.37. +b.T.38. +5.39.T. +b.T.3h. +b.T.3i. +b.T.3m. +b.T.3p. +a.3v.T. +a.3y.T. +2.3z.T. +5.3A.T. +2.3B.T. +b.T.3C. +a.3F.T. +5.3G.T. +b.T.3M. +b.T.3Q. +5.40.T. +b.T.47. +b.T.4d. +b.T.4O. +b.T.4Q. +5.4T.T. +b.T.4V. +b.T.51. +b.T.52. +a.5a.T. +2.5b.T. +5.5d.T. +b.T.5B. +b.T.5C. +b.T.5F. +b.T.5H. +b.T.5I. +b.T.5K. +7.5M.T. +b.T.5O. +b.T.5R. +b.T.5U. +b.T.5W. +1.5Y.T. +b.T.5Z. +b.T.5/. +3.T.60. +2.62.T. +2.66.T. +a.6b.T. +c.6e.T. +b.T.6F. +b.T.6M. +b.T.6+. +b.T.71. +5.79.T. +b.T.7D. +b.T.2p. +b.T.2p. +3.U.U. +0.U.V. +0.W.U. +0.U.X. +b.Y.U. +0.U.Z. +0.U.+. +0.U./. +0.U.10. +0.U.11. +0.U.12. +0.U.13. +0.U.14. +0.U.15. +0.U.16. +0.U.17. +3.U.18. +0.U.19. +0.U.1a. +0.U.1b. +0.U.1c. +3.U.1d. +0.U.1e. +3.U.1f. +0.U.1g. +3.U.1h. +3.U.1i. +0.1j.U. +0.U.1k. +2.1l.U. +0.U.1m. +0.U.1n. +0.U.1o. +0.U.1p. +0.U.1q. +0.U.1r. +0.U.1s. +0.U.1t. +2.1u.U. +0.U.1v. +3.U.1w. +0.U.1x. +0.U.1y. +0.U.1z. +0.U.1A. +3.1B.U. +0.U.1C. +0.U.1D. +0.U.1E. +0.U.1F. +0.U.1G. +0.U.1H. +0.U.1I. +0.U.1J. +0.U.1K. +0.U.1L. +0.U.1M. +0.U.1N. +0.U.1O. +0.U.1P. +3.U.1Q. +0.U.1R. +3.U.1S. +3.U.1T. +3.U.1U. +0.U.1V. +0.U.1W. +0.U.1X. +0.U.1Y. +0.U.1Z. +0.U.1+. +0.U.1/. +0.U.20. +0.U.21. +0.U.22. +0.U.23. +0.U.24. +0.U.25. +b.26.U. +0.U.27. +4.U.28. +0.U.2a. +0.U.2b. +0.U.2l. +6.2m.U. +0.U.2p. +2.2q.U. +2.2r.U. +7.2s.U. +7.2t.U. +2.2u.U. +3.U.2v. +0.U.2w. +e.2x.U. +9.2y.U. +0.U.2z. +3.U.2A. +9.2B.U. +0.U.2C. +0.U.2D. +3.U.2E. +3.U.2F. +0.U.2G. +9.2H.U. +0.U.2I. +3.U.2J. +0.U.2K. +c.2M.U. +9.2N.U. +2.2O.U. +9.2Q.U. +7.2S.U. +0.U.2T. +1.2U.U. +9.2V.U. +1.2W.U. +0.U.2X. +b.2+.U. +0.U.30. +0.U.35. +b.36.U. +0.U.37. +0.U.38. +5.39.U. +0.U.3d. +3.U.3h. +3.U.3i. +0.U.3m. +0.U.3p. +a.3v.U. +a.3y.U. +2.3z.U. +5.3A.U. +2.3B.U. +0.U.3C. +a.3F.U. +5.3G.U. +0.U.3M. +0.U.3Q. +5.40.U. +0.U.47. +0.U.4d. +3.U.4O. +3.U.4Q. +5.4T.U. +3.U.4V. +0.U.51. +0.U.52. +a.5a.U. +2.5b.U. +5.5d.U. +0.U.5B. +0.U.5C. +0.U.5F. +3.U.5H. +0.U.5I. +f.U.5K. +7.5M.U. +0.U.5O. +3.U.5R. +0.U.5U. +0.U.5W. +1.5Y.U. +0.U.5Z. +0.U.5/. +0.U.60. +2.62.U. +2.66.U. +a.6b.U. +c.6e.U. +0.U.6F. +0.U.6M. +0.U.6+. +3.U.71. +5.79.U. +3.U.7D. +0.U.2p. +0.U.2p. +0.V.V. +0.W.V. +0.V.X. +b.Y.V. +0.V.Z. +0.V.+. +0.V./. +0.V.10. +0.V.11. +0.V.12. +0.V.13. +0.V.14. +0.V.15. +0.V.16. +0.V.17. +0.V.18. +0.V.19. +0.V.1a. +0.V.1b. +0.V.1c. +3.V.1d. +0.V.1e. +0.V.1f. +0.V.1g. +3.V.1h. +3.V.1i. +0.1j.V. +0.V.1k. +2.1l.V. +0.V.1m. +0.V.1n. +0.V.1o. +0.V.1p. +0.V.1q. +0.V.1r. +0.V.1s. +0.V.1t. +2.1u.V. +0.V.1v. +0.V.1w. +0.V.1x. +0.V.1y. +0.V.1z. +0.V.1A. +3.1B.V. +0.V.1C. +0.V.1D. +0.V.1E. +0.V.1F. +0.V.1G. +0.V.1H. +0.V.1I. +0.V.1J. +0.V.1K. +0.V.1L. +0.V.1M. +0.V.1N. +0.V.1O. +0.V.1P. +0.V.1Q. +0.V.1R. +0.V.1S. +0.V.1T. +3.V.1U. +0.V.1V. +0.V.1W. +0.V.1X. +0.V.1Y. +0.V.1Z. +0.V.1+. +0.V.1/. +0.V.20. +0.V.21. +0.V.22. +0.V.23. +0.V.24. +0.V.25. +b.26.V. +0.V.27. +4.V.28. +0.V.2a. +0.V.2b. +0.V.2l. +6.2m.V. +0.V.2p. +2.2q.V. +2.2r.V. +7.2s.V. +7.2t.V. +2.2u.V. +3.V.2v. +0.V.2w. +e.2x.V. +9.2y.V. +0.V.2z. +3.V.2A. +9.2B.V. +0.V.2C. +0.V.2D. +3.V.2E. +3.V.2F. +0.V.2G. +9.2H.V. +0.V.2I. +3.V.2J. +0.V.2K. +c.2M.V. +9.2N.V. +2.2O.V. +9.2Q.V. +7.2S.V. +0.V.2T. +1.2U.V. +9.2V.V. +1.2W.V. +0.V.2X. +b.2+.V. +0.V.30. +0.V.35. +b.36.V. +0.V.37. +0.V.38. +5.39.V. +0.V.3d. +3.V.3h. +0.V.3i. +0.V.3m. +0.V.3p. +a.3v.V. +a.3y.V. +2.3z.V. +5.3A.V. +2.3B.V. +0.V.3C. +a.3F.V. +5.3G.V. +0.V.3M. +0.V.3Q. +5.40.V. +0.V.47. +0.V.4d. +3.V.4O. +3.V.4Q. +5.4T.V. +3.V.4V. +0.V.51. +0.V.52. +a.5a.V. +2.5b.V. +5.5d.V. +0.V.5B. +0.V.5C. +0.V.5F. +3.V.5H. +0.V.5I. +f.V.5K. +7.5M.V. +0.V.5O. +3.V.5R. +0.V.5U. +0.V.5W. +1.5Y.V. +0.V.5Z. +0.V.5/. +0.V.60. +2.62.V. +2.66.V. +a.6b.V. +c.6e.V. +0.V.6F. +0.V.6M. +0.V.6+. +3.V.71. +5.79.V. +3.V.7D. +0.V.2p. +0.V.2p. +0.W.W. +0.W.X. +b.Y.W. +0.W.Z. +0.W.+. +0.W./. +0.W.10. +0.W.11. +0.W.12. +0.W.13. +0.W.14. +0.W.15. +0.W.16. +0.W.17. +3.W.18. +0.W.19. +0.W.1a. +0.W.1b. +0.W.1c. +3.W.1d. +0.W.1e. +0.W.1f. +0.W.1g. +3.W.1h. +3.W.1i. +0.1j.W. +0.W.1k. +2.1l.W. +0.W.1m. +0.W.1n. +0.W.1o. +0.W.1p. +0.W.1q. +0.W.1r. +0.W.1s. +0.W.1t. +2.1u.W. +0.W.1v. +3.W.1w. +0.W.1x. +0.W.1y. +0.W.1z. +0.W.1A. +0.1B.W. +0.W.1C. +0.W.1D. +0.W.1E. +0.W.1F. +0.W.1G. +0.W.1H. +0.W.1I. +0.W.1J. +0.W.1K. +0.W.1L. +0.W.1M. +0.W.1N. +0.W.1O. +0.W.1P. +0.W.1Q. +0.W.1R. +0.W.1S. +0.W.1T. +0.W.1U. +0.W.1V. +0.W.1W. +0.W.1X. +0.W.1Y. +0.W.1Z. +0.W.1+. +0.W.1/. +0.W.20. +0.W.21. +0.W.22. +0.W.23. +0.W.24. +0.W.25. +b.26.W. +0.W.27. +4.W.28. +0.W.2a. +0.W.2b. +0.W.2l. +6.2m.W. +0.W.2p. +2.2q.W. +2.2r.W. +7.2s.W. +7.2t.W. +2.2u.W. +0.W.2v. +0.W.2w. +e.2x.W. +9.2y.W. +0.W.2z. +3.W.2A. +9.2B.W. +0.W.2C. +0.W.2D. +3.W.2E. +3.W.2F. +0.W.2G. +9.2H.W. +0.W.2I. +3.W.2J. +0.W.2K. +c.2M.W. +9.2N.W. +2.2O.W. +9.2Q.W. +7.2S.W. +0.W.2T. +1.2U.W. +9.2V.W. +1.2W.W. +0.W.2X. +b.2+.W. +0.W.30. +0.W.35. +b.36.W. +0.W.37. +0.W.38. +5.39.W. +0.W.3d. +3.W.3h. +0.W.3i. +0.W.3m. +0.W.3p. +a.3v.W. +a.3y.W. +2.3z.W. +5.3A.W. +2.3B.W. +0.W.3C. +a.3F.W. +5.3G.W. +0.W.3M. +0.W.3Q. +5.40.W. +0.W.47. +0.W.4d. +0.W.4O. +3.W.4Q. +5.4T.W. +3.W.4V. +0.W.51. +0.W.52. +a.5a.W. +2.5b.W. +5.5d.W. +0.W.5B. +0.W.5C. +0.W.5F. +3.W.5H. +0.W.5I. +f.W.5K. +7.5M.W. +0.W.5O. +0.W.5R. +0.W.5U. +0.W.5W. +1.5Y.W. +0.W.5Z. +0.W.5/. +0.W.60. +2.62.W. +2.66.W. +a.6b.W. +c.6e.W. +0.W.6F. +0.W.6M. +0.W.6+. +3.W.71. +5.79.W. +3.W.7D. +0.W.2p. +0.W.2p. +0.X.X. +b.Y.X. +0.X.Z. +0.+.X. +0./.X. +0.X.10. +0.X.11. +0.X.12. +0.X.13. +0.X.14. +0.X.15. +0.X.16. +0.X.17. +0.X.18. +0.X.19. +0.X.1a. +0.X.1b. +0.X.1c. +3.X.1d. +0.X.1e. +0.X.1f. +0.X.1g. +3.X.1h. +3.X.1i. +0.1j.X. +0.1k.X. +2.1l.X. +0.1m.X. +0.1n.X. +0.1o.X. +0.X.1p. +0.X.1q. +0.X.1r. +0.X.1s. +0.1t.X. +2.1u.X. +0.X.1v. +3.X.1w. +0.X.1x. +0.X.1y. +0.X.1z. +0.X.1A. +0.1B.X. +0.X.1C. +0.X.1D. +0.1E.X. +0.X.1F. +0.X.1G. +0.X.1H. +0.X.1I. +0.X.1J. +0.X.1K. +0.X.1L. +0.X.1M. +0.X.1N. +0.X.1O. +0.X.1P. +3.X.1Q. +0.X.1R. +0.X.1S. +0.X.1T. +0.X.1U. +0.X.1V. +0.X.1W. +0.X.1X. +0.X.1Y. +0.X.1Z. +0.X.1+. +0.X.1/. +0.X.20. +0.X.21. +0.X.22. +0.X.23. +0.X.24. +0.X.25. +b.26.X. +0.X.27. +4.X.28. +0.X.2a. +0.X.2b. +0.X.2l. +6.2m.X. +0.X.2p. +2.2q.X. +2.2r.X. +7.2s.X. +7.2t.X. +2.2u.X. +0.X.2v. +0.X.2w. +e.2x.X. +9.2y.X. +0.X.2z. +3.X.2A. +9.2B.X. +0.X.2C. +0.X.2D. +3.X.2E. +3.X.2F. +0.X.2G. +9.2H.X. +0.X.2I. +3.X.2J. +0.X.2K. +c.2M.X. +9.2N.X. +2.2O.X. +9.2Q.X. +7.2S.X. +0.X.2T. +1.2U.X. +9.2V.X. +1.2W.X. +0.X.2X. +b.2+.X. +0.X.30. +0.X.35. +b.36.X. +0.X.37. +0.X.38. +5.39.X. +0.X.3d. +3.X.3h. +0.X.3i. +0.X.3m. +0.X.3p. +a.3v.X. +a.3y.X. +2.3z.X. +5.3A.X. +2.3B.X. +0.X.3C. +a.3F.X. +5.3G.X. +0.X.3M. +0.X.3Q. +5.40.X. +0.X.47. +0.X.4d. +0.X.4O. +3.X.4Q. +5.4T.X. +3.X.4V. +0.X.51. +0.X.52. +a.5a.X. +2.5b.X. +5.5d.X. +0.X.5B. +0.X.5C. +0.X.5F. +3.X.5H. +0.X.5I. +f.X.5K. +7.5M.X. +0.X.5O. +0.X.5R. +0.X.5U. +0.X.5W. +1.5Y.X. +0.X.5Z. +0.X.5/. +0.X.60. +2.62.X. +2.66.X. +a.6b.X. +c.6e.X. +0.X.6F. +0.X.6M. +0.X.6+. +3.X.71. +5.79.X. +3.X.7D. +0.X.2p. +0.X.2p. +b.Y.Y. +b.Y.Z. +b.Y.+. +b.Y./. +b.Y.10. +b.Y.11. +b.Y.12. +b.Y.13. +b.Y.14. +b.Y.15. +b.Y.16. +b.Y.17. +b.Y.18. +b.Y.19. +b.Y.1a. +b.Y.1b. +b.Y.1c. +b.Y.1d. +b.Y.1e. +b.Y.1f. +b.Y.1g. +b.Y.1h. +b.Y.1i. +b.Y.1j. +b.Y.1k. +2.1l.Y. +b.Y.1m. +b.Y.1n. +b.Y.1o. +b.Y.1p. +b.Y.1q. +b.Y.1r. +b.Y.1s. +b.Y.1t. +2.1u.Y. +b.Y.1v. +b.Y.1w. +b.Y.1x. +b.Y.1y. +b.Y.1z. +b.Y.1A. +b.Y.1B. +b.Y.1C. +b.Y.1D. +b.Y.1E. +b.Y.1F. +b.Y.1G. +b.Y.1H. +b.Y.1I. +b.Y.1J. +b.Y.1K. +b.Y.1L. +b.Y.1M. +b.Y.1N. +b.Y.1O. +b.Y.1P. +b.Y.1Q. +b.Y.1R. +b.Y.1S. +b.Y.1T. +b.Y.1U. +b.Y.1V. +b.Y.1W. +b.Y.1X. +b.Y.1Y. +b.Y.1Z. +b.Y.1+. +b.Y.1/. +b.Y.20. +b.Y.21. +b.Y.22. +b.Y.23. +b.Y.24. +b.Y.25. +b.26.Y. +b.Y.27. +b.Y.28. +b.Y.2a. +b.Y.2b. +b.Y.2l. +6.2m.Y. +b.Y.2p. +2.2q.Y. +2.2r.Y. +7.2s.Y. +7.2t.Y. +2.2u.Y. +b.Y.2v. +b.Y.2w. +e.2x.Y. +9.2y.Y. +b.Y.2z. +b.Y.2A. +9.2B.Y. +b.Y.2C. +b.Y.2D. +b.Y.2E. +b.Y.2F. +b.Y.2G. +9.2H.Y. +b.Y.2I. +b.Y.2J. +b.Y.2K. +c.2M.Y. +9.2N.Y. +2.2O.Y. +9.2Q.Y. +7.2S.Y. +b.Y.2T. +1.2U.Y. +9.2V.Y. +1.2W.Y. +b.Y.2X. +b.2+.Y. +b.Y.30. +b.Y.35. +b.36.Y. +b.Y.37. +b.Y.38. +5.39.Y. +b.Y.3d. +b.Y.3h. +b.Y.3i. +b.Y.3m. +b.Y.3p. +a.3v.Y. +a.3y.Y. +2.3z.Y. +5.3A.Y. +2.3B.Y. +b.Y.3C. +a.3F.Y. +5.3G.Y. +b.Y.3M. +b.Y.3Q. +5.40.Y. +b.Y.47. +b.Y.4d. +b.Y.4O. +b.Y.4Q. +5.4T.Y. +b.Y.4V. +b.Y.51. +b.Y.52. +a.5a.Y. +2.5b.Y. +5.5d.Y. +b.Y.5B. +b.Y.5C. +b.Y.5F. +b.Y.5H. +b.Y.5I. +b.Y.5K. +7.5M.Y. +b.Y.5O. +b.Y.5R. +b.Y.5U. +b.Y.5W. +1.5Y.Y. +b.Y.5Z. +b.Y.5/. +b.Y.60. +2.62.Y. +2.66.Y. +a.6b.Y. +c.6e.Y. +b.Y.6F. +b.Y.6M. +b.Y.6+. +b.Y.71. +5.79.Y. +b.Y.7D. +b.Y.2p. +b.Y.2p. +0.Z.Z. +0.+.Z. +0./.Z. +0.10.Z. +0.11.Z. +3.12.Z. +0.13.Z. +3.14.Z. +3.15.Z. +0.16.Z. +0.17.Z. +3.18.Z. +0.19.Z. +3.1a.Z. +0.1b.Z. +0.1c.Z. +0.1d.Z. +3.1e.Z. +0.1f.Z. +3.Z.1g. +0.1h.Z. +3.1i.Z. +0.1j.Z. +0.1k.Z. +2.1l.Z. +0.1m.Z. +0.1n.Z. +0.1o.Z. +0.1p.Z. +0.1q.Z. +0.1r.Z. +0.1s.Z. +3.1t.Z. +2.1u.Z. +0.1v.Z. +0.1w.Z. +0.1x.Z. +0.1y.Z. +0.1z.Z. +0.1A.Z. +0.1B.Z. +0.1C.Z. +0.1D.Z. +0.1E.Z. +0.1F.Z. +0.1G.Z. +0.1H.Z. +0.1I.Z. +3.1J.Z. +3.1K.Z. +0.1L.Z. +0.1M.Z. +3.1N.Z. +0.1O.Z. +0.1P.Z. +0.Z.1Q. +0.1R.Z. +3.1S.Z. +3.Z.1T. +0.Z.1U. +0.Z.1V. +0.Z.1W. +0.Z.1X. +0.Z.1Y. +0.Z.1Z. +0.Z.1+. +0.Z.1/. +0.Z.20. +0.Z.21. +0.Z.22. +0.Z.23. +0.Z.24. +0.Z.25. +b.26.Z. +0.Z.27. +4.Z.28. +0.Z.2a. +0.Z.2b. +0.Z.2l. +6.2m.Z. +0.Z.2p. +2.2q.Z. +2.2r.Z. +7.2s.Z. +7.2t.Z. +2.2u.Z. +0.Z.2v. +3.Z.2w. +e.2x.Z. +9.2y.Z. +0.Z.2z. +3.Z.2A. +9.2B.Z. +0.Z.2C. +0.Z.2D. +0.Z.2E. +0.Z.2F. +0.Z.2G. +9.2H.Z. +0.Z.2I. +3.Z.2J. +0.Z.2K. +c.2M.Z. +9.2N.Z. +2.2O.Z. +9.2Q.Z. +7.2S.Z. +0.Z.2T. +1.2U.Z. +9.2V.Z. +1.2W.Z. +3.Z.2X. +b.2+.Z. +0.Z.30. +3.Z.35. +b.36.Z. +0.Z.37. +3.Z.38. +5.39.Z. +0.Z.3d. +3.Z.3h. +0.Z.3i. +0.Z.3m. +0.Z.3p. +a.3v.Z. +a.3y.Z. +2.3z.Z. +5.3A.Z. +2.3B.Z. +0.Z.3C. +a.3F.Z. +5.3G.Z. +0.Z.3M. +0.Z.3Q. +5.40.Z. +0.Z.47. +0.Z.4d. +0.Z.4O. +3.Z.4Q. +5.4T.Z. +3.Z.4V. +3.Z.51. +0.Z.52. +a.5a.Z. +2.5b.Z. +5.5d.Z. +0.Z.5B. +0.Z.5C. +5.5F.Z. +0.Z.5H. +0.Z.5I. +f.Z.5K. +7.5M.Z. +3.Z.5O. +0.Z.5R. +0.Z.5U. +0.Z.5W. +1.5Y.Z. +0.Z.5Z. +0.Z.5/. +0.Z.60. +2.62.Z. +2.66.Z. +a.6b.Z. +c.6e.Z. +0.Z.6F. +0.Z.6M. +0.Z.6+. +3.Z.71. +5.79.Z. +0.Z.7D. +0.Z.2p. +0.Z.2p. +0.+.+. +0./.+. +0.+.10. +0.+.11. +0.+.12. +0.+.13. +0.+.14. +0.+.15. +0.+.16. +0.+.17. +0.+.18. +0.+.19. +0.+.1a. +0.+.1b. +0.+.1c. +0.+.1d. +0.+.1e. +0.+.1f. +0.+.1g. +0.+.1h. +0.+.1i. +0.1j.+. +0.+.1k. +2.1l.+. +0.+.1m. +0.+.1n. +0.+.1o. +0.+.1p. +0.+.1q. +0.+.1r. +0.+.1s. +0.+.1t. +2.1u.+. +0.+.1v. +0.+.1w. +0.+.1x. +0.+.1y. +0.+.1z. +0.+.1A. +0.1B.+. +0.+.1C. +0.+.1D. +0.1E.+. +0.+.1F. +0.+.1G. +0.+.1H. +0.+.1I. +0.+.1J. +0.+.1K. +0.+.1L. +0.+.1M. +0.+.1N. +0.+.1O. +0.+.1P. +0.+.1Q. +0.+.1R. +0.+.1S. +0.+.1T. +0.+.1U. +0.+.1V. +0.+.1W. +0.+.1X. +0.+.1Y. +0.+.1Z. +0.+.1+. +0.+.1/. +0.+.20. +0.+.21. +0.+.22. +0.+.23. +0.+.24. +0.+.25. +b.26.+. +0.+.27. +4.+.28. +0.+.2a. +0.+.2b. +0.+.2l. +6.2m.+. +0.+.2p. +2.2q.+. +2.2r.+. +7.2s.+. +7.2t.+. +2.2u.+. +0.+.2v. +0.+.2w. +e.2x.+. +9.2y.+. +0.+.2z. +0.+.2A. +9.2B.+. +0.+.2C. +0.+.2D. +0.+.2E. +0.+.2F. +0.+.2G. +9.2H.+. +0.+.2I. +0.+.2J. +0.+.2K. +c.2M.+. +9.2N.+. +2.2O.+. +9.2Q.+. +7.2S.+. +0.+.2T. +1.2U.+. +9.2V.+. +1.2W.+. +0.+.2X. +b.2+.+. +0.+.30. +0.+.35. +b.36.+. +0.+.37. +0.+.38. +5.39.+. +0.+.3d. +0.+.3h. +0.+.3i. +0.+.3m. +0.+.3p. +a.3v.+. +a.3y.+. +2.3z.+. +5.3A.+. +2.3B.+. +0.+.3C. +a.3F.+. +5.3G.+. +0.+.3M. +0.+.3Q. +5.40.+. +0.+.47. +0.+.4d. +0.+.4O. +0.+.4Q. +5.4T.+. +0.+.4V. +0.+.51. +0.+.52. +a.5a.+. +2.5b.+. +5.5d.+. +0.+.5B. +0.+.5C. +0.+.5F. +0.+.5H. +0.+.5I. +f.+.5K. +7.5M.+. +0.+.5O. +0.+.5R. +0.+.5U. +0.+.5W. +1.5Y.+. +0.+.5Z. +0.+.5/. +0.+.60. +2.62.+. +2.66.+. +a.6b.+. +c.6e.+. +0.+.6F. +0.+.6M. +0.+.6+. +0.+.71. +5.79.+. +0.+.7D. +0.+.2p. +0.+.2p. +0././. +0./.10. +0./.11. +0./.12. +0./.13. +0./.14. +0./.15. +0./.16. +0./.17. +3./.18. +0./.19. +0./.1a. +0./.1b. +0./.1c. +3./.1d. +0./.1e. +0./.1f. +0./.1g. +3./.1h. +3./.1i. +0.1j./. +0./.1k. +2.1l./. +0./.1m. +0./.1n. +0./.1o. +3./.1p. +0./.1q. +0./.1r. +0./.1s. +0./.1t. +2.1u./. +0./.1v. +3./.1w. +0./.1x. +0./.1y. +0./.1z. +0./.1A. +3.1B./. +0./.1C. +0./.1D. +0.1E./. +0./.1F. +0./.1G. +0./.1H. +0./.1I. +0./.1J. +0./.1K. +0./.1L. +0./.1M. +0./.1N. +0./.1O. +0./.1P. +3./.1Q. +0./.1R. +0./.1S. +3./.1T. +3./.1U. +0./.1V. +0./.1W. +0./.1X. +0./.1Y. +0./.1Z. +0./.1+. +0./.1/. +0./.20. +0./.21. +0./.22. +0./.23. +0./.24. +0./.25. +b.26./. +0./.27. +4./.28. +4./.29. +0./.2a. +0./.2b. +4./.2c. +4./.2d. +4./.2f. +4./.2g. +4./.2h. +4./.2i. +4./.2j. +0./.2l. +6.2m./. +1./.2o. +0./.2p. +2.2q./. +2.2r./. +7.2s./. +7.2t./. +2.2u./. +3./.2v. +0./.2w. +e.2x./. +9.2y./. +0./.2z. +3./.2A. +9.2B./. +0./.2C. +0./.2D. +3./.2E. +3./.2F. +0./.2G. +9.2H./. +0./.2I. +3./.2J. +0./.2K. +4./.2L. +c.2M./. +9.2N./. +2.2O./. +4./.2P. +9.2Q./. +8./.2R. +7.2S./. +0./.2T. +1.2U./. +9.2V./. +1.2W./. +0./.2X. +8./.2Y. +8./.2Z. +b.2+./. +8./.2/. +0./.30. +3./.31. +3./.32. +8./.33. +3./.34. +0./.35. +b.36./. +0./.37. +0./.38. +5.39./. +4./.3a. +4./.3c. +0./.3d. +1./.3e. +4./.3f. +8./.3g. +3./.3h. +0./.3i. +1./.3j. +3./.3k. +4./.3l. +0./.3m. +3./.3n. +8./.3o. +0./.3p. +3./.3q. +4./.3r. +4./.3s. +8./.3t. +8./.3u. +a.3v./. +4./.3w. +4./.3x. +a.3y./. +2.3z./. +5.3A./. +2.3B./. +0./.3C. +4./.3D. +4./.3E. +a.3F./. +5.3G./. +4./.3H. +4./.3I. +4./.3J. +4./.3K. +4./.3L. +0./.3M. +4./.3N. +4./.3O. +4./.3P. +0./.3Q. +4./.3R. +4./.3S. +1./.3T. +4./.3U. +4./.3V. +4./.3W. +4./.3X. +4./.3Y. +4./.3Z. +1./.3+. +1./.3/. +5.40./. +4./.41. +4./.42. +4./.43. +4./.44. +4./.45. +0./.47. +4./.48. +4./.49. +1./.4a. +4./.4b. +4./.4c. +0./.4d. +4./.4e. +4./.4f. +4./.4g. +4./.4h. +4./.4i. +4./.4j. +3./.4k. +4./.4l. +4./.4m. +4./.4n. +4./.4o. +4./.4p. +4./.4q. +4./.4r. +4./.4s. +4./.4t. +4./.4u. +4./.4v. +4./.4w. +4./.4x. +4./.4y. +4./.4z. +4./.4A. +4./.4B. +4./.4C. +4./.4D. +4./.4E. +1./.4F. +8./.4G. +8./.4H. +1./.4I. +8./.4J. +8./.4K. +8./.4L. +4./.4M. +4./.4N. +3./.4O. +4./.4P. +3./.4Q. +1./.4R. +4./.4S. +4./.4T. +8./.4U. +3./.4V. +4./.4W. +3./.4Y. +1./.4Z. +4./.4+. +4./.4/. +1./.50. +0./.51. +0./.52. +8./.53. +3./.54. +8./.55. +8./.56. +1./.57. +3./.58. +8./.59. +a.5a./. +2.5b./. +4./.5c. +5.5d./. +8./.5e. +8./.5f. +4./.5g. +8./.5h. +4./.5i. +4./.5j. +4./.5k. +4./.5l. +4./.5m. +4./.5n. +4./.5o. +4./.5p. +4./.5q. +4./.5r. +1./.5s. +4./.5t. +4./.5u. +4./.5v. +1./.5w. +1./.5y. +1./.5z. +8./.5A. +0./.5B. +0./.5C. +1./.5D. +8./.5E. +0./.5F. +8./.5G. +3./.5H. +0./.5I. +f./.5K. +3./.5L. +8./.5M. +3./.5N. +0./.5O. +8./.5P. +4./.5Q. +3./.5R. +4./.5S. +4./.5T. +0./.5U. +8./.5V. +0./.5W. +8./.5X. +8./.5Y. +0./.5Z. +0./.5/. +0./.60. +4./.61. +2.62./. +4./.64. +4./.65. +2.66./. +4./.67. +4./.68. +4./.69. +4./.6a. +a.6b./. +4./.6c. +4./.6d. +4./.6e. +4./.6f. +4./.6g. +4./.6h. +4./.6i. +4./.6j. +4./.6k. +4./.6l. +4./.6m. +4./.6n. +4./.6o. +4./.6p. +4./.6q. +4./.6r. +4./.6s. +4./.6t. +4./.6u. +4./.6v. +4./.6w. +4./.6x. +4./.6y. +4./.6z. +4./.6A. +4./.6B. +4./.6C. +4./.6D. +4./.6E. +0./.6F. +4./.6G. +4./.6H. +4./.6I. +4./.6J. +4./.6K. +3./.6L. +0./.6M. +4./.6N. +4./.6O. +4./.6P. +4./.6Q. +4./.6R. +4./.6S. +4./.6T. +4./.6U. +4./.6V. +4./.6W. +1./.6X. +4./.6Y. +1./.6Z. +0./.6+. +1./.6/. +1./.70. +3./.71. +4./.72. +4./.73. +1./.74. +4./.75. +1./.76. +1./.77. +4./.78. +5.79./. +1./.7a. +4./.7b. +4./.7c. +4./.7d. +4./.7e. +4./.7f. +4./.7g. +4./.7h. +4./.7i. +4./.7j. +4./.7k. +4./.7l. +1./.7m. +4./.7o. +4./.7p. +1./.7r. +1./.7s. +8./.7t. +4./.7u. +4./.7v. +4./.7w. +4./.7x. +4./.7y. +1./.7z. +4./.7A. +8./.7B. +4./.7C. +3./.7D. +4./.7E. +4./.7F. +4./.7G. +4./.7H. +4./.7I. +4./.7J. +1./.7K. +1./.7L. +1./.7M. +1./.7N. +1./.7O. +1./.7P. +4./.7Q. +3./.7R. +4./.7S. +4./.7T. +1./.7U. +8./.7V. +4./.7W. +4./.7X. +4./.7Y. +4./.7Z. +1./.7/. +1./.80. +1./.81. +1./.82. +4./.83. +1./.84. +1./.85. +1./.87. +3./.89. +4./.8a. +3./.8b. +4./.8c. +8./.8d. +4./.8e. +4./.8f. +4./.8g. +4./.8h. +4./.8i. +4./.8j. +4./.8k. +4./.8l. +4./.8m. +4./.8n. +4./.8o. +4./.8p. +4./.8q. +4./.8r. +4./.8s. +4./.8t. +4./.8u. +4./.8v. +4./.8w. +4./.8x. +4./.8y. +4./.8z. +4./.8A. +4./.8B. +4./.8C. +4./.8D. +4./.8E. +4./.8F. +4./.8G. +4./.8H. +4./.8I. +4./.8J. +4./.8K. +4./.8L. +4./.8M. +4./.8N. +4./.8O. +4./.8P. +0./.2p. +0./.2p. +8./.3b. +1./.4X. +1./.5x. +1./.5J. +1./.7n. +1./.7q. +0.10.10. +0.11.10. +0.12.10. +0.13.10. +0.14.10. +0.15.10. +0.16.10. +0.17.10. +0.18.10. +0.19.10. +0.1a.10. +0.1b.10. +0.1c.10. +0.1d.10. +0.10.1e. +0.10.1f. +0.10.1g. +3.10.1h. +3.10.1i. +0.1j.10. +0.1k.10. +2.1l.10. +0.1m.10. +0.1n.10. +0.1o.10. +0.1p.10. +0.1q.10. +0.1r.10. +0.1s.10. +0.1t.10. +2.1u.10. +0.1v.10. +0.1w.10. +0.1x.10. +0.1y.10. +0.1z.10. +0.1A.10. +0.1B.10. +0.1C.10. +0.1D.10. +0.1E.10. +0.1F.10. +0.1G.10. +0.1H.10. +0.1I.10. +0.1J.10. +0.1K.10. +0.1L.10. +0.1M.10. +3.1N.10. +0.1O.10. +0.1P.10. +3.10.1Q. +0.10.1R. +0.10.1S. +0.10.1T. +3.10.1U. +0.10.1V. +0.10.1W. +0.10.1X. +0.10.1Y. +0.10.1Z. +0.10.1+. +0.10.1/. +0.10.20. +0.10.21. +0.10.22. +0.10.23. +0.10.24. +0.10.25. +b.26.10. +0.10.27. +4.10.28. +0.10.2a. +0.10.2b. +0.10.2l. +6.2m.10. +0.10.2p. +2.2q.10. +2.2r.10. +7.2s.10. +7.2t.10. +2.2u.10. +3.10.2v. +0.10.2w. +e.2x.10. +9.2y.10. +0.10.2z. +3.10.2A. +9.2B.10. +0.10.2C. +0.10.2D. +3.10.2E. +3.10.2F. +0.10.2G. +9.2H.10. +0.10.2I. +3.10.2J. +0.10.2K. +c.2M.10. +9.2N.10. +2.2O.10. +9.2Q.10. +7.2S.10. +0.10.2T. +1.2U.10. +9.2V.10. +1.2W.10. +0.10.2X. +b.2+.10. +0.10.30. +0.10.35. +b.36.10. +0.10.37. +3.10.38. +5.39.10. +0.10.3d. +3.10.3h. +0.10.3i. +0.10.3m. +0.10.3p. +a.3v.10. +a.3y.10. +2.3z.10. +5.3A.10. +2.3B.10. +0.10.3C. +a.3F.10. +5.3G.10. +0.10.3M. +0.10.3Q. +5.40.10. +0.10.47. +0.10.4d. +3.10.4O. +3.10.4Q. +5.4T.10. +3.10.4V. +0.10.51. +0.10.52. +a.5a.10. +2.5b.10. +5.5d.10. +0.10.5B. +0.10.5C. +0.10.5F. +3.10.5H. +0.10.5I. +f.10.5K. +7.5M.10. +0.10.5O. +3.10.5R. +0.10.5U. +0.10.5W. +1.5Y.10. +0.10.5Z. +0.10.5/. +0.10.60. +2.62.10. +2.66.10. +a.6b.10. +c.6e.10. +0.10.6F. +0.10.6M. +0.10.6+. +3.10.71. +5.79.10. +3.10.7D. +0.10.2p. +0.10.2p. +0.11.11. +0.11.12. +0.13.11. +0.14.11. +0.15.11. +0.16.11. +0.17.11. +0.18.11. +0.19.11. +0.1a.11. +0.1b.11. +0.1c.11. +0.1d.11. +0.11.1e. +0.11.1f. +0.11.1g. +3.11.1h. +3.11.1i. +0.1j.11. +0.1k.11. +2.1l.11. +0.1m.11. +0.1n.11. +0.1o.11. +0.1p.11. +0.1q.11. +0.1r.11. +0.1s.11. +0.1t.11. +2.1u.11. +0.1v.11. +0.1w.11. +0.1x.11. +0.1y.11. +0.1z.11. +0.1A.11. +0.1B.11. +0.1C.11. +0.1D.11. +0.1E.11. +0.1F.11. +0.1G.11. +0.1H.11. +0.1I.11. +0.11.1J. +0.1K.11. +0.1L.11. +0.1M.11. +0.1N.11. +0.1O.11. +0.1P.11. +0.11.1Q. +0.11.1R. +0.11.1S. +0.11.1T. +0.11.1U. +0.11.1V. +0.11.1W. +0.11.1X. +0.11.1Y. +0.11.1Z. +0.11.1+. +0.11.1/. +0.11.20. +0.11.21. +0.11.22. +0.11.23. +0.11.24. +0.11.25. +b.26.11. +0.11.27. +4.11.28. +0.11.2a. +0.11.2b. +0.11.2l. +6.2m.11. +0.11.2p. +2.2q.11. +2.2r.11. +7.2s.11. +7.2t.11. +2.2u.11. +3.11.2v. +0.11.2w. +e.2x.11. +9.2y.11. +0.11.2z. +3.11.2A. +9.2B.11. +0.11.2C. +0.11.2D. +3.11.2E. +3.11.2F. +0.11.2G. +9.2H.11. +0.11.2I. +3.11.2J. +0.11.2K. +c.2M.11. +9.2N.11. +2.2O.11. +9.2Q.11. +7.2S.11. +0.11.2T. +1.2U.11. +9.2V.11. +1.2W.11. +0.11.2X. +b.2+.11. +0.11.30. +0.11.35. +b.36.11. +0.11.37. +3.11.38. +5.39.11. +0.11.3d. +3.11.3h. +0.11.3i. +0.11.3m. +0.11.3p. +a.3v.11. +a.3y.11. +2.3z.11. +5.3A.11. +2.3B.11. +0.11.3C. +a.3F.11. +5.3G.11. +0.11.3M. +0.11.3Q. +5.40.11. +0.11.47. +0.11.4d. +3.11.4O. +3.11.4Q. +5.4T.11. +3.11.4V. +0.11.51. +0.11.52. +a.5a.11. +2.5b.11. +5.5d.11. +0.11.5B. +0.11.5C. +0.11.5F. +3.11.5H. +0.11.5I. +f.11.5K. +7.5M.11. +0.11.5O. +3.11.5R. +0.11.5U. +0.11.5W. +1.5Y.11. +0.11.5Z. +0.11.5/. +0.11.60. +2.62.11. +2.66.11. +a.6b.11. +c.6e.11. +0.11.6F. +0.11.6M. +0.11.6+. +3.11.71. +5.79.11. +3.11.7D. +0.11.2p. +0.11.2p. +0.12.12. +0.13.12. +0.14.12. +0.15.12. +0.16.12. +0.17.12. +0.18.12. +0.19.12. +0.1a.12. +0.1b.12. +0.1c.12. +0.1d.12. +0.12.1e. +0.12.1f. +0.12.1g. +3.12.1h. +3.12.1i. +0.1j.12. +0.1k.12. +2.1l.12. +0.1m.12. +0.1n.12. +0.1o.12. +0.1p.12. +0.1q.12. +0.1r.12. +0.1s.12. +3.1t.12. +2.1u.12. +0.1v.12. +0.1w.12. +0.1x.12. +0.1y.12. +0.1z.12. +0.1A.12. +0.1B.12. +0.1C.12. +0.1D.12. +0.1E.12. +0.1F.12. +0.1G.12. +0.1H.12. +0.1I.12. +0.1J.12. +0.1K.12. +0.1L.12. +0.1M.12. +3.1N.12. +0.1O.12. +0.1P.12. +3.12.1Q. +0.12.1R. +3.12.1S. +0.12.1T. +3.12.1U. +0.12.1V. +3.12.1W. +0.12.1X. +0.12.1Y. +3.12.1Z. +3.12.1+. +0.12.1/. +3.12.20. +3.12.21. +0.12.22. +0.12.23. +0.12.24. +0.12.25. +b.26.12. +0.12.27. +4.12.28. +3.12.2a. +3.12.2b. +0.12.2l. +6.2m.12. +3.12.2p. +2.2q.12. +2.2r.12. +7.2s.12. +7.2t.12. +2.2u.12. +3.12.2v. +3.12.2w. +e.2x.12. +9.2y.12. +3.12.2z. +3.12.2A. +9.2B.12. +3.12.2C. +3.12.2D. +3.12.2E. +3.12.2F. +3.12.2G. +9.2H.12. +3.12.2I. +3.12.2J. +3.12.2K. +c.2M.12. +9.2N.12. +2.2O.12. +9.2Q.12. +7.2S.12. +3.12.2T. +1.2U.12. +9.2V.12. +1.2W.12. +3.12.2X. +b.2+.12. +3.12.30. +0.12.35. +b.36.12. +0.12.37. +0.12.38. +5.39.12. +0.12.3d. +3.12.3h. +3.12.3i. +0.12.3m. +0.12.3p. +a.3v.12. +a.3y.12. +2.3z.12. +5.3A.12. +2.3B.12. +3.12.3C. +a.3F.12. +5.3G.12. +0.12.3M. +0.12.3Q. +5.40.12. +3.12.47. +3.12.4d. +3.12.4O. +3.12.4Q. +5.4T.12. +3.12.4V. +3.12.51. +3.12.52. +a.5a.12. +2.5b.12. +5.5d.12. +3.12.5B. +3.12.5C. +3.12.5F. +3.12.5H. +0.12.5I. +f.12.5K. +7.5M.12. +3.12.5O. +3.12.5R. +3.12.5U. +3.12.5W. +1.5Y.12. +3.12.5Z. +3.12.5/. +3.12.60. +2.62.12. +2.66.12. +a.6b.12. +c.6e.12. +3.12.6F. +3.12.6M. +3.12.6+. +3.12.71. +5.79.12. +3.12.7D. +3.12.2p. +3.12.2p. +0.13.13. +0.13.14. +0.13.15. +0.16.13. +0.17.13. +0.18.13. +0.13.19. +0.13.1a. +0.13.1b. +0.1c.13. +0.1d.13. +0.13.1e. +0.13.1f. +0.13.1g. +3.13.1h. +3.13.1i. +0.1j.13. +0.1k.13. +2.1l.13. +0.1m.13. +0.1n.13. +0.1o.13. +0.1p.13. +0.1q.13. +0.1r.13. +0.1s.13. +0.1t.13. +2.1u.13. +0.1v.13. +0.1w.13. +0.1x.13. +0.1y.13. +0.1z.13. +0.1A.13. +0.1B.13. +0.1C.13. +0.1D.13. +0.1E.13. +0.1F.13. +0.1G.13. +0.1H.13. +0.1I.13. +0.13.1J. +0.13.1K. +0.13.1L. +0.13.1M. +0.13.1N. +0.13.1O. +0.13.1P. +3.13.1Q. +0.13.1R. +0.13.1S. +0.13.1T. +0.13.1U. +0.13.1V. +0.13.1W. +0.13.1X. +0.13.1Y. +0.13.1Z. +0.13.1+. +0.13.1/. +0.13.20. +0.13.21. +0.13.22. +0.13.23. +0.13.24. +0.13.25. +b.26.13. +0.13.27. +4.13.28. +0.13.2a. +0.13.2b. +0.13.2l. +6.2m.13. +0.13.2p. +2.2q.13. +2.2r.13. +7.2s.13. +7.2t.13. +2.2u.13. +0.13.2v. +0.13.2w. +e.2x.13. +9.2y.13. +0.13.2z. +3.13.2A. +9.2B.13. +0.13.2C. +0.13.2D. +0.13.2E. +0.13.2F. +0.13.2G. +9.2H.13. +0.13.2I. +3.13.2J. +0.13.2K. +c.2M.13. +9.2N.13. +2.2O.13. +9.2Q.13. +7.2S.13. +0.13.2T. +1.2U.13. +9.2V.13. +1.2W.13. +0.13.2X. +b.2+.13. +0.13.30. +0.13.35. +b.36.13. +0.13.37. +0.13.38. +5.39.13. +0.13.3d. +3.13.3h. +3.13.3i. +0.13.3m. +0.13.3p. +a.3v.13. +a.3y.13. +2.3z.13. +5.3A.13. +2.3B.13. +0.13.3C. +a.3F.13. +5.3G.13. +0.13.3M. +0.13.3Q. +5.40.13. +0.13.47. +0.13.4d. +0.13.4O. +3.13.4Q. +5.4T.13. +3.13.4V. +0.13.51. +0.13.52. +a.5a.13. +2.5b.13. +5.5d.13. +0.13.5B. +0.13.5C. +0.13.5F. +3.13.5H. +0.13.5I. +f.13.5K. +7.5M.13. +0.13.5O. +0.13.5R. +0.13.5U. +0.13.5W. +1.5Y.13. +0.13.5Z. +0.13.5/. +0.13.60. +2.62.13. +2.66.13. +a.6b.13. +c.6e.13. +0.13.6F. +0.13.6M. +0.13.6+. +3.13.71. +5.79.13. +3.13.7D. +0.13.2p. +0.13.2p. +3.14.14. +0.14.15. +0.16.14. +0.17.14. +0.18.14. +0.14.19. +3.14.1a. +0.1b.14. +0.1c.14. +0.1d.14. +0.14.1e. +3.14.1f. +0.14.1g. +3.14.1h. +3.14.1i. +0.1j.14. +0.1k.14. +2.1l.14. +0.1m.14. +0.1n.14. +0.1o.14. +0.1p.14. +0.1q.14. +0.1r.14. +0.1s.14. +3.1t.14. +2.1u.14. +0.1v.14. +0.1w.14. +0.1x.14. +0.1y.14. +0.1z.14. +0.1A.14. +0.1B.14. +0.1C.14. +0.1D.14. +0.1E.14. +0.1F.14. +0.1G.14. +0.1H.14. +0.1I.14. +0.14.1J. +0.14.1K. +0.14.1L. +0.14.1M. +0.14.1N. +0.14.1O. +0.14.1P. +3.14.1Q. +0.14.1R. +3.14.1S. +3.14.1T. +3.14.1U. +0.14.1V. +0.14.1W. +0.14.1X. +0.14.1Y. +0.14.1Z. +0.14.1+. +0.14.1/. +0.14.20. +0.14.21. +0.14.22. +0.14.23. +0.14.24. +0.14.25. +b.26.14. +0.14.27. +4.14.28. +0.14.2a. +0.14.2b. +0.14.2l. +6.2m.14. +3.14.2p. +2.2q.14. +2.2r.14. +7.2s.14. +7.2t.14. +2.2u.14. +3.14.2v. +0.14.2w. +e.2x.14. +9.2y.14. +0.14.2z. +3.14.2A. +9.2B.14. +0.14.2C. +0.14.2D. +3.14.2E. +3.14.2F. +0.14.2G. +9.2H.14. +0.14.2I. +3.14.2J. +0.14.2K. +c.2M.14. +9.2N.14. +2.2O.14. +9.2Q.14. +7.2S.14. +0.14.2T. +1.2U.14. +9.2V.14. +1.2W.14. +0.14.2X. +b.2+.14. +0.14.30. +0.14.35. +b.36.14. +0.14.37. +3.14.38. +5.39.14. +0.14.3d. +3.14.3h. +3.14.3i. +0.14.3m. +0.14.3p. +a.3v.14. +a.3y.14. +2.3z.14. +5.3A.14. +2.3B.14. +0.14.3C. +a.3F.14. +5.3G.14. +0.14.3M. +0.14.3Q. +5.40.14. +0.14.47. +0.14.4d. +3.14.4O. +3.14.4Q. +5.4T.14. +3.14.4V. +0.14.51. +0.14.52. +a.5a.14. +2.5b.14. +5.5d.14. +0.14.5B. +0.14.5C. +0.14.5F. +3.14.5H. +0.14.5I. +f.14.5K. +7.5M.14. +0.14.5O. +3.14.5R. +0.14.5U. +3.14.5W. +1.5Y.14. +0.14.5Z. +0.14.5/. +0.14.60. +2.62.14. +2.66.14. +a.6b.14. +c.6e.14. +0.14.6F. +0.14.6M. +0.14.6+. +3.14.71. +5.79.14. +3.14.7D. +3.14.2p. +3.14.2p. +0.15.15. +0.16.15. +0.17.15. +0.18.15. +0.15.19. +3.15.1a. +0.1b.15. +0.1c.15. +0.1d.15. +0.15.1e. +3.15.1f. +0.15.1g. +3.15.1h. +3.15.1i. +0.1j.15. +0.1k.15. +2.1l.15. +0.1m.15. +0.1n.15. +0.1o.15. +0.1p.15. +0.1q.15. +0.1r.15. +0.1s.15. +0.1t.15. +2.1u.15. +0.1v.15. +0.1w.15. +0.1x.15. +0.1y.15. +0.1z.15. +0.1A.15. +0.1B.15. +0.1C.15. +0.1D.15. +0.1E.15. +0.1F.15. +0.1G.15. +0.1H.15. +0.1I.15. +0.15.1J. +0.15.1K. +0.15.1L. +0.15.1M. +0.15.1N. +0.15.1O. +0.15.1P. +3.15.1Q. +0.15.1R. +0.15.1S. +3.15.1T. +3.15.1U. +0.15.1V. +0.15.1W. +0.15.1X. +0.15.1Y. +0.15.1Z. +0.15.1+. +0.15.1/. +0.15.20. +0.15.21. +0.15.22. +0.15.23. +0.15.24. +0.15.25. +b.26.15. +0.15.27. +4.15.28. +0.15.2a. +0.15.2b. +0.15.2l. +6.2m.15. +3.15.2p. +2.2q.15. +2.2r.15. +7.2s.15. +7.2t.15. +2.2u.15. +3.15.2v. +0.15.2w. +e.2x.15. +9.2y.15. +0.15.2z. +3.15.2A. +9.2B.15. +0.15.2C. +0.15.2D. +3.15.2E. +3.15.2F. +0.15.2G. +9.2H.15. +0.15.2I. +3.15.2J. +0.15.2K. +c.2M.15. +9.2N.15. +2.2O.15. +9.2Q.15. +7.2S.15. +0.15.2T. +1.2U.15. +9.2V.15. +1.2W.15. +0.15.2X. +b.2+.15. +0.15.30. +0.15.35. +b.36.15. +0.15.37. +3.15.38. +5.39.15. +0.15.3d. +3.15.3h. +0.15.3i. +0.15.3m. +0.15.3p. +a.3v.15. +a.3y.15. +2.3z.15. +5.3A.15. +2.3B.15. +0.15.3C. +a.3F.15. +5.3G.15. +0.15.3M. +0.15.3Q. +5.40.15. +0.15.47. +0.15.4d. +3.15.4O. +3.15.4Q. +5.4T.15. +3.15.4V. +0.15.51. +0.15.52. +a.5a.15. +2.5b.15. +5.5d.15. +0.15.5B. +0.15.5C. +0.15.5F. +3.15.5H. +0.15.5I. +f.15.5K. +7.5M.15. +0.15.5O. +3.15.5R. +0.15.5U. +3.15.5W. +1.5Y.15. +0.15.5Z. +0.15.5/. +0.15.60. +2.62.15. +2.66.15. +a.6b.15. +c.6e.15. +0.15.6F. +0.15.6M. +0.15.6+. +3.15.71. +5.79.15. +3.15.7D. +3.15.2p. +3.15.2p. +0.16.16. +0.16.17. +3.16.18. +0.16.19. +3.16.1a. +0.16.1b. +0.1c.16. +0.1d.16. +0.16.1e. +0.16.1f. +0.16.1g. +3.16.1h. +3.16.1i. +0.1j.16. +0.1k.16. +2.1l.16. +0.1m.16. +0.1n.16. +0.1o.16. +0.1p.16. +0.1q.16. +0.1r.16. +0.1s.16. +0.1t.16. +2.1u.16. +0.1v.16. +0.1w.16. +0.1x.16. +0.1y.16. +0.1z.16. +0.1A.16. +0.1B.16. +0.1C.16. +0.1D.16. +0.1E.16. +0.1F.16. +0.1G.16. +0.1H.16. +0.1I.16. +0.16.1J. +0.16.1K. +0.16.1L. +0.16.1M. +0.16.1N. +0.16.1O. +0.16.1P. +0.16.1Q. +0.16.1R. +0.16.1S. +0.16.1T. +3.16.1U. +0.16.1V. +0.16.1W. +0.16.1X. +0.16.1Y. +0.16.1Z. +0.16.1+. +0.16.1/. +0.16.20. +0.16.21. +0.16.22. +0.16.23. +0.16.24. +0.16.25. +b.26.16. +0.16.27. +4.16.28. +0.16.2a. +0.16.2b. +0.16.2l. +6.2m.16. +0.16.2p. +2.2q.16. +2.2r.16. +7.2s.16. +7.2t.16. +2.2u.16. +3.16.2v. +0.16.2w. +e.2x.16. +9.2y.16. +0.16.2z. +3.16.2A. +9.2B.16. +0.16.2C. +0.16.2D. +0.16.2E. +3.16.2F. +0.16.2G. +9.2H.16. +0.16.2I. +3.16.2J. +0.16.2K. +c.2M.16. +9.2N.16. +2.2O.16. +9.2Q.16. +7.2S.16. +0.16.2T. +1.2U.16. +9.2V.16. +1.2W.16. +0.16.2X. +b.2+.16. +0.16.30. +0.16.35. +b.36.16. +0.16.37. +0.16.38. +5.39.16. +0.16.3d. +3.16.3h. +3.16.3i. +0.16.3m. +0.16.3p. +a.3v.16. +a.3y.16. +2.3z.16. +5.3A.16. +2.3B.16. +0.16.3C. +a.3F.16. +5.3G.16. +0.16.3M. +0.16.3Q. +5.40.16. +0.16.47. +0.16.4d. +0.16.4O. +3.16.4Q. +5.4T.16. +3.16.4V. +0.16.51. +0.16.52. +a.5a.16. +2.5b.16. +5.5d.16. +0.16.5B. +0.16.5C. +0.16.5F. +3.16.5H. +0.16.5I. +f.16.5K. +7.5M.16. +0.16.5O. +3.16.5R. +0.16.5U. +0.16.5W. +1.5Y.16. +0.16.5Z. +0.16.5/. +0.16.60. +2.62.16. +2.66.16. +a.6b.16. +c.6e.16. +0.16.6F. +0.16.6M. +0.16.6+. +3.16.71. +5.79.16. +0.16.7D. +0.16.2p. +0.16.2p. +0.17.17. +0.17.18. +0.17.19. +3.17.1a. +0.17.1b. +0.1c.17. +0.1d.17. +0.17.1e. +3.17.1f. +0.17.1g. +3.17.1h. +3.17.1i. +0.1j.17. +0.1k.17. +2.1l.17. +0.1m.17. +0.1n.17. +0.1o.17. +0.1p.17. +0.1q.17. +0.1r.17. +0.1s.17. +0.1t.17. +2.1u.17. +0.1v.17. +0.1w.17. +0.1x.17. +0.1y.17. +0.1z.17. +0.1A.17. +0.1B.17. +0.1C.17. +0.1D.17. +0.1E.17. +0.1F.17. +0.1G.17. +0.1H.17. +0.1I.17. +0.17.1J. +0.17.1K. +0.17.1L. +0.17.1M. +0.17.1N. +0.17.1O. +0.17.1P. +3.17.1Q. +0.17.1R. +3.17.1S. +3.17.1T. +3.17.1U. +0.17.1V. +0.17.1W. +0.17.1X. +0.17.1Y. +0.17.1Z. +0.17.1+. +0.17.1/. +0.17.20. +0.17.21. +0.17.22. +0.17.23. +0.17.24. +0.17.25. +b.26.17. +0.17.27. +4.17.28. +0.17.2a. +0.17.2b. +0.17.2l. +6.2m.17. +3.17.2p. +2.2q.17. +2.2r.17. +7.2s.17. +7.2t.17. +2.2u.17. +3.17.2v. +0.17.2w. +e.2x.17. +9.2y.17. +0.17.2z. +3.17.2A. +9.2B.17. +0.17.2C. +0.17.2D. +3.17.2E. +3.17.2F. +0.17.2G. +9.2H.17. +0.17.2I. +3.17.2J. +0.17.2K. +c.2M.17. +9.2N.17. +2.2O.17. +9.2Q.17. +7.2S.17. +0.17.2T. +1.2U.17. +9.2V.17. +1.2W.17. +0.17.2X. +b.2+.17. +0.17.30. +0.17.35. +b.36.17. +0.17.37. +3.17.38. +5.39.17. +0.17.3d. +3.17.3h. +3.17.3i. +0.17.3m. +0.17.3p. +a.3v.17. +a.3y.17. +2.3z.17. +5.3A.17. +2.3B.17. +0.17.3C. +a.3F.17. +5.3G.17. +0.17.3M. +0.17.3Q. +5.40.17. +0.17.47. +0.17.4d. +3.17.4O. +3.17.4Q. +5.4T.17. +3.17.4V. +0.17.51. +0.17.52. +a.5a.17. +2.5b.17. +5.5d.17. +l.17.5B. +l.17.5C. +0.17.5F. +3.17.5H. +0.17.5I. +f.17.5K. +7.5M.17. +0.17.5O. +3.17.5R. +0.17.5U. +0.17.5W. +1.5Y.17. +0.17.5Z. +0.17.5/. +0.17.60. +2.62.17. +2.66.17. +a.6b.17. +c.6e.17. +0.17.6F. +0.17.6M. +0.17.6+. +3.17.71. +5.79.17. +3.17.7D. +3.17.2p. +3.17.2p. +3.18.18. +0.18.19. +0.18.1a. +0.18.1b. +3.1c.18. +3.1d.18. +0.18.1e. +3.18.1f. +0.18.1g. +3.18.1h. +3.18.1i. +0.1j.18. +0.1k.18. +2.1l.18. +3.1m.18. +0.1n.18. +0.1o.18. +0.1p.18. +3.1q.18. +3.1r.18. +3.1s.18. +3.1t.18. +2.1u.18. +0.1v.18. +0.1w.18. +0.1x.18. +3.1y.18. +3.1z.18. +3.1A.18. +0.1B.18. +0.1C.18. +3.1D.18. +3.1E.18. +0.1F.18. +0.1G.18. +3.1H.18. +3.1I.18. +0.18.1J. +0.18.1K. +0.18.1L. +0.18.1M. +0.18.1N. +0.18.1O. +0.18.1P. +3.18.1Q. +0.18.1R. +3.18.1S. +0.18.1T. +3.18.1U. +0.18.1V. +0.18.1W. +0.18.1X. +0.18.1Y. +0.18.1Z. +0.18.1+. +0.18.1/. +0.18.20. +0.18.21. +0.18.22. +0.18.23. +0.18.24. +0.18.25. +b.26.18. +0.18.27. +4.18.28. +0.18.2a. +0.18.2b. +0.18.2l. +6.2m.18. +3.18.2p. +2.2q.18. +2.2r.18. +7.2s.18. +7.2t.18. +2.2u.18. +3.18.2v. +0.18.2w. +e.2x.18. +9.2y.18. +0.18.2z. +3.18.2A. +9.2B.18. +0.18.2C. +0.18.2D. +3.18.2E. +3.18.2F. +0.18.2G. +9.2H.18. +0.18.2I. +3.18.2J. +0.18.2K. +c.2M.18. +9.2N.18. +2.2O.18. +9.2Q.18. +7.2S.18. +0.18.2T. +1.2U.18. +9.2V.18. +1.2W.18. +0.18.2X. +b.2+.18. +0.18.30. +0.18.35. +b.36.18. +0.18.37. +3.18.38. +5.39.18. +0.18.3d. +3.18.3h. +3.18.3i. +0.18.3m. +0.18.3p. +a.3v.18. +a.3y.18. +2.3z.18. +5.3A.18. +2.3B.18. +0.18.3C. +a.3F.18. +5.3G.18. +0.18.3M. +0.18.3Q. +5.40.18. +0.18.47. +0.18.4d. +3.18.4O. +3.18.4Q. +5.4T.18. +3.18.4V. +0.18.51. +0.18.52. +a.5a.18. +2.5b.18. +5.5d.18. +0.18.5B. +0.18.5C. +0.18.5F. +3.18.5H. +0.18.5I. +f.18.5K. +7.5M.18. +0.18.5O. +3.18.5R. +0.18.5U. +0.18.5W. +1.5Y.18. +0.18.5Z. +0.18.5/. +0.18.60. +2.62.18. +2.66.18. +a.6b.18. +c.6e.18. +0.18.6F. +0.18.6M. +0.18.6+. +3.18.71. +5.79.18. +3.18.7D. +3.18.2p. +3.18.2p. +0.19.19. +0.19.1a. +0.1b.19. +0.1c.19. +0.1d.19. +0.19.1e. +0.19.1f. +0.19.1g. +0.19.1h. +0.19.1i. +0.1j.19. +0.1k.19. +2.1l.19. +0.1m.19. +0.1n.19. +0.1o.19. +0.1p.19. +0.1q.19. +0.1r.19. +0.1s.19. +0.1t.19. +2.1u.19. +0.1v.19. +0.1w.19. +0.1x.19. +0.1y.19. +0.1z.19. +0.1A.19. +0.1B.19. +0.1C.19. +0.1D.19. +0.1E.19. +0.1F.19. +0.1G.19. +0.1H.19. +0.1I.19. +0.19.1J. +0.19.1K. +0.19.1L. +0.19.1M. +0.19.1N. +0.19.1O. +0.19.1P. +0.19.1Q. +0.19.1R. +0.19.1S. +0.19.1T. +0.19.1U. +0.19.1V. +0.19.1W. +0.19.1X. +0.19.1Y. +0.19.1Z. +0.19.1+. +0.19.1/. +0.19.20. +0.19.21. +0.19.22. +0.19.23. +0.19.24. +0.19.25. +b.26.19. +0.19.27. +4.19.28. +0.19.2a. +0.19.2b. +0.19.2l. +6.2m.19. +0.19.2p. +2.2q.19. +2.2r.19. +7.2s.19. +7.2t.19. +2.2u.19. +0.19.2v. +0.19.2w. +e.2x.19. +9.2y.19. +0.19.2z. +0.19.2A. +9.2B.19. +0.19.2C. +0.19.2D. +0.19.2E. +0.19.2F. +0.19.2G. +9.2H.19. +0.19.2I. +3.19.2J. +0.19.2K. +c.2M.19. +9.2N.19. +2.2O.19. +9.2Q.19. +7.2S.19. +0.19.2T. +1.2U.19. +9.2V.19. +1.2W.19. +0.19.2X. +b.2+.19. +0.19.30. +0.19.35. +3.36.19. +0.19.37. +0.19.38. +5.39.19. +0.19.3d. +0.19.3h. +0.19.3i. +0.19.3m. +0.19.3p. +a.3v.19. +a.3y.19. +2.3z.19. +5.3A.19. +2.3B.19. +0.19.3C. +a.3F.19. +5.3G.19. +0.19.3M. +0.19.3Q. +5.40.19. +0.19.47. +0.19.4d. +0.19.4O. +0.19.4Q. +5.4T.19. +0.19.4V. +0.19.51. +0.19.52. +a.5a.19. +2.5b.19. +5.5d.19. +0.19.5B. +0.19.5C. +0.19.5F. +0.19.5H. +0.19.5I. +f.19.5K. +7.5M.19. +0.19.5O. +0.19.5R. +0.19.5U. +0.19.5W. +1.5Y.19. +0.19.5Z. +0.19.5/. +0.19.60. +2.62.19. +2.66.19. +a.6b.19. +c.6e.19. +0.19.6F. +0.19.6M. +0.19.6+. +0.19.71. +5.79.19. +0.19.7D. +0.19.2p. +0.19.2p. +3.1a.1a. +0.1b.1a. +0.1c.1a. +3.1d.1a. +0.1a.1e. +3.1a.1f. +0.1a.1g. +3.1a.1h. +3.1a.1i. +0.1j.1a. +0.1k.1a. +2.1l.1a. +0.1m.1a. +0.1n.1a. +0.1o.1a. +0.1p.1a. +0.1q.1a. +0.1r.1a. +3.1s.1a. +3.1t.1a. +2.1u.1a. +0.1v.1a. +0.1w.1a. +0.1x.1a. +0.1y.1a. +0.1z.1a. +0.1A.1a. +0.1B.1a. +3.1C.1a. +0.1D.1a. +0.1E.1a. +0.1F.1a. +0.1G.1a. +0.1H.1a. +0.1I.1a. +0.1a.1J. +0.1a.1K. +0.1a.1L. +0.1a.1M. +3.1a.1N. +0.1a.1O. +0.1a.1P. +3.1a.1Q. +0.1a.1R. +0.1a.1S. +0.1a.1T. +3.1a.1U. +0.1a.1V. +0.1a.1W. +0.1a.1X. +0.1a.1Y. +0.1a.1Z. +0.1a.1+. +0.1a.1/. +0.1a.20. +0.1a.21. +0.1a.22. +0.1a.23. +0.1a.24. +0.1a.25. +b.26.1a. +0.1a.27. +4.1a.28. +0.1a.2a. +0.1a.2b. +0.1a.2l. +6.2m.1a. +3.1a.2p. +2.2q.1a. +2.2r.1a. +7.2s.1a. +7.2t.1a. +2.2u.1a. +3.1a.2v. +3.1a.2w. +e.2x.1a. +9.2y.1a. +0.1a.2z. +3.1a.2A. +9.2B.1a. +0.1a.2C. +3.1a.2D. +3.1a.2E. +3.1a.2F. +0.1a.2G. +9.2H.1a. +0.1a.2I. +3.1a.2J. +0.1a.2K. +c.2M.1a. +9.2N.1a. +2.2O.1a. +9.2Q.1a. +7.2S.1a. +3.1a.2T. +1.2U.1a. +9.2V.1a. +1.2W.1a. +0.1a.2X. +d.2+.1a. +3.1a.30. +0.1a.35. +b.36.1a. +0.1a.37. +3.1a.38. +5.39.1a. +0.1a.3d. +3.1a.3h. +3.1a.3i. +0.1a.3m. +0.1a.3p. +a.3v.1a. +a.3y.1a. +2.3z.1a. +5.3A.1a. +2.3B.1a. +3.1a.3C. +a.3F.1a. +5.3G.1a. +0.1a.3M. +0.1a.3Q. +5.40.1a. +0.1a.47. +0.1a.4d. +3.1a.4O. +3.1a.4Q. +5.4T.1a. +3.1a.4V. +0.1a.51. +0.1a.52. +a.5a.1a. +2.5b.1a. +5.5d.1a. +0.1a.5B. +0.1a.5C. +0.1a.5F. +3.1a.5H. +0.1a.5I. +f.1a.5K. +7.5M.1a. +0.1a.5O. +3.1a.5R. +3.1a.5U. +0.1a.5W. +1.5Y.1a. +0.1a.5Z. +3.1a.5/. +3.1a.60. +2.62.1a. +2.66.1a. +a.6b.1a. +c.6e.1a. +0.1a.6F. +0.1a.6M. +0.1a.6+. +3.1a.71. +5.79.1a. +3.1a.7D. +3.1a.2p. +3.1a.2p. +0.1b.1b. +0.1c.1b. +0.1d.1b. +0.1b.1e. +3.1b.1f. +0.1b.1g. +3.1b.1h. +3.1b.1i. +0.1j.1b. +0.1k.1b. +2.1l.1b. +0.1m.1b. +0.1n.1b. +0.1o.1b. +0.1p.1b. +0.1q.1b. +0.1r.1b. +0.1s.1b. +3.1t.1b. +2.1u.1b. +0.1v.1b. +0.1w.1b. +0.1x.1b. +0.1y.1b. +0.1z.1b. +0.1A.1b. +0.1B.1b. +0.1C.1b. +0.1D.1b. +0.1E.1b. +0.1F.1b. +0.1G.1b. +0.1H.1b. +0.1I.1b. +0.1b.1J. +0.1b.1K. +0.1b.1L. +0.1b.1M. +0.1b.1N. +0.1b.1O. +0.1b.1P. +3.1b.1Q. +0.1b.1R. +3.1b.1S. +3.1b.1T. +3.1b.1U. +0.1b.1V. +0.1b.1W. +0.1b.1X. +0.1b.1Y. +0.1b.1Z. +0.1b.1+. +0.1b.1/. +0.1b.20. +0.1b.21. +0.1b.22. +0.1b.23. +0.1b.24. +0.1b.25. +b.26.1b. +0.1b.27. +4.1b.28. +0.1b.2a. +0.1b.2b. +0.1b.2l. +6.2m.1b. +0.1b.2p. +2.2q.1b. +2.2r.1b. +7.2s.1b. +7.2t.1b. +2.2u.1b. +3.1b.2v. +0.1b.2w. +e.2x.1b. +9.2y.1b. +0.1b.2z. +3.1b.2A. +9.2B.1b. +0.1b.2C. +0.1b.2D. +3.1b.2E. +3.1b.2F. +0.1b.2G. +9.2H.1b. +0.1b.2I. +3.1b.2J. +0.1b.2K. +c.2M.1b. +9.2N.1b. +2.2O.1b. +9.2Q.1b. +7.2S.1b. +0.1b.2T. +1.2U.1b. +9.2V.1b. +1.2W.1b. +0.1b.2X. +b.2+.1b. +0.1b.30. +0.1b.35. +b.36.1b. +0.1b.37. +3.1b.38. +5.39.1b. +0.1b.3d. +3.1b.3h. +3.1b.3i. +0.1b.3m. +0.1b.3p. +a.3v.1b. +a.3y.1b. +2.3z.1b. +5.3A.1b. +2.3B.1b. +0.1b.3C. +a.3F.1b. +5.3G.1b. +0.1b.3M. +0.1b.3Q. +5.40.1b. +0.1b.47. +0.1b.4d. +3.1b.4O. +3.1b.4Q. +5.4T.1b. +3.1b.4V. +0.1b.51. +0.1b.52. +a.5a.1b. +2.5b.1b. +5.5d.1b. +0.1b.5B. +0.1b.5C. +0.1b.5F. +3.1b.5H. +0.1b.5I. +f.1b.5K. +7.5M.1b. +0.1b.5O. +3.1b.5R. +0.1b.5U. +3.1b.5W. +1.5Y.1b. +0.1b.5Z. +0.1b.5/. +0.1b.60. +2.62.1b. +2.66.1b. +a.6b.1b. +c.6e.1b. +0.1b.6F. +0.1b.6M. +0.1b.6+. +3.1b.71. +5.79.1b. +3.1b.7D. +0.1b.2p. +0.1b.2p. +0.1c.1c. +0.1d.1c. +0.1c.1e. +3.1c.1f. +0.1c.1g. +3.1c.1h. +3.1c.1i. +0.1j.1c. +0.1k.1c. +2.1l.1c. +0.1m.1c. +0.1n.1c. +0.1o.1c. +0.1p.1c. +0.1q.1c. +0.1r.1c. +0.1s.1c. +0.1t.1c. +2.1u.1c. +0.1v.1c. +0.1w.1c. +0.1x.1c. +0.1y.1c. +0.1z.1c. +0.1A.1c. +3.1B.1c. +0.1c.1C. +0.1D.1c. +0.1E.1c. +0.1F.1c. +0.1G.1c. +0.1H.1c. +0.1I.1c. +0.1c.1J. +0.1c.1K. +0.1c.1L. +0.1c.1M. +0.1c.1N. +0.1c.1O. +0.1c.1P. +3.1c.1Q. +0.1c.1R. +3.1c.1S. +3.1c.1T. +3.1c.1U. +0.1c.1V. +0.1c.1W. +0.1c.1X. +0.1c.1Y. +0.1c.1Z. +0.1c.1+. +0.1c.1/. +0.1c.20. +0.1c.21. +0.1c.22. +0.1c.23. +0.1c.24. +0.1c.25. +b.26.1c. +0.1c.27. +4.1c.28. +0.1c.2a. +0.1c.2b. +0.1c.2l. +6.2m.1c. +3.1c.2p. +2.2q.1c. +2.2r.1c. +7.2s.1c. +7.2t.1c. +2.2u.1c. +3.1c.2v. +0.1c.2w. +e.2x.1c. +9.2y.1c. +0.1c.2z. +3.1c.2A. +9.2B.1c. +0.1c.2C. +0.1c.2D. +3.1c.2E. +3.1c.2F. +0.1c.2G. +9.2H.1c. +0.1c.2I. +3.1c.2J. +0.1c.2K. +c.2M.1c. +9.2N.1c. +2.2O.1c. +9.2Q.1c. +7.2S.1c. +0.1c.2T. +1.2U.1c. +9.2V.1c. +1.2W.1c. +0.1c.2X. +b.2+.1c. +0.1c.30. +0.1c.35. +b.36.1c. +0.1c.37. +3.1c.38. +5.39.1c. +0.1c.3d. +3.1c.3h. +3.1c.3i. +0.1c.3m. +0.1c.3p. +a.3v.1c. +a.3y.1c. +2.3z.1c. +5.3A.1c. +2.3B.1c. +0.1c.3C. +a.3F.1c. +5.3G.1c. +0.1c.3M. +0.1c.3Q. +5.40.1c. +0.1c.47. +0.1c.4d. +3.1c.4O. +3.1c.4Q. +5.4T.1c. +3.1c.4V. +0.1c.51. +0.1c.52. +a.5a.1c. +2.5b.1c. +5.5d.1c. +0.1c.5B. +0.1c.5C. +0.1c.5F. +3.1c.5H. +0.1c.5I. +f.1c.5K. +7.5M.1c. +0.1c.5O. +3.1c.5R. +0.1c.5U. +0.1c.5W. +1.5Y.1c. +0.1c.5Z. +0.1c.5/. +0.1c.60. +2.62.1c. +2.66.1c. +a.6b.1c. +c.6e.1c. +0.1c.6F. +0.1c.6M. +0.1c.6+. +3.1c.71. +5.79.1c. +3.1c.7D. +3.1c.2p. +3.1c.2p. +3.1d.1d. +0.1d.1e. +3.1d.1f. +0.1d.1g. +3.1d.1h. +3.1d.1i. +0.1j.1d. +3.1k.1d. +2.1l.1d. +3.1m.1d. +3.1n.1d. +0.1o.1d. +3.1p.1d. +3.1q.1d. +3.1r.1d. +3.1s.1d. +3.1t.1d. +2.1u.1d. +3.1v.1d. +3.1w.1d. +3.1x.1d. +3.1y.1d. +0.1d.1z. +0.1d.1A. +0.1B.1d. +0.1d.1C. +0.1d.1D. +3.1E.1d. +0.1d.1F. +0.1d.1G. +0.1d.1H. +0.1d.1I. +0.1d.1J. +0.1d.1K. +0.1d.1L. +0.1d.1M. +0.1d.1N. +0.1d.1O. +0.1d.1P. +3.1d.1Q. +0.1d.1R. +0.1d.1S. +0.1d.1T. +0.1d.1U. +0.1d.1V. +0.1d.1W. +0.1d.1X. +0.1d.1Y. +0.1d.1Z. +0.1d.1+. +0.1d.1/. +0.1d.20. +0.1d.21. +0.1d.22. +0.1d.23. +0.1d.24. +0.1d.25. +b.26.1d. +0.1d.27. +4.1d.28. +0.1d.2a. +0.1d.2b. +0.1d.2l. +6.2m.1d. +3.1d.2p. +2.2q.1d. +2.2r.1d. +7.2s.1d. +7.2t.1d. +2.2u.1d. +3.1d.2v. +0.1d.2w. +e.2x.1d. +9.2y.1d. +0.1d.2z. +3.1d.2A. +9.2B.1d. +0.1d.2C. +0.1d.2D. +3.1d.2E. +3.1d.2F. +0.1d.2G. +9.2H.1d. +0.1d.2I. +3.1d.2J. +0.1d.2K. +c.2M.1d. +9.2N.1d. +2.2O.1d. +9.2Q.1d. +7.2S.1d. +0.1d.2T. +1.2U.1d. +9.2V.1d. +1.2W.1d. +0.1d.2X. +b.2+.1d. +0.1d.30. +0.1d.35. +b.36.1d. +0.1d.37. +3.1d.38. +5.39.1d. +0.1d.3d. +3.1d.3h. +3.1d.3i. +0.1d.3m. +0.1d.3p. +a.3v.1d. +a.3y.1d. +2.3z.1d. +5.3A.1d. +2.3B.1d. +3.1d.3C. +a.3F.1d. +5.3G.1d. +0.1d.3M. +0.1d.3Q. +5.40.1d. +0.1d.47. +0.1d.4d. +0.1d.4O. +3.1d.4Q. +5.4T.1d. +3.1d.4V. +3.1d.51. +0.1d.52. +a.5a.1d. +2.5b.1d. +5.5d.1d. +0.1d.5B. +0.1d.5C. +0.1d.5F. +3.1d.5H. +0.1d.5I. +f.1d.5K. +7.5M.1d. +0.1d.5O. +3.1d.5R. +0.1d.5U. +0.1d.5W. +1.5Y.1d. +0.1d.5Z. +0.1d.5/. +0.1d.60. +2.62.1d. +2.66.1d. +a.6b.1d. +c.6e.1d. +0.1d.6F. +0.1d.6M. +0.1d.6+. +3.1d.71. +5.79.1d. +0.1d.7D. +3.1d.2p. +3.1d.2p. +0.1e.1e. +0.1e.1f. +0.1e.1g. +3.1e.1h. +3.1e.1i. +0.1j.1e. +0.1k.1e. +2.1l.1e. +0.1m.1e. +0.1n.1e. +0.1o.1e. +0.1p.1e. +0.1q.1e. +0.1r.1e. +0.1s.1e. +0.1t.1e. +2.1u.1e. +0.1v.1e. +0.1w.1e. +0.1x.1e. +0.1y.1e. +0.1z.1e. +0.1A.1e. +3.1B.1e. +0.1C.1e. +0.1D.1e. +0.1E.1e. +0.1F.1e. +0.1G.1e. +0.1H.1e. +0.1I.1e. +0.1J.1e. +0.1K.1e. +0.1L.1e. +0.1M.1e. +3.1N.1e. +0.1O.1e. +0.1P.1e. +0.1e.1Q. +d.1R.1e. +0.1e.1S. +0.1e.1T. +0.1e.1U. +0.1e.1V. +0.1e.1W. +0.1e.1X. +0.1e.1Y. +0.1e.1Z. +0.1e.1+. +0.1e.1/. +0.1e.20. +0.1e.21. +0.1e.22. +0.1e.23. +0.1e.24. +0.1e.25. +b.26.1e. +0.1e.27. +0.1e.28. +0.1e.2a. +0.1e.2b. +0.1e.2l. +6.2m.1e. +0.1e.2p. +2.2q.1e. +2.2r.1e. +7.2s.1e. +7.2t.1e. +2.2u.1e. +3.1e.2v. +0.1e.2w. +e.2x.1e. +9.2y.1e. +0.1e.2z. +3.1e.2A. +9.2B.1e. +0.1e.2C. +0.1e.2D. +3.1e.2E. +0.1e.2F. +0.1e.2G. +9.2H.1e. +3.1e.2I. +3.1e.2J. +0.1e.2K. +c.2M.1e. +9.2N.1e. +2.2O.1e. +9.2Q.1e. +7.2S.1e. +0.1e.2T. +1.2U.1e. +9.2V.1e. +1.2W.1e. +0.1e.2X. +d.2+.1e. +0.1e.30. +0.1e.35. +b.36.1e. +0.1e.37. +3.1e.38. +5.39.1e. +0.1e.3d. +3.1e.3h. +0.1e.3i. +0.1e.3m. +0.1e.3p. +a.3v.1e. +a.3y.1e. +2.3z.1e. +5.3A.1e. +2.3B.1e. +0.1e.3C. +a.3F.1e. +5.3G.1e. +0.1e.3M. +0.1e.3Q. +5.40.1e. +0.1e.47. +0.1e.4d. +0.1e.4O. +0.1e.4Q. +5.4T.1e. +3.1e.4V. +0.1e.51. +0.1e.52. +a.5a.1e. +2.5b.1e. +5.5d.1e. +0.1e.5B. +0.1e.5C. +5.5F.1e. +3.1e.5H. +0.1e.5I. +f.1e.5K. +7.5M.1e. +0.1e.5O. +3.1e.5R. +0.1e.5U. +0.1e.5W. +1.5Y.1e. +0.1e.5Z. +0.1e.5/. +0.1e.60. +2.62.1e. +2.66.1e. +a.6b.1e. +c.6e.1e. +0.1e.6F. +0.1e.6M. +0.1e.6+. +3.1e.71. +5.79.1e. +3.1e.7D. +0.1e.2p. +0.1e.2p. +0.1f.1f. +0.1f.1g. +3.1f.1h. +3.1f.1i. +0.1j.1f. +0.1k.1f. +2.1l.1f. +0.1m.1f. +0.1n.1f. +0.1o.1f. +0.1p.1f. +0.1q.1f. +0.1r.1f. +0.1s.1f. +3.1t.1f. +2.1u.1f. +0.1v.1f. +0.1w.1f. +0.1x.1f. +0.1y.1f. +0.1z.1f. +0.1A.1f. +0.1B.1f. +0.1C.1f. +3.1D.1f. +3.1E.1f. +0.1F.1f. +0.1G.1f. +0.1H.1f. +3.1I.1f. +0.1J.1f. +3.1K.1f. +0.1L.1f. +0.1M.1f. +3.1N.1f. +0.1O.1f. +0.1P.1f. +3.1f.1Q. +d.1R.1f. +3.1S.1f. +0.1f.1T. +3.1f.1U. +0.1f.1V. +0.1f.1W. +0.1f.1X. +0.1f.1Y. +0.1f.1Z. +0.1f.1+. +0.1f.1/. +0.1f.20. +0.1f.21. +0.1f.22. +0.1f.23. +0.1f.24. +0.1f.25. +b.26.1f. +0.1f.27. +4.1f.28. +0.1f.2a. +0.1f.2b. +0.1f.2l. +6.2m.1f. +0.1f.2p. +2.2q.1f. +2.2r.1f. +7.2s.1f. +7.2t.1f. +2.2u.1f. +3.1f.2v. +0.1f.2w. +e.2x.1f. +9.2y.1f. +0.1f.2z. +3.1f.2A. +9.2B.1f. +0.1f.2C. +0.1f.2D. +3.1f.2E. +3.1f.2F. +0.1f.2G. +9.2H.1f. +0.1f.2I. +3.1f.2J. +0.1f.2K. +c.2M.1f. +9.2N.1f. +2.2O.1f. +9.2Q.1f. +7.2S.1f. +0.1f.2T. +1.2U.1f. +9.2V.1f. +1.2W.1f. +0.1f.2X. +d.2+.1f. +0.1f.30. +0.1f.35. +3.36.1f. +0.1f.37. +3.1f.38. +5.39.1f. +0.1f.3d. +3.1f.3h. +0.1f.3i. +0.1f.3m. +0.1f.3p. +a.3v.1f. +a.3y.1f. +2.3z.1f. +5.3A.1f. +2.3B.1f. +0.1f.3C. +a.3F.1f. +5.3G.1f. +0.1f.3M. +0.1f.3Q. +5.40.1f. +0.1f.47. +0.1f.4d. +3.1f.4O. +3.1f.4Q. +5.4T.1f. +3.1f.4V. +0.1f.51. +0.1f.52. +a.5a.1f. +2.5b.1f. +5.5d.1f. +0.1f.5B. +0.1f.5C. +5.5F.1f. +3.1f.5H. +0.1f.5I. +f.1f.5K. +7.5M.1f. +0.1f.5O. +3.1f.5R. +0.1f.5U. +0.1f.5W. +1.5Y.1f. +0.1f.5Z. +0.1f.5/. +0.1f.60. +2.62.1f. +2.66.1f. +a.6b.1f. +c.6e.1f. +0.1f.6F. +0.1f.6M. +0.1f.6+. +3.1f.71. +5.79.1f. +3.1f.7D. +0.1f.2p. +0.1f.2p. +f.1g.1g. +0.1h.1g. +0.1i.1g. +0.1j.1g. +0.1k.1g. +2.1l.1g. +0.1m.1g. +0.1n.1g. +0.1o.1g. +0.1p.1g. +0.1q.1g. +0.1r.1g. +0.1s.1g. +0.1t.1g. +2.1u.1g. +0.1v.1g. +0.1w.1g. +0.1x.1g. +0.1y.1g. +0.1z.1g. +0.1A.1g. +3.1B.1g. +0.1C.1g. +0.1D.1g. +0.1E.1g. +0.1F.1g. +0.1G.1g. +0.1H.1g. +0.1I.1g. +0.1J.1g. +3.1K.1g. +0.1L.1g. +0.1M.1g. +3.1N.1g. +0.1O.1g. +0.1P.1g. +d.1Q.1g. +0.1R.1g. +0.1S.1g. +d.1T.1g. +0.1U.1g. +0.1V.1g. +f.1g.1W. +0.1X.1g. +0.1Y.1g. +0.1Z.1g. +d.1+.1g. +0.1/.1g. +0.20.1g. +d.21.1g. +0.22.1g. +0.23.1g. +0.24.1g. +0.25.1g. +b.26.1g. +0.27.1g. +4.28.1g. +0.2a.1g. +3.2b.1g. +0.2l.1g. +6.2m.1g. +d.2p.1g. +2.2q.1g. +2.2r.1g. +7.2s.1g. +7.2t.1g. +2.2u.1g. +d.2v.1g. +0.2w.1g. +e.2x.1g. +9.2y.1g. +3.2z.1g. +d.2A.1g. +9.2B.1g. +3.2C.1g. +d.2D.1g. +d.2E.1g. +0.2F.1g. +0.2G.1g. +9.2H.1g. +d.2I.1g. +d.2J.1g. +0.2K.1g. +c.2M.1g. +9.2N.1g. +2.2O.1g. +9.2Q.1g. +7.2S.1g. +d.2T.1g. +1.2U.1g. +9.2V.1g. +1.2W.1g. +0.2X.1g. +b.2+.1g. +d.30.1g. +0.35.1g. +b.36.1g. +f.1g.37. +d.38.1g. +5.39.1g. +5.3d.1g. +0.3h.1g. +5.3i.1g. +f.1g.3m. +5.3p.1g. +a.3v.1g. +a.3y.1g. +2.3z.1g. +5.3A.1g. +2.3B.1g. +5.3C.1g. +a.3F.1g. +5.3G.1g. +f.1g.3M. +f.1g.3Q. +5.40.1g. +f.1g.47. +5.4d.1g. +f.1g.4O. +3.4Q.1g. +5.4T.1g. +f.1g.4V. +5.51.1g. +0.52.1g. +a.5a.1g. +2.5b.1g. +5.5d.1g. +5.5B.1g. +5.5C.1g. +5.5F.1g. +0.5H.1g. +0.5I.1g. +f.1g.5K. +7.5M.1g. +5.5O.1g. +5.5R.1g. +f.1g.5U. +3.5W.1g. +1.5Y.1g. +5.5Z.1g. +5.5/.1g. +0.60.1g. +2.62.1g. +2.66.1g. +a.6b.1g. +c.6e.1g. +f.1g.6F. +0.6M.1g. +d.6+.1g. +d.71.1g. +5.79.1g. +f.1g.7D. +d.2p.1g. +d.2p.1g. +3.1h.1h. +3.1h.1i. +0.1j.1h. +3.1k.1h. +2.1l.1h. +3.1m.1h. +3.1n.1h. +0.1o.1h. +3.1p.1h. +3.1q.1h. +3.1r.1h. +3.1s.1h. +3.1t.1h. +2.1u.1h. +3.1v.1h. +3.1w.1h. +3.1x.1h. +3.1y.1h. +3.1z.1h. +3.1A.1h. +0.1B.1h. +3.1C.1h. +3.1D.1h. +3.1E.1h. +3.1F.1h. +3.1G.1h. +3.1H.1h. +3.1I.1h. +3.1J.1h. +3.1K.1h. +0.1L.1h. +3.1M.1h. +3.1N.1h. +0.1O.1h. +0.1P.1h. +3.1h.1Q. +0.1R.1h. +3.1S.1h. +3.1h.1T. +3.1h.1U. +0.1h.1V. +0.1h.1W. +0.1h.1X. +0.1h.1Y. +0.1h.1Z. +0.1h.1+. +0.1h.1/. +0.1h.20. +0.1h.21. +0.1h.22. +0.1h.23. +0.1h.24. +0.1h.25. +b.26.1h. +0.1h.27. +4.1h.28. +4.1h.29. +0.1h.2a. +0.1h.2b. +4.1h.2c. +4.1h.2d. +4.1h.2f. +4.1h.2g. +4.1h.2h. +4.1h.2i. +4.1h.2j. +0.1h.2l. +6.2m.1h. +1.1h.2o. +0.1h.2p. +2.2q.1h. +2.2r.1h. +7.2s.1h. +7.2t.1h. +2.2u.1h. +3.1h.2v. +0.1h.2w. +e.2x.1h. +9.2y.1h. +0.1h.2z. +3.1h.2A. +9.2B.1h. +0.1h.2C. +0.1h.2D. +3.1h.2E. +3.1h.2F. +0.1h.2G. +9.2H.1h. +0.1h.2I. +3.1h.2J. +0.1h.2K. +4.1h.2L. +c.2M.1h. +9.2N.1h. +2.2O.1h. +4.1h.2P. +9.2Q.1h. +4.1h.2R. +7.2S.1h. +0.1h.2T. +1.2U.1h. +9.2V.1h. +1.2W.1h. +0.1h.2X. +4.1h.2Y. +4.1h.2Z. +b.2+.1h. +4.1h.2/. +0.1h.30. +4.1h.31. +4.1h.32. +4.1h.33. +4.1h.34. +0.1h.35. +b.36.1h. +0.1h.37. +3.1h.38. +5.39.1h. +4.1h.3a. +4.1h.3c. +0.1h.3d. +1.1h.3e. +4.1h.3f. +4.1h.3g. +3.1h.3h. +3.1h.3i. +1.1h.3j. +4.1h.3k. +4.1h.3l. +0.1h.3m. +4.1h.3n. +4.1h.3o. +0.1h.3p. +4.1h.3q. +4.1h.3r. +4.1h.3s. +4.1h.3t. +4.1h.3u. +a.3v.1h. +4.1h.3w. +4.1h.3x. +a.3y.1h. +2.3z.1h. +5.3A.1h. +2.3B.1h. +0.1h.3C. +4.1h.3D. +4.1h.3E. +a.3F.1h. +5.3G.1h. +4.1h.3H. +4.1h.3I. +4.1h.3J. +4.1h.3K. +4.1h.3L. +0.1h.3M. +4.1h.3N. +4.1h.3O. +4.1h.3P. +0.1h.3Q. +4.1h.3R. +4.1h.3S. +1.1h.3T. +4.1h.3U. +4.1h.3V. +4.1h.3W. +4.1h.3X. +4.1h.3Y. +4.1h.3Z. +1.1h.3+. +1.1h.3/. +5.40.1h. +4.1h.41. +4.1h.42. +4.1h.43. +4.1h.44. +4.1h.45. +0.1h.47. +4.1h.48. +4.1h.49. +1.1h.4a. +4.1h.4b. +4.1h.4c. +0.1h.4d. +4.1h.4e. +4.1h.4f. +4.1h.4g. +4.1h.4h. +4.1h.4i. +4.1h.4j. +4.1h.4k. +4.1h.4l. +4.1h.4m. +4.1h.4n. +4.1h.4o. +4.1h.4p. +4.1h.4q. +4.1h.4r. +4.1h.4s. +4.1h.4t. +4.1h.4u. +4.1h.4v. +4.1h.4w. +4.1h.4x. +4.1h.4y. +4.1h.4z. +4.1h.4A. +4.1h.4B. +4.1h.4C. +4.1h.4D. +4.1h.4E. +1.1h.4F. +4.1h.4G. +4.1h.4H. +1.1h.4I. +4.1h.4J. +4.1h.4K. +4.1h.4L. +4.1h.4M. +4.1h.4N. +3.1h.4O. +4.1h.4P. +3.1h.4Q. +1.1h.4R. +4.1h.4S. +4.1h.4T. +4.1h.4U. +3.1h.4V. +4.1h.4W. +4.1h.4Y. +1.1h.4Z. +4.1h.4+. +4.1h.4/. +1.1h.50. +0.1h.51. +0.1h.52. +4.1h.53. +4.1h.54. +4.1h.55. +4.1h.56. +1.1h.57. +4.1h.58. +4.1h.59. +a.5a.1h. +2.5b.1h. +4.1h.5c. +5.5d.1h. +4.1h.5e. +4.1h.5f. +4.1h.5g. +4.1h.5h. +4.1h.5i. +4.1h.5j. +4.1h.5k. +4.1h.5l. +4.1h.5m. +4.1h.5n. +4.1h.5o. +4.1h.5p. +4.1h.5q. +4.1h.5r. +1.1h.5s. +4.1h.5t. +4.1h.5u. +4.1h.5v. +1.1h.5w. +1.1h.5y. +1.1h.5z. +4.1h.5A. +0.1h.5B. +0.1h.5C. +1.1h.5D. +4.1h.5E. +5.5F.1h. +4.1h.5G. +3.1h.5H. +0.1h.5I. +f.1h.5K. +4.1h.5L. +4.1h.5M. +4.1h.5N. +0.1h.5O. +4.1h.5P. +4.1h.5Q. +3.1h.5R. +4.1h.5S. +4.1h.5T. +0.1h.5U. +4.1h.5V. +3.1h.5W. +4.1h.5X. +4.1h.5Y. +0.1h.5Z. +h.5+.1h. +0.1h.5/. +0.1h.60. +4.1h.61. +2.62.1h. +4.1h.64. +4.1h.65. +2.66.1h. +4.1h.67. +4.1h.68. +4.1h.69. +4.1h.6a. +a.6b.1h. +4.1h.6c. +4.1h.6d. +4.1h.6e. +4.1h.6f. +4.1h.6g. +4.1h.6h. +4.1h.6i. +4.1h.6j. +4.1h.6k. +4.1h.6l. +4.1h.6m. +4.1h.6n. +4.1h.6o. +4.1h.6p. +4.1h.6q. +4.1h.6r. +4.1h.6s. +4.1h.6t. +4.1h.6u. +4.1h.6v. +4.1h.6w. +4.1h.6x. +4.1h.6y. +4.1h.6z. +4.1h.6A. +4.1h.6B. +4.1h.6C. +4.1h.6D. +4.1h.6E. +0.1h.6F. +4.1h.6G. +4.1h.6H. +4.1h.6I. +4.1h.6J. +4.1h.6K. +4.1h.6L. +0.1h.6M. +4.1h.6N. +4.1h.6O. +4.1h.6P. +4.1h.6Q. +4.1h.6R. +4.1h.6S. +4.1h.6T. +4.1h.6U. +4.1h.6V. +4.1h.6W. +1.1h.6X. +4.1h.6Y. +1.1h.6Z. +0.1h.6+. +1.1h.6/. +1.1h.70. +3.1h.71. +4.1h.72. +4.1h.73. +1.1h.74. +4.1h.75. +1.1h.76. +1.1h.77. +4.1h.78. +5.79.1h. +1.1h.7a. +4.1h.7b. +4.1h.7c. +4.1h.7d. +4.1h.7e. +4.1h.7f. +4.1h.7g. +4.1h.7h. +4.1h.7i. +4.1h.7j. +4.1h.7k. +4.1h.7l. +1.1h.7m. +4.1h.7o. +4.1h.7p. +1.1h.7r. +1.1h.7s. +4.1h.7t. +4.1h.7u. +4.1h.7v. +4.1h.7w. +4.1h.7x. +4.1h.7y. +1.1h.7z. +4.1h.7A. +4.1h.7B. +4.1h.7C. +3.1h.7D. +4.1h.7E. +4.1h.7F. +4.1h.7G. +4.1h.7H. +4.1h.7I. +4.1h.7J. +1.1h.7K. +1.1h.7L. +1.1h.7M. +1.1h.7N. +1.1h.7O. +1.1h.7P. +4.1h.7Q. +4.1h.7R. +4.1h.7S. +4.1h.7T. +1.1h.7U. +4.1h.7V. +4.1h.7W. +4.1h.7X. +4.1h.7Y. +4.1h.7Z. +1.1h.7+. +1.1h.7/. +1.1h.80. +1.1h.81. +1.1h.82. +4.1h.83. +1.1h.84. +1.1h.85. +h.86.1h. +1.1h.87. +1.1h.88. +4.1h.89. +4.1h.8a. +4.1h.8b. +4.1h.8c. +4.1h.8d. +4.1h.8e. +4.1h.8f. +4.1h.8g. +4.1h.8h. +4.1h.8i. +4.1h.8j. +4.1h.8k. +4.1h.8l. +4.1h.8m. +4.1h.8n. +4.1h.8o. +4.1h.8p. +4.1h.8q. +4.1h.8r. +4.1h.8s. +4.1h.8t. +4.1h.8u. +4.1h.8v. +4.1h.8w. +4.1h.8x. +4.1h.8y. +4.1h.8z. +4.1h.8A. +4.1h.8B. +4.1h.8C. +4.1h.8D. +4.1h.8E. +4.1h.8F. +4.1h.8G. +4.1h.8H. +4.1h.8I. +4.1h.8J. +4.1h.8K. +4.1h.8L. +4.1h.8M. +4.1h.8N. +4.1h.8O. +4.1h.8P. +0.1h.2p. +0.1h.2p. +4.1h.3b. +1.1h.46. +1.1h.4X. +1.1h.5x. +1.1h.5J. +1.1h.7n. +1.1h.7q. +3.1i.1i. +0.1j.1i. +3.1k.1i. +2.1l.1i. +3.1m.1i. +3.1n.1i. +0.1o.1i. +3.1p.1i. +3.1q.1i. +3.1r.1i. +3.1s.1i. +3.1t.1i. +2.1u.1i. +3.1v.1i. +3.1w.1i. +3.1x.1i. +0.1y.1i. +3.1z.1i. +3.1A.1i. +0.1B.1i. +3.1C.1i. +3.1D.1i. +3.1E.1i. +3.1F.1i. +3.1G.1i. +3.1H.1i. +3.1I.1i. +3.1J.1i. +3.1K.1i. +0.1L.1i. +3.1M.1i. +3.1N.1i. +0.1O.1i. +0.1P.1i. +3.1i.1Q. +d.1R.1i. +3.1S.1i. +3.1i.1T. +3.1i.1U. +3.1i.1V. +0.1i.1W. +0.1i.1X. +0.1i.1Y. +0.1i.1Z. +0.1i.1+. +0.1i.1/. +0.1i.20. +0.1i.21. +0.1i.22. +0.1i.23. +0.1i.24. +0.1i.25. +b.26.1i. +3.1i.27. +0.1i.28. +0.1i.2a. +3.1i.2b. +3.1i.2l. +6.2m.1i. +3.1i.2p. +2.2q.1i. +2.2r.1i. +7.2s.1i. +7.2t.1i. +2.2u.1i. +3.1i.2v. +3.1i.2w. +e.2x.1i. +9.2y.1i. +0.1i.2z. +3.1i.2A. +9.2B.1i. +0.1i.2C. +3.1i.2D. +3.1i.2E. +3.1i.2F. +0.1i.2G. +9.2H.1i. +3.1i.2I. +3.1i.2J. +3.1i.2K. +c.2M.1i. +9.2N.1i. +2.2O.1i. +9.2Q.1i. +7.2S.1i. +0.1i.2T. +1.2U.1i. +9.2V.1i. +1.2W.1i. +0.1i.2X. +b.2+.1i. +0.1i.30. +0.1i.35. +b.36.1i. +0.1i.37. +3.1i.38. +5.39.1i. +3.1i.3d. +3.1i.3h. +3.1i.3i. +0.1i.3m. +0.1i.3p. +a.3v.1i. +a.3y.1i. +2.3z.1i. +5.3A.1i. +2.3B.1i. +3.1i.3C. +a.3F.1i. +5.3G.1i. +0.1i.3M. +0.1i.3Q. +5.40.1i. +0.1i.47. +0.1i.4d. +3.1i.4O. +3.1i.4Q. +5.4T.1i. +3.1i.4V. +3.1i.51. +0.1i.52. +a.5a.1i. +2.5b.1i. +5.5d.1i. +3.1i.5B. +0.1i.5C. +5.5F.1i. +3.1i.5H. +0.1i.5I. +f.1i.5K. +7.5M.1i. +0.1i.5O. +3.1i.5R. +3.1i.5U. +3.1i.5W. +1.5Y.1i. +0.1i.5Z. +0.1i.5/. +0.1i.60. +2.62.1i. +2.66.1i. +a.6b.1i. +c.6e.1i. +0.1i.6F. +0.1i.6M. +0.1i.6+. +3.1i.71. +5.79.1i. +3.1i.7D. +3.1i.2p. +3.1i.2p. +0.1j.1j. +0.1j.1k. +2.1l.1j. +0.1j.1m. +0.1j.1n. +0.1j.1o. +0.1j.1p. +0.1j.1q. +0.1j.1r. +0.1j.1s. +0.1j.1t. +2.1u.1j. +0.1j.1v. +0.1j.1w. +0.1j.1x. +0.1j.1y. +0.1j.1z. +0.1j.1A. +3.1B.1j. +0.1j.1C. +0.1j.1D. +0.1j.1E. +0.1j.1F. +0.1j.1G. +0.1j.1H. +0.1j.1I. +0.1j.1J. +0.1j.1K. +0.1j.1L. +0.1j.1M. +0.1j.1N. +0.1j.1O. +0.1j.1P. +0.1j.1Q. +0.1j.1R. +0.1j.1S. +0.1j.1T. +0.1j.1U. +0.1j.1V. +0.1j.1W. +0.1j.1X. +0.1j.1Y. +0.1j.1Z. +0.1j.1+. +0.1j.1/. +0.1j.20. +0.1j.21. +0.1j.22. +0.1j.23. +0.1j.24. +0.1j.25. +b.26.1j. +0.1j.27. +0.1j.28. +0.1j.2a. +0.1j.2b. +3.1j.2l. +6.2m.1j. +0.1j.2p. +2.2q.1j. +2.2r.1j. +7.2s.1j. +7.2t.1j. +2.2u.1j. +0.1j.2v. +0.1j.2w. +e.2x.1j. +9.2y.1j. +0.1j.2z. +0.1j.2A. +9.2B.1j. +0.1j.2C. +0.1j.2D. +0.1j.2E. +0.1j.2F. +0.1j.2G. +9.2H.1j. +0.1j.2I. +0.1j.2J. +0.1j.2K. +c.2M.1j. +9.2N.1j. +2.2O.1j. +9.2Q.1j. +7.2S.1j. +0.1j.2T. +1.2U.1j. +9.2V.1j. +1.2W.1j. +0.1j.2X. +d.2+.1j. +0.1j.30. +0.1j.35. +b.36.1j. +0.1j.37. +0.1j.38. +5.39.1j. +0.1j.3d. +0.1j.3h. +0.1j.3i. +0.1j.3m. +0.1j.3p. +a.3v.1j. +a.3y.1j. +2.3z.1j. +5.3A.1j. +2.3B.1j. +0.1j.3C. +a.3F.1j. +5.3G.1j. +0.1j.3M. +0.1j.3Q. +5.40.1j. +0.1j.47. +0.1j.4d. +0.1j.4O. +0.1j.4Q. +5.4T.1j. +0.1j.4V. +0.1j.51. +0.1j.52. +a.5a.1j. +2.5b.1j. +5.5d.1j. +0.1j.5B. +0.1j.5C. +0.1j.5F. +0.1j.5H. +0.1j.5I. +f.1j.5K. +7.5M.1j. +0.1j.5O. +0.1j.5R. +0.1j.5U. +0.1j.5W. +1.5Y.1j. +0.1j.5Z. +0.1j.5/. +0.1j.60. +2.62.1j. +2.66.1j. +a.6b.1j. +c.6e.1j. +0.1j.6F. +0.1j.6M. +0.1j.6+. +0.1j.71. +5.79.1j. +0.1j.7D. +0.1j.2p. +0.1j.2p. +0.1k.1k. +2.1l.1k. +0.1k.1m. +0.1n.1k. +0.1o.1k. +0.1k.1p. +0.1k.1q. +0.1k.1r. +0.1k.1s. +0.1k.1t. +2.1u.1k. +0.1k.1v. +3.1k.1w. +0.1k.1x. +0.1k.1y. +0.1k.1z. +0.1k.1A. +0.1B.1k. +0.1k.1C. +0.1k.1D. +0.1E.1k. +0.1k.1F. +0.1k.1G. +0.1k.1H. +0.1k.1I. +0.1k.1J. +0.1k.1K. +0.1k.1L. +0.1k.1M. +0.1k.1N. +0.1k.1O. +0.1k.1P. +3.1k.1Q. +0.1k.1R. +0.1k.1S. +0.1k.1T. +3.1k.1U. +0.1k.1V. +0.1k.1W. +0.1k.1X. +0.1k.1Y. +0.1k.1Z. +0.1k.1+. +0.1k.1/. +0.1k.20. +0.1k.21. +0.1k.22. +0.1k.23. +0.1k.24. +0.1k.25. +b.26.1k. +0.1k.27. +0.1k.28. +0.1k.2a. +0.1k.2b. +0.1k.2l. +6.2m.1k. +0.1k.2p. +2.2q.1k. +2.2r.1k. +7.2s.1k. +7.2t.1k. +2.2u.1k. +0.1k.2v. +0.1k.2w. +e.2x.1k. +9.2y.1k. +0.1k.2z. +3.1k.2A. +9.2B.1k. +0.1k.2C. +0.1k.2D. +3.1k.2E. +0.1k.2F. +0.1k.2G. +9.2H.1k. +0.1k.2I. +3.1k.2J. +0.1k.2K. +c.2M.1k. +9.2N.1k. +2.2O.1k. +9.2Q.1k. +7.2S.1k. +0.1k.2T. +1.2U.1k. +9.2V.1k. +1.2W.1k. +0.1k.2X. +b.2+.1k. +0.1k.30. +0.1k.35. +b.36.1k. +0.1k.37. +0.1k.38. +5.39.1k. +0.1k.3d. +3.1k.3h. +0.1k.3i. +0.1k.3m. +0.1k.3p. +a.3v.1k. +a.3y.1k. +2.3z.1k. +5.3A.1k. +2.3B.1k. +0.1k.3C. +a.3F.1k. +5.3G.1k. +0.1k.3M. +0.1k.3Q. +5.40.1k. +0.1k.47. +0.1k.4d. +0.1k.4O. +3.1k.4Q. +5.4T.1k. +3.1k.4V. +0.1k.51. +0.1k.52. +a.5a.1k. +2.5b.1k. +5.5d.1k. +0.1k.5B. +0.1k.5C. +0.1k.5F. +3.1k.5H. +0.1k.5I. +f.1k.5K. +7.5M.1k. +0.1k.5O. +3.1k.5R. +0.1k.5U. +0.1k.5W. +1.5Y.1k. +0.1k.5Z. +0.1k.5/. +0.1k.60. +2.62.1k. +2.66.1k. +a.6b.1k. +c.6e.1k. +0.1k.6F. +0.1k.6M. +0.1k.6+. +3.1k.71. +5.79.1k. +3.1k.7D. +0.1k.2p. +0.1k.2p. +2.1l.1l. +2.1l.1m. +2.1l.1n. +2.1l.1o. +2.1l.1p. +2.1l.1q. +2.1l.1r. +2.1l.1s. +2.1l.1t. +2.1u.1l. +2.1l.1v. +2.1l.1w. +2.1l.1x. +2.1l.1y. +2.1l.1z. +2.1l.1A. +2.1l.1B. +2.1l.1C. +2.1l.1D. +2.1l.1E. +2.1l.1F. +2.1l.1G. +2.1l.1H. +2.1l.1I. +2.1l.1J. +2.1l.1K. +2.1l.1L. +2.1l.1M. +2.1l.1N. +2.1l.1O. +2.1l.1P. +2.1l.1Q. +2.1l.1R. +2.1l.1S. +2.1l.1T. +2.1l.1U. +2.1l.1V. +2.1l.1W. +2.1l.1X. +2.1l.1Y. +2.1l.1Z. +2.1l.1+. +2.1l.1/. +2.1l.20. +2.1l.21. +2.1l.22. +2.1l.23. +2.1l.24. +2.1l.25. +2.1l.26. +2.1l.27. +2.1l.28. +2.1l.2a. +2.1l.2b. +2.1l.2l. +6.2m.1l. +2.1l.2p. +2.1l.2q. +2.1l.2r. +7.2s.1l. +7.2t.1l. +2.2u.1l. +2.1l.2v. +2.1l.2w. +e.2x.1l. +2.1l.2y. +2.1l.2z. +2.1l.2A. +2.1l.2B. +2.1l.2C. +2.1l.2D. +2.1l.2E. +2.1l.2F. +2.1l.2G. +2.1l.2H. +2.1l.2I. +2.1l.2J. +2.1l.2K. +c.2M.1l. +2.1l.2N. +2.1l.2O. +2.1l.2Q. +7.2S.1l. +2.1l.2T. +1.2U.1l. +2.1l.2V. +1.2W.1l. +2.1l.2X. +2.1l.2+. +2.1l.30. +2.1l.35. +2.1l.36. +2.1l.37. +2.1l.38. +2.1l.39. +2.1l.3d. +2.1l.3h. +2.1l.3i. +2.1l.3m. +2.1l.3p. +a.3v.1l. +a.3y.1l. +2.3z.1l. +2.1l.3A. +2.3B.1l. +2.1l.3C. +a.3F.1l. +2.1l.3G. +2.1l.3M. +2.1l.3Q. +2.1l.40. +2.1l.47. +2.1l.4d. +2.1l.4O. +2.1l.4Q. +5.4T.1l. +2.1l.4V. +2.1l.51. +2.1l.52. +a.5a.1l. +2.5b.1l. +2.1l.5d. +2.1l.5B. +2.1l.5C. +2.1l.5F. +2.1l.5H. +2.1l.5I. +f.1l.5K. +7.5M.1l. +2.1l.5O. +2.1l.5R. +2.1l.5U. +2.1l.5W. +1.5Y.1l. +2.1l.5Z. +2.1l.5/. +2.1l.60. +2.1l.62. +2.1l.66. +a.6b.1l. +c.6e.1l. +2.1l.6F. +2.1l.6M. +2.1l.6+. +2.1l.71. +2.1l.79. +2.1l.7D. +2.1l.2p. +2.1l.2p. +0.1m.1m. +0.1n.1m. +0.1o.1m. +0.1m.1p. +0.1m.1q. +0.1m.1r. +0.1m.1s. +0.1m.1t. +2.1u.1m. +0.1m.1v. +3.1m.1w. +0.1m.1x. +0.1m.1y. +0.1m.1z. +0.1m.1A. +0.1B.1m. +0.1m.1C. +0.1m.1D. +0.1E.1m. +0.1m.1F. +0.1m.1G. +0.1m.1H. +0.1m.1I. +0.1m.1J. +0.1m.1K. +0.1m.1L. +0.1m.1M. +0.1m.1N. +0.1m.1O. +0.1m.1P. +3.1m.1Q. +0.1m.1R. +0.1m.1S. +0.1m.1T. +3.1m.1U. +0.1m.1V. +0.1m.1W. +0.1m.1X. +0.1m.1Y. +0.1m.1Z. +0.1m.1+. +0.1m.1/. +0.1m.20. +0.1m.21. +0.1m.22. +0.1m.23. +0.1m.24. +0.1m.25. +b.26.1m. +0.1m.27. +4.1m.28. +0.1m.2a. +0.1m.2b. +0.1m.2l. +6.2m.1m. +0.1m.2p. +2.2q.1m. +2.2r.1m. +7.2s.1m. +7.2t.1m. +2.2u.1m. +3.1m.2v. +0.1m.2w. +e.2x.1m. +9.2y.1m. +0.1m.2z. +3.1m.2A. +9.2B.1m. +0.1m.2C. +0.1m.2D. +3.1m.2E. +3.1m.2F. +0.1m.2G. +9.2H.1m. +0.1m.2I. +3.1m.2J. +0.1m.2K. +c.2M.1m. +9.2N.1m. +2.2O.1m. +9.2Q.1m. +7.2S.1m. +0.1m.2T. +1.2U.1m. +9.2V.1m. +1.2W.1m. +0.1m.2X. +b.2+.1m. +0.1m.30. +0.1m.35. +b.36.1m. +0.1m.37. +0.1m.38. +5.39.1m. +0.1m.3d. +3.1m.3h. +3.1m.3i. +0.1m.3m. +0.1m.3p. +a.3v.1m. +a.3y.1m. +2.3z.1m. +5.3A.1m. +2.3B.1m. +0.1m.3C. +a.3F.1m. +5.3G.1m. +0.1m.3M. +0.1m.3Q. +5.40.1m. +0.1m.47. +0.1m.4d. +3.1m.4O. +3.1m.4Q. +5.4T.1m. +3.1m.4V. +0.1m.51. +0.1m.52. +a.5a.1m. +2.5b.1m. +5.5d.1m. +0.1m.5B. +0.1m.5C. +0.1m.5F. +3.1m.5H. +0.1m.5I. +f.1m.5K. +7.5M.1m. +0.1m.5O. +3.1m.5R. +0.1m.5U. +0.1m.5W. +1.5Y.1m. +0.1m.5Z. +0.1m.5/. +0.1m.60. +2.62.1m. +2.66.1m. +a.6b.1m. +c.6e.1m. +0.1m.6F. +0.1m.6M. +0.1m.6+. +3.1m.71. +5.79.1m. +3.1m.7D. +0.1m.2p. +0.1m.2p. +0.1n.1n. +0.1o.1n. +0.1n.1p. +0.1n.1q. +0.1n.1r. +0.1n.1s. +0.1n.1t. +2.1u.1n. +0.1n.1v. +3.1n.1w. +0.1n.1x. +0.1n.1y. +0.1n.1z. +0.1n.1A. +0.1B.1n. +0.1n.1C. +0.1n.1D. +0.1E.1n. +0.1n.1F. +0.1n.1G. +0.1n.1H. +0.1n.1I. +0.1n.1J. +0.1n.1K. +0.1n.1L. +0.1n.1M. +0.1n.1N. +0.1n.1O. +0.1n.1P. +3.1n.1Q. +0.1n.1R. +0.1n.1S. +0.1n.1T. +0.1n.1U. +0.1n.1V. +0.1n.1W. +0.1n.1X. +0.1n.1Y. +0.1n.1Z. +0.1n.1+. +0.1n.1/. +0.1n.20. +0.1n.21. +0.1n.22. +0.1n.23. +0.1n.24. +0.1n.25. +b.26.1n. +0.1n.27. +0.1n.28. +0.1n.2a. +0.1n.2b. +0.1n.2l. +6.2m.1n. +0.1n.2p. +2.2q.1n. +2.2r.1n. +7.2s.1n. +7.2t.1n. +2.2u.1n. +0.1n.2v. +0.1n.2w. +e.2x.1n. +9.2y.1n. +0.1n.2z. +3.1n.2A. +9.2B.1n. +0.1n.2C. +0.1n.2D. +3.1n.2E. +0.1n.2F. +0.1n.2G. +9.2H.1n. +0.1n.2I. +3.1n.2J. +0.1n.2K. +c.2M.1n. +9.2N.1n. +2.2O.1n. +9.2Q.1n. +7.2S.1n. +0.1n.2T. +1.2U.1n. +9.2V.1n. +1.2W.1n. +0.1n.2X. +b.2+.1n. +0.1n.30. +0.1n.35. +b.36.1n. +0.1n.37. +0.1n.38. +5.39.1n. +0.1n.3d. +3.1n.3h. +0.1n.3i. +0.1n.3m. +0.1n.3p. +a.3v.1n. +a.3y.1n. +2.3z.1n. +5.3A.1n. +2.3B.1n. +0.1n.3C. +a.3F.1n. +5.3G.1n. +0.1n.3M. +0.1n.3Q. +5.40.1n. +0.1n.47. +0.1n.4d. +0.1n.4O. +3.1n.4Q. +5.4T.1n. +3.1n.4V. +0.1n.51. +0.1n.52. +a.5a.1n. +2.5b.1n. +5.5d.1n. +0.1n.5B. +0.1n.5C. +0.1n.5F. +3.1n.5H. +0.1n.5I. +f.1n.5K. +7.5M.1n. +0.1n.5O. +3.1n.5R. +0.1n.5U. +0.1n.5W. +1.5Y.1n. +0.1n.5Z. +0.1n.5/. +0.1n.60. +2.62.1n. +2.66.1n. +a.6b.1n. +c.6e.1n. +0.1n.6F. +0.1n.6M. +0.1n.6+. +3.1n.71. +5.79.1n. +3.1n.7D. +0.1n.2p. +0.1n.2p. +0.1o.1o. +0.1o.1p. +0.1o.1q. +0.1o.1r. +0.1o.1s. +0.1o.1t. +2.1u.1o. +0.1o.1v. +0.1o.1w. +0.1o.1x. +0.1o.1y. +0.1o.1z. +0.1o.1A. +0.1B.1o. +0.1o.1C. +0.1o.1D. +0.1E.1o. +0.1o.1F. +0.1o.1G. +0.1o.1H. +0.1o.1I. +0.1o.1J. +0.1o.1K. +0.1o.1L. +0.1o.1M. +0.1o.1N. +0.1o.1O. +0.1o.1P. +0.1o.1Q. +0.1o.1R. +0.1o.1S. +0.1o.1T. +0.1o.1U. +0.1o.1V. +0.1o.1W. +0.1o.1X. +0.1o.1Y. +0.1o.1Z. +0.1o.1+. +0.1o.1/. +0.1o.20. +0.1o.21. +0.1o.22. +0.1o.23. +0.1o.24. +0.1o.25. +b.26.1o. +0.1o.27. +0.1o.28. +0.1o.2a. +0.1o.2b. +0.1o.2l. +6.2m.1o. +0.1o.2p. +2.2q.1o. +2.2r.1o. +7.2s.1o. +7.2t.1o. +2.2u.1o. +0.1o.2v. +0.1o.2w. +e.2x.1o. +9.2y.1o. +0.1o.2z. +0.1o.2A. +9.2B.1o. +0.1o.2C. +0.1o.2D. +0.1o.2E. +0.1o.2F. +0.1o.2G. +9.2H.1o. +0.1o.2I. +0.1o.2J. +0.1o.2K. +c.2M.1o. +9.2N.1o. +2.2O.1o. +9.2Q.1o. +7.2S.1o. +0.1o.2T. +1.2U.1o. +9.2V.1o. +1.2W.1o. +0.1o.2X. +b.2+.1o. +0.1o.30. +0.1o.35. +b.36.1o. +0.1o.37. +0.1o.38. +5.39.1o. +0.1o.3d. +0.1o.3h. +0.1o.3i. +0.1o.3m. +0.1o.3p. +a.3v.1o. +a.3y.1o. +2.3z.1o. +5.3A.1o. +2.3B.1o. +0.1o.3C. +a.3F.1o. +5.3G.1o. +0.1o.3M. +0.1o.3Q. +5.40.1o. +0.1o.47. +0.1o.4d. +0.1o.4O. +0.1o.4Q. +5.4T.1o. +0.1o.4V. +0.1o.51. +0.1o.52. +a.5a.1o. +2.5b.1o. +5.5d.1o. +0.1o.5B. +0.1o.5C. +0.1o.5F. +0.1o.5H. +0.1o.5I. +f.1o.5K. +7.5M.1o. +0.1o.5O. +0.1o.5R. +0.1o.5U. +0.1o.5W. +1.5Y.1o. +0.1o.5Z. +0.1o.5/. +0.1o.60. +2.62.1o. +2.66.1o. +a.6b.1o. +c.6e.1o. +0.1o.6F. +0.1o.6M. +0.1o.6+. +0.1o.71. +5.79.1o. +0.1o.7D. +0.1o.2p. +0.1o.2p. +0.1p.1p. +0.1p.1q. +0.1p.1r. +0.1p.1s. +3.1t.1p. +2.1u.1p. +0.1v.1p. +0.1w.1p. +0.1x.1p. +0.1y.1p. +0.1p.1z. +0.1p.1A. +0.1B.1p. +0.1p.1C. +0.1p.1D. +3.1E.1p. +0.1p.1F. +0.1p.1G. +0.1p.1H. +0.1p.1I. +0.1p.1J. +0.1p.1K. +0.1p.1L. +0.1p.1M. +0.1p.1N. +0.1p.1O. +0.1p.1P. +3.1p.1Q. +0.1p.1R. +0.1p.1S. +0.1p.1T. +0.1p.1U. +0.1p.1V. +0.1p.1W. +0.1p.1X. +0.1p.1Y. +0.1p.1Z. +0.1p.1+. +0.1p.1/. +0.1p.20. +0.1p.21. +0.1p.22. +0.1p.23. +0.1p.24. +0.1p.25. +b.26.1p. +0.1p.27. +4.1p.28. +0.1p.2a. +0.1p.2b. +0.1p.2l. +6.2m.1p. +0.1p.2p. +2.2q.1p. +2.2r.1p. +7.2s.1p. +7.2t.1p. +2.2u.1p. +0.1p.2v. +0.1p.2w. +e.2x.1p. +9.2y.1p. +0.1p.2z. +3.1p.2A. +9.2B.1p. +0.1p.2C. +0.1p.2D. +3.1p.2E. +0.1p.2F. +0.1p.2G. +9.2H.1p. +0.1p.2I. +3.1p.2J. +0.1p.2K. +c.2M.1p. +9.2N.1p. +2.2O.1p. +9.2Q.1p. +7.2S.1p. +0.1p.2T. +1.2U.1p. +9.2V.1p. +1.2W.1p. +0.1p.2X. +b.2+.1p. +0.1p.30. +0.1p.35. +b.36.1p. +0.1p.37. +0.1p.38. +5.39.1p. +0.1p.3d. +3.1p.3h. +0.1p.3i. +0.1p.3m. +0.1p.3p. +a.3v.1p. +a.3y.1p. +2.3z.1p. +5.3A.1p. +2.3B.1p. +0.1p.3C. +a.3F.1p. +5.3G.1p. +0.1p.3M. +0.1p.3Q. +5.40.1p. +0.1p.47. +0.1p.4d. +0.1p.4O. +3.1p.4Q. +5.4T.1p. +3.1p.4V. +0.1p.51. +0.1p.52. +a.5a.1p. +2.5b.1p. +5.5d.1p. +0.1p.5B. +0.1p.5C. +0.1p.5F. +3.1p.5H. +0.1p.5I. +f.1p.5K. +7.5M.1p. +0.1p.5O. +0.1p.5R. +0.1p.5U. +0.1p.5W. +1.5Y.1p. +0.1p.5Z. +0.1p.5/. +0.1p.60. +2.62.1p. +2.66.1p. +a.6b.1p. +c.6e.1p. +0.1p.6F. +0.1p.6M. +0.1p.6+. +3.1p.71. +5.79.1p. +3.1p.7D. +0.1p.2p. +0.1p.2p. +0.1q.1q. +0.1q.1r. +0.1q.1s. +3.1t.1q. +2.1u.1q. +0.1v.1q. +0.1w.1q. +0.1x.1q. +0.1y.1q. +0.1q.1z. +0.1q.1A. +0.1B.1q. +0.1q.1C. +0.1q.1D. +3.1E.1q. +0.1q.1F. +0.1q.1G. +0.1q.1H. +0.1q.1I. +0.1q.1J. +0.1q.1K. +0.1q.1L. +0.1q.1M. +0.1q.1N. +0.1q.1O. +0.1q.1P. +3.1q.1Q. +0.1q.1R. +0.1q.1S. +0.1q.1T. +3.1q.1U. +0.1q.1V. +0.1q.1W. +0.1q.1X. +0.1q.1Y. +0.1q.1Z. +0.1q.1+. +0.1q.1/. +0.1q.20. +0.1q.21. +0.1q.22. +0.1q.23. +0.1q.24. +0.1q.25. +b.26.1q. +0.1q.27. +4.1q.28. +0.1q.2a. +0.1q.2b. +0.1q.2l. +6.2m.1q. +0.1q.2p. +2.2q.1q. +2.2r.1q. +7.2s.1q. +7.2t.1q. +2.2u.1q. +0.1q.2v. +0.1q.2w. +e.2x.1q. +9.2y.1q. +0.1q.2z. +3.1q.2A. +9.2B.1q. +0.1q.2C. +0.1q.2D. +3.1q.2E. +3.1q.2F. +0.1q.2G. +9.2H.1q. +0.1q.2I. +3.1q.2J. +0.1q.2K. +c.2M.1q. +9.2N.1q. +2.2O.1q. +9.2Q.1q. +7.2S.1q. +0.1q.2T. +1.2U.1q. +9.2V.1q. +1.2W.1q. +0.1q.2X. +b.2+.1q. +0.1q.30. +0.1q.35. +b.36.1q. +0.1q.37. +0.1q.38. +5.39.1q. +0.1q.3d. +3.1q.3h. +0.1q.3i. +0.1q.3m. +0.1q.3p. +a.3v.1q. +a.3y.1q. +2.3z.1q. +5.3A.1q. +2.3B.1q. +0.1q.3C. +a.3F.1q. +5.3G.1q. +0.1q.3M. +0.1q.3Q. +5.40.1q. +0.1q.47. +0.1q.4d. +3.1q.4O. +3.1q.4Q. +5.4T.1q. +3.1q.4V. +0.1q.51. +0.1q.52. +a.5a.1q. +2.5b.1q. +5.5d.1q. +0.1q.5B. +0.1q.5C. +0.1q.5F. +3.1q.5H. +0.1q.5I. +f.1q.5K. +7.5M.1q. +0.1q.5O. +3.1q.5R. +0.1q.5U. +0.1q.5W. +1.5Y.1q. +0.1q.5Z. +0.1q.5/. +0.1q.60. +2.62.1q. +2.66.1q. +a.6b.1q. +c.6e.1q. +0.1q.6F. +0.1q.6M. +0.1q.6+. +3.1q.71. +5.79.1q. +3.1q.7D. +0.1q.2p. +0.1q.2p. +0.1r.1r. +0.1r.1s. +3.1t.1r. +2.1u.1r. +0.1v.1r. +0.1w.1r. +0.1x.1r. +0.1y.1r. +0.1r.1z. +0.1r.1A. +0.1B.1r. +0.1r.1C. +0.1r.1D. +3.1E.1r. +0.1r.1F. +0.1r.1G. +0.1r.1H. +0.1r.1I. +0.1r.1J. +0.1r.1K. +0.1r.1L. +0.1r.1M. +0.1r.1N. +0.1r.1O. +0.1r.1P. +3.1r.1Q. +0.1r.1R. +3.1r.1S. +0.1r.1T. +3.1r.1U. +0.1r.1V. +0.1r.1W. +0.1r.1X. +0.1r.1Y. +0.1r.1Z. +0.1r.1+. +0.1r.1/. +0.1r.20. +0.1r.21. +0.1r.22. +0.1r.23. +0.1r.24. +0.1r.25. +b.26.1r. +0.1r.27. +4.1r.28. +0.1r.2a. +0.1r.2b. +0.1r.2l. +6.2m.1r. +0.1r.2p. +2.2q.1r. +2.2r.1r. +7.2s.1r. +7.2t.1r. +2.2u.1r. +3.1r.2v. +0.1r.2w. +e.2x.1r. +9.2y.1r. +0.1r.2z. +3.1r.2A. +9.2B.1r. +0.1r.2C. +0.1r.2D. +3.1r.2E. +3.1r.2F. +0.1r.2G. +9.2H.1r. +0.1r.2I. +3.1r.2J. +0.1r.2K. +c.2M.1r. +9.2N.1r. +2.2O.1r. +9.2Q.1r. +7.2S.1r. +0.1r.2T. +1.2U.1r. +9.2V.1r. +1.2W.1r. +0.1r.2X. +b.2+.1r. +0.1r.30. +0.1r.35. +b.36.1r. +0.1r.37. +3.1r.38. +5.39.1r. +0.1r.3d. +3.1r.3h. +3.1r.3i. +0.1r.3m. +0.1r.3p. +a.3v.1r. +a.3y.1r. +2.3z.1r. +5.3A.1r. +2.3B.1r. +0.1r.3C. +a.3F.1r. +5.3G.1r. +0.1r.3M. +0.1r.3Q. +5.40.1r. +0.1r.47. +0.1r.4d. +3.1r.4O. +3.1r.4Q. +5.4T.1r. +3.1r.4V. +0.1r.51. +0.1r.52. +a.5a.1r. +2.5b.1r. +5.5d.1r. +0.1r.5B. +0.1r.5C. +0.1r.5F. +3.1r.5H. +0.1r.5I. +f.1r.5K. +7.5M.1r. +0.1r.5O. +3.1r.5R. +0.1r.5U. +0.1r.5W. +1.5Y.1r. +0.1r.5Z. +0.1r.5/. +0.1r.60. +2.62.1r. +2.66.1r. +a.6b.1r. +c.6e.1r. +0.1r.6F. +0.1r.6M. +0.1r.6+. +3.1r.71. +5.79.1r. +3.1r.7D. +0.1r.2p. +0.1r.2p. +0.1s.1s. +0.1t.1s. +2.1u.1s. +0.1v.1s. +0.1w.1s. +0.1x.1s. +0.1y.1s. +0.1s.1z. +0.1s.1A. +3.1B.1s. +0.1s.1C. +0.1s.1D. +0.1E.1s. +0.1s.1F. +0.1s.1G. +0.1s.1H. +0.1s.1I. +0.1s.1J. +0.1s.1K. +0.1s.1L. +0.1s.1M. +0.1s.1N. +0.1s.1O. +0.1s.1P. +3.1s.1Q. +0.1s.1R. +0.1s.1S. +0.1s.1T. +3.1s.1U. +0.1s.1V. +0.1s.1W. +0.1s.1X. +0.1s.1Y. +0.1s.1Z. +0.1s.1+. +0.1s.1/. +0.1s.20. +0.1s.21. +0.1s.22. +0.1s.23. +0.1s.24. +0.1s.25. +b.26.1s. +0.1s.27. +4.1s.28. +0.1s.2a. +0.1s.2b. +0.1s.2l. +6.2m.1s. +0.1s.2p. +2.2q.1s. +2.2r.1s. +7.2s.1s. +7.2t.1s. +2.2u.1s. +3.1s.2v. +0.1s.2w. +e.2x.1s. +9.2y.1s. +0.1s.2z. +3.1s.2A. +9.2B.1s. +0.1s.2C. +0.1s.2D. +3.1s.2E. +3.1s.2F. +0.1s.2G. +9.2H.1s. +0.1s.2I. +3.1s.2J. +0.1s.2K. +c.2M.1s. +9.2N.1s. +2.2O.1s. +9.2Q.1s. +7.2S.1s. +0.1s.2T. +1.2U.1s. +9.2V.1s. +1.2W.1s. +0.1s.2X. +b.2+.1s. +0.1s.30. +0.1s.35. +b.36.1s. +0.1s.37. +0.1s.38. +5.39.1s. +0.1s.3d. +3.1s.3h. +0.1s.3i. +0.1s.3m. +0.1s.3p. +a.3v.1s. +a.3y.1s. +2.3z.1s. +5.3A.1s. +2.3B.1s. +0.1s.3C. +a.3F.1s. +5.3G.1s. +0.1s.3M. +0.1s.3Q. +5.40.1s. +0.1s.47. +0.1s.4d. +3.1s.4O. +3.1s.4Q. +5.4T.1s. +3.1s.4V. +0.1s.51. +0.1s.52. +a.5a.1s. +2.5b.1s. +5.5d.1s. +0.1s.5B. +0.1s.5C. +0.1s.5F. +3.1s.5H. +0.1s.5I. +f.1s.5K. +7.5M.1s. +0.1s.5O. +3.1s.5R. +0.1s.5U. +0.1s.5W. +1.5Y.1s. +0.1s.5Z. +0.1s.5/. +0.1s.60. +2.62.1s. +2.66.1s. +a.6b.1s. +c.6e.1s. +0.1s.6F. +0.1s.6M. +0.1s.6+. +3.1s.71. +5.79.1s. +3.1s.7D. +0.1s.2p. +0.1s.2p. +3.1t.1t. +2.1u.1t. +3.1t.1v. +3.1t.1w. +3.1t.1x. +3.1t.1y. +0.1t.1z. +0.1t.1A. +0.1B.1t. +3.1t.1C. +0.1t.1D. +0.1E.1t. +0.1t.1F. +3.1t.1G. +0.1t.1H. +0.1t.1I. +0.1t.1J. +0.1t.1K. +0.1t.1L. +0.1t.1M. +3.1t.1N. +0.1t.1O. +0.1t.1P. +3.1t.1Q. +0.1t.1R. +3.1t.1S. +3.1t.1T. +3.1t.1U. +0.1t.1V. +0.1t.1W. +0.1t.1X. +0.1t.1Y. +0.1t.1Z. +0.1t.1+. +0.1t.1/. +0.1t.20. +0.1t.21. +0.1t.22. +0.1t.23. +0.1t.24. +0.1t.25. +b.26.1t. +3.1t.27. +0.1t.28. +4.1t.29. +0.1t.2a. +3.1t.2b. +4.1t.2c. +4.1t.2d. +4.1t.2f. +4.1t.2g. +4.1t.2h. +4.1t.2i. +4.1t.2j. +0.1t.2l. +6.2m.1t. +1.1t.2o. +3.1t.2p. +2.2q.1t. +2.2r.1t. +7.2s.1t. +7.2t.1t. +2.2u.1t. +3.1t.2v. +3.1t.2w. +e.2x.1t. +9.2y.1t. +0.1t.2z. +3.1t.2A. +9.2B.1t. +0.1t.2C. +3.1t.2D. +3.1t.2E. +3.1t.2F. +0.1t.2G. +9.2H.1t. +3.1t.2I. +3.1t.2J. +0.1t.2K. +4.1t.2L. +c.2M.1t. +9.2N.1t. +2.2O.1t. +4.1t.2P. +9.2Q.1t. +8.1t.2R. +7.2S.1t. +0.1t.2T. +1.2U.1t. +9.2V.1t. +1.2W.1t. +0.1t.2X. +8.1t.2Y. +3.1t.2Z. +b.2+.1t. +8.1t.2/. +0.1t.30. +3.1t.31. +3.1t.32. +3.1t.33. +3.1t.34. +0.1t.35. +b.36.1t. +0.1t.37. +3.1t.38. +5.39.1t. +4.1t.3a. +4.1t.3c. +3.1t.3d. +1.1t.3e. +4.1t.3f. +3.1t.3g. +3.1t.3h. +3.1t.3i. +1.1t.3j. +3.1t.3k. +4.1t.3l. +0.1t.3m. +3.1t.3n. +3.1t.3o. +0.1t.3p. +3.1t.3q. +4.1t.3r. +4.1t.3s. +8.1t.3t. +8.1t.3u. +a.3v.1t. +4.1t.3w. +4.1t.3x. +a.3y.1t. +2.3z.1t. +5.3A.1t. +2.3B.1t. +3.1t.3C. +4.1t.3D. +4.1t.3E. +a.3F.1t. +5.3G.1t. +4.1t.3H. +4.1t.3I. +4.1t.3J. +4.1t.3K. +4.1t.3L. +0.1t.3M. +4.1t.3N. +4.1t.3O. +4.1t.3P. +0.1t.3Q. +4.1t.3R. +4.1t.3S. +1.1t.3T. +4.1t.3U. +4.1t.3V. +4.1t.3W. +4.1t.3X. +4.1t.3Y. +4.1t.3Z. +1.1t.3+. +1.1t.3/. +5.40.1t. +4.1t.41. +4.1t.42. +4.1t.43. +4.1t.44. +4.1t.45. +0.1t.47. +4.1t.48. +4.1t.49. +1.1t.4a. +4.1t.4b. +4.1t.4c. +0.1t.4d. +4.1t.4e. +4.1t.4f. +4.1t.4g. +4.1t.4h. +4.1t.4i. +4.1t.4j. +3.1t.4k. +4.1t.4l. +4.1t.4m. +4.1t.4n. +4.1t.4o. +4.1t.4p. +4.1t.4q. +4.1t.4r. +4.1t.4s. +4.1t.4t. +4.1t.4u. +4.1t.4v. +4.1t.4w. +4.1t.4x. +4.1t.4y. +4.1t.4z. +4.1t.4A. +4.1t.4B. +4.1t.4C. +4.1t.4D. +4.1t.4E. +1.1t.4F. +8.1t.4G. +3.1t.4H. +1.1t.4I. +8.1t.4J. +8.1t.4K. +3.1t.4L. +4.1t.4M. +4.1t.4N. +3.1t.4O. +4.1t.4P. +3.1t.4Q. +1.1t.4R. +4.1t.4S. +4.1t.4T. +8.1t.4U. +3.1t.4V. +4.1t.4W. +3.1t.4Y. +1.1t.4Z. +4.1t.4+. +4.1t.4/. +1.1t.50. +0.1t.51. +0.1t.52. +8.1t.53. +3.1t.54. +8.1t.55. +8.1t.56. +1.1t.57. +3.1t.58. +8.1t.59. +a.5a.1t. +2.5b.1t. +4.1t.5c. +5.5d.1t. +8.1t.5e. +8.1t.5f. +4.1t.5g. +8.1t.5h. +4.1t.5i. +4.1t.5j. +4.1t.5k. +4.1t.5l. +4.1t.5m. +4.1t.5n. +4.1t.5o. +4.1t.5p. +4.1t.5q. +4.1t.5r. +1.1t.5s. +4.1t.5t. +4.1t.5u. +4.1t.5v. +1.1t.5w. +1.1t.5y. +1.1t.5z. +3.1t.5A. +0.1t.5B. +0.1t.5C. +1.1t.5D. +3.1t.5E. +0.1t.5F. +8.1t.5G. +3.1t.5H. +0.1t.5I. +f.1t.5K. +8.1t.5L. +8.1t.5M. +3.1t.5N. +0.1t.5O. +8.1t.5P. +4.1t.5Q. +3.1t.5R. +4.1t.5S. +4.1t.5T. +0.1t.5U. +8.1t.5V. +3.1t.5W. +8.1t.5X. +8.1t.5Y. +0.1t.5Z. +0.1t.5/. +0.1t.60. +4.1t.61. +2.62.1t. +4.1t.64. +4.1t.65. +5.66.1t. +4.1t.67. +4.1t.68. +4.1t.69. +4.1t.6a. +a.6b.1t. +4.1t.6c. +4.1t.6d. +4.1t.6e. +4.1t.6f. +4.1t.6g. +4.1t.6h. +4.1t.6i. +4.1t.6j. +4.1t.6k. +4.1t.6l. +4.1t.6m. +4.1t.6n. +4.1t.6o. +4.1t.6p. +4.1t.6q. +4.1t.6r. +4.1t.6s. +4.1t.6t. +4.1t.6u. +4.1t.6v. +4.1t.6w. +4.1t.6x. +4.1t.6y. +4.1t.6z. +4.1t.6A. +4.1t.6B. +4.1t.6C. +4.1t.6D. +4.1t.6E. +3.1t.6F. +4.1t.6G. +4.1t.6H. +4.1t.6I. +4.1t.6J. +4.1t.6K. +3.1t.6L. +0.1t.6M. +4.1t.6N. +4.1t.6O. +4.1t.6P. +4.1t.6Q. +4.1t.6R. +4.1t.6S. +4.1t.6T. +4.1t.6U. +4.1t.6V. +4.1t.6W. +1.1t.6X. +4.1t.6Y. +1.1t.6Z. +0.1t.6+. +1.1t.6/. +1.1t.70. +3.1t.71. +4.1t.72. +4.1t.73. +1.1t.74. +4.1t.75. +1.1t.76. +1.1t.77. +4.1t.78. +5.79.1t. +1.1t.7a. +4.1t.7b. +4.1t.7c. +4.1t.7d. +4.1t.7e. +4.1t.7f. +4.1t.7g. +4.1t.7h. +4.1t.7i. +4.1t.7j. +4.1t.7k. +4.1t.7l. +1.1t.7m. +4.1t.7o. +4.1t.7p. +1.1t.7r. +1.1t.7s. +3.1t.7t. +4.1t.7u. +4.1t.7v. +4.1t.7w. +4.1t.7x. +4.1t.7y. +1.1t.7z. +4.1t.7A. +8.1t.7B. +4.1t.7C. +3.1t.7D. +4.1t.7E. +4.1t.7F. +4.1t.7G. +4.1t.7H. +4.1t.7I. +4.1t.7J. +1.1t.7K. +1.1t.7L. +1.1t.7M. +1.1t.7N. +1.1t.7O. +1.1t.7P. +4.1t.7Q. +3.1t.7R. +4.1t.7S. +4.1t.7T. +1.1t.7U. +8.1t.7V. +4.1t.7W. +4.1t.7X. +4.1t.7Y. +4.1t.7Z. +1.1t.7+. +1.1t.7/. +1.1t.80. +1.1t.81. +1.1t.82. +4.1t.83. +1.1t.84. +1.1t.85. +1.1t.87. +1.1t.88. +3.1t.89. +4.1t.8a. +3.1t.8b. +4.1t.8c. +8.1t.8d. +4.1t.8e. +4.1t.8f. +4.1t.8g. +4.1t.8h. +4.1t.8i. +4.1t.8j. +4.1t.8k. +4.1t.8l. +4.1t.8m. +4.1t.8n. +4.1t.8o. +4.1t.8p. +4.1t.8q. +4.1t.8r. +4.1t.8s. +4.1t.8t. +4.1t.8u. +4.1t.8v. +4.1t.8w. +4.1t.8x. +4.1t.8y. +4.1t.8z. +4.1t.8A. +4.1t.8B. +4.1t.8C. +4.1t.8D. +4.1t.8E. +4.1t.8F. +4.1t.8G. +4.1t.8H. +4.1t.8I. +4.1t.8J. +4.1t.8K. +4.1t.8L. +4.1t.8M. +4.1t.8N. +4.1t.8O. +4.1t.8P. +3.1t.2p. +3.1t.2p. +8.1t.3b. +1.1t.46. +1.1t.4X. +1.1t.5x. +1.1t.5J. +1.1t.7n. +1.1t.7q. +2.1u.1u. +2.1u.1v. +2.1u.1w. +2.1u.1x. +2.1u.1y. +2.1u.1z. +2.1u.1A. +2.1u.1B. +2.1u.1C. +2.1u.1D. +2.1u.1E. +2.1u.1F. +2.1u.1G. +2.1u.1H. +2.1u.1I. +2.1u.1J. +2.1u.1K. +2.1u.1L. +2.1u.1M. +2.1u.1N. +2.1u.1O. +2.1u.1P. +2.1u.1Q. +2.1u.1R. +2.1u.1S. +2.1u.1T. +2.1u.1U. +2.1u.1V. +2.1u.1W. +2.1u.1X. +2.1u.1Y. +2.1u.1Z. +2.1u.1+. +2.1u.1/. +2.1u.20. +2.1u.21. +2.1u.22. +2.1u.23. +2.1u.24. +2.1u.25. +2.1u.26. +2.1u.27. +2.1u.28. +2.1u.2a. +2.1u.2b. +2.1u.2l. +6.2m.1u. +2.1u.2p. +2.1u.2q. +2.1u.2r. +7.2s.1u. +7.2t.1u. +2.2u.1u. +2.1u.2v. +2.1u.2w. +e.2x.1u. +2.1u.2y. +2.1u.2z. +2.1u.2A. +2.1u.2B. +2.1u.2C. +2.1u.2D. +2.1u.2E. +2.1u.2F. +2.1u.2G. +2.1u.2H. +2.1u.2I. +2.1u.2J. +2.1u.2K. +c.2M.1u. +2.1u.2N. +2.1u.2O. +2.1u.2Q. +7.2S.1u. +2.1u.2T. +1.2U.1u. +2.1u.2V. +1.2W.1u. +2.1u.2X. +2.1u.2+. +2.1u.30. +2.1u.35. +2.1u.36. +2.1u.37. +2.1u.38. +2.1u.39. +2.1u.3d. +2.1u.3h. +2.1u.3i. +2.1u.3m. +2.1u.3p. +a.3v.1u. +a.3y.1u. +2.3z.1u. +2.1u.3A. +2.3B.1u. +2.1u.3C. +a.3F.1u. +2.1u.3G. +2.1u.3M. +2.1u.3Q. +2.1u.40. +2.1u.47. +2.1u.4d. +2.1u.4O. +2.1u.4Q. +5.4T.1u. +2.1u.4V. +2.1u.51. +2.1u.52. +a.5a.1u. +2.5b.1u. +2.1u.5d. +2.1u.5B. +2.1u.5C. +2.1u.5F. +2.1u.5H. +2.1u.5I. +f.1u.5K. +7.5M.1u. +2.1u.5O. +2.1u.5R. +2.1u.5U. +2.1u.5W. +1.5Y.1u. +2.1u.5Z. +2.1u.5/. +2.1u.60. +2.1u.62. +2.1u.66. +a.6b.1u. +c.6e.1u. +2.1u.6F. +2.1u.6M. +2.1u.6+. +2.1u.71. +2.1u.79. +2.1u.7D. +2.1u.2p. +2.1u.2p. +0.1v.1v. +0.1w.1v. +0.1x.1v. +0.1y.1v. +0.1v.1z. +0.1v.1A. +0.1B.1v. +0.1v.1C. +0.1v.1D. +0.1E.1v. +0.1v.1F. +0.1v.1G. +0.1v.1H. +0.1v.1I. +0.1v.1J. +0.1v.1K. +0.1v.1L. +0.1v.1M. +0.1v.1N. +0.1v.1O. +0.1v.1P. +3.1v.1Q. +0.1v.1R. +0.1v.1S. +0.1v.1T. +0.1v.1U. +0.1v.1V. +0.1v.1W. +0.1v.1X. +0.1v.1Y. +0.1v.1Z. +0.1v.1+. +0.1v.1/. +0.1v.20. +0.1v.21. +0.1v.22. +0.1v.23. +0.1v.24. +0.1v.25. +b.26.1v. +0.1v.27. +4.1v.28. +0.1v.2a. +0.1v.2b. +0.1v.2l. +6.2m.1v. +0.1v.2p. +2.2q.1v. +2.2r.1v. +7.2s.1v. +7.2t.1v. +2.2u.1v. +0.1v.2v. +0.1v.2w. +e.2x.1v. +9.2y.1v. +0.1v.2z. +3.1v.2A. +9.2B.1v. +0.1v.2C. +0.1v.2D. +3.1v.2E. +0.1v.2F. +0.1v.2G. +9.2H.1v. +0.1v.2I. +3.1v.2J. +0.1v.2K. +c.2M.1v. +9.2N.1v. +2.2O.1v. +9.2Q.1v. +7.2S.1v. +0.1v.2T. +1.2U.1v. +9.2V.1v. +1.2W.1v. +0.1v.2X. +b.2+.1v. +0.1v.30. +0.1v.35. +b.36.1v. +0.1v.37. +0.1v.38. +5.39.1v. +0.1v.3d. +3.1v.3h. +0.1v.3i. +0.1v.3m. +0.1v.3p. +a.3v.1v. +a.3y.1v. +2.3z.1v. +5.3A.1v. +2.3B.1v. +0.1v.3C. +a.3F.1v. +5.3G.1v. +0.1v.3M. +0.1v.3Q. +5.40.1v. +0.1v.47. +0.1v.4d. +0.1v.4O. +3.1v.4Q. +5.4T.1v. +3.1v.4V. +0.1v.51. +0.1v.52. +a.5a.1v. +2.5b.1v. +5.5d.1v. +0.1v.5B. +0.1v.5C. +0.1v.5F. +3.1v.5H. +0.1v.5I. +f.1v.5K. +7.5M.1v. +0.1v.5O. +0.1v.5R. +0.1v.5U. +0.1v.5W. +1.5Y.1v. +0.1v.5Z. +0.1v.5/. +0.1v.60. +2.62.1v. +2.66.1v. +a.6b.1v. +c.6e.1v. +0.1v.6F. +0.1v.6M. +0.1v.6+. +3.1v.71. +5.79.1v. +3.1v.7D. +0.1v.2p. +0.1v.2p. +0.1w.1w. +3.1x.1w. +3.1y.1w. +0.1w.1z. +0.1w.1A. +0.1B.1w. +0.1w.1C. +0.1w.1D. +3.1E.1w. +0.1w.1F. +0.1w.1G. +0.1w.1H. +0.1w.1I. +0.1w.1J. +0.1w.1K. +0.1w.1L. +0.1w.1M. +0.1w.1N. +0.1w.1O. +0.1w.1P. +3.1w.1Q. +0.1w.1R. +0.1w.1S. +0.1w.1T. +3.1w.1U. +0.1w.1V. +0.1w.1W. +0.1w.1X. +0.1w.1Y. +0.1w.1Z. +0.1w.1+. +0.1w.1/. +0.1w.20. +0.1w.21. +0.1w.22. +0.1w.23. +0.1w.24. +0.1w.25. +b.26.1w. +0.1w.27. +4.1w.28. +0.1w.2a. +0.1w.2b. +0.1w.2l. +6.2m.1w. +0.1w.2p. +2.2q.1w. +2.2r.1w. +7.2s.1w. +7.2t.1w. +2.2u.1w. +3.1w.2v. +0.1w.2w. +e.2x.1w. +9.2y.1w. +0.1w.2z. +3.1w.2A. +9.2B.1w. +0.1w.2C. +0.1w.2D. +3.1w.2E. +3.1w.2F. +0.1w.2G. +9.2H.1w. +0.1w.2I. +3.1w.2J. +0.1w.2K. +c.2M.1w. +9.2N.1w. +2.2O.1w. +9.2Q.1w. +7.2S.1w. +0.1w.2T. +1.2U.1w. +9.2V.1w. +1.2W.1w. +0.1w.2X. +b.2+.1w. +0.1w.30. +0.1w.35. +b.36.1w. +0.1w.37. +3.1w.38. +5.39.1w. +0.1w.3d. +3.1w.3h. +0.1w.3i. +0.1w.3m. +0.1w.3p. +a.3v.1w. +a.3y.1w. +2.3z.1w. +5.3A.1w. +2.3B.1w. +0.1w.3C. +a.3F.1w. +5.3G.1w. +0.1w.3M. +0.1w.3Q. +5.40.1w. +0.1w.47. +0.1w.4d. +3.1w.4O. +3.1w.4Q. +5.4T.1w. +3.1w.4V. +0.1w.51. +0.1w.52. +a.5a.1w. +2.5b.1w. +5.5d.1w. +0.1w.5B. +0.1w.5C. +0.1w.5F. +3.1w.5H. +0.1w.5I. +f.1w.5K. +7.5M.1w. +0.1w.5O. +3.1w.5R. +0.1w.5U. +0.1w.5W. +1.5Y.1w. +0.1w.5Z. +0.1w.5/. +0.1w.60. +2.62.1w. +2.66.1w. +a.6b.1w. +c.6e.1w. +0.1w.6F. +0.1w.6M. +0.1w.6+. +3.1w.71. +5.79.1w. +3.1w.7D. +0.1w.2p. +0.1w.2p. +0.1x.1x. +0.1y.1x. +0.1x.1z. +0.1x.1A. +0.1B.1x. +0.1x.1C. +0.1x.1D. +0.1E.1x. +0.1x.1F. +0.1x.1G. +0.1x.1H. +0.1x.1I. +0.1x.1J. +0.1x.1K. +0.1x.1L. +0.1x.1M. +0.1x.1N. +0.1x.1O. +0.1x.1P. +0.1x.1Q. +0.1x.1R. +0.1x.1S. +0.1x.1T. +0.1x.1U. +0.1x.1V. +0.1x.1W. +0.1x.1X. +0.1x.1Y. +0.1x.1Z. +0.1x.1+. +0.1x.1/. +0.1x.20. +0.1x.21. +0.1x.22. +0.1x.23. +0.1x.24. +0.1x.25. +b.26.1x. +0.1x.27. +4.1x.28. +0.1x.2a. +0.1x.2b. +0.1x.2l. +6.2m.1x. +0.1x.2p. +2.2q.1x. +2.2r.1x. +7.2s.1x. +7.2t.1x. +2.2u.1x. +0.1x.2v. +0.1x.2w. +e.2x.1x. +9.2y.1x. +0.1x.2z. +3.1x.2A. +9.2B.1x. +0.1x.2C. +0.1x.2D. +3.1x.2E. +0.1x.2F. +0.1x.2G. +9.2H.1x. +0.1x.2I. +3.1x.2J. +0.1x.2K. +c.2M.1x. +9.2N.1x. +2.2O.1x. +9.2Q.1x. +7.2S.1x. +0.1x.2T. +1.2U.1x. +9.2V.1x. +1.2W.1x. +0.1x.2X. +b.2+.1x. +0.1x.30. +0.1x.35. +b.36.1x. +0.1x.37. +0.1x.38. +5.39.1x. +0.1x.3d. +3.1x.3h. +0.1x.3i. +0.1x.3m. +0.1x.3p. +a.3v.1x. +a.3y.1x. +2.3z.1x. +5.3A.1x. +2.3B.1x. +0.1x.3C. +a.3F.1x. +5.3G.1x. +0.1x.3M. +0.1x.3Q. +5.40.1x. +0.1x.47. +0.1x.4d. +0.1x.4O. +3.1x.4Q. +5.4T.1x. +3.1x.4V. +0.1x.51. +0.1x.52. +a.5a.1x. +2.5b.1x. +5.5d.1x. +0.1x.5B. +0.1x.5C. +0.1x.5F. +3.1x.5H. +0.1x.5I. +f.1x.5K. +7.5M.1x. +0.1x.5O. +3.1x.5R. +0.1x.5U. +0.1x.5W. +1.5Y.1x. +0.1x.5Z. +0.1x.5/. +0.1x.60. +2.62.1x. +2.66.1x. +a.6b.1x. +c.6e.1x. +0.1x.6F. +0.1x.6M. +0.1x.6+. +0.1x.71. +5.79.1x. +0.1x.7D. +0.1x.2p. +0.1x.2p. +0.1y.1y. +0.1y.1z. +0.1y.1A. +0.1B.1y. +0.1y.1C. +0.1y.1D. +0.1E.1y. +0.1y.1F. +0.1y.1G. +0.1y.1H. +0.1y.1I. +0.1y.1J. +0.1y.1K. +0.1y.1L. +0.1y.1M. +0.1y.1N. +0.1y.1O. +0.1y.1P. +0.1y.1Q. +0.1y.1R. +0.1y.1S. +0.1y.1T. +0.1y.1U. +0.1y.1V. +0.1y.1W. +0.1y.1X. +0.1y.1Y. +0.1y.1Z. +0.1y.1+. +0.1y.1/. +0.1y.20. +0.1y.21. +0.1y.22. +0.1y.23. +0.1y.24. +0.1y.25. +b.26.1y. +0.1y.27. +4.1y.28. +0.1y.2a. +0.1y.2b. +0.1y.2l. +6.2m.1y. +0.1y.2p. +2.2q.1y. +2.2r.1y. +7.2s.1y. +7.2t.1y. +2.2u.1y. +0.1y.2v. +0.1y.2w. +e.2x.1y. +9.2y.1y. +0.1y.2z. +3.1y.2A. +9.2B.1y. +0.1y.2C. +0.1y.2D. +3.1y.2E. +0.1y.2F. +0.1y.2G. +9.2H.1y. +0.1y.2I. +3.1y.2J. +0.1y.2K. +c.2M.1y. +9.2N.1y. +2.2O.1y. +9.2Q.1y. +7.2S.1y. +0.1y.2T. +1.2U.1y. +9.2V.1y. +1.2W.1y. +0.1y.2X. +b.2+.1y. +0.1y.30. +0.1y.35. +b.36.1y. +0.1y.37. +0.1y.38. +5.39.1y. +0.1y.3d. +3.1y.3h. +0.1y.3i. +0.1y.3m. +0.1y.3p. +a.3v.1y. +a.3y.1y. +2.3z.1y. +5.3A.1y. +2.3B.1y. +0.1y.3C. +a.3F.1y. +5.3G.1y. +0.1y.3M. +0.1y.3Q. +5.40.1y. +0.1y.47. +0.1y.4d. +0.1y.4O. +3.1y.4Q. +5.4T.1y. +3.1y.4V. +0.1y.51. +0.1y.52. +a.5a.1y. +2.5b.1y. +5.5d.1y. +0.1y.5B. +0.1y.5C. +0.1y.5F. +3.1y.5H. +0.1y.5I. +f.1y.5K. +7.5M.1y. +0.1y.5O. +3.1y.5R. +0.1y.5U. +0.1y.5W. +1.5Y.1y. +0.1y.5Z. +0.1y.5/. +0.1y.60. +2.62.1y. +2.66.1y. +a.6b.1y. +c.6e.1y. +0.1y.6F. +0.1y.6M. +0.1y.6+. +0.1y.71. +5.79.1y. +0.1y.7D. +0.1y.2p. +0.1y.2p. +0.1z.1z. +0.1A.1z. +0.1B.1z. +0.1z.1C. +0.1z.1D. +0.1E.1z. +0.1z.1F. +0.1z.1G. +0.1z.1H. +0.1z.1I. +0.1z.1J. +0.1z.1K. +0.1z.1L. +0.1z.1M. +0.1z.1N. +0.1z.1O. +0.1z.1P. +3.1z.1Q. +0.1z.1R. +3.1z.1S. +0.1z.1T. +3.1z.1U. +0.1z.1V. +0.1z.1W. +0.1z.1X. +0.1z.1Y. +0.1z.1Z. +0.1z.1+. +0.1z.1/. +0.1z.20. +0.1z.21. +0.1z.22. +0.1z.23. +0.1z.24. +0.1z.25. +b.26.1z. +0.1z.27. +4.1z.28. +0.1z.2a. +0.1z.2b. +0.1z.2l. +6.2m.1z. +0.1z.2p. +2.2q.1z. +2.2r.1z. +7.2s.1z. +7.2t.1z. +2.2u.1z. +3.1z.2v. +0.1z.2w. +e.2x.1z. +9.2y.1z. +0.1z.2z. +3.1z.2A. +9.2B.1z. +0.1z.2C. +0.1z.2D. +3.1z.2E. +3.1z.2F. +0.1z.2G. +9.2H.1z. +0.1z.2I. +3.1z.2J. +0.1z.2K. +c.2M.1z. +9.2N.1z. +2.2O.1z. +9.2Q.1z. +7.2S.1z. +0.1z.2T. +1.2U.1z. +9.2V.1z. +1.2W.1z. +0.1z.2X. +b.2+.1z. +0.1z.30. +0.1z.35. +b.36.1z. +0.1z.37. +3.1z.38. +5.39.1z. +0.1z.3d. +3.1z.3h. +3.1z.3i. +0.1z.3m. +0.1z.3p. +a.3v.1z. +a.3y.1z. +2.3z.1z. +5.3A.1z. +2.3B.1z. +0.1z.3C. +a.3F.1z. +5.3G.1z. +0.1z.3M. +0.1z.3Q. +5.40.1z. +0.1z.47. +0.1z.4d. +3.1z.4O. +3.1z.4Q. +5.4T.1z. +3.1z.4V. +0.1z.51. +0.1z.52. +a.5a.1z. +2.5b.1z. +5.5d.1z. +0.1z.5B. +0.1z.5C. +0.1z.5F. +3.1z.5H. +0.1z.5I. +f.1z.5K. +7.5M.1z. +0.1z.5O. +3.1z.5R. +0.1z.5U. +0.1z.5W. +1.5Y.1z. +0.1z.5Z. +0.1z.5/. +0.1z.60. +2.62.1z. +2.66.1z. +a.6b.1z. +c.6e.1z. +0.1z.6F. +0.1z.6M. +0.1z.6+. +3.1z.71. +5.79.1z. +3.1z.7D. +0.1z.2p. +0.1z.2p. +0.1A.1A. +0.1B.1A. +0.1A.1C. +0.1A.1D. +0.1E.1A. +0.1A.1F. +0.1A.1G. +0.1A.1H. +0.1A.1I. +0.1A.1J. +0.1A.1K. +0.1A.1L. +0.1A.1M. +0.1A.1N. +0.1A.1O. +0.1A.1P. +3.1A.1Q. +0.1A.1R. +3.1A.1S. +0.1A.1T. +3.1A.1U. +0.1A.1V. +0.1A.1W. +0.1A.1X. +0.1A.1Y. +0.1A.1Z. +0.1A.1+. +0.1A.1/. +0.1A.20. +0.1A.21. +0.1A.22. +0.1A.23. +0.1A.24. +0.1A.25. +b.26.1A. +0.1A.27. +4.1A.28. +0.1A.2a. +0.1A.2b. +0.1A.2l. +6.2m.1A. +0.1A.2p. +2.2q.1A. +2.2r.1A. +7.2s.1A. +7.2t.1A. +2.2u.1A. +3.1A.2v. +0.1A.2w. +e.2x.1A. +9.2y.1A. +0.1A.2z. +3.1A.2A. +9.2B.1A. +0.1A.2C. +0.1A.2D. +3.1A.2E. +3.1A.2F. +0.1A.2G. +9.2H.1A. +0.1A.2I. +3.1A.2J. +0.1A.2K. +c.2M.1A. +9.2N.1A. +2.2O.1A. +9.2Q.1A. +7.2S.1A. +0.1A.2T. +1.2U.1A. +9.2V.1A. +1.2W.1A. +0.1A.2X. +b.2+.1A. +0.1A.30. +0.1A.35. +b.36.1A. +0.1A.37. +3.1A.38. +5.39.1A. +0.1A.3d. +3.1A.3h. +3.1A.3i. +0.1A.3m. +0.1A.3p. +a.3v.1A. +a.3y.1A. +2.3z.1A. +5.3A.1A. +2.3B.1A. +0.1A.3C. +a.3F.1A. +5.3G.1A. +0.1A.3M. +0.1A.3Q. +5.40.1A. +0.1A.47. +0.1A.4d. +3.1A.4O. +3.1A.4Q. +5.4T.1A. +3.1A.4V. +0.1A.51. +0.1A.52. +a.5a.1A. +2.5b.1A. +5.5d.1A. +0.1A.5B. +0.1A.5C. +0.1A.5F. +3.1A.5H. +0.1A.5I. +f.1A.5K. +7.5M.1A. +0.1A.5O. +3.1A.5R. +0.1A.5U. +0.1A.5W. +1.5Y.1A. +0.1A.5Z. +0.1A.5/. +0.1A.60. +2.62.1A. +2.66.1A. +a.6b.1A. +c.6e.1A. +0.1A.6F. +0.1A.6M. +0.1A.6+. +3.1A.71. +5.79.1A. +3.1A.7D. +0.1A.2p. +0.1A.2p. +0.1B.1B. +0.1B.1C. +0.1B.1D. +3.1B.1E. +0.1B.1F. +0.1B.1G. +0.1B.1H. +0.1B.1I. +0.1B.1J. +0.1B.1K. +0.1B.1L. +0.1B.1M. +0.1B.1N. +0.1B.1O. +0.1B.1P. +0.1B.1Q. +0.1B.1R. +0.1B.1S. +3.1B.1T. +0.1B.1U. +0.1B.1V. +0.1B.1W. +0.1B.1X. +3.1B.1Y. +0.1B.1Z. +0.1B.1+. +0.1B.1/. +0.1B.20. +3.1B.21. +0.1B.22. +0.1B.23. +0.1B.24. +0.1B.25. +b.26.1B. +0.1B.27. +4.1B.28. +0.1B.2a. +0.1B.2b. +0.1B.2l. +6.2m.1B. +0.1B.2p. +2.2q.1B. +2.2r.1B. +7.2s.1B. +7.2t.1B. +2.2u.1B. +3.1B.2v. +3.1B.2w. +e.2x.1B. +9.2y.1B. +0.1B.2z. +3.1B.2A. +9.2B.1B. +0.1B.2C. +0.1B.2D. +0.1B.2E. +0.1B.2F. +0.1B.2G. +9.2H.1B. +0.1B.2I. +3.1B.2J. +0.1B.2K. +c.2M.1B. +9.2N.1B. +2.2O.1B. +9.2Q.1B. +7.2S.1B. +0.1B.2T. +1.2U.1B. +9.2V.1B. +1.2W.1B. +0.1B.2X. +b.2+.1B. +0.1B.30. +3.1B.35. +b.36.1B. +0.1B.37. +3.1B.38. +5.39.1B. +0.1B.3d. +3.1B.3h. +0.1B.3i. +0.1B.3m. +0.1B.3p. +a.3v.1B. +a.3y.1B. +2.3z.1B. +5.3A.1B. +2.3B.1B. +0.1B.3C. +a.3F.1B. +5.3G.1B. +0.1B.3M. +3.1B.3Q. +5.40.1B. +0.1B.47. +0.1B.4d. +0.1B.4O. +0.1B.4Q. +5.4T.1B. +3.1B.4V. +3.1B.51. +0.1B.52. +a.5a.1B. +2.5b.1B. +5.5d.1B. +0.1B.5B. +0.1B.5C. +0.1B.5F. +0.1B.5H. +0.1B.5I. +f.1B.5K. +7.5M.1B. +3.1B.5O. +0.1B.5R. +3.1B.5U. +0.1B.5W. +1.5Y.1B. +0.1B.5Z. +0.1B.5/. +0.1B.60. +2.62.1B. +2.66.1B. +a.6b.1B. +c.6e.1B. +0.1B.6F. +0.1B.6M. +0.1B.6+. +0.1B.71. +5.79.1B. +3.1B.7D. +0.1B.2p. +0.1B.2p. +0.1C.1C. +0.1D.1C. +0.1E.1C. +0.1F.1C. +0.1G.1C. +0.1H.1C. +0.1I.1C. +0.1C.1J. +0.1C.1K. +0.1C.1L. +0.1C.1M. +0.1C.1N. +0.1C.1O. +0.1C.1P. +0.1C.1Q. +0.1C.1R. +0.1C.1S. +0.1C.1T. +0.1C.1U. +0.1C.1V. +0.1C.1W. +0.1C.1X. +0.1C.1Y. +0.1C.1Z. +0.1C.1+. +0.1C.1/. +0.1C.20. +0.1C.21. +0.1C.22. +0.1C.23. +0.1C.24. +0.1C.25. +b.26.1C. +0.1C.27. +4.1C.28. +0.1C.2a. +0.1C.2b. +0.1C.2l. +6.2m.1C. +0.1C.2p. +2.2q.1C. +2.2r.1C. +7.2s.1C. +7.2t.1C. +2.2u.1C. +0.1C.2v. +0.1C.2w. +e.2x.1C. +9.2y.1C. +0.1C.2z. +3.1C.2A. +9.2B.1C. +0.1C.2C. +0.1C.2D. +3.1C.2E. +3.1C.2F. +0.1C.2G. +9.2H.1C. +0.1C.2I. +3.1C.2J. +0.1C.2K. +c.2M.1C. +9.2N.1C. +2.2O.1C. +9.2Q.1C. +7.2S.1C. +0.1C.2T. +1.2U.1C. +9.2V.1C. +1.2W.1C. +0.1C.2X. +b.2+.1C. +0.1C.30. +0.1C.35. +b.36.1C. +0.1C.37. +0.1C.38. +5.39.1C. +0.1C.3d. +3.1C.3h. +3.1C.3i. +0.1C.3m. +0.1C.3p. +a.3v.1C. +a.3y.1C. +2.3z.1C. +5.3A.1C. +2.3B.1C. +0.1C.3C. +a.3F.1C. +5.3G.1C. +0.1C.3M. +0.1C.3Q. +5.40.1C. +0.1C.47. +0.1C.4d. +0.1C.4O. +0.1C.4Q. +5.4T.1C. +3.1C.4V. +0.1C.51. +0.1C.52. +a.5a.1C. +2.5b.1C. +5.5d.1C. +0.1C.5B. +0.1C.5C. +0.1C.5F. +3.1C.5H. +0.1C.5I. +f.1C.5K. +7.5M.1C. +0.1C.5O. +0.1C.5R. +0.1C.5U. +0.1C.5W. +1.5Y.1C. +0.1C.5Z. +0.1C.5/. +0.1C.60. +2.62.1C. +2.66.1C. +a.6b.1C. +c.6e.1C. +0.1C.6F. +0.1C.6M. +0.1C.6+. +3.1C.71. +5.79.1C. +0.1C.7D. +0.1C.2p. +0.1C.2p. +0.1D.1D. +0.1E.1D. +0.1F.1D. +0.1G.1D. +0.1D.1H. +0.1D.1I. +0.1D.1J. +0.1D.1K. +0.1D.1L. +0.1D.1M. +0.1D.1N. +0.1D.1O. +0.1D.1P. +3.1D.1Q. +0.1D.1R. +3.1D.1S. +3.1D.1T. +3.1D.1U. +0.1D.1V. +0.1D.1W. +0.1D.1X. +0.1D.1Y. +0.1D.1Z. +0.1D.1+. +0.1D.1/. +0.1D.20. +0.1D.21. +0.1D.22. +0.1D.23. +0.1D.24. +0.1D.25. +b.26.1D. +0.1D.27. +4.1D.28. +0.1D.2a. +0.1D.2b. +0.1D.2l. +6.2m.1D. +0.1D.2p. +2.2q.1D. +2.2r.1D. +7.2s.1D. +7.2t.1D. +2.2u.1D. +3.1D.2v. +0.1D.2w. +e.2x.1D. +9.2y.1D. +0.1D.2z. +3.1D.2A. +9.2B.1D. +0.1D.2C. +0.1D.2D. +3.1D.2E. +3.1D.2F. +0.1D.2G. +9.2H.1D. +0.1D.2I. +3.1D.2J. +0.1D.2K. +c.2M.1D. +9.2N.1D. +2.2O.1D. +9.2Q.1D. +7.2S.1D. +0.1D.2T. +1.2U.1D. +9.2V.1D. +1.2W.1D. +0.1D.2X. +b.2+.1D. +0.1D.30. +0.1D.35. +b.36.1D. +0.1D.37. +3.1D.38. +5.39.1D. +0.1D.3d. +3.1D.3h. +3.1D.3i. +0.1D.3m. +0.1D.3p. +a.3v.1D. +a.3y.1D. +2.3z.1D. +5.3A.1D. +2.3B.1D. +0.1D.3C. +a.3F.1D. +5.3G.1D. +0.1D.3M. +0.1D.3Q. +5.40.1D. +0.1D.47. +0.1D.4d. +3.1D.4O. +3.1D.4Q. +5.4T.1D. +3.1D.4V. +0.1D.51. +0.1D.52. +a.5a.1D. +2.5b.1D. +5.5d.1D. +0.1D.5B. +0.1D.5C. +0.1D.5F. +3.1D.5H. +0.1D.5I. +f.1D.5K. +7.5M.1D. +0.1D.5O. +3.1D.5R. +0.1D.5U. +3.1D.5W. +1.5Y.1D. +0.1D.5Z. +0.1D.5/. +0.1D.60. +2.62.1D. +2.66.1D. +a.6b.1D. +c.6e.1D. +0.1D.6F. +0.1D.6M. +0.1D.6+. +3.1D.71. +5.79.1D. +3.1D.7D. +0.1D.2p. +0.1D.2p. +0.1E.1E. +0.1E.1F. +0.1E.1G. +0.1E.1H. +0.1E.1I. +0.1E.1J. +0.1E.1K. +0.1E.1L. +0.1E.1M. +0.1E.1N. +0.1E.1O. +0.1E.1P. +3.1E.1Q. +0.1E.1R. +0.1E.1S. +3.1E.1T. +3.1E.1U. +0.1E.1V. +0.1E.1W. +0.1E.1X. +0.1E.1Y. +0.1E.1Z. +0.1E.1+. +0.1E.1/. +0.1E.20. +0.1E.21. +0.1E.22. +0.1E.23. +0.1E.24. +0.1E.25. +b.26.1E. +0.1E.27. +4.1E.28. +0.1E.2a. +0.1E.2b. +0.1E.2l. +6.2m.1E. +0.1E.2p. +2.2q.1E. +2.2r.1E. +7.2s.1E. +7.2t.1E. +2.2u.1E. +3.1E.2v. +0.1E.2w. +e.2x.1E. +9.2y.1E. +0.1E.2z. +3.1E.2A. +9.2B.1E. +0.1E.2C. +0.1E.2D. +3.1E.2E. +3.1E.2F. +0.1E.2G. +9.2H.1E. +0.1E.2I. +3.1E.2J. +0.1E.2K. +c.2M.1E. +9.2N.1E. +2.2O.1E. +9.2Q.1E. +7.2S.1E. +0.1E.2T. +1.2U.1E. +9.2V.1E. +1.2W.1E. +0.1E.2X. +b.2+.1E. +0.1E.30. +0.1E.35. +b.36.1E. +0.1E.37. +3.1E.38. +5.39.1E. +0.1E.3d. +3.1E.3h. +3.1E.3i. +0.1E.3m. +0.1E.3p. +a.3v.1E. +a.3y.1E. +2.3z.1E. +5.3A.1E. +2.3B.1E. +0.1E.3C. +a.3F.1E. +5.3G.1E. +0.1E.3M. +0.1E.3Q. +5.40.1E. +0.1E.47. +0.1E.4d. +3.1E.4O. +3.1E.4Q. +5.4T.1E. +3.1E.4V. +0.1E.51. +0.1E.52. +a.5a.1E. +2.5b.1E. +5.5d.1E. +0.1E.5B. +0.1E.5C. +0.1E.5F. +3.1E.5H. +0.1E.5I. +f.1E.5K. +7.5M.1E. +0.1E.5O. +3.1E.5R. +0.1E.5U. +3.1E.5W. +1.5Y.1E. +0.1E.5Z. +0.1E.5/. +0.1E.60. +2.62.1E. +2.66.1E. +a.6b.1E. +c.6e.1E. +0.1E.6F. +0.1E.6M. +0.1E.6+. +3.1E.71. +5.79.1E. +3.1E.7D. +0.1E.2p. +0.1E.2p. +0.1F.1F. +0.1G.1F. +0.1F.1H. +0.1F.1I. +0.1F.1J. +0.1F.1K. +0.1F.1L. +0.1F.1M. +0.1F.1N. +0.1F.1O. +0.1F.1P. +0.1F.1Q. +0.1F.1R. +0.1F.1S. +0.1F.1T. +0.1F.1U. +0.1F.1V. +0.1F.1W. +0.1F.1X. +0.1F.1Y. +0.1F.1Z. +0.1F.1+. +0.1F.1/. +0.1F.20. +0.1F.21. +0.1F.22. +0.1F.23. +0.1F.24. +0.1F.25. +b.26.1F. +0.1F.27. +4.1F.28. +0.1F.2a. +0.1F.2b. +0.1F.2l. +6.2m.1F. +0.1F.2p. +2.2q.1F. +2.2r.1F. +7.2s.1F. +7.2t.1F. +2.2u.1F. +3.1F.2v. +0.1F.2w. +e.2x.1F. +9.2y.1F. +0.1F.2z. +3.1F.2A. +9.2B.1F. +0.1F.2C. +0.1F.2D. +3.1F.2E. +0.1F.2F. +0.1F.2G. +9.2H.1F. +0.1F.2I. +3.1F.2J. +0.1F.2K. +c.2M.1F. +9.2N.1F. +2.2O.1F. +9.2Q.1F. +7.2S.1F. +0.1F.2T. +1.2U.1F. +9.2V.1F. +1.2W.1F. +0.1F.2X. +b.2+.1F. +0.1F.30. +0.1F.35. +b.36.1F. +0.1F.37. +0.1F.38. +5.39.1F. +0.1F.3d. +3.1F.3h. +0.1F.3i. +0.1F.3m. +0.1F.3p. +a.3v.1F. +a.3y.1F. +2.3z.1F. +5.3A.1F. +2.3B.1F. +0.1F.3C. +a.3F.1F. +5.3G.1F. +0.1F.3M. +0.1F.3Q. +5.40.1F. +0.1F.47. +0.1F.4d. +0.1F.4O. +3.1F.4Q. +5.4T.1F. +3.1F.4V. +0.1F.51. +0.1F.52. +a.5a.1F. +2.5b.1F. +5.5d.1F. +0.1F.5B. +0.1F.5C. +0.1F.5F. +3.1F.5H. +0.1F.5I. +f.1F.5K. +7.5M.1F. +0.1F.5O. +3.1F.5R. +0.1F.5U. +0.1F.5W. +1.5Y.1F. +0.1F.5Z. +0.1F.5/. +0.1F.60. +2.62.1F. +2.66.1F. +a.6b.1F. +c.6e.1F. +0.1F.6F. +0.1F.6M. +0.1F.6+. +3.1F.71. +5.79.1F. +3.1F.7D. +0.1F.2p. +0.1F.2p. +0.1G.1G. +0.1G.1H. +0.1G.1I. +0.1G.1J. +0.1G.1K. +0.1G.1L. +0.1G.1M. +0.1G.1N. +0.1G.1O. +0.1G.1P. +0.1G.1Q. +0.1G.1R. +0.1G.1S. +0.1G.1T. +0.1G.1U. +0.1G.1V. +0.1G.1W. +0.1G.1X. +0.1G.1Y. +0.1G.1Z. +0.1G.1+. +0.1G.1/. +0.1G.20. +0.1G.21. +0.1G.22. +0.1G.23. +0.1G.24. +0.1G.25. +b.26.1G. +0.1G.27. +4.1G.28. +0.1G.2a. +0.1G.2b. +0.1G.2l. +6.2m.1G. +0.1G.2p. +2.2q.1G. +2.2r.1G. +7.2s.1G. +7.2t.1G. +2.2u.1G. +3.1G.2v. +0.1G.2w. +e.2x.1G. +9.2y.1G. +0.1G.2z. +3.1G.2A. +9.2B.1G. +0.1G.2C. +0.1G.2D. +3.1G.2E. +0.1G.2F. +0.1G.2G. +9.2H.1G. +0.1G.2I. +3.1G.2J. +0.1G.2K. +c.2M.1G. +9.2N.1G. +2.2O.1G. +9.2Q.1G. +7.2S.1G. +0.1G.2T. +1.2U.1G. +9.2V.1G. +1.2W.1G. +0.1G.2X. +b.2+.1G. +0.1G.30. +0.1G.35. +b.36.1G. +0.1G.37. +0.1G.38. +5.39.1G. +0.1G.3d. +3.1G.3h. +0.1G.3i. +0.1G.3m. +0.1G.3p. +a.3v.1G. +a.3y.1G. +2.3z.1G. +5.3A.1G. +2.3B.1G. +0.1G.3C. +a.3F.1G. +5.3G.1G. +0.1G.3M. +0.1G.3Q. +5.40.1G. +0.1G.47. +0.1G.4d. +0.1G.4O. +3.1G.4Q. +5.4T.1G. +3.1G.4V. +0.1G.51. +0.1G.52. +a.5a.1G. +2.5b.1G. +5.5d.1G. +0.1G.5B. +0.1G.5C. +0.1G.5F. +3.1G.5H. +0.1G.5I. +f.1G.5K. +7.5M.1G. +0.1G.5O. +3.1G.5R. +0.1G.5U. +0.1G.5W. +1.5Y.1G. +0.1G.5Z. +0.1G.5/. +0.1G.60. +2.62.1G. +2.66.1G. +a.6b.1G. +c.6e.1G. +0.1G.6F. +0.1G.6M. +0.1G.6+. +3.1G.71. +5.79.1G. +3.1G.7D. +0.1G.2p. +0.1G.2p. +0.1H.1H. +0.1H.1I. +0.1H.1J. +0.1H.1K. +0.1H.1L. +0.1H.1M. +0.1H.1N. +0.1H.1O. +0.1H.1P. +3.1H.1Q. +0.1H.1R. +3.1H.1S. +0.1H.1T. +0.1H.1U. +0.1H.1V. +0.1H.1W. +0.1H.1X. +0.1H.1Y. +0.1H.1Z. +0.1H.1+. +0.1H.1/. +0.1H.20. +0.1H.21. +0.1H.22. +0.1H.23. +0.1H.24. +0.1H.25. +b.26.1H. +0.1H.27. +4.1H.28. +0.1H.2a. +0.1H.2b. +0.1H.2l. +6.2m.1H. +0.1H.2p. +2.2q.1H. +2.2r.1H. +7.2s.1H. +7.2t.1H. +2.2u.1H. +3.1H.2v. +0.1H.2w. +e.2x.1H. +9.2y.1H. +0.1H.2z. +3.1H.2A. +9.2B.1H. +0.1H.2C. +0.1H.2D. +3.1H.2E. +3.1H.2F. +0.1H.2G. +9.2H.1H. +0.1H.2I. +3.1H.2J. +0.1H.2K. +c.2M.1H. +9.2N.1H. +2.2O.1H. +9.2Q.1H. +7.2S.1H. +0.1H.2T. +1.2U.1H. +9.2V.1H. +1.2W.1H. +0.1H.2X. +b.2+.1H. +0.1H.30. +0.1H.35. +b.36.1H. +0.1H.37. +3.1H.38. +5.39.1H. +0.1H.3d. +3.1H.3h. +0.1H.3i. +0.1H.3m. +0.1H.3p. +a.3v.1H. +a.3y.1H. +2.3z.1H. +5.3A.1H. +2.3B.1H. +0.1H.3C. +a.3F.1H. +5.3G.1H. +0.1H.3M. +0.1H.3Q. +5.40.1H. +0.1H.47. +0.1H.4d. +3.1H.4O. +3.1H.4Q. +5.4T.1H. +3.1H.4V. +0.1H.51. +0.1H.52. +a.5a.1H. +2.5b.1H. +5.5d.1H. +0.1H.5B. +0.1H.5C. +0.1H.5F. +3.1H.5H. +0.1H.5I. +f.1H.5K. +7.5M.1H. +0.1H.5O. +3.1H.5R. +0.1H.5U. +0.1H.5W. +1.5Y.1H. +0.1H.5Z. +0.1H.5/. +0.1H.60. +2.62.1H. +2.66.1H. +a.6b.1H. +c.6e.1H. +0.1H.6F. +0.1H.6M. +0.1H.6+. +3.1H.71. +5.79.1H. +3.1H.7D. +0.1H.2p. +0.1H.2p. +3.1I.1I. +0.1I.1J. +0.1I.1K. +0.1I.1L. +0.1I.1M. +0.1I.1N. +0.1I.1O. +0.1I.1P. +3.1I.1Q. +0.1I.1R. +0.1I.1S. +3.1I.1T. +3.1I.1U. +0.1I.1V. +0.1I.1W. +0.1I.1X. +0.1I.1Y. +0.1I.1Z. +0.1I.1+. +0.1I.1/. +0.1I.20. +0.1I.21. +0.1I.22. +0.1I.23. +0.1I.24. +0.1I.25. +b.26.1I. +0.1I.27. +4.1I.28. +0.1I.2a. +0.1I.2b. +0.1I.2l. +6.2m.1I. +3.1I.2p. +2.2q.1I. +2.2r.1I. +7.2s.1I. +7.2t.1I. +2.2u.1I. +3.1I.2v. +0.1I.2w. +e.2x.1I. +9.2y.1I. +0.1I.2z. +3.1I.2A. +9.2B.1I. +0.1I.2C. +0.1I.2D. +3.1I.2E. +3.1I.2F. +0.1I.2G. +9.2H.1I. +3.1I.2I. +3.1I.2J. +0.1I.2K. +c.2M.1I. +9.2N.1I. +2.2O.1I. +9.2Q.1I. +7.2S.1I. +0.1I.2T. +1.2U.1I. +9.2V.1I. +1.2W.1I. +0.1I.2X. +b.2+.1I. +0.1I.30. +0.1I.35. +b.36.1I. +0.1I.37. +3.1I.38. +5.39.1I. +0.1I.3d. +3.1I.3h. +0.1I.3i. +0.1I.3m. +0.1I.3p. +a.3v.1I. +a.3y.1I. +2.3z.1I. +5.3A.1I. +2.3B.1I. +0.1I.3C. +a.3F.1I. +5.3G.1I. +0.1I.3M. +0.1I.3Q. +5.40.1I. +0.1I.47. +0.1I.4d. +3.1I.4O. +3.1I.4Q. +5.4T.1I. +3.1I.4V. +0.1I.51. +0.1I.52. +a.5a.1I. +2.5b.1I. +5.5d.1I. +0.1I.5B. +0.1I.5C. +0.1I.5F. +3.1I.5H. +0.1I.5I. +f.1I.5K. +7.5M.1I. +0.1I.5O. +3.1I.5R. +0.1I.5U. +0.1I.5W. +1.5Y.1I. +0.1I.5Z. +0.1I.5/. +0.1I.60. +2.62.1I. +2.66.1I. +a.6b.1I. +c.6e.1I. +0.1I.6F. +0.1I.6M. +0.1I.6+. +3.1I.71. +5.79.1I. +3.1I.7D. +3.1I.2p. +3.1I.2p. +0.1J.1J. +0.1K.1J. +0.1L.1J. +0.1M.1J. +3.1N.1J. +0.1O.1J. +0.1P.1J. +3.1J.1Q. +0.1J.1R. +3.1J.1S. +0.1J.1T. +3.1J.1U. +0.1J.1V. +0.1J.1W. +0.1J.1X. +0.1J.1Y. +0.1J.1Z. +0.1J.1+. +0.1J.1/. +0.1J.20. +0.1J.21. +0.1J.22. +0.1J.23. +0.1J.24. +0.1J.25. +b.26.1J. +0.1J.27. +4.1J.28. +0.1J.2a. +0.1J.2b. +0.1J.2l. +6.2m.1J. +0.1J.2p. +2.2q.1J. +2.2r.1J. +7.2s.1J. +7.2t.1J. +2.2u.1J. +0.1J.2v. +0.1J.2w. +e.2x.1J. +9.2y.1J. +0.1J.2z. +3.1J.2A. +9.2B.1J. +0.1J.2C. +0.1J.2D. +3.1J.2E. +0.1J.2F. +0.1J.2G. +9.2H.1J. +0.1J.2I. +3.1J.2J. +0.1J.2K. +c.2M.1J. +9.2N.1J. +2.2O.1J. +9.2Q.1J. +7.2S.1J. +0.1J.2T. +1.2U.1J. +9.2V.1J. +1.2W.1J. +0.1J.2X. +d.2+.1J. +0.1J.30. +0.1J.35. +b.36.1J. +0.1J.37. +0.1J.38. +5.39.1J. +0.1J.3d. +3.1J.3h. +3.1J.3i. +0.1J.3m. +0.1J.3p. +a.3v.1J. +a.3y.1J. +2.3z.1J. +5.3A.1J. +2.3B.1J. +0.1J.3C. +a.3F.1J. +5.3G.1J. +0.1J.3M. +0.1J.3Q. +5.40.1J. +0.1J.47. +0.1J.4d. +3.1J.4O. +3.1J.4Q. +5.4T.1J. +3.1J.4V. +0.1J.51. +0.1J.52. +a.5a.1J. +2.5b.1J. +5.5d.1J. +0.1J.5B. +0.1J.5C. +0.1J.5F. +3.1J.5H. +0.1J.5I. +f.1J.5K. +7.5M.1J. +0.1J.5O. +3.1J.5R. +0.1J.5U. +0.1J.5W. +1.5Y.1J. +0.1J.5Z. +0.1J.5/. +0.1J.60. +2.62.1J. +2.66.1J. +a.6b.1J. +c.6e.1J. +0.1J.6F. +0.1J.6M. +0.1J.6+. +3.1J.71. +5.79.1J. +3.1J.7D. +0.1J.2p. +0.1J.2p. +0.1K.1K. +0.1K.1L. +0.1K.1M. +0.1K.1N. +0.1K.1O. +0.1K.1P. +3.1K.1Q. +0.1K.1R. +0.1K.1S. +3.1K.1T. +3.1K.1U. +0.1K.1V. +0.1K.1W. +0.1K.1X. +0.1K.1Y. +0.1K.1Z. +0.1K.1+. +0.1K.1/. +0.1K.20. +0.1K.21. +0.1K.22. +0.1K.23. +0.1K.24. +0.1K.25. +b.26.1K. +0.1K.27. +4.1K.28. +0.1K.2a. +0.1K.2b. +0.1K.2l. +6.2m.1K. +3.1K.2p. +2.2q.1K. +2.2r.1K. +7.2s.1K. +7.2t.1K. +2.2u.1K. +3.1K.2v. +0.1K.2w. +e.2x.1K. +9.2y.1K. +0.1K.2z. +3.1K.2A. +9.2B.1K. +0.1K.2C. +3.1K.2D. +3.1K.2E. +3.1K.2F. +0.1K.2G. +9.2H.1K. +0.1K.2I. +3.1K.2J. +0.1K.2K. +c.2M.1K. +9.2N.1K. +2.2O.1K. +9.2Q.1K. +7.2S.1K. +0.1K.2T. +1.2U.1K. +9.2V.1K. +1.2W.1K. +0.1K.2X. +b.2+.1K. +0.1K.30. +0.1K.35. +b.36.1K. +0.1K.37. +3.1K.38. +5.39.1K. +0.1K.3d. +3.1K.3h. +3.1K.3i. +0.1K.3m. +0.1K.3p. +a.3v.1K. +a.3y.1K. +2.3z.1K. +5.3A.1K. +2.3B.1K. +0.1K.3C. +a.3F.1K. +5.3G.1K. +0.1K.3M. +0.1K.3Q. +5.40.1K. +0.1K.47. +0.1K.4d. +3.1K.4O. +3.1K.4Q. +5.4T.1K. +3.1K.4V. +0.1K.51. +0.1K.52. +a.5a.1K. +2.5b.1K. +5.5d.1K. +0.1K.5B. +0.1K.5C. +0.1K.5F. +3.1K.5H. +0.1K.5I. +f.1K.5K. +7.5M.1K. +0.1K.5O. +3.1K.5R. +0.1K.5U. +3.1K.5W. +1.5Y.1K. +0.1K.5Z. +0.1K.5/. +0.1K.60. +2.62.1K. +2.66.1K. +a.6b.1K. +c.6e.1K. +0.1K.6F. +0.1K.6M. +0.1K.6+. +3.1K.71. +5.79.1K. +3.1K.7D. +3.1K.2p. +3.1K.2p. +0.1L.1L. +0.1M.1L. +0.1L.1N. +0.1L.1O. +0.1L.1P. +0.1L.1Q. +0.1L.1R. +0.1L.1S. +0.1L.1T. +0.1L.1U. +0.1L.1V. +0.1L.1W. +0.1L.1X. +0.1L.1Y. +0.1L.1Z. +0.1L.1+. +0.1L.1/. +0.1L.20. +0.1L.21. +0.1L.22. +0.1L.23. +0.1L.24. +0.1L.25. +b.26.1L. +0.1L.27. +4.1L.28. +0.1L.2a. +0.1L.2b. +0.1L.2l. +6.2m.1L. +0.1L.2p. +2.2q.1L. +2.2r.1L. +7.2s.1L. +7.2t.1L. +2.2u.1L. +0.1L.2v. +0.1L.2w. +e.2x.1L. +9.2y.1L. +0.1L.2z. +0.1L.2A. +9.2B.1L. +0.1L.2C. +0.1L.2D. +3.1L.2E. +0.1L.2F. +0.1L.2G. +9.2H.1L. +0.1L.2I. +3.1L.2J. +0.1L.2K. +c.2M.1L. +9.2N.1L. +2.2O.1L. +9.2Q.1L. +7.2S.1L. +0.1L.2T. +1.2U.1L. +9.2V.1L. +1.2W.1L. +0.1L.2X. +b.2+.1L. +0.1L.30. +0.1L.35. +3.36.1L. +0.1L.37. +0.1L.38. +5.39.1L. +0.1L.3d. +0.1L.3h. +0.1L.3i. +0.1L.3m. +0.1L.3p. +a.3v.1L. +a.3y.1L. +2.3z.1L. +5.3A.1L. +2.3B.1L. +0.1L.3C. +a.3F.1L. +5.3G.1L. +0.1L.3M. +0.1L.3Q. +5.40.1L. +0.1L.47. +0.1L.4d. +0.1L.4O. +3.1L.4Q. +5.4T.1L. +0.1L.4V. +0.1L.51. +0.1L.52. +a.5a.1L. +2.5b.1L. +5.5d.1L. +0.1L.5B. +0.1L.5C. +0.1L.5F. +0.1L.5H. +0.1L.5I. +f.1L.5K. +7.5M.1L. +0.1L.5O. +0.1L.5R. +0.1L.5U. +0.1L.5W. +1.5Y.1L. +0.1L.5Z. +0.1L.5/. +0.1L.60. +2.62.1L. +2.66.1L. +a.6b.1L. +c.6e.1L. +0.1L.6F. +0.1L.6M. +0.1L.6+. +3.1L.71. +5.79.1L. +0.1L.7D. +0.1L.2p. +0.1L.2p. +3.1M.1M. +3.1M.1N. +0.1M.1O. +0.1M.1P. +3.1M.1Q. +0.1M.1R. +0.1M.1S. +0.1M.1T. +3.1M.1U. +0.1M.1V. +0.1M.1W. +0.1M.1X. +0.1M.1Y. +0.1M.1Z. +0.1M.1+. +0.1M.1/. +0.1M.20. +0.1M.21. +0.1M.22. +0.1M.23. +0.1M.24. +0.1M.25. +b.26.1M. +0.1M.27. +4.1M.28. +0.1M.2a. +0.1M.2b. +0.1M.2l. +6.2m.1M. +0.1M.2p. +2.2q.1M. +2.2r.1M. +7.2s.1M. +7.2t.1M. +2.2u.1M. +0.1M.2v. +0.1M.2w. +e.2x.1M. +9.2y.1M. +0.1M.2z. +3.1M.2A. +9.2B.1M. +0.1M.2C. +0.1M.2D. +3.1M.2E. +3.1M.2F. +0.1M.2G. +9.2H.1M. +0.1M.2I. +3.1M.2J. +0.1M.2K. +c.2M.1M. +9.2N.1M. +2.2O.1M. +9.2Q.1M. +7.2S.1M. +0.1M.2T. +1.2U.1M. +9.2V.1M. +1.2W.1M. +0.1M.2X. +b.2+.1M. +0.1M.30. +0.1M.35. +b.36.1M. +0.1M.37. +0.1M.38. +5.39.1M. +0.1M.3d. +3.1M.3h. +0.1M.3i. +0.1M.3m. +0.1M.3p. +a.3v.1M. +a.3y.1M. +2.3z.1M. +5.3A.1M. +2.3B.1M. +0.1M.3C. +a.3F.1M. +5.3G.1M. +0.1M.3M. +0.1M.3Q. +5.40.1M. +0.1M.47. +0.1M.4d. +0.1M.4O. +3.1M.4Q. +5.4T.1M. +3.1M.4V. +0.1M.51. +0.1M.52. +a.5a.1M. +2.5b.1M. +5.5d.1M. +0.1M.5B. +0.1M.5C. +0.1M.5F. +3.1M.5H. +0.1M.5I. +f.1M.5K. +7.5M.1M. +0.1M.5O. +3.1M.5R. +0.1M.5U. +0.1M.5W. +1.5Y.1M. +0.1M.5Z. +0.1M.5/. +0.1M.60. +2.62.1M. +2.66.1M. +a.6b.1M. +c.6e.1M. +0.1M.6F. +0.1M.6M. +0.1M.6+. +3.1M.71. +5.79.1M. +3.1M.7D. +0.1M.2p. +0.1M.2p. +0.1N.1N. +3.1N.1O. +0.1N.1P. +3.1N.1Q. +0.1N.1R. +3.1N.1S. +3.1N.1T. +3.1N.1U. +3.1N.1V. +0.1N.1W. +3.1N.1X. +0.1N.1Y. +3.1N.1Z. +3.1N.1+. +3.1N.1/. +3.1N.20. +3.1N.21. +3.1N.22. +0.1N.23. +3.1N.24. +3.1N.25. +b.26.1N. +3.1N.27. +4.1N.28. +0.1N.2a. +3.1N.2b. +0.1N.2l. +6.2m.1N. +3.1N.2p. +2.2q.1N. +2.2r.1N. +7.2s.1N. +7.2t.1N. +2.2u.1N. +3.1N.2v. +3.1N.2w. +e.2x.1N. +9.2y.1N. +3.1N.2z. +3.1N.2A. +9.2B.1N. +3.1N.2C. +3.1N.2D. +3.1N.2E. +3.1N.2F. +3.1N.2G. +9.2H.1N. +3.1N.2I. +3.1N.2J. +3.1N.2K. +c.2M.1N. +9.2N.1N. +2.2O.1N. +9.2Q.1N. +7.2S.1N. +3.1N.2T. +1.2U.1N. +9.2V.1N. +1.2W.1N. +3.1N.2X. +d.2+.1N. +3.1N.30. +0.1N.35. +3.36.1N. +0.1N.37. +3.1N.38. +5.39.1N. +3.1N.3d. +3.1N.3h. +3.1N.3i. +0.1N.3m. +0.1N.3p. +a.3v.1N. +a.3y.1N. +2.3z.1N. +5.3A.1N. +2.3B.1N. +3.1N.3C. +a.3F.1N. +5.3G.1N. +3.1N.3M. +3.1N.3Q. +5.40.1N. +3.1N.47. +3.1N.4d. +3.1N.4O. +3.1N.4Q. +5.4T.1N. +3.1N.4V. +0.1N.51. +0.1N.52. +a.5a.1N. +2.5b.1N. +5.5d.1N. +3.1N.5B. +3.1N.5C. +3.1N.5F. +3.1N.5H. +0.1N.5I. +f.1N.5K. +7.5M.1N. +0.1N.5O. +3.1N.5R. +3.1N.5U. +3.1N.5W. +1.5Y.1N. +3.1N.5Z. +3.1N.5/. +3.1N.60. +2.62.1N. +2.66.1N. +a.6b.1N. +c.6e.1N. +3.1N.6F. +3.1N.6M. +3.1N.6+. +3.1N.71. +5.79.1N. +3.1N.7D. +3.1N.2p. +3.1N.2p. +0.1O.1O. +0.1P.1O. +0.1O.1Q. +0.1O.1R. +0.1O.1S. +0.1O.1T. +0.1O.1U. +0.1O.1V. +0.1O.1W. +0.1O.1X. +0.1O.1Y. +0.1O.1Z. +0.1O.1+. +0.1O.1/. +0.1O.20. +0.1O.21. +0.1O.22. +0.1O.23. +0.1O.24. +0.1O.25. +b.26.1O. +0.1O.27. +4.1O.28. +0.1O.2a. +0.1O.2b. +4.2e.1O. +0.1O.2l. +6.2m.1O. +4.2n.1O. +0.1O.2p. +2.2q.1O. +2.2r.1O. +7.2s.1O. +7.2t.1O. +2.2u.1O. +0.1O.2v. +0.1O.2w. +e.2x.1O. +9.2y.1O. +0.1O.2z. +0.1O.2A. +9.2B.1O. +0.1O.2C. +0.1O.2D. +3.1O.2E. +0.1O.2F. +0.1O.2G. +9.2H.1O. +0.1O.2I. +0.1O.2J. +0.1O.2K. +c.2M.1O. +9.2N.1O. +2.2O.1O. +9.2Q.1O. +7.2S.1O. +0.1O.2T. +1.2U.1O. +9.2V.1O. +1.2W.1O. +0.1O.2X. +d.2+.1O. +0.1O.30. +0.1O.35. +b.36.1O. +0.1O.37. +0.1O.38. +5.39.1O. +0.1O.3d. +3.1O.3h. +0.1O.3i. +0.1O.3m. +0.1O.3p. +a.3v.1O. +a.3y.1O. +2.3z.1O. +5.3A.1O. +2.3B.1O. +0.1O.3C. +a.3F.1O. +5.3G.1O. +0.1O.3M. +0.1O.3Q. +5.40.1O. +0.1O.47. +0.1O.4d. +0.1O.4O. +0.1O.4Q. +5.4T.1O. +0.1O.4V. +0.1O.51. +0.1O.52. +a.5a.1O. +2.5b.1O. +5.5d.1O. +0.1O.5B. +0.1O.5C. +0.1O.5F. +0.1O.5H. +0.1O.5I. +f.1O.5K. +7.5M.1O. +0.1O.5O. +0.1O.5R. +0.1O.5U. +0.1O.5W. +1.5Y.1O. +0.1O.5Z. +0.1O.5/. +0.1O.60. +2.62.1O. +2.66.1O. +a.6b.1O. +c.6e.1O. +0.1O.6F. +0.1O.6M. +0.1O.6+. +0.1O.71. +5.79.1O. +0.1O.7D. +0.1O.2p. +0.1O.2p. +0.1P.1P. +0.1P.1Q. +0.1P.1R. +0.1P.1S. +0.1P.1T. +0.1P.1U. +0.1P.1V. +0.1P.1W. +0.1P.1X. +0.1P.1Y. +0.1P.1Z. +0.1P.1+. +0.1P.1/. +0.1P.20. +0.1P.21. +0.1P.22. +0.1P.23. +0.1P.24. +0.1P.25. +b.26.1P. +0.1P.27. +4.1P.28. +0.1P.2a. +0.1P.2b. +d.2e.1P. +0.1P.2l. +6.2m.1P. +4.2n.1P. +0.1P.2p. +2.2q.1P. +2.2r.1P. +7.2s.1P. +7.2t.1P. +2.2u.1P. +0.1P.2v. +0.1P.2w. +e.2x.1P. +9.2y.1P. +0.1P.2z. +0.1P.2A. +9.2B.1P. +0.1P.2C. +0.1P.2D. +d.1P.2E. +0.1P.2F. +0.1P.2G. +9.2H.1P. +0.1P.2I. +0.1P.2J. +0.1P.2K. +c.2M.1P. +9.2N.1P. +2.2O.1P. +9.2Q.1P. +7.2S.1P. +0.1P.2T. +1.2U.1P. +9.2V.1P. +1.2W.1P. +0.1P.2X. +d.2+.1P. +0.1P.30. +0.1P.35. +b.36.1P. +0.1P.37. +0.1P.38. +5.39.1P. +0.1P.3d. +d.1P.3h. +0.1P.3i. +0.1P.3m. +0.1P.3p. +a.3v.1P. +a.3y.1P. +2.3z.1P. +5.3A.1P. +2.3B.1P. +0.1P.3C. +a.3F.1P. +5.3G.1P. +0.1P.3M. +0.1P.3Q. +5.40.1P. +0.1P.47. +0.1P.4d. +0.1P.4O. +0.1P.4Q. +5.4T.1P. +0.1P.4V. +0.1P.51. +0.1P.52. +a.5a.1P. +2.5b.1P. +5.5d.1P. +0.1P.5B. +0.1P.5C. +0.1P.5F. +0.1P.5H. +0.1P.5I. +f.1P.5K. +7.5M.1P. +0.1P.5O. +0.1P.5R. +0.1P.5U. +0.1P.5W. +1.5Y.1P. +0.1P.5Z. +0.1P.5/. +0.1P.60. +2.62.1P. +2.66.1P. +a.6b.1P. +c.6e.1P. +0.1P.6F. +0.1P.6M. +0.1P.6+. +0.1P.71. +5.79.1P. +0.1P.7D. +0.1P.2p. +0.1P.2p. +0.1Q.1Q. +0.1R.1Q. +0.1S.1Q. +d.1Q.1T. +d.1Q.1U. +0.1Q.1V. +0.1Q.1W. +0.1Q.1X. +0.1Q.1Y. +0.1Q.1Z. +0.1Q.1+. +0.1Q.1/. +d.1Q.20. +0.1Q.21. +0.1Q.22. +0.1Q.23. +0.1Q.24. +0.1Q.25. +b.26.1Q. +0.1Q.27. +0.1Q.28. +0.1Q.2a. +0.1Q.2b. +d.2e.1Q. +0.1Q.2l. +6.2m.1Q. +4.2n.1Q. +0.1Q.2p. +2.2q.1Q. +2.2r.1Q. +7.2s.1Q. +7.2t.1Q. +2.2u.1Q. +d.1Q.2v. +0.1Q.2w. +e.2x.1Q. +9.2y.1Q. +0.1Q.2z. +d.1Q.2A. +9.2B.1Q. +0.1Q.2C. +d.1Q.2D. +d.1Q.2E. +0.1Q.2F. +0.1Q.2G. +9.2H.1Q. +d.1Q.2I. +0.1Q.2J. +0.1Q.2K. +c.2M.1Q. +9.2N.1Q. +2.2O.1Q. +9.2Q.1Q. +7.2S.1Q. +0.1Q.2T. +1.2U.1Q. +9.2V.1Q. +1.2W.1Q. +0.1Q.2X. +b.2+.1Q. +0.1Q.30. +0.1Q.35. +3.36.1Q. +0.1Q.37. +d.1Q.38. +5.39.1Q. +0.1Q.3d. +d.1Q.3h. +d.1Q.3i. +0.1Q.3m. +0.1Q.3p. +a.3v.1Q. +a.3y.1Q. +2.3z.1Q. +5.3A.1Q. +2.3B.1Q. +0.1Q.3C. +a.3F.1Q. +5.3G.1Q. +0.1Q.3M. +0.1Q.3Q. +5.40.1Q. +0.1Q.47. +0.1Q.4d. +0.1Q.4O. +0.1Q.4Q. +5.4T.1Q. +d.1Q.4V. +0.1Q.51. +0.1Q.52. +a.5a.1Q. +2.5b.1Q. +5.5d.1Q. +0.1Q.5B. +0.1Q.5C. +5.5F.1Q. +d.1Q.5H. +0.1Q.5I. +f.1Q.5K. +7.5M.1Q. +0.1Q.5O. +0.1Q.5R. +0.1Q.5U. +0.1Q.5W. +1.5Y.1Q. +0.1Q.5Z. +0.1Q.5/. +d.1Q.60. +2.62.1Q. +2.66.1Q. +a.6b.1Q. +c.6e.1Q. +0.1Q.6F. +0.1Q.6M. +0.1Q.6+. +d.1Q.71. +5.79.1Q. +0.1Q.7D. +0.1Q.2p. +0.1Q.2p. +0.1R.1R. +0.1R.1S. +0.1R.1T. +0.1R.1U. +0.1R.1V. +d.1R.1W. +d.1R.1X. +0.1R.1Y. +0.1R.1Z. +d.1R.1+. +d.1R.1/. +d.1R.20. +0.1R.21. +d.1R.22. +0.1R.23. +0.1R.24. +d.1R.25. +b.26.1R. +d.1R.27. +4.1R.28. +0.1R.2a. +0.1R.2b. +d.2e.1R. +d.1R.2l. +6.2m.1R. +4.2n.1R. +d.1R.2p. +2.2q.1R. +2.2r.1R. +7.2s.1R. +7.2t.1R. +2.2u.1R. +d.1R.2v. +0.1R.2w. +e.2x.1R. +9.2y.1R. +0.1R.2z. +d.1R.2A. +9.2B.1R. +0.1R.2C. +d.1R.2D. +d.1R.2E. +d.1R.2F. +d.1R.2G. +9.2H.1R. +d.1R.2I. +d.1R.2J. +d.1R.2K. +c.2M.1R. +9.2N.1R. +2.2O.1R. +9.2Q.1R. +7.2S.1R. +d.1R.2T. +1.2U.1R. +9.2V.1R. +1.2W.1R. +d.1R.2X. +b.2+.1R. +d.1R.30. +d.1R.35. +b.36.1R. +0.1R.37. +d.1R.38. +5.39.1R. +d.1R.3d. +d.1R.3h. +d.1R.3i. +d.1R.3m. +d.1R.3p. +a.3v.1R. +a.3y.1R. +2.3z.1R. +5.3A.1R. +2.3B.1R. +0.1R.3C. +a.3F.1R. +5.3G.1R. +d.1R.3M. +d.1R.3Q. +5.40.1R. +d.1R.47. +0.1R.4d. +d.1R.4O. +d.1R.4Q. +5.4T.1R. +0.1R.4V. +d.1R.51. +0.1R.52. +a.5a.1R. +2.5b.1R. +5.5d.1R. +d.1R.5B. +d.1R.5C. +5.5F.1R. +d.1R.5H. +d.1R.5I. +d.1R.5K. +7.5M.1R. +0.1R.5O. +0.1R.5R. +d.1R.5U. +d.1R.5W. +1.5Y.1R. +d.1R.5Z. +d.1R.5/. +d.1R.60. +2.62.1R. +2.66.1R. +a.6b.1R. +c.6e.1R. +0.1R.6F. +0.1R.6M. +d.1R.6+. +d.1R.71. +5.79.1R. +d.1R.7D. +d.1R.2p. +d.1R.2p. +0.1S.1S. +0.1S.1T. +0.1S.1U. +0.1S.1V. +0.1S.1W. +0.1S.1X. +0.1S.1Y. +0.1S.1Z. +0.1S.1+. +0.1S.1/. +0.1S.20. +0.1S.21. +0.1S.22. +0.1S.23. +0.1S.24. +0.1S.25. +b.26.1S. +3.1S.27. +0.1S.28. +0.1S.2a. +0.1S.2b. +0.1S.2l. +6.2m.1S. +4.2n.1S. +3.1S.2p. +2.2q.1S. +2.2r.1S. +7.2s.1S. +7.2t.1S. +2.2u.1S. +3.1S.2v. +3.1S.2w. +e.2x.1S. +9.2y.1S. +0.1S.2z. +3.1S.2A. +9.2B.1S. +0.1S.2C. +3.1S.2D. +3.1S.2E. +3.1S.2F. +0.1S.2G. +9.2H.1S. +3.1S.2I. +3.1S.2J. +0.1S.2K. +c.2M.1S. +9.2N.1S. +2.2O.1S. +9.2Q.1S. +7.2S.1S. +3.1S.2T. +1.2U.1S. +9.2V.1S. +1.2W.1S. +0.1S.2X. +d.2+.1S. +0.1S.30. +0.1S.35. +b.36.1S. +0.1S.37. +3.1S.38. +5.39.1S. +0.1S.3d. +3.1S.3h. +3.1S.3i. +0.1S.3m. +0.1S.3p. +a.3v.1S. +a.3y.1S. +2.3z.1S. +5.3A.1S. +2.3B.1S. +0.1S.3C. +a.3F.1S. +5.3G.1S. +0.1S.3M. +0.1S.3Q. +5.40.1S. +0.1S.47. +0.1S.4d. +3.1S.4O. +3.1S.4Q. +5.4T.1S. +3.1S.4V. +0.1S.51. +0.1S.52. +a.5a.1S. +2.5b.1S. +5.5d.1S. +0.1S.5B. +0.1S.5C. +5.5F.1S. +3.1S.5H. +0.1S.5I. +f.1S.5K. +7.5M.1S. +0.1S.5O. +3.1S.5R. +0.1S.5U. +0.1S.5W. +1.5Y.1S. +0.1S.5Z. +3.1S.5/. +0.1S.60. +2.62.1S. +2.66.1S. +a.6b.1S. +c.6e.1S. +0.1S.6F. +0.1S.6M. +0.1S.6+. +3.1S.71. +5.79.1S. +0.1S.7D. +3.1S.2p. +3.1S.2p. +d.1T.1T. +0.1T.1U. +0.1V.1T. +0.1T.1W. +0.1T.1X. +d.1T.1Y. +0.1T.1Z. +d.1T.1+. +0.1T.1/. +d.1T.20. +d.1T.21. +0.1T.22. +0.1T.23. +0.1T.24. +0.1T.25. +b.26.1T. +d.1T.27. +d.1T.28. +4.1T.29. +0.1T.2a. +0.1T.2b. +4.1T.2c. +4.1T.2d. +4.2e.1T. +4.1T.2f. +4.1T.2i. +4.1T.2j. +0.1T.2l. +6.2m.1T. +4.2n.1T. +1.1T.2o. +0.1T.2p. +2.2q.1T. +2.2r.1T. +7.2s.1T. +7.2t.1T. +2.2u.1T. +d.1T.2v. +d.1T.2w. +e.2x.1T. +9.2y.1T. +0.1T.2z. +0.1T.2A. +9.2B.1T. +d.1T.2C. +0.1T.2D. +0.1T.2E. +0.1T.2F. +0.1T.2G. +9.2H.1T. +0.1T.2I. +d.1T.2J. +0.1T.2K. +4.1T.2L. +c.2M.1T. +9.2N.1T. +2.2O.1T. +4.1T.2P. +9.2Q.1T. +d.1T.2R. +7.2S.1T. +0.1T.2T. +1.2U.1T. +9.2V.1T. +1.2W.1T. +0.1T.2X. +d.1T.2Y. +8.1T.2Z. +b.2+.1T. +8.1T.2/. +d.1T.30. +d.1T.31. +d.1T.32. +8.1T.33. +8.1T.34. +d.1T.35. +b.36.1T. +d.1T.37. +d.1T.38. +5.39.1T. +4.1T.3a. +4.1T.3c. +0.1T.3d. +1.1T.3e. +4.1T.3f. +8.1T.3g. +d.1T.3h. +0.1T.3i. +1.1T.3j. +8.1T.3k. +4.1T.3l. +0.1T.3m. +4.1T.3n. +8.1T.3o. +0.1T.3p. +8.1T.3q. +4.1T.3r. +4.1T.3s. +8.1T.3t. +d.1T.3u. +a.3v.1T. +4.1T.3w. +4.1T.3x. +a.3y.1T. +2.3z.1T. +5.3A.1T. +2.3B.1T. +0.1T.3C. +4.1T.3D. +4.1T.3E. +a.3F.1T. +5.3G.1T. +4.1T.3H. +4.1T.3I. +4.1T.3J. +4.1T.3K. +4.1T.3L. +0.1T.3M. +4.1T.3N. +4.1T.3O. +4.1T.3P. +0.1T.3Q. +4.1T.3R. +4.1T.3S. +1.1T.3T. +4.1T.3U. +4.1T.3V. +4.1T.3W. +4.1T.3X. +4.1T.3Y. +4.1T.3Z. +1.1T.3+. +1.1T.3/. +5.40.1T. +4.1T.41. +4.1T.42. +4.1T.43. +4.1T.44. +4.1T.45. +0.1T.47. +4.1T.48. +4.1T.49. +1.1T.4a. +4.1T.4b. +4.1T.4c. +0.1T.4d. +4.1T.4e. +4.1T.4f. +4.1T.4g. +4.1T.4h. +4.1T.4i. +4.1T.4j. +8.1T.4k. +4.1T.4l. +4.1T.4m. +4.1T.4n. +4.1T.4o. +4.1T.4p. +4.1T.4q. +4.1T.4r. +4.1T.4s. +4.1T.4t. +4.1T.4u. +4.1T.4v. +4.1T.4w. +4.1T.4x. +4.1T.4y. +4.1T.4z. +4.1T.4A. +4.1T.4B. +4.1T.4C. +4.1T.4D. +4.1T.4E. +1.1T.4F. +d.1T.4G. +d.1T.4H. +1.1T.4I. +d.1T.4J. +8.1T.4K. +d.1T.4L. +4.1T.4M. +4.1T.4N. +0.1T.4O. +4.1T.4P. +0.1T.4Q. +1.1T.4R. +4.1T.4S. +4.1T.4T. +4.1T.4U. +0.1T.4V. +4.1T.4W. +d.1T.4Y. +1.1T.4Z. +4.1T.4+. +4.1T.4/. +1.1T.50. +d.1T.51. +0.1T.52. +8.1T.53. +d.1T.54. +8.1T.55. +8.1T.56. +1.1T.57. +d.1T.58. +8.1T.59. +a.5a.1T. +2.5b.1T. +4.1T.5c. +5.5d.1T. +4.1T.5e. +8.1T.5f. +4.1T.5g. +4.1T.5h. +4.1T.5l. +4.1T.5m. +4.1T.5n. +4.1T.5o. +4.1T.5p. +4.1T.5q. +4.1T.5r. +1.1T.5s. +4.1T.5t. +4.1T.5u. +4.1T.5v. +1.1T.5w. +1.1T.5y. +1.1T.5z. +4.1T.5A. +0.1T.5B. +0.1T.5C. +1.1T.5D. +4.1T.5E. +5.5F.1T. +8.1T.5G. +d.1T.5H. +0.1T.5I. +0.1T.5K. +8.1T.5L. +8.1T.5M. +d.1T.5N. +d.1T.5O. +8.1T.5P. +4.1T.5Q. +0.1T.5R. +4.1T.5S. +4.1T.5T. +0.1T.5U. +8.1T.5V. +0.1T.5W. +8.1T.5X. +4.1T.5Y. +0.1T.5Z. +d.1T.5/. +0.1T.60. +4.1T.61. +2.62.1T. +4.1T.64. +4.1T.65. +2.66.1T. +4.1T.67. +4.1T.68. +4.1T.69. +4.1T.6a. +a.6b.1T. +4.1T.6c. +4.1T.6d. +4.1T.6e. +4.1T.6f. +4.1T.6g. +4.1T.6h. +4.1T.6i. +4.1T.6j. +4.1T.6k. +4.1T.6l. +4.1T.6m. +4.1T.6n. +4.1T.6o. +4.1T.6p. +4.1T.6q. +4.1T.6r. +4.1T.6s. +4.1T.6t. +4.1T.6u. +4.1T.6v. +4.1T.6w. +4.1T.6x. +4.1T.6y. +4.1T.6z. +4.1T.6A. +4.1T.6B. +4.1T.6C. +4.1T.6D. +4.1T.6E. +0.1T.6F. +4.1T.6G. +4.1T.6H. +4.1T.6I. +4.1T.6J. +4.1T.6K. +4.1T.6L. +d.1T.6M. +4.1T.6N. +4.1T.6O. +4.1T.6P. +4.1T.6Q. +4.1T.6R. +4.1T.6S. +4.1T.6T. +4.1T.6U. +4.1T.6V. +4.1T.6W. +1.1T.6X. +4.1T.6Y. +n.1T.6Z. +d.1T.6+. +1.1T.6/. +1.1T.70. +d.1T.71. +4.1T.72. +4.1T.73. +1.1T.74. +4.1T.75. +1.1T.76. +1.1T.77. +4.1T.78. +5.79.1T. +1.1T.7a. +4.1T.7b. +4.1T.7c. +4.1T.7d. +4.1T.7e. +4.1T.7f. +4.1T.7g. +4.1T.7h. +4.1T.7i. +4.1T.7j. +4.1T.7k. +4.1T.7l. +1.1T.7m. +4.1T.7o. +4.1T.7p. +1.1T.7r. +1.1T.7s. +8.1T.7t. +4.1T.7u. +4.1T.7v. +4.1T.7w. +4.1T.7x. +4.1T.7y. +1.1T.7z. +4.1T.7A. +8.1T.7B. +4.1T.7C. +0.1T.7D. +4.1T.7E. +4.1T.7F. +d.1T.7G. +4.1T.7H. +4.1T.7I. +4.1T.7J. +1.1T.7K. +1.1T.7L. +1.1T.7M. +1.1T.7N. +1.1T.7O. +1.1T.7P. +4.1T.7Q. +8.1T.7R. +4.1T.7S. +4.1T.7T. +1.1T.7U. +d.1T.7V. +4.1T.7W. +4.1T.7X. +4.1T.7Y. +4.1T.7Z. +1.1T.7+. +1.1T.7/. +1.1T.80. +1.1T.81. +1.1T.82. +4.1T.83. +1.1T.84. +1.1T.85. +1.1T.87. +1.1T.88. +8.1T.89. +4.1T.8a. +8.1T.8b. +4.1T.8c. +8.1T.8d. +4.1T.8e. +4.1T.8f. +4.1T.8g. +4.1T.8h. +4.1T.8i. +4.1T.8j. +4.1T.8k. +4.1T.8l. +4.1T.8m. +4.1T.8n. +4.1T.8o. +4.1T.8p. +4.1T.8q. +4.1T.8r. +4.1T.8s. +4.1T.8t. +4.1T.8u. +4.1T.8v. +4.1T.8w. +4.1T.8x. +4.1T.8y. +4.1T.8z. +4.1T.8A. +4.1T.8B. +4.1T.8C. +4.1T.8D. +4.1T.8E. +4.1T.8F. +4.1T.8G. +4.1T.8H. +4.1T.8I. +4.1T.8J. +4.1T.8K. +4.1T.8L. +4.1T.8M. +4.1T.8N. +4.1T.8O. +4.1T.8P. +0.1T.2p. +0.1T.2p. +d.1T.3b. +1.1T.46. +1.1T.4X. +1.1T.5x. +1.1T.5J. +1.1T.7n. +1.1T.7q. +d.1U.1U. +0.1V.1U. +0.1U.1W. +0.1U.1X. +0.1U.1Y. +0.1U.1Z. +0.1U.1+. +0.1U.1/. +0.1U.20. +0.1U.21. +0.1U.22. +0.1U.23. +0.1U.24. +0.1U.25. +b.26.1U. +0.1U.27. +4.1U.28. +0.1U.2a. +0.1U.2b. +d.2e.1U. +0.1U.2l. +6.2m.1U. +4.2n.1U. +d.1U.2p. +2.2q.1U. +2.2r.1U. +7.2s.1U. +7.2t.1U. +2.2u.1U. +0.1U.2v. +d.1U.2w. +e.2x.1U. +9.2y.1U. +0.1U.2z. +d.1U.2A. +9.2B.1U. +0.1U.2C. +0.1U.2D. +0.1U.2E. +d.1U.2F. +0.1U.2G. +9.2H.1U. +0.1U.2I. +0.1U.2J. +0.1U.2K. +c.2M.1U. +9.2N.1U. +2.2O.1U. +9.2Q.1U. +7.2S.1U. +0.1U.2T. +1.2U.1U. +9.2V.1U. +1.2W.1U. +0.1U.2X. +d.2+.1U. +0.1U.30. +0.1U.35. +b.36.1U. +0.1U.37. +0.1U.38. +5.39.1U. +0.1U.3d. +d.1U.3h. +d.1U.3i. +0.1U.3m. +0.1U.3p. +a.3v.1U. +a.3y.1U. +2.3z.1U. +5.3A.1U. +2.3B.1U. +0.1U.3C. +a.3F.1U. +5.3G.1U. +0.1U.3M. +0.1U.3Q. +5.40.1U. +0.1U.47. +0.1U.4d. +0.1U.4O. +d.1U.4Q. +5.4T.1U. +0.1U.4V. +0.1U.51. +0.1U.52. +a.5a.1U. +2.5b.1U. +5.5d.1U. +0.1U.5B. +0.1U.5C. +5.5F.1U. +d.1U.5H. +0.1U.5I. +d.1U.5K. +7.5M.1U. +0.1U.5O. +d.1U.5R. +0.1U.5U. +0.1U.5W. +1.5Y.1U. +0.1U.5Z. +0.1U.5/. +0.1U.60. +2.62.1U. +2.66.1U. +a.6b.1U. +c.6e.1U. +0.1U.6F. +0.1U.6M. +0.1U.6+. +d.1U.71. +5.79.1U. +d.1U.7D. +d.1U.2p. +d.1U.2p. +0.1V.1V. +0.1V.1W. +0.1V.1X. +0.1V.1Y. +0.1V.1Z. +0.1V.1+. +0.1V.1/. +0.1V.20. +0.1V.21. +0.1V.22. +0.1V.23. +0.1V.24. +0.1V.25. +b.26.1V. +0.1V.27. +0.1V.28. +0.1V.2a. +0.1V.2b. +d.2e.1V. +0.1V.2l. +6.2m.1V. +4.2n.1V. +3.1V.2p. +2.2q.1V. +2.2r.1V. +7.2s.1V. +7.2t.1V. +2.2u.1V. +0.1V.2v. +0.1V.2w. +e.2x.1V. +9.2y.1V. +0.1V.2z. +0.1V.2A. +9.2B.1V. +0.1V.2C. +0.1V.2D. +0.1V.2E. +0.1V.2F. +0.1V.2G. +9.2H.1V. +0.1V.2I. +0.1V.2J. +0.1V.2K. +c.2M.1V. +9.2N.1V. +2.2O.1V. +9.2Q.1V. +7.2S.1V. +0.1V.2T. +1.2U.1V. +9.2V.1V. +1.2W.1V. +0.1V.2X. +b.2+.1V. +0.1V.30. +0.1V.35. +b.36.1V. +0.1V.37. +0.1V.38. +5.39.1V. +0.1V.3d. +3.1V.3h. +3.1V.3i. +0.1V.3m. +0.1V.3p. +a.3v.1V. +a.3y.1V. +2.3z.1V. +5.3A.1V. +2.3B.1V. +0.1V.3C. +a.3F.1V. +5.3G.1V. +0.1V.3M. +0.1V.3Q. +5.40.1V. +0.1V.47. +0.1V.4d. +0.1V.4O. +0.1V.4Q. +5.4T.1V. +0.1V.4V. +0.1V.51. +0.1V.52. +a.5a.1V. +2.5b.1V. +5.5d.1V. +0.1V.5B. +0.1V.5C. +5.5F.1V. +0.1V.5H. +0.1V.5I. +f.1V.5K. +7.5M.1V. +0.1V.5O. +3.1V.5R. +0.1V.5U. +0.1V.5W. +1.5Y.1V. +0.1V.5Z. +0.1V.5/. +3.1V.60. +2.62.1V. +2.66.1V. +a.6b.1V. +c.6e.1V. +0.1V.6F. +0.1V.6M. +0.1V.6+. +3.1V.71. +5.79.1V. +0.1V.7D. +3.1V.2p. +3.1V.2p. +0.1W.1W. +0.1X.1W. +0.1Y.1W. +0.1Z.1W. +0.1+.1W. +0.20.1W. +0.21.1W. +0.22.1W. +d.23.1W. +0.24.1W. +0.25.1W. +b.26.1W. +0.27.1W. +4.28.1W. +0.2a.1W. +3.2b.1W. +0.2l.1W. +6.2m.1W. +4.2n.1W. +0.2p.1W. +2.2q.1W. +2.2r.1W. +7.2s.1W. +7.2t.1W. +2.2u.1W. +0.2v.1W. +0.2w.1W. +e.2x.1W. +9.2y.1W. +0.2z.1W. +0.2A.1W. +9.2B.1W. +0.2C.1W. +0.2D.1W. +0.2E.1W. +0.2F.1W. +0.2G.1W. +9.2H.1W. +0.2I.1W. +d.2J.1W. +0.2K.1W. +c.2M.1W. +9.2N.1W. +2.2O.1W. +9.2Q.1W. +7.2S.1W. +0.2T.1W. +1.2U.1W. +9.2V.1W. +1.2W.1W. +0.2X.1W. +b.2+.1W. +d.30.1W. +0.35.1W. +3.36.1W. +0.1W.37. +0.38.1W. +5.39.1W. +5.3d.1W. +0.3h.1W. +5.3i.1W. +0.1W.3m. +5.3p.1W. +a.3v.1W. +a.3y.1W. +2.3z.1W. +5.3A.1W. +2.3B.1W. +5.3C.1W. +a.3F.1W. +5.3G.1W. +0.1W.3M. +0.1W.3Q. +5.40.1W. +0.1W.47. +5.4d.1W. +0.1W.4O. +0.4Q.1W. +7.4T.1W. +3.1W.4V. +5.51.1W. +0.52.1W. +a.5a.1W. +2.5b.1W. +9.5d.1W. +5.5B.1W. +5.5C.1W. +5.5F.1W. +0.5H.1W. +0.5I.1W. +f.1W.5K. +7.5M.1W. +5.5O.1W. +0.1W.5U. +3.5W.1W. +1.5Y.1W. +5.5Z.1W. +5.5/.1W. +0.60.1W. +2.62.1W. +2.66.1W. +a.6b.1W. +c.6e.1W. +0.1W.6F. +0.6M.1W. +0.6+.1W. +0.71.1W. +5.79.1W. +0.1W.7D. +0.2p.1W. +0.2p.1W. +0.1X.1X. +0.1Y.1X. +0.1Z.1X. +0.1+.1X. +0.1/.1X. +0.20.1X. +0.21.1X. +0.22.1X. +0.1X.23. +0.1X.24. +0.1X.25. +b.26.1X. +0.27.1X. +0.1X.28. +0.1X.2a. +0.1X.2b. +d.1X.2l. +6.2m.1X. +4.2n.1X. +0.1X.2p. +2.2q.1X. +2.2r.1X. +7.2s.1X. +7.2t.1X. +2.2u.1X. +d.1X.2v. +0.1X.2w. +e.2x.1X. +9.2y.1X. +0.1X.2z. +d.1X.2A. +9.2B.1X. +0.1X.2C. +0.1X.2D. +d.1X.2E. +d.1X.2F. +0.1X.2G. +9.2H.1X. +d.1X.2I. +d.1X.2J. +0.1X.2K. +c.2M.1X. +9.2N.1X. +2.2O.1X. +9.2Q.1X. +7.2S.1X. +0.1X.2T. +1.2U.1X. +9.2V.1X. +1.2W.1X. +0.1X.2X. +d.2+.1X. +0.1X.30. +0.1X.35. +b.36.1X. +0.1X.37. +0.1X.38. +5.39.1X. +0.1X.3d. +d.1X.3h. +d.1X.3i. +d.1X.3m. +0.1X.3p. +a.3v.1X. +a.3y.1X. +2.3z.1X. +5.3A.1X. +2.3B.1X. +d.1X.3C. +a.3F.1X. +5.3G.1X. +d.1X.3M. +0.1X.3Q. +5.40.1X. +0.1X.47. +0.1X.4d. +0.1X.4O. +d.1X.4Q. +5.4T.1X. +d.1X.4V. +d.1X.51. +0.1X.52. +a.5a.1X. +2.5b.1X. +5.5d.1X. +0.1X.5B. +0.1X.5C. +5.5F.1X. +d.1X.5H. +0.1X.5I. +f.1X.5K. +7.5M.1X. +0.1X.5O. +d.1X.5R. +0.1X.5U. +d.1X.5W. +1.5Y.1X. +0.1X.5Z. +d.1X.5/. +0.1X.60. +2.62.1X. +2.66.1X. +a.6b.1X. +c.6e.1X. +0.1X.6F. +0.6M.1X. +0.1X.6+. +d.1X.71. +5.79.1X. +d.1X.7D. +0.1X.2p. +0.1X.2p. +0.1Y.1Y. +0.1Y.1Z. +0.1Y.1+. +0.1Y.1/. +0.1Y.20. +0.1Y.21. +0.1Y.22. +0.1Y.23. +0.1Y.24. +0.1Y.25. +b.26.1Y. +0.1Y.27. +0.1Y.28. +6.1Y.29. +0.1Y.2a. +0.1Y.2b. +6.1Y.2c. +6.1Y.2d. +6.1Y.2f. +d.1Y.2g. +6.1Y.2h. +6.1Y.2i. +6.1Y.2j. +0.1Y.2l. +6.2m.1Y. +4.2n.1Y. +1.1Y.2o. +0.1Y.2p. +2.2q.1Y. +2.2r.1Y. +7.2s.1Y. +7.2t.1Y. +2.2u.1Y. +d.1Y.2v. +0.1Y.2w. +e.2x.1Y. +9.2y.1Y. +0.1Y.2z. +d.1Y.2A. +9.2B.1Y. +0.1Y.2C. +0.1Y.2D. +0.1Y.2E. +0.1Y.2F. +0.1Y.2G. +9.2H.1Y. +0.1Y.2I. +d.1Y.2J. +0.1Y.2K. +6.1Y.2L. +c.2M.1Y. +9.2N.1Y. +2.2O.1Y. +6.1Y.2P. +9.2Q.1Y. +8.1Y.2R. +7.2S.1Y. +0.1Y.2T. +1.2U.1Y. +9.2V.1Y. +1.2W.1Y. +0.1Y.2X. +8.1Y.2Y. +8.1Y.2Z. +b.2+.1Y. +8.1Y.2/. +0.1Y.30. +8.1Y.31. +8.1Y.32. +8.1Y.33. +8.1Y.34. +0.1Y.35. +3.36.1Y. +0.1Y.37. +0.1Y.38. +5.39.1Y. +6.1Y.3a. +6.1Y.3c. +0.1Y.3d. +1.1Y.3e. +6.1Y.3f. +8.1Y.3g. +d.1Y.3h. +0.1Y.3i. +1.1Y.3j. +8.1Y.3k. +6.1Y.3l. +0.1Y.3m. +8.1Y.3n. +8.1Y.3o. +0.1Y.3p. +8.1Y.3q. +6.1Y.3r. +4.1Y.3s. +8.1Y.3t. +8.1Y.3u. +6.1Y.3v. +6.1Y.3w. +6.1Y.3x. +a.3y.1Y. +2.3z.1Y. +5.3A.1Y. +2.3B.1Y. +0.1Y.3C. +6.1Y.3D. +6.1Y.3E. +a.3F.1Y. +5.3G.1Y. +6.1Y.3H. +6.1Y.3I. +6.1Y.3J. +6.1Y.3K. +6.1Y.3L. +0.1Y.3M. +6.1Y.3N. +d.1Y.3O. +6.1Y.3P. +0.1Y.3Q. +6.1Y.3R. +6.1Y.3S. +1.1Y.3T. +6.1Y.3U. +6.1Y.3V. +6.1Y.3W. +6.1Y.3X. +6.1Y.3Y. +6.1Y.3Z. +1.1Y.3+. +1.1Y.3/. +5.40.1Y. +6.1Y.41. +6.1Y.42. +6.1Y.43. +6.1Y.44. +6.1Y.45. +0.1Y.47. +6.1Y.48. +6.1Y.49. +1.1Y.4a. +6.1Y.4b. +6.1Y.4c. +0.1Y.4d. +6.1Y.4e. +6.1Y.4f. +6.1Y.4g. +6.1Y.4h. +6.1Y.4i. +6.1Y.4j. +8.1Y.4k. +6.1Y.4l. +6.1Y.4m. +6.1Y.4n. +6.1Y.4o. +6.1Y.4p. +6.1Y.4q. +6.1Y.4r. +6.1Y.4s. +6.1Y.4t. +6.1Y.4u. +6.1Y.4v. +6.1Y.4w. +6.1Y.4x. +6.1Y.4y. +6.1Y.4z. +6.1Y.4A. +6.1Y.4B. +6.1Y.4C. +6.1Y.4D. +6.1Y.4E. +1.1Y.4F. +8.1Y.4G. +8.1Y.4H. +1.1Y.4I. +8.1Y.4J. +8.1Y.4K. +8.1Y.4L. +6.1Y.4M. +6.1Y.4N. +0.1Y.4O. +6.1Y.4P. +d.1Y.4Q. +1.1Y.4R. +6.1Y.4S. +4.1Y.4T. +8.1Y.4U. +d.1Y.4V. +6.1Y.4W. +8.1Y.4Y. +1.1Y.4Z. +6.1Y.4+. +6.1Y.4/. +1.1Y.50. +0.1Y.51. +0.1Y.52. +8.1Y.53. +8.1Y.54. +8.1Y.55. +8.1Y.56. +1.1Y.57. +8.1Y.58. +8.1Y.59. +a.5a.1Y. +2.5b.1Y. +6.1Y.5c. +5.5d.1Y. +8.1Y.5e. +8.1Y.5f. +6.1Y.5g. +8.1Y.5h. +6.1Y.5i. +6.1Y.5j. +6.1Y.5k. +6.1Y.5l. +6.1Y.5m. +6.1Y.5n. +6.1Y.5o. +6.1Y.5p. +6.1Y.5q. +6.1Y.5r. +1.1Y.5s. +6.1Y.5t. +d.1Y.5u. +d.1Y.5v. +1.1Y.5w. +1.1Y.5y. +1.1Y.5z. +8.1Y.5A. +0.1Y.5B. +0.1Y.5C. +1.1Y.5D. +8.1Y.5E. +5.5F.1Y. +8.1Y.5G. +0.1Y.5H. +0.1Y.5I. +f.1Y.5K. +8.1Y.5L. +8.1Y.5M. +8.1Y.5N. +0.1Y.5O. +8.1Y.5P. +6.1Y.5Q. +0.1Y.5R. +6.1Y.5S. +d.1Y.5T. +0.1Y.5U. +8.1Y.5V. +0.1Y.5W. +8.1Y.5X. +8.1Y.5Y. +0.1Y.5Z. +h.5+.1Y. +0.1Y.5/. +0.1Y.60. +6.1Y.61. +2.62.1Y. +6.1Y.64. +6.1Y.65. +2.66.1Y. +6.1Y.67. +6.1Y.68. +6.1Y.69. +6.1Y.6a. +6.1Y.6b. +6.1Y.6c. +6.1Y.6d. +6.1Y.6e. +6.1Y.6f. +6.1Y.6g. +6.1Y.6h. +d.1Y.6i. +6.1Y.6j. +6.1Y.6k. +6.1Y.6l. +d.1Y.6m. +6.1Y.6n. +6.1Y.6o. +6.1Y.6p. +6.1Y.6q. +6.1Y.6r. +6.1Y.6s. +6.1Y.6t. +6.1Y.6u. +6.1Y.6v. +6.1Y.6w. +6.1Y.6x. +6.1Y.6y. +6.1Y.6z. +6.1Y.6A. +6.1Y.6B. +6.1Y.6C. +6.1Y.6D. +6.1Y.6E. +0.1Y.6F. +6.1Y.6G. +6.1Y.6H. +6.1Y.6I. +6.1Y.6J. +6.1Y.6K. +8.1Y.6L. +0.1Y.6M. +6.1Y.6N. +6.1Y.6O. +6.1Y.6P. +6.1Y.6Q. +6.1Y.6R. +6.1Y.6S. +6.1Y.6T. +6.1Y.6U. +6.1Y.6V. +6.1Y.6W. +1.1Y.6X. +6.1Y.6Y. +1.1Y.6Z. +0.1Y.6+. +1.1Y.6/. +1.1Y.70. +0.1Y.71. +6.1Y.72. +6.1Y.73. +1.1Y.74. +6.1Y.75. +1.1Y.76. +1.1Y.77. +6.1Y.78. +5.79.1Y. +1.1Y.7a. +6.1Y.7b. +6.1Y.7c. +6.1Y.7d. +6.1Y.7e. +6.1Y.7f. +6.1Y.7g. +6.1Y.7h. +6.1Y.7i. +6.1Y.7j. +6.1Y.7k. +6.1Y.7l. +1.1Y.7m. +6.1Y.7o. +6.1Y.7p. +1.1Y.7r. +1.1Y.7s. +8.1Y.7t. +6.1Y.7u. +6.1Y.7v. +6.1Y.7w. +6.1Y.7x. +6.1Y.7y. +1.1Y.7z. +6.1Y.7A. +8.1Y.7B. +6.1Y.7C. +0.1Y.7D. +6.1Y.7E. +6.1Y.7F. +6.1Y.7G. +6.1Y.7H. +d.1Y.7I. +6.1Y.7J. +1.1Y.7K. +1.1Y.7L. +1.1Y.7M. +1.1Y.7N. +1.1Y.7O. +1.1Y.7P. +6.1Y.7Q. +8.1Y.7R. +6.1Y.7S. +6.1Y.7T. +1.1Y.7U. +8.1Y.7V. +6.1Y.7W. +6.1Y.7X. +6.1Y.7Y. +6.1Y.7Z. +1.1Y.7+. +1.1Y.7/. +1.1Y.80. +1.1Y.81. +1.1Y.82. +6.1Y.83. +1.1Y.84. +1.1Y.85. +h.86.1Y. +1.1Y.87. +1.1Y.88. +8.1Y.89. +6.1Y.8a. +8.1Y.8b. +6.1Y.8c. +8.1Y.8d. +6.1Y.8e. +6.1Y.8f. +6.1Y.8g. +6.1Y.8h. +6.1Y.8i. +6.1Y.8j. +6.1Y.8k. +6.1Y.8l. +6.1Y.8m. +6.1Y.8n. +6.1Y.8o. +6.1Y.8p. +6.1Y.8q. +6.1Y.8r. +6.1Y.8s. +6.1Y.8t. +6.1Y.8u. +6.1Y.8v. +6.1Y.8w. +6.1Y.8x. +6.1Y.8y. +6.1Y.8z. +6.1Y.8A. +6.1Y.8B. +6.1Y.8C. +6.1Y.8D. +6.1Y.8E. +6.1Y.8F. +6.1Y.8G. +4.1Y.8H. +4.1Y.8I. +6.1Y.8J. +6.1Y.8K. +6.1Y.8L. +6.1Y.8M. +6.1Y.8N. +6.1Y.8O. +6.1Y.8P. +0.1Y.2p. +0.1Y.2p. +8.1Y.3b. +1.1Y.46. +1.1Y.4X. +1.1Y.5x. +1.1Y.5J. +1.1Y.7n. +1.1Y.7q. +0.1Z.1Z. +0.1Z.1+. +0.1Z.1/. +0.1Z.20. +0.1Z.21. +0.1Z.22. +0.1Z.23. +0.1Z.24. +0.1Z.25. +b.26.1Z. +0.1Z.27. +4.1Z.28. +0.1Z.2a. +0.1Z.2b. +0.1Z.2l. +6.2m.1Z. +4.2n.1Z. +0.1Z.2p. +2.2q.1Z. +2.2r.1Z. +7.2s.1Z. +7.2t.1Z. +2.2u.1Z. +0.1Z.2v. +0.1Z.2w. +e.2x.1Z. +9.2y.1Z. +0.1Z.2z. +d.1Z.2A. +9.2B.1Z. +0.1Z.2C. +0.1Z.2D. +d.1Z.2E. +0.1Z.2F. +0.1Z.2G. +9.2H.1Z. +0.1Z.2I. +d.1Z.2J. +0.1Z.2K. +c.2M.1Z. +9.2N.1Z. +2.2O.1Z. +9.2Q.1Z. +7.2S.1Z. +0.1Z.2T. +1.2U.1Z. +9.2V.1Z. +1.2W.1Z. +0.1Z.2X. +b.2+.1Z. +0.1Z.30. +0.1Z.35. +b.36.1Z. +0.1Z.37. +0.1Z.38. +5.39.1Z. +0.1Z.3d. +0.1Z.3h. +0.1Z.3i. +0.1Z.3m. +0.1Z.3p. +a.3v.1Z. +a.3y.1Z. +2.3z.1Z. +5.3A.1Z. +2.3B.1Z. +0.1Z.3C. +a.3F.1Z. +5.3G.1Z. +0.1Z.3M. +0.1Z.3Q. +5.40.1Z. +0.1Z.47. +0.1Z.4d. +0.1Z.4O. +0.1Z.4Q. +5.4T.1Z. +d.1Z.4V. +0.1Z.51. +0.1Z.52. +a.5a.1Z. +2.5b.1Z. +5.5d.1Z. +0.1Z.5B. +0.1Z.5C. +5.5F.1Z. +d.1Z.5H. +0.1Z.5I. +f.1Z.5K. +7.5M.1Z. +0.1Z.5O. +0.1Z.5R. +0.1Z.5U. +0.1Z.5W. +1.5Y.1Z. +0.1Z.5Z. +0.1Z.5/. +0.1Z.60. +2.62.1Z. +2.66.1Z. +a.6b.1Z. +c.6e.1Z. +0.1Z.6F. +0.1Z.6M. +0.1Z.6+. +0.1Z.71. +5.79.1Z. +0.1Z.7D. +0.1Z.2p. +0.1Z.2p. +0.1+.1+. +0.1+.1/. +0.1+.20. +0.1+.21. +0.1+.22. +0.1+.23. +0.1+.24. +0.1+.25. +b.26.1+. +0.1+.27. +0.1+.28. +0.1+.2a. +0.1+.2b. +0.1+.2l. +6.2m.1+. +4.2n.1+. +0.1+.2p. +2.2q.1+. +2.2r.1+. +7.2s.1+. +7.2t.1+. +2.2u.1+. +d.1+.2v. +d.1+.2w. +e.2x.1+. +9.2y.1+. +0.1+.2z. +d.1+.2A. +9.2B.1+. +0.1+.2C. +0.1+.2D. +d.1+.2E. +0.1+.2F. +0.1+.2G. +9.2H.1+. +0.1+.2I. +d.1+.2J. +0.1+.2K. +c.2M.1+. +9.2N.1+. +2.2O.1+. +9.2Q.1+. +7.2S.1+. +0.1+.2T. +1.2U.1+. +9.2V.1+. +1.2W.1+. +d.1+.2X. +b.2+.1+. +0.1+.30. +d.1+.35. +b.36.1+. +d.1+.37. +0.1+.38. +5.39.1+. +0.1+.3d. +d.1+.3h. +0.1+.3i. +0.1+.3m. +0.1+.3p. +a.3v.1+. +a.3y.1+. +2.3z.1+. +5.3A.1+. +2.3B.1+. +0.1+.3C. +a.3F.1+. +5.3G.1+. +0.1+.3M. +0.1+.3Q. +5.40.1+. +0.1+.47. +0.1+.4d. +0.1+.4O. +0.1+.4Q. +5.4T.1+. +0.1+.4V. +0.1+.51. +0.1+.52. +a.5a.1+. +2.5b.1+. +5.5d.1+. +0.1+.5B. +0.1+.5C. +5.5F.1+. +0.1+.5H. +0.1+.5I. +f.1+.5K. +7.5M.1+. +0.1+.5O. +0.1+.5R. +0.1+.5U. +0.1+.5W. +1.5Y.1+. +0.1+.5Z. +0.1+.5/. +0.1+.60. +2.62.1+. +2.66.1+. +a.6b.1+. +c.6e.1+. +0.1+.6F. +0.1+.6M. +0.1+.6+. +0.1+.71. +5.79.1+. +0.1+.7D. +0.1+.2p. +0.1+.2p. +0.1/.1/. +0.1/.20. +0.1/.21. +0.1/.22. +0.1/.23. +0.1/.24. +0.1/.25. +b.26.1/. +0.1/.27. +0.1/.28. +0.1/.2a. +0.1/.2b. +0.1/.2l. +6.2m.1/. +4.2n.1/. +i.1/.2p. +2.2q.1/. +2.2r.1/. +7.2s.1/. +7.2t.1/. +2.2u.1/. +e.2x.1/. +9.2y.1/. +9.2B.1/. +9.2H.1/. +c.2M.1/. +9.2N.1/. +2.2O.1/. +9.2Q.1/. +7.2S.1/. +i.1/.2T. +1.2U.1/. +9.2V.1/. +1.2W.1/. +b.2+.1/. +3.36.1/. +5.39.1/. +0.1/.3d. +0.1/.3i. +a.3v.1/. +a.3y.1/. +2.3z.1/. +5.3A.1/. +2.3B.1/. +a.3F.1/. +5.3G.1/. +5.40.1/. +0.1/.47. +0.1/.4O. +5.4T.1/. +0.1/.51. +a.5a.1/. +2.5b.1/. +5.5d.1/. +0.1/.5B. +0.1/.5C. +5.5F.1/. +i.1/.5H. +0.1/.5I. +f.1/.5K. +7.5M.1/. +d.1/.5R. +1.5Y.1/. +d.1/.5/. +0.1/.60. +2.62.1/. +2.66.1/. +a.6b.1/. +c.6e.1/. +0.1/.6F. +0.1/.6M. +5.79.1/. +i.1/.2p. +i.1/.2p. +0.20.20. +0.20.21. +0.20.22. +0.20.23. +0.20.24. +0.20.25. +b.26.20. +0.20.27. +4.20.28. +6.20.29. +0.20.2a. +0.20.2b. +6.20.2c. +6.20.2d. +6.20.2f. +6.20.2g. +6.20.2h. +6.20.2i. +6.20.2j. +0.20.2l. +6.2m.20. +4.2n.20. +1.20.2o. +d.20.2p. +2.2q.20. +2.2r.20. +7.2s.20. +7.2t.20. +2.2u.20. +d.20.2v. +0.20.2w. +e.2x.20. +9.2y.20. +0.20.2z. +d.20.2A. +9.2B.20. +0.20.2C. +0.20.2D. +0.20.2E. +0.20.2F. +0.20.2G. +9.2H.20. +0.20.2I. +d.20.2J. +0.20.2K. +6.20.2L. +c.2M.20. +9.2N.20. +2.2O.20. +6.20.2P. +9.2Q.20. +8.20.2R. +7.2S.20. +0.20.2T. +1.2U.20. +9.2V.20. +1.2W.20. +0.20.2X. +8.20.2Y. +8.20.2Z. +b.2+.20. +8.20.2/. +0.20.30. +8.20.31. +8.20.32. +8.20.33. +8.20.34. +0.20.35. +b.36.20. +0.20.37. +0.20.38. +5.39.20. +6.20.3a. +6.20.3c. +0.20.3d. +1.20.3e. +6.20.3f. +8.20.3g. +d.20.3h. +0.20.3i. +1.20.3j. +8.20.3k. +6.20.3l. +0.20.3m. +8.20.3n. +8.20.3o. +0.20.3p. +8.20.3q. +6.20.3r. +4.20.3s. +8.20.3t. +8.20.3u. +6.20.3v. +6.20.3w. +6.20.3x. +a.3y.20. +2.3z.20. +5.3A.20. +2.3B.20. +0.20.3C. +6.20.3D. +6.20.3E. +a.3F.20. +5.3G.20. +6.20.3H. +6.20.3I. +6.20.3J. +6.20.3K. +6.20.3L. +d.20.3M. +6.20.3N. +6.20.3O. +6.20.3P. +0.20.3Q. +6.20.3R. +6.20.3S. +1.20.3T. +6.20.3U. +6.20.3V. +6.20.3W. +6.20.3X. +6.20.3Y. +6.20.3Z. +1.20.3+. +1.20.3/. +5.40.20. +6.20.41. +6.20.42. +6.20.43. +6.20.44. +6.20.45. +0.20.47. +6.20.48. +6.20.49. +1.20.4a. +6.20.4b. +6.20.4c. +0.20.4d. +6.20.4e. +6.20.4f. +6.20.4g. +6.20.4h. +6.20.4i. +6.20.4j. +8.20.4k. +6.20.4l. +6.20.4m. +6.20.4n. +6.20.4o. +6.20.4p. +6.20.4q. +6.20.4r. +6.20.4s. +6.20.4t. +6.20.4u. +6.20.4v. +6.20.4w. +6.20.4x. +6.20.4y. +6.20.4z. +6.20.4A. +6.20.4B. +6.20.4C. +6.20.4D. +6.20.4E. +1.20.4F. +8.20.4G. +8.20.4H. +1.20.4I. +8.20.4J. +8.20.4K. +8.20.4L. +6.20.4M. +6.20.4N. +0.20.4O. +6.20.4P. +d.20.4Q. +1.20.4R. +6.20.4S. +4.20.4T. +8.20.4U. +d.20.4V. +6.20.4W. +8.20.4Y. +1.20.4Z. +6.20.4+. +6.20.4/. +1.20.50. +d.20.51. +0.20.52. +8.20.53. +8.20.54. +8.20.55. +8.20.56. +1.20.57. +8.20.58. +8.20.59. +a.5a.20. +2.5b.20. +6.20.5c. +5.5d.20. +8.20.5e. +8.20.5f. +6.20.5g. +8.20.5h. +6.20.5i. +6.20.5j. +6.20.5k. +6.20.5l. +6.20.5m. +6.20.5n. +6.20.5o. +6.20.5p. +6.20.5q. +6.20.5r. +1.20.5s. +6.20.5t. +6.20.5u. +6.20.5v. +1.20.5w. +1.20.5y. +1.20.5z. +8.20.5A. +0.20.5B. +0.20.5C. +1.20.5D. +8.20.5E. +5.5F.20. +8.20.5G. +d.20.5H. +0.20.5I. +f.20.5K. +8.20.5L. +8.20.5M. +8.20.5N. +0.20.5O. +d.20.5P. +6.20.5Q. +0.20.5R. +6.20.5S. +6.20.5T. +d.20.5U. +8.20.5V. +0.20.5W. +8.20.5X. +8.20.5Y. +0.20.5Z. +0.20.5/. +0.20.60. +6.20.61. +2.62.20. +6.20.64. +6.20.65. +2.66.20. +6.20.67. +6.20.68. +6.20.69. +6.20.6a. +6.20.6b. +6.20.6c. +6.20.6d. +6.20.6e. +6.20.6f. +6.20.6g. +6.20.6h. +6.20.6i. +6.20.6j. +6.20.6k. +6.20.6l. +6.20.6m. +6.20.6n. +6.20.6o. +6.20.6p. +6.20.6q. +6.20.6r. +6.20.6s. +6.20.6t. +6.20.6u. +6.20.6v. +6.20.6w. +6.20.6x. +6.20.6y. +6.20.6z. +6.20.6A. +6.20.6B. +6.20.6C. +6.20.6D. +6.20.6E. +0.20.6F. +6.20.6G. +6.20.6H. +6.20.6I. +6.20.6J. +6.20.6K. +8.20.6L. +0.20.6M. +6.20.6N. +6.20.6O. +6.20.6P. +6.20.6Q. +6.20.6R. +6.20.6S. +6.20.6T. +6.20.6U. +6.20.6V. +6.20.6W. +1.20.6X. +6.20.6Y. +1.20.6Z. +0.20.6+. +1.20.6/. +1.20.70. +d.20.71. +6.20.72. +6.20.73. +1.20.74. +6.20.75. +1.20.76. +1.20.77. +6.20.78. +5.79.20. +1.20.7a. +6.20.7b. +6.20.7c. +6.20.7d. +6.20.7e. +6.20.7f. +6.20.7g. +6.20.7h. +6.20.7i. +6.20.7j. +6.20.7k. +6.20.7l. +1.20.7m. +6.20.7o. +6.20.7p. +1.20.7r. +1.20.7s. +8.20.7t. +6.20.7u. +6.20.7v. +6.20.7w. +6.20.7x. +6.20.7y. +1.20.7z. +6.20.7A. +8.20.7B. +6.20.7C. +d.20.7D. +6.20.7E. +6.20.7F. +d.20.7G. +6.20.7H. +6.20.7I. +6.20.7J. +1.20.7K. +1.20.7L. +1.20.7M. +1.20.7N. +1.20.7O. +1.20.7P. +6.20.7Q. +8.20.7R. +6.20.7S. +6.20.7T. +1.20.7U. +8.20.7V. +6.20.7W. +6.20.7X. +6.20.7Y. +6.20.7Z. +1.20.7+. +1.20.7/. +1.20.80. +1.20.81. +1.20.82. +6.20.83. +1.20.84. +1.20.85. +1.20.87. +1.20.88. +8.20.89. +6.20.8a. +8.20.8b. +6.20.8c. +8.20.8d. +6.20.8e. +6.20.8f. +6.20.8g. +6.20.8h. +6.20.8i. +6.20.8j. +6.20.8k. +6.20.8l. +6.20.8m. +6.20.8n. +6.20.8o. +6.20.8p. +6.20.8q. +6.20.8r. +6.20.8s. +6.20.8t. +6.20.8u. +6.20.8v. +6.20.8w. +6.20.8x. +6.20.8y. +6.20.8z. +6.20.8A. +6.20.8B. +6.20.8C. +6.20.8D. +6.20.8E. +6.20.8F. +6.20.8G. +4.20.8H. +4.20.8I. +6.20.8J. +6.20.8K. +6.20.8L. +6.20.8M. +6.20.8N. +6.20.8O. +6.20.8P. +d.20.2p. +d.20.2p. +8.20.3b. +1.20.46. +1.20.4X. +1.20.5x. +1.20.5J. +1.20.7n. +1.20.7q. +d.21.21. +0.21.22. +0.21.23. +0.21.24. +0.21.25. +b.26.21. +0.21.27. +0.21.28. +0.21.2a. +0.21.2b. +0.21.2l. +6.2m.21. +4.2n.21. +0.21.2p. +2.2q.21. +2.2r.21. +7.2s.21. +7.2t.21. +2.2u.21. +0.21.2v. +d.21.2w. +e.2x.21. +9.2y.21. +0.21.2z. +d.21.2A. +9.2B.21. +0.21.2C. +0.21.2D. +0.21.2E. +0.21.2F. +0.21.2G. +9.2H.21. +0.21.2I. +d.21.2J. +0.21.2K. +c.2M.21. +9.2N.21. +2.2O.21. +9.2Q.21. +7.2S.21. +0.21.2T. +1.2U.21. +9.2V.21. +1.2W.21. +d.21.2X. +d.2+.21. +0.21.30. +d.21.35. +b.36.21. +0.21.37. +0.21.38. +5.39.21. +0.21.3d. +d.21.3h. +0.21.3i. +0.21.3m. +0.21.3p. +a.3v.21. +a.3y.21. +2.3z.21. +5.3A.21. +2.3B.21. +0.21.3C. +a.3F.21. +5.3G.21. +0.21.3M. +0.21.3Q. +5.40.21. +0.21.47. +0.21.4d. +0.21.4O. +0.21.4Q. +5.4T.21. +0.21.4V. +d.21.51. +0.21.52. +a.5a.21. +2.5b.21. +5.5d.21. +0.21.5B. +0.21.5C. +5.5F.21. +0.21.5H. +0.21.5I. +f.21.5K. +7.5M.21. +d.21.5O. +0.21.5R. +0.21.5U. +0.21.5W. +1.5Y.21. +0.21.5Z. +0.21.5/. +0.21.60. +2.62.21. +2.66.21. +a.6b.21. +c.6e.21. +0.21.6F. +0.21.6M. +0.21.6+. +0.21.71. +5.79.21. +0.21.7D. +0.21.2p. +0.21.2p. +0.22.22. +0.22.23. +0.22.24. +0.22.25. +b.26.22. +0.27.22. +0.22.28. +d.22.29. +0.22.2a. +0.22.2b. +d.22.2c. +6.22.2d. +6.22.2f. +d.22.2g. +6.22.2h. +6.22.2i. +6.22.2j. +0.22.2l. +6.2m.22. +1.22.2o. +0.22.2p. +2.2q.22. +2.2r.22. +7.2s.22. +7.2t.22. +2.2u.22. +0.22.2v. +0.22.2w. +e.2x.22. +9.2y.22. +0.22.2z. +0.22.2A. +9.2B.22. +0.22.2C. +0.22.2D. +0.22.2E. +0.22.2F. +0.22.2G. +9.2H.22. +0.22.2I. +0.22.2J. +0.22.2K. +d.22.2L. +c.2M.22. +9.2N.22. +2.2O.22. +6.22.2P. +9.2Q.22. +8.22.2R. +7.2S.22. +0.22.2T. +1.2U.22. +9.2V.22. +1.2W.22. +0.22.2X. +8.22.2Y. +8.22.2Z. +b.2+.22. +8.22.2/. +0.22.30. +8.22.31. +8.22.32. +8.22.33. +8.22.34. +0.22.35. +b.36.22. +0.22.37. +0.22.38. +5.39.22. +6.22.3a. +6.22.3c. +0.22.3d. +1.22.3e. +d.22.3f. +8.22.3g. +0.22.3h. +0.22.3i. +1.22.3j. +8.22.3k. +d.22.3l. +0.22.3m. +8.22.3n. +8.22.3o. +0.22.3p. +8.22.3q. +6.22.3r. +4.22.3s. +8.22.3t. +8.22.3u. +6.22.3v. +6.22.3w. +6.22.3x. +a.3y.22. +2.3z.22. +5.3A.22. +2.3B.22. +0.22.3C. +6.22.3D. +6.22.3E. +a.3F.22. +5.3G.22. +6.22.3H. +6.22.3I. +6.22.3J. +6.22.3K. +6.22.3L. +0.22.3M. +6.22.3N. +6.22.3O. +6.22.3P. +0.22.3Q. +6.22.3R. +6.22.3S. +1.22.3T. +6.22.3U. +6.22.3V. +6.22.3W. +6.22.3X. +6.22.3Y. +6.22.3Z. +1.22.3+. +1.22.3/. +5.40.22. +d.22.41. +6.22.42. +d.22.43. +6.22.44. +6.22.45. +0.22.47. +6.22.48. +6.22.49. +1.22.4a. +6.22.4b. +6.22.4c. +0.22.4d. +d.22.4e. +6.22.4f. +6.22.4g. +6.22.4h. +6.22.4i. +6.22.4j. +8.22.4k. +6.22.4l. +6.22.4m. +6.22.4n. +6.22.4o. +6.22.4p. +d.22.4q. +6.22.4r. +6.22.4s. +6.22.4t. +6.22.4u. +6.22.4v. +6.22.4w. +6.22.4x. +6.22.4y. +6.22.4z. +6.22.4A. +6.22.4B. +6.22.4C. +6.22.4D. +6.22.4E. +1.22.4F. +8.22.4G. +8.22.4H. +1.22.4I. +8.22.4J. +8.22.4K. +8.22.4L. +6.22.4M. +d.22.4N. +0.22.4O. +6.22.4P. +0.22.4Q. +1.22.4R. +6.22.4S. +4.22.4T. +8.22.4U. +0.22.4V. +6.22.4W. +8.22.4Y. +1.22.4Z. +6.22.4+. +6.22.4/. +1.22.50. +0.22.51. +0.22.52. +8.22.53. +8.22.54. +8.22.55. +8.22.56. +1.22.57. +8.22.58. +8.22.59. +a.5a.22. +2.5b.22. +6.22.5c. +5.5d.22. +8.22.5e. +8.22.5f. +6.22.5g. +8.22.5h. +6.22.5i. +6.22.5j. +6.22.5k. +6.22.5l. +6.22.5m. +6.22.5n. +6.22.5o. +6.22.5p. +6.22.5q. +6.22.5r. +1.22.5s. +6.22.5t. +6.22.5u. +6.22.5v. +1.22.5w. +1.22.5y. +1.22.5z. +8.22.5A. +0.22.5B. +0.22.5C. +1.22.5D. +8.22.5E. +5.5F.22. +8.22.5G. +0.22.5H. +0.22.5I. +f.22.5K. +8.22.5L. +8.22.5M. +8.22.5N. +0.22.5O. +8.22.5P. +6.22.5Q. +0.22.5R. +6.22.5S. +6.22.5T. +0.22.5U. +8.22.5V. +0.22.5W. +8.22.5X. +8.22.5Y. +0.22.5Z. +h.5+.22. +0.22.5/. +0.22.60. +d.22.61. +2.62.22. +d.22.64. +6.22.65. +2.66.22. +6.22.67. +6.22.68. +6.22.69. +6.22.6a. +6.22.6b. +6.22.6c. +6.22.6d. +6.22.6e. +6.22.6f. +6.22.6g. +6.22.6h. +6.22.6i. +d.22.6j. +6.22.6k. +6.22.6l. +6.22.6m. +6.22.6n. +6.22.6o. +6.22.6p. +d.22.6q. +6.22.6r. +6.22.6s. +6.22.6t. +6.22.6u. +6.22.6v. +6.22.6w. +6.22.6x. +6.22.6y. +6.22.6z. +6.22.6A. +6.22.6B. +6.22.6C. +6.22.6D. +6.22.6E. +0.22.6F. +6.22.6G. +6.22.6H. +6.22.6I. +6.22.6J. +6.22.6K. +8.22.6L. +0.6M.22. +6.22.6N. +d.22.6O. +6.22.6P. +6.22.6Q. +6.22.6R. +6.22.6S. +6.22.6T. +6.22.6U. +6.22.6V. +6.22.6W. +1.22.6X. +6.22.6Y. +1.22.6Z. +0.22.6+. +1.22.6/. +1.22.70. +0.22.71. +6.22.72. +6.22.73. +1.22.74. +d.22.75. +1.22.76. +1.22.77. +6.22.78. +5.79.22. +1.22.7a. +6.22.7b. +6.22.7c. +6.22.7d. +6.22.7e. +6.22.7f. +6.22.7g. +6.22.7h. +6.22.7i. +6.22.7j. +6.22.7k. +6.22.7l. +1.22.7m. +6.22.7o. +6.22.7p. +1.22.7r. +1.22.7s. +8.22.7t. +6.22.7u. +6.22.7v. +6.22.7w. +6.22.7x. +6.22.7y. +1.22.7z. +6.22.7A. +8.22.7B. +6.22.7C. +0.22.7D. +6.22.7E. +d.22.7F. +6.22.7G. +6.22.7H. +6.22.7I. +6.22.7J. +1.22.7K. +1.22.7L. +1.22.7M. +1.22.7N. +1.22.7O. +1.22.7P. +6.22.7Q. +8.22.7R. +6.22.7S. +d.22.7T. +1.22.7U. +8.22.7V. +6.22.7W. +6.22.7X. +6.22.7Y. +d.22.7Z. +1.22.7+. +1.22.7/. +1.22.80. +1.22.81. +1.22.82. +6.22.83. +1.22.84. +1.22.85. +h.86.22. +1.22.87. +1.22.88. +8.22.89. +d.22.8a. +8.22.8b. +6.22.8c. +8.22.8d. +6.22.8e. +d.22.8f. +6.22.8g. +6.22.8h. +6.22.8i. +6.22.8j. +6.22.8k. +6.22.8l. +6.22.8m. +6.22.8n. +6.22.8o. +6.22.8p. +6.22.8q. +6.22.8r. +6.22.8s. +6.22.8t. +6.22.8u. +6.22.8v. +6.22.8w. +6.22.8x. +6.22.8y. +6.22.8z. +6.22.8A. +6.22.8B. +d.22.8C. +6.22.8D. +6.22.8E. +6.22.8F. +6.22.8G. +4.22.8H. +4.22.8I. +6.22.8J. +6.22.8K. +6.22.8L. +6.22.8M. +6.22.8N. +d.22.8O. +d.22.8P. +0.22.2p. +0.22.2p. +8.22.3b. +1.22.46. +1.22.4X. +1.22.5x. +1.22.5J. +1.22.7n. +1.22.7q. +0.23.23. +3.24.23. +0.23.25. +b.26.23. +0.27.23. +0.23.28. +0.23.2a. +0.23.2b. +7.2e.23. +0.23.2l. +6.2m.23. +4.2n.23. +0.23.2p. +2.2q.23. +2.2r.23. +7.2s.23. +7.2t.23. +2.2u.23. +0.23.2v. +d.23.2w. +e.2x.23. +9.2y.23. +0.23.2z. +0.23.2A. +9.2B.23. +0.23.2C. +0.23.2D. +0.23.2E. +0.23.2F. +0.23.2G. +9.2H.23. +0.23.2I. +0.23.2J. +0.23.2K. +c.2M.23. +9.2N.23. +2.2O.23. +9.2Q.23. +7.2S.23. +0.23.2T. +1.2U.23. +9.2V.23. +1.2W.23. +0.23.2X. +d.2+.23. +0.23.30. +d.23.35. +b.36.23. +d.23.37. +0.23.38. +5.39.23. +0.23.3d. +d.23.3h. +5.3i.23. +0.23.3m. +0.23.3p. +a.3v.23. +a.3y.23. +2.3z.23. +5.3A.23. +2.3B.23. +0.23.3C. +a.3F.23. +5.3G.23. +0.23.3M. +d.23.3Q. +5.40.23. +0.23.47. +0.23.4d. +0.23.4O. +0.23.4Q. +5.4T.23. +0.23.4V. +0.23.51. +0.23.52. +a.5a.23. +2.5b.23. +5.5d.23. +0.23.5B. +0.23.5C. +5.5F.23. +0.23.5H. +0.23.5I. +0.23.5K. +7.5M.23. +0.23.5O. +0.23.5R. +0.23.5U. +0.23.5W. +1.5Y.23. +0.23.5Z. +0.23.5/. +0.23.60. +2.62.23. +d.23.63. +2.66.23. +a.6b.23. +c.6e.23. +d.23.6F. +0.6M.23. +0.23.6+. +0.23.71. +5.79.23. +0.23.7D. +0.23.2p. +0.23.2p. +3.24.24. +3.24.25. +b.26.24. +0.27.24. +0.24.28. +6.24.29. +0.24.2a. +0.24.2b. +6.24.2c. +6.24.2d. +7.2e.24. +6.24.2f. +6.24.2g. +6.24.2h. +6.24.2i. +6.24.2j. +3.24.2l. +6.2m.24. +4.2n.24. +1.24.2o. +0.24.2p. +2.2q.24. +2.2r.24. +7.2s.24. +7.2t.24. +2.2u.24. +0.24.2v. +0.24.2w. +e.2x.24. +9.2y.24. +0.24.2z. +0.24.2A. +9.2B.24. +0.24.2C. +0.24.2D. +0.24.2E. +0.24.2F. +0.24.2G. +9.2H.24. +0.24.2I. +3.24.2J. +0.24.2K. +6.24.2L. +c.2M.24. +9.2N.24. +2.2O.24. +6.24.2P. +9.2Q.24. +8.24.2R. +7.2S.24. +0.24.2T. +1.2U.24. +9.2V.24. +1.2W.24. +0.24.2X. +8.24.2Y. +8.24.2Z. +d.2+.24. +8.24.2/. +0.24.30. +8.24.31. +8.24.32. +8.24.33. +8.24.34. +0.24.35. +6.36.24. +0.24.37. +3.24.38. +5.39.24. +6.24.3a. +6.24.3c. +3.24.3d. +1.24.3e. +6.24.3f. +8.24.3g. +0.24.3h. +5.3i.24. +1.24.3j. +8.24.3k. +6.24.3l. +3.24.3m. +8.24.3n. +8.24.3o. +0.24.3p. +8.24.3q. +6.24.3r. +4.24.3s. +8.24.3t. +8.24.3u. +6.24.3v. +6.24.3w. +6.24.3x. +a.3y.24. +2.3z.24. +5.3A.24. +2.3B.24. +3.24.3C. +3.24.3D. +6.24.3E. +a.3F.24. +5.3G.24. +6.24.3H. +6.24.3I. +6.24.3J. +6.24.3K. +6.24.3L. +3.24.3M. +6.24.3N. +6.24.3O. +6.24.3P. +3.24.3Q. +6.24.3R. +6.24.3S. +1.24.3T. +6.24.3U. +6.24.3V. +6.24.3W. +6.24.3X. +6.24.3Y. +6.24.3Z. +1.24.3+. +1.24.3/. +5.40.24. +6.24.41. +6.24.42. +3.24.43. +6.24.44. +6.24.45. +3.24.47. +6.24.48. +6.24.49. +1.24.4a. +6.24.4b. +6.24.4c. +0.24.4d. +6.24.4e. +6.24.4f. +6.24.4g. +6.24.4h. +6.24.4i. +6.24.4j. +8.24.4k. +6.24.4l. +6.24.4m. +6.24.4n. +6.24.4o. +6.24.4p. +6.24.4q. +6.24.4r. +6.24.4s. +6.24.4t. +6.24.4u. +6.24.4v. +6.24.4w. +6.24.4x. +6.24.4y. +6.24.4z. +6.24.4A. +6.24.4B. +6.24.4C. +6.24.4D. +6.24.4E. +1.24.4F. +8.24.4G. +8.24.4H. +1.24.4I. +8.24.4J. +8.24.4K. +8.24.4L. +3.24.4M. +3.24.4N. +3.24.4O. +6.24.4P. +3.24.4Q. +1.24.4R. +6.24.4S. +4.24.4T. +8.24.4U. +3.24.4V. +6.24.4W. +8.24.4Y. +1.24.4Z. +6.24.4+. +6.24.4/. +1.24.50. +3.24.51. +3.24.52. +8.24.53. +8.24.54. +8.24.55. +8.24.56. +1.24.57. +8.24.58. +8.24.59. +a.5a.24. +2.5b.24. +6.24.5c. +5.5d.24. +8.24.5e. +8.24.5f. +6.24.5g. +8.24.5h. +6.24.5i. +6.24.5j. +6.24.5k. +6.24.5l. +6.24.5m. +6.24.5n. +6.24.5o. +6.24.5p. +6.24.5q. +6.24.5r. +1.24.5s. +6.24.5t. +3.24.5u. +3.24.5v. +1.24.5w. +1.24.5y. +1.24.5z. +8.24.5A. +0.24.5B. +0.24.5C. +1.24.5D. +8.24.5E. +5.5F.24. +8.24.5G. +3.24.5H. +0.24.5I. +f.24.5K. +8.24.5L. +8.24.5M. +8.24.5N. +0.24.5O. +8.24.5P. +6.24.5Q. +0.24.5R. +6.24.5S. +6.24.5T. +3.24.5U. +8.24.5V. +3.24.5W. +8.24.5X. +8.24.5Y. +3.24.5Z. +h.5+.24. +0.24.5/. +0.24.60. +6.24.61. +2.62.24. +6.24.64. +6.24.65. +2.66.24. +6.24.67. +6.24.68. +6.24.69. +6.24.6a. +6.24.6b. +6.24.6c. +6.24.6d. +6.24.6e. +6.24.6f. +6.24.6g. +6.24.6h. +6.24.6i. +6.24.6j. +6.24.6k. +6.24.6l. +6.24.6m. +6.24.6n. +6.24.6o. +6.24.6p. +6.24.6q. +6.24.6r. +6.24.6s. +6.24.6t. +6.24.6u. +6.24.6v. +6.24.6w. +6.24.6x. +6.24.6y. +6.24.6z. +6.24.6A. +3.24.6B. +6.24.6C. +6.24.6D. +6.24.6E. +0.24.6F. +6.24.6G. +6.24.6H. +6.24.6I. +6.24.6J. +6.24.6K. +8.24.6L. +0.6M.24. +6.24.6N. +6.24.6O. +6.24.6P. +6.24.6Q. +6.24.6R. +6.24.6S. +6.24.6T. +6.24.6U. +6.24.6V. +6.24.6W. +1.24.6X. +6.24.6Y. +1.24.6Z. +3.24.6+. +1.24.6/. +1.24.70. +0.24.71. +6.24.72. +6.24.73. +1.24.74. +6.24.75. +1.24.76. +1.24.77. +6.24.78. +5.79.24. +1.24.7a. +6.24.7b. +6.24.7c. +6.24.7d. +6.24.7e. +6.24.7f. +6.24.7g. +6.24.7h. +6.24.7i. +6.24.7j. +6.24.7k. +6.24.7l. +1.24.7m. +6.24.7o. +6.24.7p. +1.24.7r. +1.24.7s. +8.24.7t. +6.24.7u. +6.24.7v. +6.24.7w. +6.24.7x. +6.24.7y. +1.24.7z. +6.24.7A. +8.24.7B. +6.24.7C. +3.24.7D. +6.24.7E. +6.24.7F. +3.24.7G. +6.24.7H. +6.24.7I. +6.24.7J. +1.24.7K. +1.24.7L. +1.24.7M. +1.24.7N. +1.24.7O. +1.24.7P. +6.24.7Q. +8.24.7R. +6.24.7S. +6.24.7T. +1.24.7U. +8.24.7V. +6.24.7W. +6.24.7X. +6.24.7Y. +6.24.7Z. +1.24.7+. +1.24.7/. +1.24.80. +1.24.81. +1.24.82. +6.24.83. +1.24.84. +1.24.85. +h.86.24. +1.24.87. +1.24.88. +8.24.89. +6.24.8a. +8.24.8b. +6.24.8c. +8.24.8d. +6.24.8e. +6.24.8f. +6.24.8g. +6.24.8h. +6.24.8i. +6.24.8j. +6.24.8k. +6.24.8l. +6.24.8m. +6.24.8n. +6.24.8o. +6.24.8p. +6.24.8q. +6.24.8r. +6.24.8s. +6.24.8t. +6.24.8u. +6.24.8v. +6.24.8w. +6.24.8x. +6.24.8y. +6.24.8z. +6.24.8A. +6.24.8B. +6.24.8C. +6.24.8D. +6.24.8E. +6.24.8F. +6.24.8G. +4.24.8H. +4.24.8I. +6.24.8J. +6.24.8K. +6.24.8L. +6.24.8M. +6.24.8N. +6.24.8O. +6.24.8P. +0.24.2p. +0.24.2p. +8.24.3b. +1.24.46. +1.24.4X. +1.24.5x. +1.24.5J. +1.24.7n. +1.24.7q. +0.25.25. +b.26.25. +0.27.25. +4.25.28. +0.25.2a. +0.25.2b. +0.25.2l. +6.2m.25. +4.2n.25. +0.25.2p. +2.2q.25. +2.2r.25. +7.2s.25. +7.2t.25. +2.2u.25. +0.25.2v. +0.25.2w. +e.2x.25. +9.2y.25. +0.25.2z. +d.25.2A. +9.2B.25. +0.25.2C. +0.25.2D. +d.25.2E. +0.25.2F. +0.25.2G. +9.2H.25. +0.25.2I. +d.25.2J. +0.25.2K. +c.2M.25. +9.2N.25. +2.2O.25. +9.2Q.25. +7.2S.25. +0.25.2T. +1.2U.25. +9.2V.25. +1.2W.25. +0.25.2X. +d.2+.25. +0.25.30. +0.25.35. +b.36.25. +0.25.37. +0.25.38. +5.39.25. +0.25.3d. +d.25.3h. +5.3i.25. +0.25.3m. +0.25.3p. +a.3v.25. +a.3y.25. +2.3z.25. +5.3A.25. +2.3B.25. +0.25.3C. +a.3F.25. +5.3G.25. +0.25.3M. +0.25.3Q. +5.40.25. +0.25.47. +0.25.4d. +0.25.4O. +d.25.4Q. +7.4T.25. +d.25.4V. +0.25.51. +0.25.52. +a.5a.25. +2.5b.25. +5.5d.25. +0.25.5B. +0.25.5C. +5.5F.25. +d.25.5H. +0.25.5I. +f.25.5K. +7.5M.25. +0.25.5O. +0.25.5R. +0.25.5U. +0.25.5W. +1.5Y.25. +0.25.5Z. +0.25.5/. +0.25.60. +2.62.25. +2.66.25. +a.6b.25. +c.6e.25. +0.25.6F. +0.6M.25. +0.25.6+. +0.25.71. +5.79.25. +0.25.7D. +0.25.2p. +0.25.2p. +b.26.26. +b.26.27. +b.26.28. +b.26.2a. +b.26.2b. +b.26.2l. +6.2m.26. +4.2n.26. +b.26.2p. +2.2q.26. +2.2r.26. +7.2s.26. +7.2t.26. +2.2u.26. +b.26.2v. +b.26.2w. +e.2x.26. +9.2y.26. +b.26.2z. +b.26.2A. +9.2B.26. +b.26.2C. +b.26.2D. +b.26.2E. +b.26.2F. +b.26.2G. +9.2H.26. +b.26.2I. +b.26.2J. +b.26.2K. +c.2M.26. +9.2N.26. +2.2O.26. +9.2Q.26. +7.2S.26. +b.26.2T. +1.2U.26. +9.2V.26. +1.2W.26. +b.26.2X. +d.2+.26. +b.26.30. +b.26.35. +3.36.26. +b.26.37. +b.26.38. +5.39.26. +b.26.3d. +b.26.3h. +b.26.3i. +b.26.3m. +b.26.3p. +a.3v.26. +a.3y.26. +2.3z.26. +5.3A.26. +2.3B.26. +b.26.3C. +a.3F.26. +5.3G.26. +b.26.3M. +b.26.3Q. +5.40.26. +b.26.47. +b.26.4d. +b.26.4O. +b.26.4Q. +7.4T.26. +b.26.4V. +b.26.51. +b.26.52. +a.5a.26. +2.5b.26. +5.5d.26. +b.26.5B. +b.26.5C. +b.26.5F. +b.26.5H. +b.26.5I. +f.26.5K. +7.5M.26. +b.26.5O. +b.26.5R. +b.26.5U. +b.26.5W. +1.5Y.26. +b.26.5Z. +b.26.5/. +b.26.60. +2.62.26. +2.66.26. +a.6b.26. +c.6e.26. +b.26.6F. +b.26.6M. +b.26.6+. +b.26.71. +5.79.26. +b.26.7D. +b.26.2p. +b.26.2p. +0.27.27. +4.27.28. +4.27.29. +0.27.2a. +0.27.2b. +4.27.2c. +4.27.2d. +4.2e.27. +4.27.2f. +4.27.2g. +4.27.2h. +4.27.2i. +4.27.2j. +0.27.2l. +6.2m.27. +4.2n.27. +1.27.2o. +0.27.2p. +2.2q.27. +2.2r.27. +7.2s.27. +7.2t.27. +2.2u.27. +d.27.2v. +0.27.2w. +e.2x.27. +9.2y.27. +0.27.2z. +d.27.2A. +9.2B.27. +0.27.2C. +0.27.2D. +d.27.2E. +0.27.2F. +0.27.2G. +9.2H.27. +0.27.2I. +d.27.2J. +0.27.2K. +4.27.2L. +c.2M.27. +9.2N.27. +2.2O.27. +4.27.2P. +9.2Q.27. +4.27.2R. +7.2S.27. +0.27.2T. +1.2U.27. +9.2V.27. +1.2W.27. +0.27.2X. +4.27.2Y. +4.27.2Z. +d.2+.27. +4.27.2/. +0.27.30. +4.27.31. +4.27.32. +4.27.33. +4.27.34. +0.27.35. +b.36.27. +0.27.37. +0.27.38. +5.39.27. +4.27.3a. +4.27.3c. +0.27.3d. +1.27.3e. +4.27.3f. +4.27.3g. +0.27.3h. +0.27.3i. +1.27.3j. +4.27.3k. +4.27.3l. +0.27.3m. +4.27.3n. +4.27.3o. +0.27.3p. +4.27.3q. +4.27.3r. +4.27.3s. +4.27.3t. +4.27.3u. +a.3v.27. +4.27.3w. +4.27.3x. +a.3y.27. +2.3z.27. +5.3A.27. +2.3B.27. +0.27.3C. +4.27.3D. +4.27.3E. +a.3F.27. +5.3G.27. +4.27.3H. +4.27.3I. +4.27.3J. +4.27.3K. +4.27.3L. +0.27.3M. +4.27.3N. +4.27.3O. +4.27.3P. +0.27.3Q. +4.27.3R. +4.27.3S. +1.27.3T. +4.27.3U. +4.27.3V. +4.27.3W. +4.27.3X. +4.27.3Y. +4.27.3Z. +1.27.3+. +1.27.3/. +5.40.27. +4.27.41. +4.27.42. +4.27.43. +4.27.44. +4.27.45. +0.27.47. +4.27.48. +4.27.49. +1.27.4a. +4.27.4b. +4.27.4c. +0.27.4d. +4.27.4e. +4.27.4f. +4.27.4g. +4.27.4h. +4.27.4i. +4.27.4j. +4.27.4k. +4.27.4l. +4.27.4m. +4.27.4n. +4.27.4o. +4.27.4p. +4.27.4q. +4.27.4r. +4.27.4s. +4.27.4t. +4.27.4u. +4.27.4v. +4.27.4w. +4.27.4x. +4.27.4y. +4.27.4z. +4.27.4A. +4.27.4B. +4.27.4C. +4.27.4D. +4.27.4E. +1.27.4F. +4.27.4G. +4.27.4H. +1.27.4I. +4.27.4J. +4.27.4K. +4.27.4L. +4.27.4M. +4.27.4N. +0.27.4O. +4.27.4P. +d.27.4Q. +1.27.4R. +4.27.4S. +4.27.4T. +4.27.4U. +d.27.4V. +4.27.4W. +4.27.4Y. +1.27.4Z. +4.27.4+. +4.27.4/. +1.27.50. +d.27.51. +0.27.52. +4.27.53. +4.27.54. +4.27.55. +4.27.56. +1.27.57. +4.27.58. +4.27.59. +a.5a.27. +2.5b.27. +4.27.5c. +5.5d.27. +4.27.5e. +4.27.5f. +4.27.5g. +4.27.5h. +4.27.5i. +4.27.5j. +4.27.5k. +4.27.5l. +4.27.5m. +4.27.5n. +4.27.5o. +4.27.5p. +4.27.5q. +4.27.5r. +1.27.5s. +4.27.5t. +4.27.5u. +4.27.5v. +1.27.5w. +1.27.5y. +1.27.5z. +4.27.5A. +0.27.5B. +0.27.5C. +1.27.5D. +4.27.5E. +5.5F.27. +4.27.5G. +d.27.5H. +0.27.5I. +f.27.5K. +4.27.5L. +4.27.5M. +4.27.5N. +0.27.5O. +4.27.5P. +4.27.5Q. +0.27.5R. +4.27.5S. +4.27.5T. +0.27.5U. +4.27.5V. +d.27.5W. +4.27.5X. +4.27.5Y. +0.27.5Z. +0.27.5/. +0.27.60. +4.27.61. +2.62.27. +4.27.64. +4.27.65. +2.66.27. +4.27.67. +4.27.68. +4.27.69. +4.27.6a. +a.6b.27. +4.27.6c. +4.27.6d. +4.27.6e. +4.27.6f. +4.27.6g. +4.27.6h. +4.27.6i. +4.27.6j. +4.27.6k. +4.27.6l. +4.27.6m. +4.27.6n. +4.27.6o. +4.27.6p. +4.27.6q. +4.27.6r. +4.27.6s. +4.27.6t. +4.27.6u. +4.27.6v. +4.27.6w. +4.27.6x. +4.27.6y. +4.27.6z. +4.27.6A. +4.27.6B. +4.27.6C. +4.27.6D. +4.27.6E. +0.27.6F. +4.27.6G. +4.27.6H. +4.27.6I. +4.27.6J. +4.27.6K. +4.27.6L. +0.6M.27. +4.27.6N. +4.27.6O. +4.27.6P. +4.27.6Q. +4.27.6R. +4.27.6S. +4.27.6T. +4.27.6U. +4.27.6V. +4.27.6W. +1.27.6X. +4.27.6Y. +0.27.6+. +1.27.6/. +1.27.70. +d.27.71. +4.27.72. +4.27.73. +1.27.74. +4.27.75. +1.27.76. +1.27.77. +4.27.78. +5.79.27. +1.27.7a. +4.27.7b. +4.27.7c. +4.27.7d. +4.27.7e. +4.27.7f. +4.27.7g. +4.27.7h. +4.27.7i. +4.27.7j. +4.27.7k. +4.27.7l. +1.27.7m. +4.27.7o. +4.27.7p. +1.27.7r. +1.27.7s. +4.27.7t. +4.27.7u. +4.27.7v. +4.27.7w. +4.27.7x. +4.27.7y. +1.27.7z. +4.27.7A. +4.27.7B. +4.27.7C. +d.27.7D. +4.27.7E. +4.27.7F. +4.27.7G. +4.27.7H. +4.27.7I. +4.27.7J. +1.27.7K. +1.27.7L. +1.27.7M. +1.27.7N. +1.27.7O. +1.27.7P. +4.27.7Q. +4.27.7R. +4.27.7S. +4.27.7T. +1.27.7U. +4.27.7V. +4.27.7W. +4.27.7X. +4.27.7Y. +4.27.7Z. +1.27.7+. +1.27.7/. +1.27.80. +1.27.81. +1.27.82. +4.27.83. +1.27.84. +1.27.85. +1.27.87. +1.27.88. +4.27.89. +4.27.8a. +4.27.8b. +4.27.8c. +4.27.8d. +4.27.8e. +4.27.8f. +4.27.8g. +4.27.8h. +4.27.8i. +4.27.8j. +4.27.8k. +4.27.8l. +4.27.8m. +4.27.8n. +4.27.8o. +4.27.8p. +4.27.8q. +4.27.8r. +4.27.8s. +4.27.8t. +4.27.8u. +4.27.8v. +4.27.8w. +4.27.8x. +4.27.8y. +4.27.8z. +4.27.8A. +4.27.8B. +4.27.8C. +4.27.8D. +4.27.8E. +4.27.8F. +4.27.8G. +4.27.8H. +4.27.8I. +4.27.8J. +4.27.8K. +4.27.8L. +4.27.8M. +4.27.8N. +4.27.8O. +4.27.8P. +0.27.2p. +0.27.2p. +4.27.3b. +1.27.46. +1.27.4X. +1.27.5x. +1.27.5J. +1.27.7n. +1.27.7q. +d.28.28. +6.2a.28. +0.28.2b. +d.2e.28. +d.28.2l. +6.2m.28. +4.2n.28. +4.28.2p. +2.2q.28. +2.2r.28. +7.2s.28. +7.2t.28. +2.2u.28. +4.28.2v. +4.28.2w. +e.2x.28. +9.2y.28. +4.28.2z. +4.28.2A. +9.2B.28. +4.28.2C. +4.28.2D. +4.28.2E. +4.28.2F. +4.28.2G. +9.2H.28. +4.28.2I. +4.28.2J. +4.28.2K. +c.2M.28. +9.2N.28. +2.2O.28. +9.2Q.28. +7.2S.28. +4.28.2T. +1.2U.28. +9.2V.28. +1.2W.28. +4.28.2X. +4.28.2Y. +6.2+.28. +4.28.30. +4.28.35. +b.36.28. +4.28.37. +4.28.38. +5.39.28. +d.28.3d. +4.28.3h. +4.3i.28. +4.28.3m. +4.28.3o. +0.28.3p. +4.28.3s. +a.3v.28. +a.3y.28. +2.3z.28. +5.3A.28. +2.3B.28. +4.28.3C. +a.3F.28. +5.3G.28. +4.28.3M. +4.28.3Q. +5.40.28. +0.28.47. +4.28.4d. +d.28.4O. +4.28.4Q. +4.28.4T. +4.28.4V. +d.28.51. +4.28.52. +a.5a.28. +2.5b.28. +5.5d.28. +0.28.5B. +0.28.5C. +4.5F.28. +4.28.5H. +4.28.5I. +4.28.5K. +7.5M.28. +4.28.5O. +d.28.5R. +4.28.5U. +4.28.5W. +1.5Y.28. +d.28.5Z. +d.28.5/. +4.28.60. +2.62.28. +2.66.28. +a.6b.28. +c.6e.28. +4.28.6F. +0.6M.28. +d.28.6+. +4.28.71. +5.79.28. +4.28.7D. +4.28.2p. +4.28.2p. +1.2d.29. +1.2l.29. +4.2n.29. +1.51.29. +5.5I.29. +6.5K.29. +7.5M.29. +1.5Y.29. +1.5+.29. +4.60.29. +5.62.29. +1.6q.29. +1.6t.29. +3.6F.29. +c.6H.29. +8.6M.29. +1.7B.29. +1.7R.29. +1.86.29. +1.8d.29. +d.2a.2a. +d.2a.2b. +7.2e.2a. +0.2a.2l. +6.2m.2a. +d.2a.2p. +2.2q.2a. +2.2r.2a. +7.2s.2a. +7.2t.2a. +2.2u.2a. +d.2a.2v. +d.2a.2w. +e.2x.2a. +9.2y.2a. +0.2a.2z. +d.2a.2A. +9.2B.2a. +d.2a.2C. +0.2a.2D. +d.2a.2E. +d.2a.2F. +0.2a.2G. +9.2H.2a. +d.2a.2I. +d.2a.2J. +d.2a.2K. +c.2M.2a. +9.2N.2a. +2.2O.2a. +9.2Q.2a. +7.2S.2a. +d.2a.2T. +1.2U.2a. +9.2V.2a. +1.2W.2a. +0.2a.2X. +b.2+.2a. +0.2a.30. +0.2a.35. +b.36.2a. +d.2a.37. +d.2a.38. +5.39.2a. +d.2a.3d. +d.2a.3h. +5.3i.2a. +d.2a.3m. +0.2a.3p. +a.3v.2a. +a.3y.2a. +5.3z.2a. +5.3A.2a. +2.3B.2a. +d.2a.3C. +a.3F.2a. +5.3G.2a. +d.2a.3M. +d.2a.3Q. +5.40.2a. +d.2a.47. +0.2a.4d. +d.2a.4O. +d.2a.4Q. +5.4T.2a. +d.2a.4V. +0.2a.51. +d.2a.52. +a.5a.2a. +2.5b.2a. +5.5d.2a. +0.2a.5B. +0.2a.5C. +5.5F.2a. +d.2a.5H. +0.2a.5I. +f.2a.5K. +7.5M.2a. +0.2a.5O. +d.2a.5R. +d.2a.5U. +d.2a.5W. +1.5Y.2a. +0.2a.5Z. +0.2a.5/. +0.2a.60. +2.62.2a. +2.66.2a. +a.6b.2a. +c.6e.2a. +0.2a.6F. +0.6M.2a. +0.2a.6+. +d.2a.71. +5.79.2a. +d.2a.7D. +d.2a.2p. +d.2a.2p. +0.2b.2b. +0.2b.2l. +6.2m.2b. +0.2p.2b. +2.2q.2b. +2.2r.2b. +7.2s.2b. +7.2t.2b. +2.2u.2b. +0.2v.2b. +3.2b.2w. +e.2x.2b. +9.2y.2b. +0.2z.2b. +0.2A.2b. +9.2B.2b. +0.2C.2b. +0.2D.2b. +0.2E.2b. +0.2F.2b. +0.2G.2b. +9.2H.2b. +0.2I.2b. +0.2J.2b. +0.2K.2b. +c.2M.2b. +9.2N.2b. +2.2O.2b. +9.2Q.2b. +7.2S.2b. +0.2T.2b. +1.2U.2b. +9.2V.2b. +1.2W.2b. +0.2X.2b. +b.2+.2b. +d.30.2b. +3.2b.35. +b.36.2b. +0.2b.37. +0.2b.38. +5.39.2b. +5.3d.2b. +0.2b.3h. +5.3i.2b. +0.2b.3m. +5.3p.2b. +a.3v.2b. +a.3y.2b. +2.3z.2b. +5.3A.2b. +2.3B.2b. +5.3C.2b. +a.3F.2b. +5.3G.2b. +0.2b.3M. +0.2b.3Q. +5.40.2b. +0.2b.47. +5.4d.2b. +0.2b.4O. +0.2b.4Q. +5.4T.2b. +0.2b.4V. +3.2b.51. +0.52.2b. +a.5a.2b. +2.5b.2b. +5.5d.2b. +5.5B.2b. +5.5C.2b. +5.5F.2b. +0.2b.5H. +0.5I.2b. +f.2b.5K. +7.5M.2b. +5.5O.2b. +0.2b.5R. +0.2b.5U. +0.2b.5W. +1.5Y.2b. +5.5Z.2b. +0.2b.5/. +0.60.2b. +2.62.2b. +2.66.2b. +a.6b.2b. +c.6e.2b. +0.2b.6F. +0.6M.2b. +0.6+.2b. +3.2b.71. +5.79.2b. +0.2b.7D. +0.2p.2b. +0.2p.2b. +1.2d.2c. +1.51.2c. +5.5I.2c. +6.5K.2c. +7.5M.2c. +1.5Y.2c. +1.5+.2c. +4.60.2c. +5.62.2c. +8.6F.2c. +c.6H.2c. +8.6M.2c. +1.7R.2c. +1.2d.2d. +1.2d.2f. +1.2d.2g. +1.2d.2h. +1.2d.2i. +1.2d.2j. +1.2d.2o. +1.2d.2L. +1.2d.2P. +1.2d.2R. +1.2d.2Y. +1.2d.2Z. +1.2d.2/. +1.2d.31. +1.2d.32. +1.2d.33. +1.2d.34. +1.2d.3a. +1.2d.3c. +1.2d.3e. +1.2d.3f. +1.2d.3g. +1.2d.3j. +1.2d.3k. +1.2d.3l. +1.2d.3n. +1.2d.3o. +1.2d.3q. +1.2d.3r. +1.2d.3s. +1.2d.3t. +1.2d.3u. +1.2d.3w. +1.2d.3x. +1.2d.3D. +1.2d.3E. +1.2d.3H. +1.2d.3I. +1.2d.3J. +1.2d.3K. +1.2d.3L. +1.2d.3N. +1.2d.3O. +1.2d.3P. +1.2d.3R. +1.2d.3S. +1.2d.3T. +1.2d.3U. +1.2d.3V. +1.2d.3W. +1.2d.3X. +1.2d.3Y. +1.2d.3Z. +1.2d.3+. +1.2d.3/. +1.2d.41. +1.2d.42. +1.2d.43. +1.2d.44. +1.2d.45. +1.2d.48. +1.2d.49. +1.2d.4a. +1.2d.4b. +1.2d.4c. +1.2d.4e. +1.2d.4f. +1.2d.4g. +1.2d.4h. +1.2d.4i. +1.2d.4j. +1.2d.4k. +1.2d.4l. +1.2d.4m. +1.2d.4n. +1.2d.4o. +1.2d.4p. +1.2d.4q. +1.2d.4r. +1.2d.4s. +1.2d.4t. +1.2d.4u. +1.2d.4v. +1.2d.4w. +1.2d.4x. +1.2d.4y. +1.2d.4z. +1.2d.4A. +1.2d.4B. +1.2d.4C. +1.2d.4D. +1.2d.4E. +1.2d.4F. +1.2d.4G. +1.2d.4H. +1.2d.4I. +1.2d.4J. +1.2d.4K. +1.2d.4L. +1.2d.4M. +1.2d.4N. +1.2d.4P. +1.2d.4R. +1.2d.4S. +1.2d.4T. +1.2d.4U. +1.2d.4W. +1.2d.4Y. +1.2d.4Z. +1.2d.4+. +1.2d.4/. +1.2d.50. +1.51.2d. +1.2d.53. +1.2d.54. +1.2d.55. +1.2d.56. +1.2d.57. +1.2d.58. +1.2d.59. +1.2d.5c. +1.2d.5e. +1.2d.5f. +1.2d.5g. +1.2d.5h. +1.2d.5i. +1.2d.5j. +1.2d.5k. +1.2d.5l. +1.2d.5m. +1.2d.5n. +1.2d.5o. +1.2d.5p. +1.2d.5q. +1.2d.5r. +1.2d.5s. +1.2d.5t. +1.2d.5u. +1.2d.5v. +1.2d.5w. +1.2d.5y. +1.2d.5z. +1.2d.5A. +1.2d.5D. +1.2d.5E. +1.2d.5G. +5.5I.2d. +6.5K.2d. +1.2d.5L. +7.5M.2d. +1.2d.5N. +1.2d.5P. +1.2d.5Q. +1.2d.5S. +1.2d.5T. +1.2d.5V. +1.2d.5X. +1.2d.5Y. +1.5+.2d. +4.60.2d. +1.2d.61. +5.62.2d. +1.2d.64. +1.2d.65. +1.2d.67. +1.2d.68. +1.2d.69. +1.2d.6a. +1.2d.6c. +1.2d.6d. +1.2d.6e. +1.2d.6f. +1.2d.6g. +1.2d.6h. +1.2d.6i. +1.2d.6j. +1.2d.6k. +1.2d.6l. +1.2d.6m. +1.2d.6n. +1.2d.6o. +1.2d.6p. +1.2d.6q. +1.2d.6r. +1.2d.6s. +1.2d.6t. +1.2d.6u. +1.2d.6v. +1.2d.6w. +1.2d.6x. +1.2d.6y. +1.2d.6z. +1.2d.6A. +1.2d.6B. +1.2d.6C. +1.2d.6D. +1.2d.6E. +8.6F.2d. +1.2d.6G. +1.2d.6H. +1.2d.6I. +1.2d.6J. +1.2d.6K. +1.2d.6L. +3.6M.2d. +1.2d.6N. +1.2d.6O. +1.2d.6P. +1.2d.6Q. +1.2d.6R. +1.2d.6S. +1.2d.6T. +1.2d.6U. +1.2d.6V. +1.2d.6W. +1.2d.6X. +1.2d.6Y. +1.2d.6Z. +1.2d.6/. +1.2d.70. +1.2d.72. +1.2d.73. +1.2d.74. +1.2d.75. +1.2d.76. +1.2d.77. +1.2d.78. +1.2d.7a. +1.2d.7b. +1.2d.7c. +1.2d.7d. +1.2d.7e. +1.2d.7f. +1.2d.7g. +1.2d.7h. +1.2d.7i. +1.2d.7j. +1.2d.7k. +1.2d.7l. +1.2d.7m. +1.2d.7o. +1.2d.7p. +1.2d.7r. +1.2d.7s. +1.2d.7t. +1.2d.7u. +1.2d.7v. +1.2d.7w. +1.2d.7x. +1.2d.7y. +1.2d.7z. +1.2d.7A. +1.2d.7B. +1.2d.7C. +1.2d.7E. +1.2d.7F. +1.2d.7G. +1.2d.7H. +1.2d.7I. +1.2d.7J. +1.2d.7K. +1.2d.7L. +1.2d.7M. +1.2d.7N. +1.2d.7O. +1.2d.7P. +1.2d.7Q. +1.2d.7S. +1.2d.7T. +1.2d.7V. +1.2d.7W. +1.2d.7X. +1.2d.7Y. +1.2d.7+. +1.2d.7/. +1.2d.80. +1.2d.81. +1.2d.82. +1.2d.83. +1.2d.84. +1.2d.85. +1.2d.87. +1.2d.88. +1.2d.89. +1.2d.8a. +1.2d.8b. +1.2d.8d. +1.2d.8e. +1.2d.8f. +1.2d.8g. +1.2d.8h. +1.2d.8i. +1.2d.8j. +1.2d.8k. +1.2d.8l. +1.2d.8m. +1.2d.8n. +1.2d.8o. +1.2d.8p. +1.2d.8q. +1.2d.8r. +1.2d.8s. +1.2d.8t. +1.2d.8u. +1.2d.8v. +1.2d.8w. +1.2d.8x. +1.2d.8y. +1.2d.8z. +1.2d.8A. +1.2d.8B. +1.2d.8C. +1.2d.8D. +1.2d.8E. +1.2d.8F. +1.2d.8G. +1.2d.8H. +1.2d.8I. +1.2d.8J. +1.2d.3b. +1.2d.46. +1.2d.4X. +1.2d.5x. +1.2d.5J. +1.2d.7n. +1.2d.7q. +4.2e.2p. +4.2e.2q. +4.2e.2r. +7.2e.2s. +7.2e.2t. +d.2e.2u. +d.2e.2v. +d.2e.2w. +d.2e.2x. +4.2e.2y. +d.2e.2z. +d.2e.2A. +d.2e.2B. +d.2e.2C. +d.2e.2D. +d.2e.2E. +d.2e.2F. +d.2e.2G. +4.2e.2H. +d.2e.2I. +4.2e.2J. +d.2e.2K. +c.2M.2e. +d.2e.2N. +d.2e.2O. +d.2e.2Q. +7.2e.2S. +j.2e.2T. +1.2U.2e. +d.2e.2V. +1.2W.2e. +4.2e.2X. +4.2e.2+. +d.2e.30. +d.2e.35. +d.2e.36. +d.2e.37. +7.2e.38. +4.2e.39. +4.2e.3a. +d.2e.3d. +4.2e.3h. +d.2e.3i. +d.2e.3l. +d.2e.3m. +d.2e.3p. +4.2e.3v. +d.2e.3w. +d.2e.3x. +4.2e.3y. +d.2e.3z. +d.2e.3A. +4.2e.3B. +4.2e.3C. +d.2e.3D. +d.2e.3E. +4.2e.3F. +d.2e.3G. +d.2e.3H. +4.2e.3I. +4.2e.3J. +4.2e.3K. +4.2e.3M. +d.2e.3O. +d.2e.3P. +d.2e.3Q. +d.2e.3R. +4.2e.3S. +d.2e.3U. +4.2e.3X. +d.2e.3Y. +4.2e.3Z. +d.2e.40. +d.2e.41. +d.2e.42. +d.2e.43. +d.2e.44. +d.2e.45. +d.2e.47. +d.2e.48. +d.2e.49. +d.2e.4b. +d.2e.4c. +d.2e.4d. +d.2e.4i. +4.2e.4j. +d.2e.4l. +d.2e.4n. +d.2e.4q. +d.2e.4r. +4.2e.4t. +d.2e.4u. +4.2e.4B. +d.2e.4C. +d.2e.4K. +4.2e.4M. +4.2e.4N. +4.2e.4O. +d.2e.4P. +d.2e.4Q. +d.2e.4T. +d.2e.4V. +d.2e.4+. +4.2e.4/. +d.2e.51. +d.2e.52. +d.2e.5a. +d.2e.5b. +d.2e.5d. +4.2e.5B. +4.2e.5C. +4.2e.5F. +4.2e.5G. +4.2e.5H. +7.2e.5I. +d.2e.5K. +7.2e.5M. +d.2e.5O. +4.2e.5R. +d.2e.5T. +4.2e.5U. +d.2e.5W. +d.2e.5X. +1.5Y.2e. +d.2e.5Z. +4.2e.5/. +7.2e.60. +7.2e.62. +d.2e.66. +d.2e.6b. +c.6e.2e. +d.2e.6F. +4.2e.6+. +4.2e.71. +d.2e.7B. +d.2e.7D. +4.2e.89. +4.2e.2p. +4.2e.2p. +1.2l.2f. +4.2n.2f. +1.51.2f. +1.5v.2f. +6.5I.2f. +6.5K.2f. +7.5M.2f. +1.5Y.2f. +1.5+.2f. +4.60.2f. +5.62.2f. +1.6q.2f. +1.6t.2f. +8.6F.2f. +c.6H.2f. +8.6M.2f. +1.7B.2f. +1.86.2f. +1.8d.2f. +1.2l.2g. +4.2n.2g. +1.4s.2g. +1.51.2g. +1.5v.2g. +5.5I.2g. +6.5K.2g. +7.5M.2g. +1.5Y.2g. +1.5+.2g. +4.60.2g. +5.62.2g. +1.6q.2g. +1.6t.2g. +3.6F.2g. +c.6H.2g. +8.6M.2g. +1.7B.2g. +1.86.2g. +1.8d.2g. +1.2l.2h. +4.2n.2h. +1.4s.2h. +1.51.2h. +1.5v.2h. +6.5I.2h. +6.5K.2h. +7.5M.2h. +1.5Y.2h. +1.5+.2h. +4.60.2h. +5.62.2h. +1.6q.2h. +1.6t.2h. +3.6F.2h. +c.6H.2h. +8.6M.2h. +1.7B.2h. +1.7R.2h. +1.86.2h. +1.8d.2h. +1.2l.2i. +4.2n.2i. +1.51.2i. +1.5v.2i. +6.5I.2i. +6.5K.2i. +7.5M.2i. +1.5Y.2i. +1.5+.2i. +4.60.2i. +5.62.2i. +1.6q.2i. +1.6t.2i. +8.6F.2i. +c.6H.2i. +8.6M.2i. +1.7B.2i. +1.86.2i. +1.8d.2i. +1.2l.2j. +4.2n.2j. +1.51.2j. +1.5v.2j. +5.5I.2j. +6.5K.2j. +7.5M.2j. +1.5Y.2j. +1.5+.2j. +4.60.2j. +5.62.2j. +1.6q.2j. +1.6t.2j. +8.6F.2j. +c.6H.2j. +8.6M.2j. +1.7B.2j. +1.86.2j. +1.8d.2j. +0.2l.2l. +6.2m.2l. +4.2n.2l. +1.2l.2o. +0.2p.2l. +2.2q.2l. +2.2r.2l. +7.2s.2l. +7.2t.2l. +2.2u.2l. +0.2v.2l. +0.2l.2w. +e.2x.2l. +9.2y.2l. +0.2z.2l. +d.2A.2l. +9.2B.2l. +0.2C.2l. +0.2D.2l. +0.2E.2l. +0.2F.2l. +0.2G.2l. +9.2H.2l. +0.2I.2l. +d.2J.2l. +0.2K.2l. +1.2l.2L. +c.2M.2l. +9.2N.2l. +2.2O.2l. +1.2l.2P. +9.2Q.2l. +1.2l.2R. +7.2S.2l. +0.2T.2l. +1.2U.2l. +9.2V.2l. +1.2W.2l. +0.2X.2l. +1.2l.2Y. +1.2l.2Z. +d.2+.2l. +1.2l.2/. +d.30.2l. +1.2l.31. +1.2l.33. +1.2l.34. +0.2l.35. +b.36.2l. +0.2l.37. +0.38.2l. +5.39.2l. +1.2l.3a. +1.2l.3c. +5.3d.2l. +1.2l.3e. +1.2l.3f. +1.2l.3g. +0.3h.2l. +5.3i.2l. +1.2l.3j. +1.2l.3k. +1.2l.3l. +0.2l.3m. +1.2l.3n. +1.2l.3o. +5.3p.2l. +1.2l.3q. +1.2l.3r. +1.2l.3s. +1.2l.3t. +1.2l.3u. +a.3v.2l. +1.2l.3w. +1.2l.3x. +a.3y.2l. +2.3z.2l. +5.3A.2l. +2.3B.2l. +5.3C.2l. +1.2l.3D. +1.2l.3E. +a.3F.2l. +5.3G.2l. +1.2l.3H. +1.2l.3I. +1.2l.3J. +1.2l.3K. +1.2l.3L. +0.2l.3M. +1.2l.3N. +1.2l.3O. +1.2l.3P. +0.2l.3Q. +1.2l.3R. +1.2l.3S. +1.2l.3T. +1.2l.3U. +1.2l.3V. +1.2l.3W. +1.2l.3X. +1.2l.3Y. +1.2l.3Z. +1.2l.3+. +1.2l.3/. +5.40.2l. +1.2l.41. +1.2l.42. +1.2l.43. +1.2l.44. +1.2l.45. +0.2l.47. +1.2l.48. +1.2l.49. +1.2l.4a. +1.2l.4b. +1.2l.4c. +5.4d.2l. +1.2l.4e. +1.2l.4f. +1.2l.4g. +1.2l.4h. +1.2l.4i. +1.2l.4j. +1.2l.4k. +1.2l.4l. +1.2l.4m. +1.2l.4n. +1.2l.4o. +1.2l.4p. +1.2l.4q. +1.2l.4r. +1.2l.4s. +1.2l.4t. +1.2l.4u. +1.2l.4v. +1.2l.4w. +1.2l.4x. +1.2l.4y. +1.2l.4z. +1.2l.4A. +1.2l.4B. +1.2l.4C. +1.2l.4D. +1.2l.4E. +1.2l.4F. +1.2l.4G. +1.2l.4H. +1.2l.4I. +1.2l.4J. +1.2l.4K. +1.2l.4L. +1.2l.4M. +1.2l.4N. +0.2l.4O. +1.2l.4P. +0.4Q.2l. +1.2l.4R. +1.2l.4S. +5.4T.2l. +1.2l.4U. +d.2l.4V. +1.2l.4W. +1.2l.4Y. +1.2l.4Z. +1.2l.4+. +1.2l.4/. +1.2l.50. +5.51.2l. +0.52.2l. +1.2l.53. +1.2l.54. +1.2l.55. +1.2l.56. +1.2l.58. +1.2l.59. +a.5a.2l. +2.5b.2l. +1.2l.5c. +5.5d.2l. +1.2l.5e. +1.2l.5f. +1.2l.5g. +1.2l.5h. +1.2l.5i. +1.2l.5j. +1.2l.5k. +1.2l.5l. +1.2l.5m. +1.2l.5n. +1.2l.5o. +1.2l.5p. +1.2l.5q. +1.2l.5r. +1.2l.5s. +1.2l.5t. +1.2l.5u. +1.2l.5w. +1.2l.5y. +1.2l.5z. +1.2l.5A. +5.5B.2l. +5.5C.2l. +1.2l.5D. +1.2l.5E. +5.5F.2l. +1.2l.5G. +0.5H.2l. +0.5I.2l. +f.2l.5K. +1.2l.5L. +7.5M.2l. +1.2l.5N. +5.5O.2l. +1.2l.5P. +1.2l.5Q. +5.5R.2l. +1.2l.5S. +1.2l.5T. +0.2l.5U. +1.2l.5V. +0.5W.2l. +1.2l.5X. +1.5Y.2l. +5.5Z.2l. +5.5/.2l. +0.60.2l. +1.2l.61. +2.62.2l. +1.2l.64. +1.2l.65. +2.66.2l. +1.2l.67. +1.2l.68. +1.2l.69. +1.2l.6a. +a.6b.2l. +1.2l.6c. +1.2l.6d. +c.6e.2l. +1.2l.6f. +1.2l.6g. +1.2l.6h. +1.2l.6i. +1.2l.6j. +1.2l.6l. +1.2l.6m. +1.2l.6n. +1.2l.6o. +1.2l.6p. +1.2l.6q. +1.2l.6r. +1.2l.6s. +1.2l.6t. +1.2l.6u. +1.2l.6v. +1.2l.6w. +1.2l.6x. +1.2l.6z. +1.2l.6A. +1.2l.6B. +1.2l.6C. +1.2l.6D. +0.2l.6F. +1.2l.6G. +1.2l.6J. +1.2l.6K. +1.2l.6L. +0.6M.2l. +1.2l.6N. +1.2l.6O. +1.2l.6P. +1.2l.6Q. +1.2l.6R. +1.2l.6S. +1.2l.6T. +1.2l.6U. +1.2l.6V. +1.2l.6W. +1.2l.6X. +1.2l.6Y. +1.2l.6Z. +0.6+.2l. +1.2l.6/. +1.2l.70. +0.71.2l. +1.2l.72. +1.2l.73. +1.2l.74. +1.2l.75. +1.2l.76. +1.2l.77. +1.2l.78. +5.79.2l. +1.2l.7a. +1.2l.7b. +1.2l.7c. +1.2l.7d. +1.2l.7e. +1.2l.7f. +1.2l.7g. +1.2l.7h. +1.2l.7i. +1.2l.7j. +1.2l.7k. +1.2l.7l. +1.2l.7o. +1.2l.7p. +1.2l.7r. +1.2l.7s. +1.2l.7t. +1.2l.7u. +1.2l.7v. +1.2l.7w. +1.2l.7x. +1.2l.7z. +1.2l.7B. +1.2l.7C. +0.2l.7D. +1.2l.7E. +1.2l.7F. +1.2l.7G. +1.2l.7H. +1.2l.7I. +1.2l.7J. +1.2l.7K. +1.2l.7L. +1.2l.7M. +1.2l.7N. +1.2l.7O. +1.2l.7P. +1.2l.7Q. +1.2l.7R. +1.2l.7S. +1.2l.7T. +1.2l.7U. +1.2l.7V. +1.2l.7W. +1.2l.7X. +1.2l.7Y. +1.2l.7Z. +1.2l.7+. +1.2l.7/. +1.2l.80. +1.2l.81. +1.2l.82. +1.2l.83. +1.2l.84. +1.2l.85. +1.2l.87. +1.2l.88. +1.2l.89. +1.2l.8a. +1.2l.8b. +1.2l.8d. +1.2l.8e. +1.2l.8f. +1.2l.8g. +1.2l.8h. +1.2l.8i. +1.2l.8j. +1.2l.8k. +1.2l.8l. +1.2l.8m. +1.2l.8n. +1.2l.8o. +1.2l.8p. +1.2l.8q. +1.2l.8r. +1.2l.8s. +1.2l.8t. +1.2l.8u. +1.2l.8v. +1.2l.8w. +1.2l.8x. +1.2l.8y. +1.2l.8z. +1.2l.8A. +1.2l.8B. +1.2l.8C. +1.2l.8D. +1.2l.8E. +1.2l.8F. +1.2l.8G. +1.2l.8H. +1.2l.8I. +1.2l.8J. +0.2p.2l. +0.2p.2l. +1.2l.3b. +1.2l.46. +1.2l.4X. +1.2l.5x. +1.2l.7n. +1.2l.7q. +6.2m.2m. +4.2m.2n. +6.2m.2p. +6.2m.2q. +6.2m.2r. +7.2s.2m. +7.2t.2m. +6.2m.2u. +6.2m.2v. +6.2m.2w. +6.2m.2x. +6.2m.2y. +6.2m.2z. +6.2m.2A. +6.2m.2B. +6.2m.2C. +6.2m.2D. +6.2m.2E. +6.2m.2F. +6.2m.2G. +6.2m.2H. +6.2m.2I. +6.2m.2J. +6.2m.2K. +c.2M.2m. +6.2m.2N. +6.2m.2O. +6.2m.2Q. +7.2S.2m. +6.2m.2T. +1.2U.2m. +6.2m.2V. +1.2W.2m. +6.2m.2X. +6.2m.2+. +6.2m.30. +6.2m.35. +6.2m.36. +6.2m.37. +6.2m.38. +6.2m.39. +6.2m.3h. +6.2m.3i. +6.2m.3m. +6.2m.3p. +a.3v.2m. +a.3y.2m. +6.2m.3z. +6.2m.3A. +6.2m.3B. +6.2m.3C. +a.3F.2m. +6.2m.3G. +6.2m.3M. +6.2m.3Q. +6.2m.40. +6.2m.47. +6.2m.4d. +6.2m.4O. +6.2m.4Q. +7.4T.2m. +6.2m.4V. +6.2m.51. +6.2m.52. +a.2m.5a. +6.2m.5b. +6.2m.5d. +6.2m.5B. +6.2m.5C. +6.2m.5F. +6.2m.5H. +6.2m.5I. +f.2m.5K. +7.5M.2m. +6.2m.5O. +6.2m.5R. +6.2m.5U. +6.2m.5W. +1.5Y.2m. +6.2m.5Z. +6.2m.5/. +6.2m.60. +6.2m.62. +6.2m.66. +a.6b.2m. +c.6e.2m. +6.2m.6F. +6.2m.6M. +6.2m.6+. +6.2m.71. +6.2m.79. +6.2m.7D. +6.2m.2p. +6.2m.2p. +j.2n.2n. +4.2n.2p. +4.2n.2q. +4.2n.2r. +7.2s.2n. +7.2t.2n. +3.2u.2n. +4.2n.2v. +4.2n.2w. +4.2x.2n. +4.2n.2y. +4.2n.2z. +4.2n.2A. +4.2n.2B. +4.2n.2C. +4.2n.2D. +4.2n.2E. +4.2n.2F. +4.2n.2G. +4.2H.2n. +4.2n.2I. +4.2n.2J. +4.2n.2K. +4.2n.2L. +c.2M.2n. +4.2n.2N. +d.2O.2n. +4.2n.2P. +4.2n.2Q. +4.2n.2R. +7.2S.2n. +4.2n.2T. +1.2U.2n. +4.2V.2n. +1.2W.2n. +4.2n.2X. +4.2n.2Y. +4.2n.2Z. +4.2n.2+. +4.2n.2/. +4.2n.30. +4.2n.31. +4.2n.32. +4.2n.33. +4.2n.34. +4.2n.35. +4.2n.36. +4.2n.37. +j.2n.38. +4.2n.39. +4.2n.3a. +4.2n.3c. +4.2n.3d. +4.2n.3f. +4.2n.3g. +4.2n.3h. +4.2n.3i. +4.2n.3k. +4.2n.3l. +4.2n.3m. +4.2n.3n. +4.2n.3o. +4.2n.3p. +4.2n.3q. +4.2n.3r. +4.2n.3s. +4.2n.3t. +4.2n.3u. +4.3v.2n. +4.2n.3w. +4.2n.3x. +4.3y.2n. +5.3z.2n. +4.2n.3A. +5.3B.2n. +4.2n.3C. +4.2n.3D. +4.2n.3E. +4.3F.2n. +4.2n.3G. +4.2n.3H. +4.2n.3I. +4.2n.3J. +4.2n.3K. +4.2n.3L. +4.2n.3M. +4.2n.3N. +4.2n.3O. +4.2n.3P. +4.2n.3Q. +4.2n.3R. +4.2n.3S. +4.2n.3U. +4.2n.3V. +4.2n.3W. +4.2n.3X. +4.2n.3Y. +4.2n.3Z. +4.40.2n. +4.2n.41. +4.2n.42. +4.2n.43. +4.2n.44. +4.2n.45. +4.2n.47. +4.2n.48. +4.2n.49. +4.2n.4b. +4.2n.4c. +4.2n.4d. +4.2n.4e. +4.2n.4f. +4.2n.4g. +4.2n.4h. +4.2n.4i. +4.2n.4j. +4.2n.4k. +4.2n.4l. +4.2n.4m. +4.2n.4n. +4.2n.4o. +4.2n.4p. +4.2n.4q. +4.2n.4r. +4.2n.4s. +4.2n.4t. +4.2n.4u. +4.2n.4v. +4.2n.4w. +4.2n.4x. +4.2n.4y. +4.2n.4z. +4.2n.4A. +4.2n.4B. +4.2n.4C. +4.2n.4D. +4.2n.4E. +4.2n.4G. +4.2n.4H. +4.2n.4J. +4.2n.4K. +4.2n.4L. +4.2n.4M. +4.2n.4N. +4.2n.4O. +4.2n.4P. +4.2n.4Q. +4.2n.4S. +4.2n.4T. +4.2n.4U. +4.2n.4V. +4.2n.4W. +4.2n.4Y. +4.2n.4+. +4.2n.4/. +4.2n.51. +4.2n.52. +4.2n.53. +4.2n.54. +4.2n.55. +4.2n.56. +4.2n.58. +4.2n.59. +4.5a.2n. +3.5b.2n. +4.2n.5c. +4.5d.2n. +4.2n.5e. +4.2n.5f. +4.2n.5g. +4.2n.5h. +4.2n.5i. +4.2n.5j. +4.2n.5k. +4.2n.5l. +4.2n.5m. +4.2n.5n. +4.2n.5o. +4.2n.5p. +4.2n.5q. +4.2n.5r. +4.2n.5t. +4.2n.5u. +4.2n.5v. +4.2n.5B. +4.2n.5C. +4.2n.5F. +4.2n.5G. +4.2n.5H. +4.2n.5I. +3.2n.5K. +4.2n.5L. +4.2n.5M. +4.2n.5N. +4.2n.5O. +4.2n.5Q. +4.2n.5R. +4.2n.5S. +4.2n.5T. +4.2n.5U. +4.2n.5V. +4.2n.5W. +4.2n.5X. +4.2n.5Y. +4.2n.5Z. +4.2n.5/. +4.2n.60. +4.2n.61. +5.62.2n. +4.2n.64. +4.2n.65. +5.66.2n. +4.2n.67. +4.2n.68. +4.2n.69. +4.2n.6a. +4.6b.2n. +4.2n.6c. +4.2n.6d. +4.2n.6e. +4.2n.6f. +4.2n.6g. +4.2n.6h. +4.2n.6i. +4.2n.6j. +4.2n.6k. +4.2n.6l. +4.2n.6m. +4.2n.6n. +4.2n.6o. +4.2n.6p. +4.2n.6q. +4.2n.6r. +4.2n.6t. +4.2n.6u. +4.2n.6v. +4.2n.6y. +4.2n.6z. +4.2n.6A. +4.2n.6B. +4.2n.6C. +4.2n.6D. +4.2n.6E. +4.2n.6F. +4.2n.6G. +4.2n.6H. +4.2n.6I. +4.2n.6J. +4.2n.6K. +4.2n.6L. +4.2n.6M. +4.2n.6N. +4.2n.6O. +4.2n.6P. +4.2n.6Q. +4.2n.6R. +4.2n.6S. +4.2n.6T. +4.2n.6U. +4.2n.6V. +4.2n.6W. +4.2n.6Y. +4.2n.6+. +4.2n.71. +4.2n.72. +4.2n.73. +4.2n.75. +4.2n.78. +4.2n.7b. +4.2n.7c. +4.2n.7d. +4.2n.7e. +4.2n.7f. +4.2n.7g. +4.2n.7h. +4.2n.7i. +4.2n.7j. +4.2n.7k. +4.2n.7l. +4.2n.7o. +4.2n.7p. +4.2n.7t. +4.2n.7u. +4.2n.7v. +4.2n.7w. +4.2n.7x. +4.2n.7y. +4.2n.7A. +4.2n.7B. +4.2n.7C. +4.2n.7D. +4.2n.7E. +4.2n.7F. +4.2n.7G. +4.2n.7H. +4.2n.7I. +4.2n.7J. +4.2n.7Q. +4.2n.7R. +4.2n.7S. +4.2n.7T. +4.2n.7V. +4.2n.7W. +4.2n.7X. +4.2n.7Y. +4.2n.7Z. +4.2n.83. +4.2n.89. +4.2n.8a. +4.2n.8b. +4.2n.8c. +4.2n.8d. +4.2n.8e. +4.2n.8f. +4.2n.8g. +4.2n.8h. +4.2n.8i. +4.2n.8j. +4.2n.8k. +4.2n.8l. +4.2n.8m. +4.2n.8n. +4.2n.8o. +4.2n.8p. +4.2n.8q. +4.2n.8r. +4.2n.8s. +4.2n.8t. +4.2n.8y. +4.2n.8z. +4.2n.8A. +4.2n.8B. +4.2n.8D. +4.2n.8E. +4.2n.8H. +4.2n.8I. +4.2n.8J. +4.2n.8K. +4.2n.8L. +4.2n.8M. +4.2n.8N. +4.2n.8O. +4.2n.8P. +4.2n.2p. +4.2n.2p. +4.2n.3b. +1.51.2o. +1.5I.2o. +1.5M.2o. +1.5Y.2o. +1.5+.2o. +1.60.2o. +1.62.2o. +1.6q.2o. +1.6t.2o. +1.6F.2o. +1.6H.2o. +1.6M.2o. +1.7B.2o. +1.7R.2o. +1.86.2o. +1.8d.2o. +0.2p.2p. +2.2q.2p. +2.2r.2p. +7.2s.2p. +7.2t.2p. +2.2u.2p. +0.2v.2p. +d.2p.2w. +e.2x.2p. +9.2y.2p. +0.2p.2z. +0.2A.2p. +9.2B.2p. +0.2p.2C. +0.2D.2p. +0.2E.2p. +0.2F.2p. +0.2p.2G. +9.2H.2p. +0.2I.2p. +0.2J.2p. +0.2p.2K. +c.2M.2p. +9.2N.2p. +2.2O.2p. +9.2Q.2p. +7.2S.2p. +0.2p.2T. +1.2U.2p. +9.2V.2p. +1.2W.2p. +0.2p.2X. +d.2+.2p. +d.30.2p. +d.2p.35. +3.36.2p. +0.2p.37. +0.2p.38. +5.39.2p. +5.3d.2p. +d.2p.3h. +5.3i.2p. +0.2p.3m. +0.2p.3p. +a.3v.2p. +a.3y.2p. +2.3z.2p. +5.3A.2p. +2.3B.2p. +0.2p.3C. +a.3F.2p. +5.3G.2p. +0.2p.3M. +0.2p.3Q. +5.40.2p. +0.2p.47. +0.2p.4d. +0.2p.4O. +0.2p.4Q. +5.4T.2p. +0.2p.4V. +d.2p.51. +0.2p.52. +a.5a.2p. +2.5b.2p. +5.5d.2p. +0.2p.5B. +0.2p.5C. +5.5F.2p. +0.2p.5H. +0.2p.5I. +f.2p.5K. +7.5M.2p. +d.2p.5O. +0.2p.5R. +0.2p.5U. +0.2p.5W. +1.5Y.2p. +0.2p.5Z. +0.2p.5/. +0.2p.60. +2.62.2p. +2.66.2p. +a.6b.2p. +c.6e.2p. +0.2p.6F. +0.6M.2p. +0.2p.6+. +0.2p.71. +5.79.2p. +0.2p.7D. +0.2p.2p. +0.2p.2p. +2.2q.2q. +2.2r.2q. +7.2s.2q. +7.2t.2q. +2.2u.2q. +2.2q.2v. +2.2q.2w. +e.2x.2q. +2.2q.2y. +2.2q.2z. +2.2q.2A. +2.2q.2B. +2.2q.2C. +2.2q.2D. +2.2q.2E. +2.2q.2F. +2.2q.2G. +9.2H.2q. +2.2q.2I. +2.2q.2J. +2.2q.2K. +c.2M.2q. +2.2q.2N. +2.2O.2q. +2.2q.2Q. +7.2S.2q. +2.2q.2T. +1.2U.2q. +9.2V.2q. +1.2W.2q. +2.2q.2X. +2.2q.2+. +2.2q.30. +2.2q.35. +2.2q.36. +2.2q.37. +2.2q.38. +2.2q.39. +2.2q.3d. +2.2q.3h. +2.2q.3i. +2.2q.3m. +2.2q.3p. +a.3v.2q. +a.3y.2q. +2.3z.2q. +2.2q.3A. +2.3B.2q. +2.2q.3C. +a.3F.2q. +5.3G.2q. +2.2q.3M. +2.2q.3Q. +5.40.2q. +2.2q.47. +2.2q.4d. +2.2q.4O. +2.2q.4Q. +7.4T.2q. +2.2q.4V. +2.2q.51. +2.2q.52. +a.5a.2q. +2.5b.2q. +5.5d.2q. +2.2q.5B. +2.2q.5C. +2.2q.5F. +2.2q.5H. +2.2q.5I. +2.2q.5K. +7.5M.2q. +2.2q.5O. +2.2q.5R. +2.2q.5U. +2.2q.5W. +1.5Y.2q. +2.2q.5Z. +2.2q.5/. +2.2q.60. +2.62.2q. +2.66.2q. +a.6b.2q. +c.6e.2q. +2.2q.6F. +2.2q.6M. +2.2q.6+. +2.2q.71. +2.2q.79. +2.2q.7D. +2.2q.2p. +2.2q.2p. +2.2r.2r. +7.2s.2r. +7.2t.2r. +2.2u.2r. +2.2r.2v. +2.2r.2w. +e.2x.2r. +2.2r.2y. +2.2r.2z. +2.2r.2A. +2.2r.2B. +2.2r.2C. +2.2r.2D. +2.2r.2E. +2.2r.2F. +2.2r.2G. +9.2H.2r. +2.2r.2I. +2.2r.2J. +2.2r.2K. +c.2M.2r. +2.2r.2N. +2.2O.2r. +2.2r.2Q. +7.2S.2r. +2.2r.2T. +1.2U.2r. +9.2V.2r. +1.2W.2r. +2.2r.2X. +2.2r.2+. +2.2r.30. +2.2r.35. +2.2r.36. +2.2r.37. +2.2r.38. +2.2r.39. +2.2r.3d. +2.2r.3h. +2.2r.3i. +2.2r.3m. +2.2r.3p. +a.3v.2r. +a.3y.2r. +2.3z.2r. +2.2r.3A. +2.3B.2r. +2.2r.3C. +a.3F.2r. +5.3G.2r. +2.2r.3M. +2.2r.3Q. +5.40.2r. +2.2r.47. +2.2r.4d. +2.2r.4O. +2.2r.4Q. +7.4T.2r. +2.2r.4V. +2.2r.51. +2.2r.52. +a.5a.2r. +2.5b.2r. +5.5d.2r. +2.2r.5B. +2.2r.5C. +2.2r.5F. +2.2r.5H. +2.2r.5I. +2.2r.5K. +7.5M.2r. +2.2r.5O. +2.2r.5R. +2.2r.5U. +2.2r.5W. +1.5Y.2r. +2.2r.5Z. +2.2r.5/. +2.2r.60. +2.62.2r. +2.66.2r. +a.6b.2r. +c.6e.2r. +2.2r.6F. +2.2r.6M. +2.2r.6+. +2.2r.71. +2.2r.79. +2.2r.7D. +2.2r.2p. +2.2r.2p. +7.2s.2s. +7.2t.2s. +7.2s.2u. +7.2s.2v. +7.2s.2w. +7.2s.2x. +7.2s.2y. +7.2s.2z. +7.2s.2A. +7.2s.2B. +7.2s.2C. +7.2s.2D. +7.2s.2E. +7.2s.2F. +7.2s.2G. +7.2s.2H. +7.2s.2I. +7.2s.2J. +7.2s.2K. +c.2M.2s. +7.2s.2N. +7.2s.2O. +7.2s.2Q. +7.2S.2s. +7.2s.2T. +1.2U.2s. +7.2s.2V. +1.2W.2s. +7.2s.2X. +7.2s.2+. +7.2s.30. +7.2s.35. +7.2s.36. +7.2s.37. +7.2s.38. +7.2s.39. +7.2s.3d. +7.2s.3h. +7.2s.3i. +7.2s.3m. +7.2s.3p. +7.2s.3y. +7.2s.3z. +7.2s.3A. +7.2s.3B. +7.2s.3C. +7.2s.3F. +7.2s.3G. +7.2s.3M. +7.2s.3Q. +7.2s.40. +7.2s.47. +7.2s.4d. +7.2s.4O. +7.2s.4Q. +7.4T.2s. +7.2s.4V. +7.2s.51. +7.2s.52. +7.2s.5a. +7.2s.5b. +7.2s.5d. +7.2s.5B. +7.2s.5C. +7.2s.5F. +7.2s.5H. +7.2s.5I. +7.2s.5K. +7.5M.2s. +7.2s.5O. +7.2s.5R. +7.2s.5U. +7.2s.5W. +1.5Y.2s. +7.2s.5Z. +7.2s.5/. +7.2s.60. +7.2s.62. +7.2s.66. +7.2s.6b. +c.6e.2s. +7.2s.6F. +7.2s.6M. +7.2s.6+. +7.2s.71. +7.2s.79. +7.2s.7D. +7.2s.2p. +7.2s.2p. +7.2t.2t. +7.2t.2u. +7.2t.2v. +7.2t.2w. +7.2t.2x. +7.2t.2y. +7.2t.2z. +7.2t.2A. +7.2t.2B. +7.2t.2C. +7.2t.2D. +7.2t.2E. +7.2t.2F. +7.2t.2G. +7.2t.2H. +7.2t.2I. +7.2t.2J. +7.2t.2K. +c.2M.2t. +7.2t.2N. +7.2t.2O. +7.2t.2Q. +7.2S.2t. +7.2t.2T. +1.2U.2t. +7.2t.2V. +1.2W.2t. +7.2t.2X. +7.2t.2+. +7.2t.30. +7.2t.35. +7.2t.36. +7.2t.37. +7.2t.38. +7.2t.39. +7.2t.3d. +7.2t.3h. +7.2t.3i. +7.2t.3m. +7.2t.3p. +7.2t.3y. +7.2t.3z. +7.2t.3A. +7.2t.3B. +7.2t.3C. +7.2t.3F. +7.2t.3G. +7.2t.3M. +7.2t.3Q. +7.2t.40. +7.2t.47. +7.2t.4d. +7.2t.4O. +7.2t.4Q. +7.4T.2t. +7.2t.4V. +7.2t.51. +7.2t.52. +7.2t.5a. +7.2t.5b. +7.2t.5d. +7.2t.5B. +7.2t.5C. +7.2t.5F. +7.2t.5H. +7.2t.5I. +7.2t.5K. +7.5M.2t. +7.2t.5O. +7.2t.5R. +7.2t.5U. +7.2t.5W. +1.5Y.2t. +7.2t.5Z. +7.2t.5/. +7.2t.60. +7.2t.62. +7.2t.66. +7.2t.6b. +c.6e.2t. +7.2t.6F. +7.2t.6M. +7.2t.6+. +7.2t.71. +7.2t.79. +7.2t.7D. +7.2t.2p. +7.2t.2p. +2.2u.2u. +2.2u.2v. +2.2u.2w. +2.2u.2y. +2.2u.2z. +2.2u.2A. +2.2u.2B. +2.2u.2C. +2.2u.2D. +2.2u.2E. +2.2u.2F. +2.2u.2G. +2.2u.2H. +2.2u.2I. +2.2u.2J. +2.2u.2K. +c.2M.2u. +2.2u.2N. +2.2u.2O. +2.2u.2Q. +7.2S.2u. +2.2u.2T. +1.2U.2u. +2.2u.2V. +1.2W.2u. +2.2u.2X. +2.2u.2+. +2.2u.30. +2.2u.35. +2.2u.36. +2.2u.37. +2.2u.38. +2.2u.39. +2.2u.3d. +2.2u.3h. +2.2u.3i. +2.2u.3m. +2.2u.3p. +a.3v.2u. +a.3y.2u. +2.2u.3z. +2.2u.3A. +2.2u.3B. +2.2u.3C. +a.3F.2u. +2.2u.3G. +2.2u.3M. +2.2u.3Q. +2.2u.40. +2.2u.47. +2.2u.4d. +2.2u.4O. +2.2u.4Q. +7.4T.2u. +2.2u.4V. +2.2u.51. +2.2u.52. +2.2u.5a. +2.2u.5b. +2.2u.5d. +2.2u.5B. +2.2u.5C. +2.2u.5F. +2.2u.5H. +2.2u.5I. +f.2u.5K. +7.5M.2u. +2.2u.5O. +2.2u.5R. +2.2u.5U. +2.2u.5W. +1.5Y.2u. +2.2u.5Z. +2.2u.5/. +2.2u.60. +2.2u.62. +2.2u.66. +a.6b.2u. +c.6e.2u. +2.2u.6F. +2.2u.6M. +2.2u.6+. +2.2u.71. +2.2u.79. +2.2u.7D. +2.2u.2p. +2.2u.2p. +0.2v.2v. +d.2v.2w. +e.2x.2v. +9.2y.2v. +0.2v.2z. +d.2v.2A. +9.2B.2v. +0.2v.2C. +0.2v.2D. +0.2v.2E. +0.2v.2F. +0.2v.2G. +9.2H.2v. +0.2v.2I. +d.2v.2J. +0.2v.2K. +c.2M.2v. +9.2N.2v. +2.2O.2v. +9.2Q.2v. +7.2S.2v. +0.2v.2T. +1.2U.2v. +9.2V.2v. +1.2W.2v. +0.2v.2X. +d.2+.2v. +d.2v.30. +d.2v.35. +b.36.2v. +0.2v.37. +0.2v.38. +5.39.2v. +5.3d.2v. +d.2v.3h. +5.3i.2v. +0.2v.3m. +0.2v.3p. +a.3v.2v. +a.3y.2v. +2.3z.2v. +5.3A.2v. +2.3B.2v. +0.2v.3C. +a.3F.2v. +5.3G.2v. +0.2v.3M. +d.2v.3Q. +5.40.2v. +0.2v.47. +0.2v.4d. +0.2v.4O. +d.2v.4Q. +5.4T.2v. +0.2v.4V. +d.2v.51. +0.2v.52. +a.5a.2v. +2.5b.2v. +5.5d.2v. +0.2v.5B. +0.2v.5C. +5.5F.2v. +0.2v.5H. +0.2v.5I. +0.2v.5K. +7.5M.2v. +d.2v.5O. +0.2v.5R. +0.2v.5U. +0.2v.5W. +1.5Y.2v. +0.2v.5Z. +0.2v.5/. +0.2v.60. +2.62.2v. +2.66.2v. +a.6b.2v. +c.6e.2v. +0.2v.6F. +0.6M.2v. +d.2v.6+. +d.2v.71. +5.79.2v. +0.2v.7D. +0.2v.2p. +0.2v.2p. +0.2w.2w. +e.2x.2w. +9.2y.2w. +3.2z.2w. +d.2A.2w. +9.2B.2w. +0.2C.2w. +d.2D.2w. +d.2E.2w. +0.2F.2w. +0.2G.2w. +9.2H.2w. +d.2I.2w. +d.2J.2w. +0.2K.2w. +c.2M.2w. +9.2N.2w. +2.2O.2w. +9.2Q.2w. +7.2S.2w. +d.2T.2w. +1.2U.2w. +9.2V.2w. +1.2W.2w. +0.2X.2w. +b.2+.2w. +d.30.2w. +0.2w.35. +b.36.2w. +0.2w.37. +0.38.2w. +5.39.2w. +5.3d.2w. +0.3h.2w. +5.3i.2w. +0.2w.3m. +5.3p.2w. +a.3v.2w. +a.3y.2w. +2.3z.2w. +5.3A.2w. +2.3B.2w. +5.3C.2w. +a.3F.2w. +5.3G.2w. +0.2w.3M. +0.2w.3Q. +5.40.2w. +0.2w.47. +5.4d.2w. +0.2w.4O. +3.4Q.2w. +7.4T.2w. +0.2w.4V. +5.51.2w. +0.52.2w. +a.5a.2w. +2.5b.2w. +5.5d.2w. +5.5B.2w. +5.5C.2w. +5.5F.2w. +0.5H.2w. +0.5I.2w. +f.2w.5K. +7.5M.2w. +5.5O.2w. +5.5R.2w. +0.2w.5U. +3.5W.2w. +1.5Y.2w. +5.5Z.2w. +5.5/.2w. +0.60.2w. +2.62.2w. +2.66.2w. +k.6b.2w. +c.6e.2w. +0.2w.6F. +0.6M.2w. +0.6+.2w. +d.71.2w. +5.79.2w. +0.2w.7D. +d.2p.2w. +d.2p.2w. +e.2x.2x. +e.2x.2y. +e.2x.2z. +e.2x.2A. +e.2x.2B. +e.2x.2C. +e.2x.2D. +e.2x.2E. +e.2x.2F. +e.2x.2G. +e.2x.2H. +e.2x.2I. +e.2x.2J. +e.2x.2K. +c.2M.2x. +e.2x.2N. +e.2x.2O. +e.2x.2Q. +7.2S.2x. +e.2x.2T. +1.2U.2x. +e.2x.2V. +1.2W.2x. +e.2x.2X. +e.2x.2+. +e.2x.30. +e.2x.35. +e.2x.36. +e.2x.37. +e.2x.38. +e.2x.39. +e.2x.3d. +e.2x.3h. +e.2x.3i. +e.2x.3m. +e.2x.3p. +a.3v.2x. +a.3y.2x. +e.2x.3z. +e.2x.3A. +e.2x.3B. +e.2x.3C. +a.3F.2x. +e.2x.3G. +e.2x.3M. +e.2x.3Q. +e.2x.40. +e.2x.47. +e.2x.4d. +e.2x.4O. +e.2x.4Q. +7.4T.2x. +e.2x.4V. +e.2x.51. +e.2x.52. +e.2x.5b. +e.2x.5d. +e.2x.5B. +e.2x.5C. +e.2x.5F. +e.2x.5H. +e.2x.5I. +f.2x.5K. +7.5M.2x. +e.2x.5O. +e.2x.5R. +e.2x.5U. +e.2x.5W. +1.5Y.2x. +e.2x.5Z. +e.2x.5/. +e.2x.60. +e.2x.62. +e.2x.66. +a.6b.2x. +c.6e.2x. +e.2x.6F. +e.2x.6M. +e.2x.6+. +e.2x.71. +e.2x.79. +e.2x.7D. +e.2x.2p. +e.2x.2p. +9.2y.2y. +9.2y.2z. +9.2y.2A. +9.2y.2B. +9.2y.2C. +9.2y.2D. +9.2y.2E. +9.2y.2F. +9.2y.2G. +9.2H.2y. +9.2y.2I. +9.2y.2J. +9.2y.2K. +c.2M.2y. +9.2N.2y. +2.2O.2y. +9.2Q.2y. +7.2S.2y. +9.2y.2T. +1.2U.2y. +9.2V.2y. +1.2W.2y. +9.2y.2X. +9.2y.2+. +9.2y.30. +9.2y.35. +9.2y.36. +9.2y.37. +9.2y.38. +9.2y.39. +9.2y.3d. +9.2y.3h. +9.2y.3i. +9.2y.3m. +9.2y.3p. +a.3v.2y. +a.3y.2y. +2.3z.2y. +9.2y.3A. +2.3B.2y. +9.2y.3C. +a.3F.2y. +5.3G.2y. +9.2y.3M. +9.2y.3Q. +5.40.2y. +9.2y.47. +9.2y.4d. +9.2y.4O. +9.2y.4Q. +7.4T.2y. +9.2y.4V. +9.2y.51. +9.2y.52. +a.5a.2y. +2.5b.2y. +5.5d.2y. +9.2y.5B. +9.2y.5C. +9.2y.5F. +9.2y.5H. +9.2y.5I. +9.2y.5K. +7.5M.2y. +9.2y.5O. +9.2y.5R. +9.2y.5U. +9.2y.5W. +1.5Y.2y. +9.2y.5Z. +9.2y.5/. +9.2y.60. +2.62.2y. +2.66.2y. +a.6b.2y. +c.6e.2y. +9.2y.6F. +9.2y.6M. +9.2y.6+. +9.2y.71. +9.2y.79. +9.2y.7D. +9.2y.2p. +9.2y.2p. +0.2z.2z. +0.2A.2z. +9.2B.2z. +0.2z.2C. +0.2D.2z. +0.2E.2z. +0.2F.2z. +0.2G.2z. +9.2H.2z. +0.2I.2z. +0.2J.2z. +0.2z.2K. +c.2M.2z. +9.2N.2z. +2.2O.2z. +9.2Q.2z. +7.2S.2z. +0.2T.2z. +1.2U.2z. +9.2V.2z. +1.2W.2z. +0.2z.2X. +b.2+.2z. +d.30.2z. +3.2z.35. +6.36.2z. +0.2z.37. +0.2z.38. +5.39.2z. +5.3d.2z. +3.2z.3h. +5.3i.2z. +0.2z.3m. +5.3p.2z. +a.3v.2z. +a.3y.2z. +2.3z.2z. +5.3A.2z. +2.3B.2z. +0.2z.3C. +a.3F.2z. +5.3G.2z. +0.2z.3M. +3.2z.3Q. +5.40.2z. +0.2z.47. +0.2z.4d. +0.2z.4O. +0.2z.4Q. +5.4T.2z. +0.2z.4V. +3.2z.51. +0.2z.52. +a.5a.2z. +2.5b.2z. +5.5d.2z. +0.2z.5B. +0.2z.5C. +5.5F.2z. +0.2z.5H. +0.2z.5I. +f.2z.5K. +7.5M.2z. +3.2z.5O. +3.2z.5R. +0.2z.5U. +0.2z.5W. +1.5Y.2z. +0.2z.5Z. +0.2z.5/. +0.2z.60. +2.62.2z. +2.66.2z. +a.6b.2z. +c.6e.2z. +0.2z.6F. +0.6M.2z. +0.2z.6+. +3.2z.71. +5.79.2z. +0.2z.7D. +0.2p.2z. +0.2p.2z. +d.2A.2A. +9.2B.2A. +0.2A.2C. +0.2D.2A. +0.2A.2E. +0.2F.2A. +0.2A.2G. +9.2H.2A. +0.2I.2A. +d.2A.2J. +0.2A.2K. +c.2M.2A. +9.2N.2A. +2.2O.2A. +9.2Q.2A. +7.2S.2A. +0.2A.2T. +1.2U.2A. +9.2V.2A. +1.2W.2A. +0.2A.2X. +d.2+.2A. +0.2A.30. +d.2A.35. +b.36.2A. +0.2A.37. +0.2A.38. +5.39.2A. +5.3d.2A. +d.2A.3h. +5.3i.2A. +0.2A.3m. +0.2A.3p. +a.3v.2A. +a.3y.2A. +2.3z.2A. +5.3A.2A. +2.3B.2A. +0.2A.3C. +a.3F.2A. +5.3G.2A. +0.2A.3M. +d.2A.3Q. +5.40.2A. +0.2A.47. +0.2A.4d. +0.2A.4O. +0.2A.4Q. +5.4T.2A. +0.2A.4V. +d.2A.51. +0.2A.52. +a.5a.2A. +2.5b.2A. +5.5d.2A. +0.2A.5B. +0.2A.5C. +5.5F.2A. +0.2A.5H. +0.2A.5I. +f.2A.5K. +7.5M.2A. +d.2A.5O. +0.2A.5R. +0.2A.5U. +0.2A.5W. +1.5Y.2A. +0.2A.5Z. +d.2A.5/. +0.2A.60. +2.62.2A. +2.66.2A. +a.6b.2A. +c.6e.2A. +0.2A.6F. +0.6M.2A. +d.2A.6+. +d.2A.71. +5.79.2A. +0.2A.7D. +0.2A.2p. +0.2A.2p. +9.2B.2B. +9.2B.2C. +9.2B.2D. +9.2B.2E. +9.2B.2F. +9.2B.2G. +9.2H.2B. +9.2B.2I. +9.2B.2J. +9.2B.2K. +c.2M.2B. +9.2N.2B. +2.2O.2B. +9.2Q.2B. +7.2S.2B. +9.2B.2T. +1.2U.2B. +9.2V.2B. +1.2W.2B. +9.2B.2X. +9.2B.2+. +9.2B.30. +9.2B.35. +9.2B.36. +9.2B.37. +9.2B.38. +9.2B.39. +9.2B.3d. +9.2B.3h. +9.2B.3i. +9.2B.3m. +9.2B.3p. +a.3v.2B. +a.3y.2B. +2.3z.2B. +9.2B.3A. +2.3B.2B. +9.2B.3C. +a.3F.2B. +5.3G.2B. +9.2B.3M. +9.2B.3Q. +5.40.2B. +9.2B.47. +9.2B.4d. +9.2B.4O. +9.2B.4Q. +7.4T.2B. +9.2B.4V. +9.2B.51. +9.2B.52. +a.5a.2B. +2.5b.2B. +5.5d.2B. +9.2B.5B. +9.2B.5C. +9.2B.5F. +9.2B.5H. +9.2B.5I. +9.2B.5K. +7.5M.2B. +9.2B.5O. +9.2B.5R. +9.2B.5U. +9.2B.5W. +1.5Y.2B. +9.2B.5Z. +9.2B.5/. +9.2B.60. +2.62.2B. +2.66.2B. +a.6b.2B. +c.6e.2B. +9.2B.6F. +9.2B.6M. +9.2B.6+. +9.2B.71. +9.2B.79. +9.2B.7D. +9.2B.2p. +9.2B.2p. +0.2C.2C. +0.2D.2C. +0.2E.2C. +0.2F.2C. +0.2G.2C. +9.2H.2C. +0.2I.2C. +0.2J.2C. +0.2C.2K. +c.2M.2C. +9.2N.2C. +2.2O.2C. +9.2Q.2C. +7.2S.2C. +0.2T.2C. +1.2U.2C. +9.2V.2C. +1.2W.2C. +0.2C.2X. +b.2+.2C. +0.30.2C. +3.2C.35. +b.36.2C. +3.2C.37. +3.2C.38. +5.39.2C. +5.3d.2C. +3.2C.3h. +5.3i.2C. +0.2C.3m. +5.3p.2C. +a.3v.2C. +a.3y.2C. +2.3z.2C. +5.3A.2C. +2.3B.2C. +0.2C.3C. +a.3F.2C. +5.3G.2C. +0.2C.3M. +0.2C.3Q. +5.40.2C. +0.2C.47. +0.2C.4d. +0.2C.4O. +0.2C.4Q. +5.4T.2C. +3.2C.4V. +3.2C.51. +0.2C.52. +a.5a.2C. +2.5b.2C. +5.5d.2C. +0.2C.5B. +0.2C.5C. +5.5F.2C. +0.2C.5H. +0.2C.5I. +f.2C.5K. +7.5M.2C. +3.2C.5O. +0.2C.5R. +0.2C.5U. +0.2C.5W. +1.5Y.2C. +0.2C.5Z. +0.2C.5/. +0.2C.60. +2.62.2C. +2.66.2C. +a.6b.2C. +c.6e.2C. +0.2C.6F. +0.6M.2C. +0.2C.6+. +0.2C.71. +5.79.2C. +0.2C.7D. +0.2p.2C. +0.2p.2C. +0.2D.2D. +0.2D.2E. +0.2F.2D. +0.2D.2G. +9.2H.2D. +0.2I.2D. +d.2D.2J. +0.2D.2K. +c.2M.2D. +9.2N.2D. +2.2O.2D. +9.2Q.2D. +7.2S.2D. +0.2D.2T. +1.2U.2D. +9.2V.2D. +1.2W.2D. +0.2D.2X. +d.2+.2D. +0.2D.30. +d.2D.35. +b.36.2D. +0.2D.37. +0.2D.38. +5.39.2D. +5.3d.2D. +0.2D.3h. +5.3i.2D. +0.2D.3m. +0.2D.3p. +a.3v.2D. +a.3y.2D. +2.3z.2D. +5.3A.2D. +2.3B.2D. +0.2D.3C. +a.3F.2D. +5.3G.2D. +0.2D.3M. +d.2D.3Q. +5.40.2D. +0.2D.47. +0.2D.4d. +0.2D.4O. +0.2D.4Q. +5.4T.2D. +0.2D.4V. +d.2D.51. +0.2D.52. +a.5a.2D. +2.5b.2D. +5.5d.2D. +0.2D.5B. +0.2D.5C. +5.5F.2D. +0.2D.5H. +0.2D.5I. +f.2D.5K. +7.5M.2D. +d.2D.5O. +0.2D.5R. +0.2D.5U. +0.2D.5W. +1.5Y.2D. +0.2D.5Z. +0.2D.5/. +0.2D.60. +2.62.2D. +2.66.2D. +a.6b.2D. +c.6e.2D. +0.2D.6F. +0.6M.2D. +0.2D.6+. +d.2D.71. +5.79.2D. +0.2D.7D. +0.2D.2p. +0.2D.2p. +d.2E.2E. +0.2F.2E. +0.2E.2G. +9.2H.2E. +0.2I.2E. +d.2E.2J. +0.2E.2K. +c.2M.2E. +9.2N.2E. +2.2O.2E. +9.2Q.2E. +7.2S.2E. +0.2E.2T. +1.2U.2E. +9.2V.2E. +1.2W.2E. +0.2E.2X. +d.2+.2E. +0.2E.30. +d.2E.35. +3.36.2E. +0.2E.37. +d.2E.38. +5.39.2E. +5.3d.2E. +d.2E.3h. +5.3i.2E. +0.2E.3m. +0.2E.3p. +a.3v.2E. +a.3y.2E. +2.3z.2E. +5.3A.2E. +2.3B.2E. +0.2E.3C. +a.3F.2E. +5.3G.2E. +0.2E.3M. +d.2E.3Q. +5.40.2E. +0.2E.47. +0.2E.4d. +0.2E.4O. +0.2E.4Q. +5.4T.2E. +0.2E.4V. +d.2E.51. +0.2E.52. +a.5a.2E. +2.5b.2E. +5.5d.2E. +0.2E.5B. +0.2E.5C. +5.5F.2E. +0.2E.5H. +0.2E.5I. +f.2E.5K. +7.5M.2E. +0.2E.5O. +0.2E.5R. +0.2E.5U. +0.2E.5W. +1.5Y.2E. +0.2E.5Z. +d.2E.5/. +0.2E.60. +2.62.2E. +2.66.2E. +a.6b.2E. +c.6e.2E. +0.2E.6F. +0.6M.2E. +d.2E.6+. +d.2E.71. +5.79.2E. +0.2E.7D. +0.2E.2p. +0.2E.2p. +0.2F.2F. +0.2F.2G. +9.2H.2F. +0.2I.2F. +0.2F.2J. +0.2F.2K. +c.2M.2F. +9.2N.2F. +2.2O.2F. +9.2Q.2F. +7.2S.2F. +0.2F.2T. +1.2U.2F. +9.2V.2F. +1.2W.2F. +0.2F.2X. +d.2+.2F. +0.2F.30. +0.2F.35. +b.36.2F. +0.2F.37. +0.2F.38. +5.39.2F. +5.3d.2F. +0.2F.3h. +5.3i.2F. +0.2F.3m. +0.2F.3p. +a.3v.2F. +a.3y.2F. +2.3z.2F. +5.3A.2F. +2.3B.2F. +0.2F.3C. +a.3F.2F. +5.3G.2F. +0.2F.3M. +0.2F.3Q. +5.40.2F. +0.2F.47. +0.2F.4d. +0.2F.4O. +0.2F.4Q. +5.4T.2F. +0.2F.4V. +0.2F.51. +0.2F.52. +a.5a.2F. +2.5b.2F. +5.5d.2F. +0.2F.5B. +0.2F.5C. +5.5F.2F. +0.2F.5H. +0.2F.5I. +f.2F.5K. +7.5M.2F. +0.2F.5O. +0.2F.5R. +0.2F.5U. +0.2F.5W. +1.5Y.2F. +0.2F.5Z. +0.2F.5/. +0.2F.60. +2.62.2F. +2.66.2F. +a.6b.2F. +c.6e.2F. +0.2F.6F. +0.6M.2F. +0.2F.6+. +0.2F.71. +5.79.2F. +0.2F.7D. +0.2F.2p. +0.2F.2p. +3.2G.2G. +9.2H.2G. +0.2I.2G. +d.2J.2G. +0.2G.2K. +c.2M.2G. +9.2N.2G. +2.2O.2G. +9.2Q.2G. +7.2S.2G. +0.2G.2T. +1.2U.2G. +9.2V.2G. +1.2W.2G. +0.2G.2X. +b.2+.2G. +0.30.2G. +0.2G.35. +3.36.2G. +0.2G.37. +0.2G.38. +5.39.2G. +5.3d.2G. +3.2G.3h. +5.3i.2G. +0.2G.3m. +0.2G.3p. +a.3v.2G. +a.3y.2G. +2.3z.2G. +5.3A.2G. +2.3B.2G. +0.2G.3C. +a.3F.2G. +5.3G.2G. +3.2G.3M. +3.2G.3Q. +5.40.2G. +3.2G.47. +0.2G.4d. +0.2G.4O. +0.2G.4Q. +5.4T.2G. +3.2G.4V. +0.2G.51. +0.2G.52. +a.5a.2G. +2.5b.2G. +5.5d.2G. +0.2G.5B. +0.2G.5C. +5.5F.2G. +3.2G.5H. +0.2G.5I. +f.2G.5K. +7.5M.2G. +0.2G.5O. +0.2G.5R. +3.2G.5U. +0.2G.5W. +1.5Y.2G. +0.2G.5Z. +0.2G.5/. +0.2G.60. +2.62.2G. +2.66.2G. +a.6b.2G. +c.6e.2G. +0.2G.6F. +0.6M.2G. +0.2G.6+. +0.2G.71. +5.79.2G. +0.2G.7D. +0.2p.2G. +0.2p.2G. +9.2H.2H. +9.2H.2I. +9.2H.2J. +9.2H.2K. +c.2M.2H. +9.2H.2N. +2.2O.2H. +9.2H.2Q. +7.2S.2H. +9.2H.2T. +1.2U.2H. +9.2H.2V. +1.2W.2H. +9.2H.2X. +9.2H.2+. +9.2H.30. +9.2H.35. +9.2H.36. +9.2H.37. +9.2H.38. +9.2H.39. +9.2H.3d. +9.2H.3h. +9.2H.3i. +9.2H.3m. +9.2H.3p. +a.3v.2H. +a.3y.2H. +2.3z.2H. +9.2H.3A. +2.3B.2H. +9.2H.3C. +a.3F.2H. +9.2H.3G. +9.2H.3M. +9.2H.3Q. +5.40.2H. +9.2H.47. +9.2H.4d. +9.2H.4O. +9.2H.4Q. +7.4T.2H. +9.2H.4V. +9.2H.51. +9.2H.52. +a.5a.2H. +2.5b.2H. +9.2H.5d. +9.2H.5B. +9.2H.5C. +9.2H.5F. +9.2H.5H. +9.2H.5I. +9.2H.5K. +7.5M.2H. +9.2H.5O. +9.2H.5R. +9.2H.5U. +9.2H.5W. +1.5Y.2H. +9.2H.5Z. +9.2H.5/. +9.2H.60. +2.62.2H. +2.66.2H. +a.6b.2H. +c.6e.2H. +9.2H.6F. +9.2H.6M. +9.2H.6+. +9.2H.71. +9.2H.79. +9.2H.7D. +9.2H.2p. +9.2H.2p. +0.2I.2I. +d.2I.2J. +0.2I.2K. +c.2M.2I. +9.2N.2I. +2.2O.2I. +9.2Q.2I. +7.2S.2I. +0.2I.2T. +1.2U.2I. +9.2V.2I. +1.2W.2I. +0.2I.2X. +d.2+.2I. +0.2I.30. +d.2I.35. +b.36.2I. +0.2I.37. +0.2I.38. +5.39.2I. +5.3d.2I. +d.2I.3h. +5.3i.2I. +0.2I.3m. +0.2I.3p. +a.3v.2I. +a.3y.2I. +2.3z.2I. +5.3A.2I. +2.3B.2I. +0.2I.3C. +a.3F.2I. +5.3G.2I. +0.2I.3M. +d.2I.3Q. +5.40.2I. +0.2I.47. +0.2I.4d. +0.2I.4O. +0.2I.4Q. +5.4T.2I. +0.2I.4V. +d.2I.51. +0.2I.52. +a.5a.2I. +2.5b.2I. +5.5d.2I. +0.2I.5B. +0.2I.5C. +5.5F.2I. +0.2I.5H. +0.2I.5I. +f.2I.5K. +7.5M.2I. +d.2I.5O. +0.2I.5R. +0.2I.5U. +0.2I.5W. +1.5Y.2I. +0.2I.5Z. +0.2I.5/. +0.2I.60. +2.62.2I. +2.66.2I. +a.6b.2I. +c.6e.2I. +0.2I.6F. +0.6M.2I. +0.2I.6+. +d.2I.71. +5.79.2I. +0.2I.7D. +0.2I.2p. +0.2I.2p. +d.2J.2J. +d.2J.2K. +c.2M.2J. +9.2N.2J. +2.2O.2J. +9.2Q.2J. +7.2S.2J. +0.2J.2T. +1.2U.2J. +9.2V.2J. +1.2W.2J. +0.2J.2X. +d.2+.2J. +d.30.2J. +d.2J.35. +3.36.2J. +0.2J.37. +d.2J.38. +5.39.2J. +5.3d.2J. +d.2J.3h. +5.3i.2J. +0.2J.3m. +0.2J.3p. +a.3v.2J. +a.3y.2J. +2.3z.2J. +5.3A.2J. +2.3B.2J. +0.2J.3C. +a.3F.2J. +5.3G.2J. +0.2J.3M. +d.2J.3Q. +5.40.2J. +0.2J.47. +0.2J.4d. +d.2J.4O. +d.2J.4Q. +5.4T.2J. +d.2J.4V. +d.2J.51. +0.2J.52. +a.5a.2J. +2.5b.2J. +5.5d.2J. +0.2J.5B. +0.2J.5C. +5.5F.2J. +d.2J.5H. +d.2J.5I. +f.2J.5K. +7.5M.2J. +d.2J.5O. +0.2J.5R. +0.2J.5U. +d.2J.5W. +1.5Y.2J. +0.2J.5Z. +d.2J.5/. +0.2J.60. +2.62.2J. +2.66.2J. +a.6b.2J. +c.6e.2J. +0.2J.6F. +0.6M.2J. +d.2J.6+. +d.2J.71. +5.79.2J. +0.2J.7D. +0.2J.2p. +0.2J.2p. +0.2K.2K. +c.2M.2K. +9.2N.2K. +2.2O.2K. +9.2Q.2K. +7.2S.2K. +0.2T.2K. +1.2U.2K. +9.2V.2K. +1.2W.2K. +0.2X.2K. +b.2+.2K. +0.30.2K. +0.2K.35. +b.36.2K. +0.2K.37. +0.2K.38. +5.39.2K. +5.3d.2K. +0.2K.3h. +5.3i.2K. +0.2K.3m. +5.3p.2K. +a.3v.2K. +a.3y.2K. +2.3z.2K. +5.3A.2K. +2.3B.2K. +0.2K.3C. +a.3F.2K. +5.3G.2K. +0.2K.3M. +0.2K.3Q. +5.40.2K. +0.2K.47. +0.2K.4d. +0.2K.4O. +0.2K.4Q. +5.4T.2K. +0.2K.4V. +0.2K.51. +0.2K.52. +a.5a.2K. +2.5b.2K. +5.5d.2K. +0.2K.5B. +0.2K.5C. +5.5F.2K. +0.2K.5H. +0.5I.2K. +0.2K.5K. +7.5M.2K. +5.5O.2K. +0.2K.5R. +0.2K.5U. +0.2K.5W. +1.5Y.2K. +0.2K.5Z. +0.2K.5/. +0.60.2K. +2.62.2K. +2.66.2K. +a.6b.2K. +c.6e.2K. +0.2K.6F. +0.6M.2K. +0.2K.6+. +0.2K.71. +5.79.2K. +0.2K.7D. +0.2p.2K. +0.2p.2K. +1.4s.2L. +1.51.2L. +1.5v.2L. +5.5I.2L. +6.5K.2L. +7.5M.2L. +1.5Y.2L. +1.5+.2L. +4.60.2L. +5.62.2L. +1.6q.2L. +1.6t.2L. +8.6F.2L. +c.6H.2L. +8.6M.2L. +1.86.2L. +1.8d.2L. +c.2M.2M. +c.2M.2N. +c.2M.2O. +c.2M.2Q. +c.2M.2S. +c.2M.2T. +1.2U.2M. +c.2M.2V. +1.2W.2M. +c.2M.2X. +c.2M.2+. +c.2M.30. +c.2M.35. +c.2M.36. +c.2M.37. +c.2M.38. +c.2M.39. +c.2M.3d. +c.2M.3h. +c.2M.3i. +c.2M.3m. +c.2M.3p. +c.2M.3v. +c.2M.3y. +c.2M.3z. +c.2M.3A. +c.2M.3B. +c.2M.3C. +c.2M.3F. +c.2M.3G. +c.2M.3M. +c.2M.3Q. +c.2M.40. +c.2M.47. +c.2M.4d. +c.2M.4O. +c.2M.4Q. +c.2M.4T. +c.2M.4V. +c.2M.51. +c.2M.52. +c.2M.5a. +c.2M.5b. +c.2M.5d. +c.2M.5B. +c.2M.5C. +c.2M.5F. +c.2M.5H. +c.2M.5I. +c.2M.5K. +c.2M.5M. +c.2M.5O. +c.2M.5R. +c.2M.5U. +c.2M.5W. +1.5Y.2M. +c.2M.5Z. +c.2M.5/. +c.2M.60. +c.2M.62. +c.2M.66. +c.2M.6b. +c.6e.2M. +c.2M.6F. +c.2M.6M. +c.2M.6+. +c.2M.71. +c.2M.79. +c.2M.7D. +c.2M.2p. +c.2M.2p. +9.2N.2N. +2.2O.2N. +9.2N.2Q. +7.2S.2N. +9.2N.2T. +1.2U.2N. +9.2V.2N. +1.2W.2N. +9.2N.2X. +9.2N.2+. +9.2N.30. +9.2N.35. +9.2N.36. +9.2N.37. +9.2N.38. +9.2N.39. +9.2N.3d. +9.2N.3h. +9.2N.3i. +9.2N.3m. +9.2N.3p. +a.3v.2N. +a.3y.2N. +2.3z.2N. +9.2N.3A. +2.3B.2N. +9.2N.3C. +a.3F.2N. +5.3G.2N. +9.2N.3M. +9.2N.3Q. +5.40.2N. +9.2N.47. +9.2N.4d. +9.2N.4O. +9.2N.4Q. +7.4T.2N. +9.2N.4V. +9.2N.51. +9.2N.52. +a.5a.2N. +2.5b.2N. +5.5d.2N. +9.2N.5B. +9.2N.5C. +9.2N.5F. +9.2N.5H. +9.2N.5I. +f.2N.5K. +7.5M.2N. +9.2N.5O. +9.2N.5R. +9.2N.5U. +9.2N.5W. +1.5Y.2N. +9.2N.5Z. +9.2N.5/. +9.2N.60. +2.62.2N. +2.66.2N. +a.6b.2N. +c.6e.2N. +9.2N.6F. +9.2N.6M. +9.2N.6+. +9.2N.71. +9.2N.79. +9.2N.7D. +9.2N.2p. +9.2N.2p. +2.2O.2O. +2.2O.2Q. +7.2S.2O. +2.2O.2T. +1.2U.2O. +2.2O.2V. +1.2W.2O. +2.2O.2X. +2.2O.2+. +2.2O.30. +2.2O.35. +2.2O.36. +2.2O.37. +2.2O.38. +2.2O.39. +2.2O.3d. +2.2O.3h. +2.2O.3i. +2.2O.3m. +2.2O.3p. +a.3v.2O. +a.3y.2O. +2.3z.2O. +2.2O.3A. +2.3B.2O. +2.2O.3C. +a.3F.2O. +2.2O.3G. +2.2O.3M. +2.2O.3Q. +2.2O.40. +2.2O.47. +2.2O.4d. +2.2O.4O. +2.2O.4Q. +7.4T.2O. +2.2O.4V. +2.2O.51. +2.2O.52. +a.5a.2O. +2.5b.2O. +2.2O.5d. +2.2O.5B. +2.2O.5C. +2.2O.5F. +2.2O.5H. +2.2O.5I. +f.2O.5K. +7.5M.2O. +2.2O.5O. +2.2O.5R. +2.2O.5U. +2.2O.5W. +1.5Y.2O. +2.2O.5Z. +2.2O.5/. +2.2O.60. +2.62.2O. +2.66.2O. +a.6b.2O. +c.6e.2O. +2.2O.6F. +2.2O.6M. +2.2O.6+. +2.2O.71. +2.2O.79. +2.2O.7D. +2.2O.2p. +2.2O.2p. +1.4s.2P. +1.51.2P. +1.5v.2P. +6.5I.2P. +6.5K.2P. +7.5M.2P. +1.5Y.2P. +1.5+.2P. +4.60.2P. +5.62.2P. +1.6q.2P. +1.6t.2P. +3.6F.2P. +c.6H.2P. +8.6M.2P. +1.7B.2P. +1.7R.2P. +1.86.2P. +1.8d.2P. +9.2Q.2Q. +7.2S.2Q. +9.2Q.2T. +1.2U.2Q. +9.2V.2Q. +1.2W.2Q. +9.2Q.2X. +9.2Q.2+. +9.2Q.30. +9.2Q.35. +9.2Q.36. +9.2Q.37. +9.2Q.38. +9.2Q.39. +9.2Q.3d. +9.2Q.3h. +9.2Q.3i. +9.2Q.3m. +9.2Q.3p. +a.3v.2Q. +a.3y.2Q. +2.3z.2Q. +9.2Q.3A. +2.3B.2Q. +9.2Q.3C. +a.3F.2Q. +5.3G.2Q. +9.2Q.3M. +9.2Q.3Q. +5.40.2Q. +9.2Q.47. +9.2Q.4d. +9.2Q.4O. +9.2Q.4Q. +7.4T.2Q. +9.2Q.4V. +9.2Q.51. +9.2Q.52. +a.5a.2Q. +2.5b.2Q. +5.5d.2Q. +9.2Q.5B. +9.2Q.5C. +9.2Q.5F. +9.2Q.5H. +9.2Q.5I. +9.2Q.5K. +7.5M.2Q. +9.2Q.5O. +9.2Q.5R. +9.2Q.5U. +9.2Q.5W. +1.5Y.2Q. +9.2Q.5Z. +9.2Q.5/. +9.2Q.60. +2.62.2Q. +2.66.2Q. +a.6b.2Q. +c.6e.2Q. +9.2Q.6F. +9.2Q.6M. +9.2Q.6+. +9.2Q.71. +9.2Q.79. +9.2Q.7D. +9.2Q.2p. +9.2Q.2p. +1.51.2R. +1.5v.2R. +8.5I.2R. +8.5K.2R. +7.5M.2R. +1.5Y.2R. +1.5+.2R. +8.60.2R. +5.62.2R. +1.6q.2R. +1.6t.2R. +3.6F.2R. +c.6H.2R. +8.6M.2R. +1.7B.2R. +1.86.2R. +1.8d.2R. +7.2S.2S. +7.2S.2T. +1.2U.2S. +7.2S.2V. +1.2W.2S. +7.2S.2X. +7.2S.2+. +7.2S.30. +7.2S.35. +7.2S.36. +7.2S.37. +7.2S.38. +7.2S.39. +7.2S.3d. +7.2S.3h. +7.2S.3i. +7.2S.3m. +7.2S.3p. +7.2S.3v. +7.2S.3y. +7.2S.3z. +7.2S.3A. +7.2S.3B. +7.2S.3C. +7.2S.3F. +7.2S.3G. +7.2S.3M. +7.2S.3Q. +7.2S.40. +7.2S.47. +7.2S.4d. +7.2S.4O. +7.2S.4Q. +7.2S.4T. +7.2S.4V. +7.2S.51. +7.2S.52. +7.2S.5a. +7.2S.5b. +7.2S.5d. +7.2S.5B. +7.2S.5C. +7.2S.5F. +7.2S.5H. +7.2S.5I. +7.2S.5K. +7.2S.5M. +7.2S.5O. +7.2S.5R. +7.2S.5U. +7.2S.5W. +1.5Y.2S. +7.2S.5Z. +7.2S.5/. +7.2S.60. +7.2S.62. +7.2S.66. +7.2S.6b. +c.6e.2S. +7.2S.6F. +7.2S.6M. +7.2S.6+. +7.2S.71. +7.2S.79. +7.2S.7D. +7.2S.2p. +7.2S.2p. +0.2T.2T. +1.2U.2T. +9.2V.2T. +1.2W.2T. +0.2T.2X. +b.2+.2T. +d.30.2T. +d.2T.35. +b.36.2T. +0.2T.37. +0.2T.38. +5.39.2T. +5.3d.2T. +0.2T.3h. +5.3i.2T. +0.2T.3m. +5.3p.2T. +a.3v.2T. +a.3y.2T. +2.3z.2T. +5.3A.2T. +2.3B.2T. +0.2T.3C. +a.3F.2T. +5.3G.2T. +0.2T.3M. +0.2T.3Q. +5.40.2T. +0.2T.47. +0.2T.4d. +0.2T.4O. +0.2T.4Q. +5.4T.2T. +0.2T.4V. +d.2T.51. +0.2T.52. +a.5a.2T. +2.5b.2T. +5.5d.2T. +0.2T.5B. +0.2T.5C. +5.5F.2T. +0.2T.5H. +0.2T.5I. +f.2T.5K. +7.5M.2T. +d.2T.5O. +0.2T.5R. +0.2T.5U. +0.2T.5W. +1.5Y.2T. +0.2T.5Z. +0.2T.5/. +0.2T.60. +2.62.2T. +2.66.2T. +a.6b.2T. +c.6e.2T. +0.2T.6F. +0.6M.2T. +0.2T.6+. +0.2T.71. +g.79.2T. +0.2T.7D. +0.2p.2T. +0.2p.2T. +1.2U.2U. +1.2U.2V. +1.2U.2X. +1.2U.2+. +1.2U.30. +1.2U.35. +1.2U.36. +1.2U.37. +1.2U.38. +1.2U.39. +1.2U.3d. +1.2U.3h. +1.2U.3i. +1.2U.3m. +1.2U.3p. +1.2U.3v. +1.2U.3y. +1.2U.3z. +1.2U.3A. +1.2U.3B. +1.2U.3C. +1.2U.3F. +1.2U.3G. +1.2U.3M. +1.2U.3Q. +1.2U.40. +1.2U.47. +1.2U.4d. +1.2U.4O. +1.2U.4Q. +1.2U.4T. +1.2U.4V. +1.2U.51. +1.2U.52. +1.2U.5a. +1.2U.5b. +1.2U.5d. +1.2U.5B. +1.2U.5C. +1.2U.5F. +1.2U.5H. +1.2U.5I. +1.2U.5K. +1.2U.5M. +1.2U.5O. +1.2U.5R. +1.2U.5U. +1.2U.5W. +1.5Y.2U. +1.2U.5Z. +1.2U.5/. +1.2U.60. +1.2U.62. +1.2U.66. +1.2U.6b. +1.2U.6e. +1.2U.6F. +1.2U.6M. +1.2U.6+. +1.2U.71. +1.2U.79. +1.2U.7D. +1.2U.2p. +1.2U.2p. +9.2V.2V. +1.2W.2V. +9.2V.2X. +9.2V.2+. +9.2V.30. +9.2V.35. +9.2V.36. +9.2V.37. +9.2V.38. +9.2V.39. +9.2V.3d. +9.2V.3h. +9.2V.3i. +9.2V.3m. +9.2V.3p. +a.3v.2V. +a.3y.2V. +2.3z.2V. +9.2V.3A. +2.3B.2V. +9.2V.3C. +a.3F.2V. +9.2V.3G. +9.2V.3M. +9.2V.3Q. +5.40.2V. +9.2V.47. +9.2V.4d. +9.2V.4O. +9.2V.4Q. +7.4T.2V. +9.2V.4V. +9.2V.51. +9.2V.52. +a.5a.2V. +2.5b.2V. +9.2V.5d. +9.2V.5B. +9.2V.5C. +9.2V.5F. +9.2V.5H. +9.2V.5I. +9.2V.5K. +7.5M.2V. +9.2V.5O. +9.2V.5R. +9.2V.5U. +9.2V.5W. +1.5Y.2V. +9.2V.5Z. +9.2V.5/. +9.2V.60. +2.62.2V. +2.66.2V. +a.6b.2V. +c.6e.2V. +9.2V.6F. +9.2V.6M. +9.2V.6+. +9.2V.71. +9.2V.79. +9.2V.7D. +9.2V.2p. +9.2V.2p. +1.2W.2W. +1.2W.2X. +1.2W.2+. +1.2W.30. +1.2W.35. +1.2W.36. +1.2W.37. +1.2W.38. +1.2W.39. +1.2W.3d. +1.2W.3h. +1.2W.3i. +1.2W.3m. +1.2W.3p. +1.2W.3v. +1.2W.3y. +1.2W.3z. +1.2W.3A. +1.2W.3B. +1.2W.3C. +1.2W.3F. +1.2W.3G. +1.2W.3M. +1.2W.3Q. +1.2W.40. +1.2W.47. +1.2W.4d. +1.2W.4O. +1.2W.4Q. +1.2W.4T. +1.2W.4V. +1.2W.51. +1.2W.52. +1.2W.5a. +1.2W.5b. +1.2W.5d. +1.2W.5B. +1.2W.5C. +1.2W.5F. +1.2W.5H. +1.2W.5I. +1.2W.5K. +1.2W.5M. +1.2W.5O. +1.2W.5R. +1.2W.5U. +1.2W.5W. +1.5Y.2W. +1.2W.5Z. +1.2W.5/. +1.2W.60. +1.2W.62. +1.2W.66. +1.2W.6b. +1.2W.6e. +1.2W.6F. +1.2W.6M. +1.2W.6+. +1.2W.71. +1.2W.79. +1.2W.7D. +1.2W.2p. +1.2W.2p. +0.2X.2X. +b.2+.2X. +0.30.2X. +0.2X.35. +b.36.2X. +0.2X.37. +0.2X.38. +5.39.2X. +5.3d.2X. +d.2X.3h. +5.3i.2X. +0.2X.3m. +5.3p.2X. +a.3v.2X. +a.3y.2X. +2.3z.2X. +5.3A.2X. +2.3B.2X. +0.2X.3C. +a.3F.2X. +5.3G.2X. +0.2X.3M. +0.2X.3Q. +5.40.2X. +0.2X.47. +0.2X.4d. +0.2X.4O. +d.2X.4Q. +5.4T.2X. +d.2X.4V. +0.2X.51. +0.2X.52. +a.5a.2X. +2.5b.2X. +5.5d.2X. +0.2X.5B. +0.2X.5C. +5.5F.2X. +0.2X.5H. +0.2X.5I. +f.2X.5K. +7.5M.2X. +0.2X.5O. +0.2X.5R. +d.2X.5U. +0.2X.5W. +1.5Y.2X. +0.2X.5Z. +0.2X.5/. +0.2X.60. +2.62.2X. +2.66.2X. +a.6b.2X. +c.6e.2X. +0.2X.6F. +0.6M.2X. +0.2X.6+. +d.2X.71. +5.79.2X. +d.2X.7D. +0.2p.2X. +0.2p.2X. +1.4s.2Y. +1.51.2Y. +1.5v.2Y. +8.5I.2Y. +8.5K.2Y. +7.5M.2Y. +1.5Y.2Y. +1.5+.2Y. +8.60.2Y. +8.62.2Y. +1.6q.2Y. +1.6t.2Y. +3.6F.2Y. +c.6H.2Y. +3.6M.2Y. +1.7B.2Y. +1.86.2Y. +1.8d.2Y. +1.51.2Z. +1.5v.2Z. +8.5I.2Z. +8.5K.2Z. +7.5M.2Z. +1.5Y.2Z. +1.5+.2Z. +8.60.2Z. +5.62.2Z. +1.6q.2Z. +1.6t.2Z. +3.6F.2Z. +c.6H.2Z. +8.6M.2Z. +1.7B.2Z. +1.86.2Z. +1.8d.2Z. +d.2+.2+. +b.2+.30. +b.2+.35. +b.2+.36. +b.2+.37. +b.2+.38. +d.2+.39. +b.2+.3d. +d.2+.3h. +b.2+.3i. +d.2+.3m. +b.2+.3p. +a.3v.2+. +a.3y.2+. +2.3z.2+. +5.3A.2+. +2.3B.2+. +b.2+.3C. +a.3F.2+. +5.3G.2+. +b.2+.3M. +b.2+.3Q. +5.40.2+. +d.2+.47. +d.2+.4d. +d.2+.4O. +b.2+.4Q. +5.4T.2+. +b.2+.4V. +b.2+.51. +b.2+.52. +a.5a.2+. +2.5b.2+. +5.5d.2+. +b.2+.5B. +b.2+.5C. +b.2+.5F. +b.2+.5H. +d.2+.5I. +f.2+.5K. +7.5M.2+. +b.2+.5O. +d.2+.5R. +d.2+.5U. +b.2+.5W. +1.5Y.2+. +b.2+.5Z. +b.2+.5/. +b.2+.60. +2.62.2+. +2.66.2+. +a.6b.2+. +c.6e.2+. +b.2+.6F. +d.2+.6M. +b.2+.6+. +d.2+.71. +5.79.2+. +b.2+.7D. +d.2+.2p. +d.2+.2p. +1.51.2/. +1.5v.2/. +8.5I.2/. +8.5K.2/. +7.5M.2/. +1.5Y.2/. +1.5+.2/. +5.60.2/. +5.62.2/. +1.6q.2/. +1.6t.2/. +8.6F.2/. +c.6H.2/. +8.6M.2/. +1.7B.2/. +1.86.2/. +1.8d.2/. +0.30.30. +d.30.35. +b.36.30. +d.30.37. +d.30.38. +5.39.30. +5.3d.30. +d.30.3h. +5.3i.30. +0.30.3m. +0.30.3p. +a.3v.30. +a.3y.30. +2.3z.30. +5.3A.30. +2.3B.30. +d.30.3C. +a.3F.30. +5.3G.30. +d.30.3M. +0.30.3Q. +5.40.30. +0.30.47. +0.30.4d. +d.30.4O. +d.30.4Q. +5.4T.30. +0.30.4V. +d.30.51. +0.30.52. +a.5a.30. +2.5b.30. +5.5d.30. +0.30.5B. +0.30.5C. +5.5F.30. +d.30.5H. +0.30.5I. +f.30.5K. +7.5M.30. +d.30.5O. +0.30.5R. +d.30.5U. +0.30.5W. +1.5Y.30. +0.30.5Z. +d.30.5/. +d.30.60. +2.62.30. +2.66.30. +a.6b.30. +c.6e.30. +0.30.6F. +0.6M.30. +0.30.6+. +d.30.71. +5.79.30. +0.30.7D. +d.30.2p. +d.30.2p. +1.51.31. +1.5v.31. +8.5I.31. +8.5K.31. +7.5M.31. +1.5Y.31. +1.5+.31. +5.60.31. +5.62.31. +1.6q.31. +1.6t.31. +3.6F.31. +c.6H.31. +8.6M.31. +1.86.31. +1.8d.31. +1.4s.32. +1.51.32. +1.5v.32. +8.5I.32. +8.5K.32. +7.5M.32. +1.5Y.32. +1.5+.32. +5.60.32. +8.62.32. +1.6q.32. +1.6t.32. +8.6F.32. +c.6H.32. +8.6M.32. +1.86.32. +1.8d.32. +1.4s.33. +1.51.33. +1.5v.33. +8.5I.33. +8.5K.33. +7.5M.33. +1.5Y.33. +1.5+.33. +8.60.33. +5.62.33. +1.6q.33. +1.6t.33. +3.6F.33. +c.6H.33. +8.6M.33. +1.7B.33. +1.7R.33. +1.86.33. +1.8d.33. +1.51.34. +1.5v.34. +8.5I.34. +8.5K.34. +7.5M.34. +1.5Y.34. +1.5+.34. +5.60.34. +5.62.34. +1.6q.34. +1.6t.34. +8.6F.34. +c.6H.34. +8.6M.34. +1.86.34. +1.8d.34. +0.35.35. +b.36.35. +0.35.37. +d.38.35. +5.39.35. +5.3d.35. +0.3h.35. +5.3i.35. +0.35.3m. +5.3p.35. +a.3v.35. +a.3y.35. +2.3z.35. +5.3A.35. +2.3B.35. +5.3C.35. +a.3F.35. +5.3G.35. +0.35.3M. +0.35.3Q. +5.40.35. +0.35.47. +5.4d.35. +0.35.4O. +3.4Q.35. +7.4T.35. +0.35.4V. +5.51.35. +0.52.35. +a.5a.35. +2.5b.35. +5.5d.35. +5.5B.35. +5.5C.35. +5.5F.35. +0.5H.35. +0.5I.35. +f.35.5K. +7.5M.35. +5.5O.35. +5.5R.35. +3.35.5U. +3.5W.35. +1.5Y.35. +5.5Z.35. +5.5/.35. +0.60.35. +2.62.35. +2.66.35. +a.6b.35. +c.6e.35. +0.35.6F. +0.6M.35. +d.6+.35. +d.71.35. +5.79.35. +0.35.7D. +d.2p.35. +d.2p.35. +b.36.36. +b.36.37. +b.36.38. +b.36.39. +b.36.3d. +b.36.3h. +b.36.3i. +b.36.3m. +b.36.3p. +a.3v.36. +a.3y.36. +2.3z.36. +5.3A.36. +2.3B.36. +3.36.3C. +a.3F.36. +5.3G.36. +b.36.3M. +b.36.3Q. +5.40.36. +b.36.47. +b.36.4d. +b.36.4O. +b.36.4Q. +7.4T.36. +b.36.4V. +b.36.51. +3.36.52. +a.5a.36. +2.5b.36. +5.5d.36. +b.36.5B. +b.36.5C. +3.36.5F. +b.36.5H. +b.36.5I. +f.36.5K. +7.5M.36. +b.36.5O. +b.36.5R. +b.36.5U. +3.36.5W. +1.5Y.36. +b.36.5Z. +b.36.5/. +b.36.60. +2.62.36. +2.66.36. +a.6b.36. +c.6e.36. +b.36.6F. +b.36.6M. +b.36.6+. +b.36.71. +5.79.36. +b.36.7D. +3.36.2p. +3.36.2p. +0.37.37. +0.38.37. +5.39.37. +5.3d.37. +0.3h.37. +5.3i.37. +0.37.3m. +5.3p.37. +a.3v.37. +a.3y.37. +2.3z.37. +5.3A.37. +2.3B.37. +5.3C.37. +a.3F.37. +5.3G.37. +0.37.3M. +0.37.3Q. +5.40.37. +0.37.47. +5.4d.37. +0.37.4O. +0.4Q.37. +7.4T.37. +3.37.4V. +5.51.37. +0.52.37. +a.5a.37. +2.5b.37. +5.5d.37. +5.5B.37. +5.5C.37. +5.5F.37. +0.5H.37. +0.5I.37. +f.37.5K. +7.5M.37. +5.5O.37. +5.5R.37. +3.37.5U. +0.5W.37. +1.5Y.37. +5.5Z.37. +5.5/.37. +0.60.37. +2.62.37. +2.66.37. +a.6b.37. +c.6e.37. +0.37.6F. +0.6M.37. +0.6+.37. +0.71.37. +5.79.37. +0.37.7D. +0.2p.37. +0.2p.37. +0.38.38. +5.39.38. +5.3d.38. +d.38.3h. +5.3i.38. +0.38.3m. +5.3p.38. +a.3v.38. +a.3y.38. +2.3z.38. +5.3A.38. +2.3B.38. +5.3C.38. +a.3F.38. +5.3G.38. +0.38.3M. +0.38.3Q. +5.40.38. +0.38.47. +5.4d.38. +0.38.4O. +d.38.4Q. +5.4T.38. +0.38.4V. +d.38.51. +0.52.38. +a.5a.38. +2.5b.38. +5.5d.38. +5.5B.38. +5.5C.38. +5.5F.38. +0.38.5H. +0.5I.38. +f.38.5K. +7.5M.38. +5.5O.38. +0.38.5R. +0.38.5U. +0.38.5W. +1.5Y.38. +5.5Z.38. +0.38.5/. +0.60.38. +2.62.38. +2.66.38. +a.6b.38. +c.6e.38. +0.38.6F. +0.6M.38. +0.6+.38. +0.38.71. +5.79.38. +0.38.7D. +0.2p.38. +0.2p.38. +5.39.39. +5.39.3d. +5.39.3h. +5.39.3i. +5.39.3m. +5.39.3p. +a.3v.39. +a.3y.39. +2.3z.39. +5.3A.39. +2.3B.39. +5.39.3C. +a.3F.39. +5.3G.39. +5.39.3M. +5.39.3Q. +5.40.39. +5.39.47. +5.39.4d. +5.39.4O. +5.39.4Q. +7.4T.39. +5.39.4V. +5.39.51. +5.39.52. +a.5a.39. +2.5b.39. +5.5d.39. +5.39.5B. +5.39.5C. +5.39.5F. +5.39.5H. +5.39.5I. +f.39.5K. +7.5M.39. +5.39.5O. +5.39.5R. +5.39.5U. +5.39.5W. +1.5Y.39. +5.39.5Z. +5.39.5/. +5.39.60. +2.62.39. +2.66.39. +a.6b.39. +c.6e.39. +5.39.6F. +5.39.6M. +5.39.6+. +5.39.71. +5.79.39. +5.39.7D. +5.39.2p. +5.39.2p. +1.51.3a. +5.5I.3a. +6.5K.3a. +7.5M.3a. +1.5Y.3a. +1.5+.3a. +4.60.3a. +5.62.3a. +1.6q.3a. +1.6t.3a. +8.6F.3a. +c.6H.3a. +8.6M.3a. +1.7B.3a. +1.7R.3a. +1.86.3a. +1.8d.3a. +1.51.3c. +5.5I.3c. +6.5K.3c. +7.5M.3c. +1.5Y.3c. +1.5+.3c. +4.60.3c. +5.62.3c. +1.6q.3c. +1.6t.3c. +8.6F.3c. +c.6H.3c. +8.6M.3c. +1.7B.3c. +1.7R.3c. +1.86.3c. +1.8d.3c. +5.3d.3d. +5.3d.3h. +5.3i.3d. +5.3d.3m. +5.3d.3p. +a.3v.3d. +a.3y.3d. +2.3z.3d. +5.3A.3d. +2.3B.3d. +5.3d.3C. +a.3F.3d. +5.3G.3d. +5.3d.3M. +5.3d.3Q. +5.40.3d. +5.3d.47. +5.3d.4d. +5.3d.4O. +5.3d.4Q. +5.4T.3d. +5.3d.4V. +5.3d.51. +5.3d.52. +a.5a.3d. +2.5b.3d. +5.3d.5B. +5.3d.5C. +5.5F.3d. +5.3d.5H. +5.3d.5I. +f.3d.5K. +7.5M.3d. +5.3d.5O. +5.3d.5R. +5.3d.5U. +5.3d.5W. +1.5Y.3d. +5.3d.5Z. +5.3d.5/. +5.3d.60. +2.62.3d. +2.66.3d. +a.6b.3d. +c.6e.3d. +5.3d.6F. +0.6M.3d. +5.3d.6+. +5.3d.71. +5.79.3d. +5.3d.7D. +5.3d.2p. +5.3d.2p. +1.51.3e. +1.5v.3e. +1.5I.3e. +1.5M.3e. +1.5Y.3e. +1.5+.3e. +1.60.3e. +1.62.3e. +1.6q.3e. +1.6t.3e. +1.6F.3e. +1.6H.3e. +1.6M.3e. +1.7B.3e. +1.86.3e. +1.8d.3e. +1.51.3f. +1.5v.3f. +5.5I.3f. +6.5K.3f. +7.5M.3f. +1.5Y.3f. +1.5+.3f. +4.60.3f. +5.62.3f. +1.6q.3f. +1.6t.3f. +8.6F.3f. +c.6H.3f. +8.6M.3f. +1.7B.3f. +1.86.3f. +1.8d.3f. +1.51.3g. +1.5v.3g. +8.5I.3g. +8.5K.3g. +7.5M.3g. +1.5Y.3g. +1.5+.3g. +8.60.3g. +8.62.3g. +1.6q.3g. +1.6t.3g. +8.6F.3g. +c.6H.3g. +8.6M.3g. +1.7B.3g. +1.86.3g. +1.8d.3g. +0.3h.3h. +5.3i.3h. +0.3h.3m. +5.3p.3h. +a.3v.3h. +a.3y.3h. +2.3z.3h. +5.3A.3h. +2.3B.3h. +5.3C.3h. +a.3F.3h. +5.3G.3h. +0.3h.3M. +0.3h.3Q. +5.40.3h. +0.3h.47. +5.4d.3h. +0.3h.4O. +0.3h.4Q. +5.4T.3h. +0.3h.4V. +0.3h.51. +0.52.3h. +a.5a.3h. +2.5b.3h. +5.5d.3h. +5.5B.3h. +5.5C.3h. +5.5F.3h. +0.5H.3h. +0.5I.3h. +f.3h.5K. +7.5M.3h. +5.5O.3h. +0.3h.5R. +0.3h.5U. +0.5W.3h. +1.5Y.3h. +5.5Z.3h. +5.5/.3h. +0.60.3h. +2.62.3h. +2.66.3h. +a.6b.3h. +c.6e.3h. +0.3h.6F. +0.6M.3h. +d.6+.3h. +0.3h.71. +5.79.3h. +0.3h.7D. +d.2p.3h. +d.2p.3h. +5.3i.3i. +5.3i.3m. +5.3i.3p. +a.3v.3i. +a.3y.3i. +2.3z.3i. +5.3A.3i. +2.3B.3i. +5.3i.3C. +a.3F.3i. +5.3G.3i. +5.3i.3M. +5.3i.3Q. +5.40.3i. +5.3i.47. +5.3i.4d. +5.3i.4O. +5.3i.4Q. +5.4T.3i. +5.3i.4V. +5.3i.51. +5.3i.52. +a.5a.3i. +2.5b.3i. +5.5d.3i. +5.3i.5B. +5.3i.5C. +5.5F.3i. +5.3i.5H. +5.3i.5I. +f.3i.5K. +7.5M.3i. +5.3i.5O. +5.3i.5R. +5.3i.5U. +5.3i.5W. +1.5Y.3i. +5.3i.5Z. +5.3i.5/. +5.3i.60. +2.62.3i. +2.66.3i. +a.6b.3i. +c.6e.3i. +5.3i.6F. +0.6M.3i. +5.3i.6+. +5.3i.71. +5.79.3i. +5.3i.7D. +5.3i.2p. +5.3i.2p. +1.4s.3j. +1.51.3j. +1.5v.3j. +1.5I.3j. +1.5M.3j. +1.5Y.3j. +1.5+.3j. +1.60.3j. +1.62.3j. +1.6q.3j. +1.6t.3j. +1.6F.3j. +1.6H.3j. +1.6M.3j. +1.7B.3j. +1.86.3j. +1.8d.3j. +1.51.3k. +1.5v.3k. +8.5I.3k. +8.5K.3k. +7.5M.3k. +1.5Y.3k. +1.5+.3k. +5.60.3k. +5.62.3k. +1.6q.3k. +1.6t.3k. +8.6F.3k. +c.6H.3k. +8.6M.3k. +1.7B.3k. +1.86.3k. +1.8d.3k. +1.51.3l. +1.5v.3l. +5.5I.3l. +6.5K.3l. +7.5M.3l. +1.5Y.3l. +1.5+.3l. +4.60.3l. +5.62.3l. +1.6q.3l. +1.6t.3l. +8.6F.3l. +c.6H.3l. +8.6M.3l. +1.7B.3l. +1.86.3l. +1.8d.3l. +5.3m.3m. +5.3p.3m. +a.3v.3m. +a.3y.3m. +2.3z.3m. +5.3A.3m. +2.3B.3m. +5.3C.3m. +a.3F.3m. +5.3G.3m. +5.3m.3M. +5.3m.3Q. +5.40.3m. +5.3m.47. +5.4d.3m. +5.3m.4O. +0.4Q.3m. +7.4T.3m. +5.3m.4V. +5.51.3m. +0.52.3m. +a.5a.3m. +2.5b.3m. +5.5d.3m. +5.5B.3m. +5.5C.3m. +5.5F.3m. +0.5H.3m. +0.5I.3m. +f.3m.5K. +7.5M.3m. +5.5O.3m. +5.5R.3m. +5.3m.5U. +3.5W.3m. +1.5Y.3m. +5.5Z.3m. +5.5/.3m. +0.60.3m. +2.62.3m. +2.66.3m. +a.6b.3m. +c.6e.3m. +0.6F.3m. +0.6M.3m. +0.6+.3m. +0.71.3m. +5.79.3m. +5.3m.7D. +0.2p.3m. +0.2p.3m. +1.51.3n. +1.5v.3n. +8.5I.3n. +8.5K.3n. +7.5M.3n. +1.5Y.3n. +1.5+.3n. +5.60.3n. +8.62.3n. +1.6q.3n. +1.6t.3n. +8.6F.3n. +c.6H.3n. +8.6M.3n. +1.7B.3n. +1.86.3n. +1.8d.3n. +1.51.3o. +1.5v.3o. +8.5I.3o. +8.5K.3o. +7.5M.3o. +1.5Y.3o. +1.5+.3o. +8.60.3o. +8.62.3o. +1.6q.3o. +1.6t.3o. +8.6F.3o. +c.6H.3o. +8.6M.3o. +1.7B.3o. +1.86.3o. +1.8d.3o. +5.3p.3p. +a.3v.3p. +a.3y.3p. +2.3z.3p. +5.3A.3p. +2.3B.3p. +5.3p.3C. +a.3F.3p. +5.3G.3p. +5.3p.3M. +5.3p.3Q. +5.40.3p. +5.3p.47. +5.3p.4d. +5.3p.4O. +5.3p.4Q. +5.4T.3p. +5.3p.4V. +5.3p.51. +5.3p.52. +a.5a.3p. +2.5b.3p. +5.5d.3p. +5.3p.5B. +5.3p.5C. +5.5F.3p. +5.3p.5H. +5.3p.5I. +f.3p.5K. +7.5M.3p. +5.3p.5O. +5.3p.5R. +5.3p.5U. +5.3p.5W. +1.5Y.3p. +5.3p.5Z. +5.3p.5/. +5.3p.60. +2.62.3p. +2.66.3p. +a.6b.3p. +c.6e.3p. +5.3p.6F. +0.6M.3p. +5.3p.6+. +5.3p.71. +5.79.3p. +5.3p.7D. +0.2p.3p. +0.2p.3p. +1.51.3q. +1.5v.3q. +8.5I.3q. +8.5K.3q. +7.5M.3q. +1.5Y.3q. +1.5+.3q. +5.60.3q. +5.62.3q. +1.6q.3q. +1.6t.3q. +8.6F.3q. +c.6H.3q. +8.6M.3q. +1.7B.3q. +1.86.3q. +1.8d.3q. +1.51.3r. +1.5v.3r. +6.5I.3r. +6.5K.3r. +7.5M.3r. +1.5Y.3r. +1.5+.3r. +4.60.3r. +5.62.3r. +1.6q.3r. +1.6t.3r. +8.6F.3r. +c.6H.3r. +8.6M.3r. +1.7B.3r. +1.86.3r. +1.8d.3r. +1.51.3s. +1.5v.3s. +4.5I.3s. +3.5K.3s. +7.5M.3s. +1.5Y.3s. +1.5+.3s. +4.60.3s. +4.62.3s. +1.6q.3s. +1.6t.3s. +4.6F.3s. +c.6H.3s. +4.6M.3s. +1.7B.3s. +1.86.3s. +1.8d.3s. +1.51.3t. +1.5v.3t. +8.5I.3t. +8.5K.3t. +7.5M.3t. +1.5Y.3t. +1.5+.3t. +5.60.3t. +8.62.3t. +1.6q.3t. +1.6t.3t. +8.6F.3t. +c.6H.3t. +8.6M.3t. +1.7B.3t. +1.86.3t. +1.8d.3t. +1.51.3u. +1.5v.3u. +8.5I.3u. +8.5K.3u. +7.5M.3u. +1.5Y.3u. +1.5+.3u. +5.60.3u. +8.62.3u. +1.6q.3u. +1.6t.3u. +8.6F.3u. +c.6H.3u. +8.6M.3u. +1.7B.3u. +1.7R.3u. +1.86.3u. +1.8d.3u. +a.3v.3v. +a.3v.3y. +a.3v.3z. +a.3v.3A. +a.3v.3B. +a.3v.3C. +a.3v.3F. +a.3v.3G. +a.3v.3M. +a.3v.3Q. +a.3v.40. +a.3v.47. +a.3v.4d. +a.3v.4O. +a.3v.4Q. +7.4T.3v. +a.3v.4V. +a.3v.51. +a.3v.52. +a.3v.5a. +a.3v.5b. +a.3v.5d. +a.3v.5B. +a.3v.5C. +a.3v.5F. +a.3v.5H. +a.3v.5I. +f.3v.5K. +7.5M.3v. +a.3v.5O. +a.3v.5R. +a.3v.5U. +a.3v.5W. +1.5Y.3v. +a.3v.5Z. +a.3v.5/. +a.3v.60. +a.3v.62. +a.3v.66. +a.3v.6b. +c.6e.3v. +a.3v.6F. +a.3v.6M. +a.3v.6+. +a.3v.71. +a.3v.79. +a.3v.7D. +a.3v.2p. +a.3v.2p. +1.51.3w. +1.5v.3w. +5.5I.3w. +6.5K.3w. +7.5M.3w. +1.5Y.3w. +1.5+.3w. +4.60.3w. +5.62.3w. +1.6q.3w. +1.6t.3w. +3.6F.3w. +c.6H.3w. +8.6M.3w. +1.7B.3w. +1.86.3w. +1.8d.3w. +1.51.3x. +1.5v.3x. +6.5I.3x. +6.5K.3x. +7.5M.3x. +1.5Y.3x. +1.5+.3x. +4.60.3x. +5.62.3x. +1.6q.3x. +1.6t.3x. +8.6F.3x. +c.6H.3x. +8.6M.3x. +1.7B.3x. +1.86.3x. +1.8d.3x. +a.3y.3y. +a.3y.3z. +a.3y.3A. +a.3y.3B. +a.3y.3C. +a.3F.3y. +a.3y.3G. +a.3y.3M. +a.3y.3Q. +a.3y.40. +a.3y.47. +a.3y.4d. +a.3y.4O. +a.3y.4Q. +7.4T.3y. +a.3y.4V. +a.3y.51. +a.3y.52. +a.3y.5a. +a.3y.5b. +a.3y.5d. +a.3y.5B. +a.3y.5C. +a.3y.5F. +a.3y.5H. +a.3y.5I. +f.3y.5K. +7.5M.3y. +a.3y.5O. +a.3y.5R. +a.3y.5U. +a.3y.5W. +1.5Y.3y. +a.3y.5Z. +a.3y.5/. +a.3y.60. +a.3y.62. +a.3y.66. +a.6b.3y. +c.6e.3y. +a.3y.6F. +a.3y.6M. +a.3y.6+. +a.3y.71. +a.3y.79. +a.3y.7D. +a.3y.2p. +a.3y.2p. +2.3z.3z. +2.3z.3A. +2.3B.3z. +2.3z.3C. +a.3F.3z. +2.3z.3G. +2.3z.3M. +2.3z.3Q. +2.3z.40. +2.3z.47. +2.3z.4d. +2.3z.4O. +2.3z.4Q. +7.4T.3z. +2.3z.4V. +2.3z.51. +2.3z.52. +a.5a.3z. +2.5b.3z. +2.3z.5d. +2.3z.5B. +2.3z.5C. +2.3z.5F. +2.3z.5H. +2.3z.5I. +f.3z.5K. +7.5M.3z. +2.3z.5O. +2.3z.5R. +2.3z.5U. +2.3z.5W. +1.5Y.3z. +2.3z.5Z. +2.3z.5/. +2.3z.60. +2.3z.62. +2.3z.66. +a.6b.3z. +c.6e.3z. +2.3z.6F. +2.3z.6M. +2.3z.6+. +2.3z.71. +2.3z.79. +2.3z.7D. +2.3z.2p. +2.3z.2p. +5.3A.3A. +2.3B.3A. +5.3A.3C. +a.3F.3A. +5.3G.3A. +5.3A.3M. +5.3A.3Q. +5.40.3A. +5.3A.47. +5.3A.4d. +5.3A.4O. +5.3A.4Q. +7.4T.3A. +5.3A.4V. +5.3A.51. +5.3A.52. +a.5a.3A. +2.5b.3A. +5.5d.3A. +5.3A.5B. +5.3A.5C. +5.3A.5F. +5.3A.5H. +5.3A.5I. +f.3A.5K. +7.5M.3A. +5.3A.5O. +5.3A.5R. +5.3A.5U. +5.3A.5W. +1.5Y.3A. +5.3A.5Z. +5.3A.5/. +5.3A.60. +2.62.3A. +2.66.3A. +a.6b.3A. +c.6e.3A. +5.3A.6F. +5.3A.6M. +5.3A.6+. +5.3A.71. +5.79.3A. +5.3A.7D. +5.3A.2p. +5.3A.2p. +2.3B.3B. +2.3B.3C. +a.3F.3B. +2.3B.3G. +2.3B.3M. +2.3B.3Q. +2.3B.40. +2.3B.47. +2.3B.4d. +2.3B.4O. +2.3B.4Q. +7.4T.3B. +2.3B.4V. +2.3B.51. +2.3B.52. +a.5a.3B. +2.5b.3B. +2.3B.5d. +2.3B.5B. +2.3B.5C. +2.3B.5F. +2.3B.5H. +2.3B.5I. +f.3B.5K. +7.5M.3B. +2.3B.5O. +2.3B.5R. +2.3B.5U. +2.3B.5W. +1.5Y.3B. +2.3B.5Z. +2.3B.5/. +2.3B.60. +2.3B.62. +2.3B.66. +a.6b.3B. +c.6e.3B. +2.3B.6F. +2.3B.6M. +2.3B.6+. +2.3B.71. +2.3B.79. +2.3B.7D. +2.3B.2p. +2.3B.2p. +5.3C.3C. +a.3F.3C. +5.3G.3C. +5.3C.3M. +5.3C.3Q. +5.40.3C. +5.3C.47. +5.4d.3C. +5.3C.4O. +5.3C.4Q. +5.4T.3C. +5.3C.4V. +5.3C.51. +5.3C.52. +a.5a.3C. +2.5b.3C. +5.5d.3C. +5.3C.5B. +5.3C.5C. +5.5F.3C. +5.3C.5H. +0.5I.3C. +f.3C.5K. +7.5M.3C. +5.5O.3C. +5.3C.5R. +5.3C.5U. +5.3C.5W. +1.5Y.3C. +5.3C.5Z. +5.3C.5/. +0.60.3C. +2.62.3C. +2.66.3C. +a.6b.3C. +c.6e.3C. +5.3C.6F. +0.6M.3C. +5.3C.6+. +5.3C.71. +5.79.3C. +5.3C.7D. +0.2p.3C. +0.2p.3C. +1.4s.3D. +1.51.3D. +1.5v.3D. +5.5I.3D. +6.5K.3D. +7.5M.3D. +1.5Y.3D. +1.5+.3D. +4.60.3D. +5.62.3D. +1.6q.3D. +1.6t.3D. +8.6F.3D. +c.6H.3D. +8.6M.3D. +1.7B.3D. +1.86.3D. +1.8d.3D. +1.51.3E. +1.5v.3E. +6.5I.3E. +6.5K.3E. +7.5M.3E. +1.5Y.3E. +1.5+.3E. +4.60.3E. +5.62.3E. +1.6q.3E. +1.6t.3E. +3.6F.3E. +c.6H.3E. +8.6M.3E. +1.7B.3E. +1.86.3E. +1.8d.3E. +a.3F.3F. +a.3F.3G. +a.3F.3M. +a.3F.3Q. +a.3F.40. +a.3F.47. +a.3F.4d. +a.3F.4O. +a.3F.4Q. +7.4T.3F. +a.3F.4V. +a.3F.51. +a.3F.52. +a.3F.5a. +a.3F.5b. +a.3F.5d. +a.3F.5B. +a.3F.5C. +a.3F.5F. +a.3F.5H. +a.3F.5I. +f.3F.5K. +7.5M.3F. +a.3F.5O. +a.3F.5R. +a.3F.5U. +a.3F.5W. +1.5Y.3F. +a.3F.5Z. +a.3F.5/. +a.3F.60. +a.3F.62. +a.3F.66. +a.6b.3F. +c.6e.3F. +a.3F.6F. +a.3F.6M. +a.3F.6+. +a.3F.71. +a.3F.79. +a.3F.7D. +a.3F.2p. +a.3F.2p. +5.3G.3G. +5.3G.3M. +5.3G.3Q. +5.40.3G. +5.3G.47. +5.3G.4d. +5.3G.4O. +5.3G.4Q. +7.4T.3G. +5.3G.4V. +5.3G.51. +5.3G.52. +a.5a.3G. +2.5b.3G. +5.5d.3G. +5.3G.5B. +5.3G.5C. +5.3G.5F. +5.3G.5H. +5.3G.5I. +f.3G.5K. +7.5M.3G. +5.3G.5O. +5.3G.5R. +5.3G.5U. +5.3G.5W. +1.5Y.3G. +5.3G.5Z. +5.3G.5/. +5.3G.60. +2.62.3G. +2.66.3G. +a.6b.3G. +c.6e.3G. +5.3G.6F. +5.3G.6M. +5.3G.6+. +5.3G.71. +5.3G.79. +5.3G.7D. +5.3G.2p. +5.3G.2p. +1.4s.3H. +1.51.3H. +1.5v.3H. +6.5I.3H. +6.5K.3H. +7.5M.3H. +1.5Y.3H. +1.5+.3H. +4.60.3H. +5.62.3H. +1.6q.3H. +1.6t.3H. +8.6F.3H. +c.6H.3H. +8.6M.3H. +1.7B.3H. +1.7R.3H. +1.86.3H. +1.8d.3H. +1.5v.3I. +6.5I.3I. +6.5K.3I. +7.5M.3I. +1.5Y.3I. +1.5+.3I. +4.60.3I. +5.62.3I. +1.6q.3I. +1.6t.3I. +8.6F.3I. +c.6H.3I. +8.6M.3I. +1.7B.3I. +1.86.3I. +1.8d.3I. +1.51.3J. +1.5v.3J. +6.5I.3J. +6.5K.3J. +7.5M.3J. +1.5Y.3J. +1.5+.3J. +4.60.3J. +5.62.3J. +1.6q.3J. +1.6t.3J. +8.6F.3J. +c.6H.3J. +8.6M.3J. +1.7B.3J. +1.86.3J. +1.8d.3J. +1.4s.3K. +1.51.3K. +1.5v.3K. +5.5I.3K. +6.5K.3K. +7.5M.3K. +1.5Y.3K. +1.5+.3K. +4.60.3K. +8.62.3K. +1.6q.3K. +1.6t.3K. +8.6F.3K. +c.6H.3K. +8.6M.3K. +1.7B.3K. +1.7R.3K. +1.86.3K. +1.8d.3K. +1.4s.3L. +1.51.3L. +1.5v.3L. +6.5I.3L. +6.5K.3L. +7.5M.3L. +1.5Y.3L. +1.5+.3L. +4.60.3L. +5.62.3L. +1.6q.3L. +1.6t.3L. +3.6F.3L. +c.6H.3L. +8.6M.3L. +1.7B.3L. +1.7R.3L. +1.86.3L. +1.8d.3L. +0.3M.3M. +5.3Q.3M. +5.40.3M. +0.47.3M. +5.4d.3M. +5.4O.3M. +0.4Q.3M. +7.4T.3M. +0.4V.3M. +5.51.3M. +0.52.3M. +a.5a.3M. +2.5b.3M. +5.5d.3M. +5.5B.3M. +5.5C.3M. +5.5F.3M. +0.5H.3M. +0.5I.3M. +f.5K.3M. +7.5M.3M. +5.5O.3M. +5.5R.3M. +0.5U.3M. +0.5W.3M. +1.5Y.3M. +5.5Z.3M. +5.5/.3M. +0.60.3M. +2.62.3M. +2.66.3M. +a.6b.3M. +c.6e.3M. +0.6F.3M. +0.6M.3M. +0.6+.3M. +0.71.3M. +5.79.3M. +0.7D.3M. +0.2p.3M. +0.2p.3M. +1.4s.3N. +1.51.3N. +1.5v.3N. +6.5I.3N. +6.5K.3N. +7.5M.3N. +1.5Y.3N. +1.5+.3N. +4.60.3N. +8.62.3N. +1.6q.3N. +1.6t.3N. +3.6F.3N. +c.6H.3N. +8.6M.3N. +1.7B.3N. +1.7R.3N. +1.86.3N. +1.8d.3N. +1.51.3O. +1.5v.3O. +5.5I.3O. +6.5K.3O. +7.5M.3O. +1.5Y.3O. +1.5+.3O. +4.60.3O. +5.62.3O. +1.6q.3O. +1.6t.3O. +8.6F.3O. +c.6H.3O. +8.6M.3O. +1.7B.3O. +1.7R.3O. +1.86.3O. +1.8d.3O. +1.51.3P. +1.5v.3P. +5.5I.3P. +6.5K.3P. +7.5M.3P. +1.5Y.3P. +1.5+.3P. +4.60.3P. +5.62.3P. +1.6q.3P. +1.6t.3P. +8.6F.3P. +c.6H.3P. +8.6M.3P. +1.7B.3P. +1.86.3P. +1.8d.3P. +5.3Q.3Q. +5.40.3Q. +0.47.3Q. +5.4d.3Q. +5.4O.3Q. +0.4Q.3Q. +7.4T.3Q. +0.4V.3Q. +5.51.3Q. +0.52.3Q. +a.5a.3Q. +2.5b.3Q. +5.5d.3Q. +5.5B.3Q. +5.5C.3Q. +5.5F.3Q. +0.5H.3Q. +0.5I.3Q. +f.5K.3Q. +7.5M.3Q. +5.5O.3Q. +5.5R.3Q. +0.5U.3Q. +0.5W.3Q. +1.5Y.3Q. +5.5Z.3Q. +5.5/.3Q. +0.60.3Q. +2.62.3Q. +2.66.3Q. +a.6b.3Q. +c.6e.3Q. +0.6F.3Q. +0.6M.3Q. +0.6+.3Q. +0.71.3Q. +5.79.3Q. +0.7D.3Q. +0.2p.3Q. +0.2p.3Q. +1.4s.3R. +1.51.3R. +1.5v.3R. +6.5I.3R. +6.5K.3R. +7.5M.3R. +1.5Y.3R. +1.5+.3R. +4.60.3R. +5.62.3R. +1.6q.3R. +1.6t.3R. +3.6F.3R. +c.6H.3R. +8.6M.3R. +1.7B.3R. +1.7R.3R. +1.86.3R. +1.8d.3R. +1.51.3S. +1.5v.3S. +5.5I.3S. +6.5K.3S. +7.5M.3S. +1.5Y.3S. +1.5+.3S. +4.60.3S. +5.62.3S. +1.6q.3S. +1.6t.3S. +8.6F.3S. +c.6H.3S. +8.6M.3S. +1.7B.3S. +1.7R.3S. +1.86.3S. +1.8d.3S. +1.4s.3T. +1.51.3T. +1.5v.3T. +1.5I.3T. +1.5M.3T. +1.5Y.3T. +1.5+.3T. +1.60.3T. +1.62.3T. +1.6q.3T. +1.6t.3T. +1.6F.3T. +1.6H.3T. +1.6M.3T. +1.7B.3T. +1.86.3T. +1.8d.3T. +1.4s.3U. +1.51.3U. +1.5v.3U. +5.5I.3U. +6.5K.3U. +7.5M.3U. +1.5Y.3U. +1.5+.3U. +4.60.3U. +5.62.3U. +1.6q.3U. +1.6t.3U. +8.6F.3U. +c.6H.3U. +8.6M.3U. +1.7B.3U. +1.7R.3U. +1.86.3U. +1.8d.3U. +1.51.3V. +1.5v.3V. +5.5I.3V. +6.5K.3V. +7.5M.3V. +1.5Y.3V. +1.5+.3V. +4.60.3V. +8.62.3V. +1.6q.3V. +1.6t.3V. +8.6F.3V. +c.6H.3V. +8.6M.3V. +1.7B.3V. +1.7R.3V. +1.86.3V. +1.8d.3V. +1.51.3W. +1.5v.3W. +6.5I.3W. +6.5K.3W. +7.5M.3W. +1.5Y.3W. +1.5+.3W. +4.60.3W. +8.62.3W. +1.6q.3W. +1.6t.3W. +8.6F.3W. +c.6H.3W. +8.6M.3W. +1.7B.3W. +1.7R.3W. +1.86.3W. +1.8d.3W. +1.4s.3X. +1.51.3X. +1.5v.3X. +5.5I.3X. +6.5K.3X. +7.5M.3X. +1.5Y.3X. +1.5+.3X. +4.60.3X. +5.62.3X. +1.6q.3X. +1.6t.3X. +8.6F.3X. +c.6H.3X. +8.6M.3X. +1.7B.3X. +1.86.3X. +1.8d.3X. +1.4s.3Y. +1.51.3Y. +1.5v.3Y. +5.5I.3Y. +6.5K.3Y. +7.5M.3Y. +1.5Y.3Y. +1.5+.3Y. +4.60.3Y. +5.62.3Y. +1.6q.3Y. +1.6t.3Y. +8.6F.3Y. +c.6H.3Y. +8.6M.3Y. +1.7B.3Y. +1.7R.3Y. +1.86.3Y. +1.8d.3Y. +1.51.3Z. +1.5v.3Z. +6.5I.3Z. +6.5K.3Z. +7.5M.3Z. +1.5Y.3Z. +1.5+.3Z. +4.60.3Z. +8.62.3Z. +1.6q.3Z. +1.6t.3Z. +8.6F.3Z. +c.6H.3Z. +8.6M.3Z. +1.7B.3Z. +1.86.3Z. +1.8d.3Z. +1.4s.3+. +1.51.3+. +1.5v.3+. +1.5I.3+. +1.5M.3+. +1.5Y.3+. +1.5+.3+. +1.60.3+. +1.62.3+. +1.6q.3+. +1.6t.3+. +1.6F.3+. +1.6H.3+. +1.6M.3+. +1.7B.3+. +1.7R.3+. +1.86.3+. +1.8d.3+. +1.4s.3/. +1.51.3/. +1.5v.3/. +1.5I.3/. +1.5M.3/. +1.5Y.3/. +1.5+.3/. +1.60.3/. +1.62.3/. +1.6q.3/. +1.6t.3/. +1.6F.3/. +1.6H.3/. +1.6M.3/. +1.7B.3/. +1.7R.3/. +1.86.3/. +1.8d.3/. +5.40.40. +5.40.47. +5.40.4d. +5.40.4O. +5.40.4Q. +7.4T.40. +5.40.4V. +5.40.51. +5.40.52. +a.5a.40. +2.5b.40. +5.40.5d. +5.40.5B. +5.40.5C. +5.40.5F. +5.40.5H. +5.40.5I. +f.40.5K. +7.5M.40. +5.40.5O. +5.40.5R. +5.40.5U. +5.40.5W. +1.5Y.40. +5.40.5Z. +5.40.5/. +5.40.60. +2.62.40. +2.66.40. +a.6b.40. +c.6e.40. +5.40.6F. +5.40.6M. +5.40.6+. +5.40.71. +5.40.79. +5.40.7D. +5.40.2p. +5.40.2p. +1.51.41. +1.5v.41. +6.5I.41. +6.5K.41. +7.5M.41. +1.5Y.41. +1.5+.41. +4.60.41. +5.62.41. +1.6q.41. +1.6t.41. +3.6F.41. +c.6H.41. +8.6M.41. +1.7B.41. +1.86.41. +1.8d.41. +1.4s.42. +1.51.42. +1.5v.42. +6.5I.42. +6.5K.42. +7.5M.42. +1.5Y.42. +1.5+.42. +4.60.42. +8.62.42. +1.6q.42. +1.6t.42. +8.6F.42. +c.6H.42. +8.6M.42. +1.7B.42. +1.7R.42. +1.86.42. +1.8d.42. +1.4s.43. +1.51.43. +1.5v.43. +5.5I.43. +6.5K.43. +7.5M.43. +1.5Y.43. +1.5+.43. +4.60.43. +5.62.43. +1.6q.43. +1.6t.43. +3.6F.43. +c.6H.43. +8.6M.43. +1.7B.43. +1.7R.43. +1.86.43. +1.8d.43. +1.51.44. +1.5v.44. +6.5I.44. +6.5K.44. +7.5M.44. +1.5Y.44. +1.5+.44. +4.60.44. +5.62.44. +1.6q.44. +1.6t.44. +8.6F.44. +c.6H.44. +8.6M.44. +1.7B.44. +1.86.44. +1.8d.44. +1.4s.45. +1.51.45. +1.5v.45. +6.5I.45. +6.5K.45. +7.5M.45. +1.5Y.45. +1.5+.45. +4.60.45. +5.62.45. +1.6q.45. +1.6t.45. +8.6F.45. +c.6H.45. +8.6M.45. +1.7B.45. +1.86.45. +1.8d.45. +0.47.47. +5.4d.47. +5.4O.47. +0.4Q.47. +7.4T.47. +0.4V.47. +5.51.47. +0.52.47. +a.5a.47. +2.5b.47. +5.5d.47. +5.5B.47. +5.5C.47. +5.5F.47. +0.5H.47. +0.5I.47. +f.5K.47. +7.5M.47. +5.5O.47. +5.5R.47. +0.5U.47. +0.5W.47. +1.5Y.47. +5.5Z.47. +5.5/.47. +0.60.47. +2.62.47. +2.66.47. +a.6b.47. +c.6e.47. +0.6F.47. +0.6M.47. +0.6+.47. +0.71.47. +5.79.47. +0.7D.47. +0.2p.47. +0.2p.47. +1.51.48. +1.5v.48. +5.5I.48. +6.5K.48. +7.5M.48. +1.5Y.48. +1.5+.48. +4.60.48. +8.62.48. +1.6q.48. +1.6t.48. +8.6F.48. +c.6H.48. +8.6M.48. +1.7B.48. +1.86.48. +1.8d.48. +1.4s.49. +1.51.49. +1.5v.49. +6.5I.49. +6.5K.49. +7.5M.49. +1.5Y.49. +1.5+.49. +4.60.49. +8.62.49. +1.6q.49. +1.6t.49. +3.6F.49. +c.6H.49. +8.6M.49. +1.7B.49. +1.7R.49. +1.86.49. +1.8d.49. +1.51.4a. +1.5v.4a. +1.5I.4a. +1.5M.4a. +1.5Y.4a. +1.5+.4a. +1.60.4a. +1.62.4a. +1.6q.4a. +1.6t.4a. +1.6F.4a. +1.6H.4a. +1.6M.4a. +1.7B.4a. +1.7R.4a. +1.86.4a. +1.8d.4a. +1.51.4b. +1.5v.4b. +6.5I.4b. +6.5K.4b. +7.5M.4b. +1.5Y.4b. +1.5+.4b. +4.60.4b. +5.62.4b. +1.6q.4b. +1.6t.4b. +8.6F.4b. +c.6H.4b. +8.6M.4b. +1.7B.4b. +1.86.4b. +1.8d.4b. +1.4s.4c. +1.51.4c. +1.5v.4c. +6.5I.4c. +6.5K.4c. +7.5M.4c. +1.5Y.4c. +1.5+.4c. +4.60.4c. +5.62.4c. +1.6q.4c. +1.6t.4c. +8.6F.4c. +c.6H.4c. +8.6M.4c. +1.7B.4c. +1.86.4c. +1.8d.4c. +5.4d.4d. +5.4d.4O. +5.4d.4Q. +5.4T.4d. +5.4d.4V. +5.4d.51. +5.4d.52. +a.5a.4d. +2.5b.4d. +5.5d.4d. +5.4d.5B. +5.4d.5C. +5.5F.4d. +5.4d.5H. +0.5I.4d. +f.4d.5K. +7.5M.4d. +5.5O.4d. +5.4d.5R. +5.4d.5U. +5.4d.5W. +1.5Y.4d. +5.4d.5Z. +5.4d.5/. +0.60.4d. +2.62.4d. +2.66.4d. +a.6b.4d. +c.6e.4d. +5.4d.6F. +0.6M.4d. +5.4d.6+. +5.4d.71. +5.79.4d. +5.4d.7D. +0.2p.4d. +0.2p.4d. +1.4s.4e. +1.51.4e. +1.5v.4e. +6.5I.4e. +6.5K.4e. +7.5M.4e. +1.5Y.4e. +1.5+.4e. +4.60.4e. +8.62.4e. +1.6q.4e. +1.6t.4e. +8.6F.4e. +c.6H.4e. +8.6M.4e. +1.7B.4e. +1.86.4e. +1.8d.4e. +1.51.4f. +1.5v.4f. +6.5I.4f. +6.5K.4f. +7.5M.4f. +1.5Y.4f. +1.5+.4f. +4.60.4f. +8.62.4f. +1.6q.4f. +1.6t.4f. +8.6F.4f. +c.6H.4f. +8.6M.4f. +1.7B.4f. +1.86.4f. +1.8d.4f. +1.51.4g. +1.5v.4g. +6.5I.4g. +6.5K.4g. +7.5M.4g. +1.5Y.4g. +1.5+.4g. +4.60.4g. +8.62.4g. +1.6q.4g. +1.6t.4g. +8.6F.4g. +c.6H.4g. +8.6M.4g. +1.7B.4g. +1.86.4g. +1.8d.4g. +1.4s.4h. +1.51.4h. +1.5v.4h. +5.5I.4h. +6.5K.4h. +7.5M.4h. +1.5Y.4h. +1.5+.4h. +4.60.4h. +8.62.4h. +1.6q.4h. +1.6t.4h. +3.6F.4h. +c.6H.4h. +8.6M.4h. +1.7B.4h. +1.7R.4h. +1.86.4h. +1.8d.4h. +1.51.4i. +1.5v.4i. +6.5I.4i. +6.5K.4i. +7.5M.4i. +1.5Y.4i. +1.5+.4i. +4.60.4i. +8.62.4i. +1.6q.4i. +1.6t.4i. +8.6F.4i. +c.6H.4i. +8.6M.4i. +1.7B.4i. +1.7R.4i. +1.86.4i. +1.8d.4i. +1.51.4j. +1.5v.4j. +6.5I.4j. +6.5K.4j. +7.5M.4j. +1.5Y.4j. +1.5+.4j. +4.60.4j. +8.62.4j. +1.6q.4j. +1.6t.4j. +8.6F.4j. +c.6H.4j. +8.6M.4j. +1.7B.4j. +1.86.4j. +1.8d.4j. +1.51.4k. +1.5v.4k. +8.5I.4k. +8.5K.4k. +7.5M.4k. +1.5Y.4k. +1.5+.4k. +8.60.4k. +8.62.4k. +1.6q.4k. +1.6t.4k. +3.6F.4k. +c.6H.4k. +8.6M.4k. +1.7B.4k. +1.86.4k. +1.8d.4k. +1.51.4l. +1.5v.4l. +6.5I.4l. +6.5K.4l. +7.5M.4l. +1.5Y.4l. +1.5+.4l. +4.60.4l. +8.62.4l. +1.6q.4l. +1.6t.4l. +8.6F.4l. +c.6H.4l. +8.6M.4l. +1.7B.4l. +1.7R.4l. +1.86.4l. +1.8d.4l. +1.51.4m. +1.5v.4m. +5.5I.4m. +6.5K.4m. +7.5M.4m. +1.5Y.4m. +1.5+.4m. +4.60.4m. +5.62.4m. +1.6q.4m. +1.6t.4m. +8.6F.4m. +c.6H.4m. +8.6M.4m. +1.7B.4m. +1.86.4m. +1.8d.4m. +1.4s.4n. +1.51.4n. +1.5v.4n. +6.5I.4n. +6.5K.4n. +7.5M.4n. +1.5Y.4n. +1.5+.4n. +4.60.4n. +5.62.4n. +1.6q.4n. +1.6t.4n. +3.6F.4n. +c.6H.4n. +8.6M.4n. +1.7B.4n. +1.7R.4n. +1.86.4n. +1.8d.4n. +1.4s.4o. +1.51.4o. +1.5v.4o. +6.5I.4o. +6.5K.4o. +7.5M.4o. +1.5Y.4o. +1.5+.4o. +4.60.4o. +5.62.4o. +1.6q.4o. +1.6t.4o. +8.6F.4o. +c.6H.4o. +8.6M.4o. +1.7B.4o. +1.7R.4o. +1.86.4o. +1.8d.4o. +1.51.4p. +1.5v.4p. +6.5I.4p. +6.5K.4p. +7.5M.4p. +1.5Y.4p. +1.5+.4p. +4.60.4p. +8.62.4p. +1.6q.4p. +1.6t.4p. +8.6F.4p. +c.6H.4p. +8.6M.4p. +1.7B.4p. +1.7R.4p. +1.86.4p. +1.8d.4p. +1.4s.4q. +1.51.4q. +1.5v.4q. +5.5I.4q. +6.5K.4q. +7.5M.4q. +1.5Y.4q. +1.5+.4q. +4.60.4q. +5.62.4q. +1.6q.4q. +1.6t.4q. +8.6F.4q. +c.6H.4q. +8.6M.4q. +1.7B.4q. +1.86.4q. +1.8d.4q. +1.51.4r. +1.5v.4r. +6.5I.4r. +6.5K.4r. +7.5M.4r. +1.5Y.4r. +1.5+.4r. +4.60.4r. +8.62.4r. +1.6q.4r. +1.6t.4r. +8.6F.4r. +c.6H.4r. +8.6M.4r. +1.7B.4r. +1.86.4r. +1.8d.4r. +1.4s.4s. +1.4s.4y. +1.4s.4z. +1.4s.4F. +1.4s.4G. +1.4s.4N. +1.4s.4R. +1.4s.4S. +1.4s.4T. +1.4s.4U. +1.4s.4Z. +1.4s.50. +1.51.4s. +1.4s.54. +1.4s.55. +1.4s.56. +1.4s.57. +1.4s.59. +1.4s.5e. +1.4s.5f. +1.4s.5g. +1.4s.5i. +1.4s.5l. +1.4s.5q. +1.4s.5t. +1.5v.4s. +1.4s.5y. +1.4s.5z. +1.4s.5G. +6.5I.4s. +6.5K.4s. +1.4s.5L. +7.5M.4s. +1.4s.5S. +1.4s.5T. +1.4s.5V. +1.5Y.4s. +1.5+.4s. +4.60.4s. +8.62.4s. +1.4s.64. +1.4s.67. +1.4s.6a. +1.4s.6c. +1.4s.6d. +1.4s.6e. +1.4s.6f. +1.4s.6g. +1.4s.6h. +1.4s.6i. +1.4s.6j. +1.4s.6k. +1.4s.6l. +1.4s.6m. +1.4s.6n. +1.4s.6o. +1.4s.6p. +1.6q.4s. +1.4s.6r. +1.4s.6s. +1.6t.4s. +1.4s.6u. +1.4s.6v. +1.4s.6w. +1.4s.6x. +1.4s.6A. +1.4s.6C. +1.4s.6D. +1.4s.6E. +8.6F.4s. +1.4s.6G. +c.6H.4s. +1.4s.6I. +1.4s.6K. +1.4s.6L. +8.6M.4s. +1.4s.6N. +1.4s.6O. +1.4s.6Q. +1.4s.6R. +1.4s.6S. +1.4s.6U. +1.4s.6Y. +1.4s.6Z. +1.4s.70. +1.4s.76. +1.4s.77. +1.4s.78. +1.4s.7c. +1.4s.7h. +1.4s.7j. +1.4s.7l. +1.4s.7o. +1.4s.7p. +1.4s.7v. +1.4s.7w. +1.4s.7A. +1.7B.4s. +1.4s.7F. +1.4s.7G. +1.4s.7H. +1.4s.7I. +1.4s.7J. +1.4s.7P. +1.4s.7W. +1.4s.7Y. +1.4s.81. +1.4s.82. +1.4s.83. +1.4s.84. +1.86.4s. +1.4s.87. +1.4s.8d. +1.4s.8g. +1.4s.8h. +1.4s.8i. +1.4s.8j. +1.4s.8k. +1.4s.8l. +1.4s.8m. +1.4s.8n. +1.4s.8o. +1.4s.8p. +1.4s.8q. +1.4s.8r. +1.4s.8s. +1.4s.8u. +1.4s.8v. +1.4s.8w. +1.4s.8x. +1.4s.8y. +1.4s.8z. +1.4s.8A. +1.4s.8B. +1.4s.8C. +1.4s.8D. +1.4s.8E. +1.4s.8F. +1.4s.8G. +1.4s.8H. +1.4s.8I. +1.4s.8J. +1.4s.3b. +1.4s.5x. +1.4s.7q. +1.51.4t. +1.5v.4t. +6.5I.4t. +6.5K.4t. +7.5M.4t. +1.5Y.4t. +1.5+.4t. +4.60.4t. +5.62.4t. +1.6q.4t. +1.6t.4t. +8.6F.4t. +c.6H.4t. +8.6M.4t. +1.7B.4t. +1.86.4t. +1.8d.4t. +1.51.4u. +1.5v.4u. +6.5I.4u. +6.5K.4u. +7.5M.4u. +1.5Y.4u. +1.5+.4u. +4.60.4u. +8.62.4u. +1.6q.4u. +1.6t.4u. +8.6F.4u. +c.6H.4u. +8.6M.4u. +1.7B.4u. +1.86.4u. +1.8d.4u. +1.51.4v. +1.5v.4v. +6.5I.4v. +6.5K.4v. +7.5M.4v. +1.5Y.4v. +1.5+.4v. +4.60.4v. +8.62.4v. +1.6q.4v. +1.6t.4v. +8.6F.4v. +c.6H.4v. +8.6M.4v. +1.7B.4v. +1.86.4v. +1.8d.4v. +1.51.4w. +1.5v.4w. +6.5I.4w. +6.5K.4w. +7.5M.4w. +1.5Y.4w. +1.5+.4w. +4.60.4w. +8.62.4w. +1.6q.4w. +1.6t.4w. +8.6F.4w. +c.6H.4w. +8.6M.4w. +1.7B.4w. +1.86.4w. +1.8d.4w. +1.51.4x. +1.5v.4x. +6.5I.4x. +6.5K.4x. +7.5M.4x. +1.5Y.4x. +1.5+.4x. +4.60.4x. +8.62.4x. +1.6q.4x. +1.6t.4x. +8.6F.4x. +c.6H.4x. +8.6M.4x. +1.7B.4x. +1.86.4x. +1.8d.4x. +1.51.4y. +1.5v.4y. +5.5I.4y. +6.5K.4y. +7.5M.4y. +1.5Y.4y. +1.5+.4y. +4.60.4y. +8.62.4y. +1.6q.4y. +1.6t.4y. +8.6F.4y. +c.6H.4y. +8.6M.4y. +1.7B.4y. +1.86.4y. +1.8d.4y. +1.51.4z. +1.5v.4z. +5.5I.4z. +6.5K.4z. +7.5M.4z. +1.5Y.4z. +1.5+.4z. +4.60.4z. +8.62.4z. +1.6q.4z. +1.6t.4z. +8.6F.4z. +c.6H.4z. +8.6M.4z. +1.7B.4z. +1.86.4z. +1.8d.4z. +1.51.4A. +1.5v.4A. +5.5I.4A. +6.5K.4A. +7.5M.4A. +1.5Y.4A. +1.5+.4A. +4.60.4A. +5.62.4A. +1.6q.4A. +1.6t.4A. +3.6F.4A. +c.6H.4A. +8.6M.4A. +1.7B.4A. +1.86.4A. +1.8d.4A. +1.51.4B. +1.5v.4B. +6.5I.4B. +6.5K.4B. +7.5M.4B. +1.5Y.4B. +1.5+.4B. +4.60.4B. +8.62.4B. +1.6q.4B. +1.6t.4B. +8.6F.4B. +c.6H.4B. +8.6M.4B. +1.7B.4B. +1.86.4B. +1.8d.4B. +1.51.4C. +1.5v.4C. +6.5I.4C. +6.5K.4C. +7.5M.4C. +1.5Y.4C. +1.5+.4C. +4.60.4C. +5.62.4C. +1.6q.4C. +1.6t.4C. +3.6F.4C. +c.6H.4C. +8.6M.4C. +1.7B.4C. +1.86.4C. +1.8d.4C. +1.51.4D. +1.5v.4D. +6.5I.4D. +6.5K.4D. +7.5M.4D. +1.5Y.4D. +1.5+.4D. +4.60.4D. +5.62.4D. +1.6q.4D. +1.6t.4D. +3.6F.4D. +c.6H.4D. +8.6M.4D. +1.7B.4D. +1.86.4D. +1.8d.4D. +1.51.4E. +1.5v.4E. +6.5I.4E. +6.5K.4E. +7.5M.4E. +1.5Y.4E. +1.5+.4E. +4.60.4E. +5.62.4E. +1.6q.4E. +1.6t.4E. +3.6F.4E. +c.6H.4E. +8.6M.4E. +1.7B.4E. +1.7R.4E. +1.86.4E. +1.8d.4E. +1.51.4F. +1.5v.4F. +1.5I.4F. +1.5M.4F. +1.5Y.4F. +1.5+.4F. +1.60.4F. +1.62.4F. +1.6q.4F. +1.6t.4F. +1.6F.4F. +1.6H.4F. +1.6M.4F. +1.7B.4F. +1.7R.4F. +1.86.4F. +1.8d.4F. +1.51.4G. +1.5v.4G. +8.5I.4G. +8.5K.4G. +7.5M.4G. +1.5Y.4G. +1.5+.4G. +5.60.4G. +5.62.4G. +1.6q.4G. +1.6t.4G. +3.6F.4G. +c.6H.4G. +8.6M.4G. +1.7B.4G. +1.7R.4G. +1.86.4G. +1.8d.4G. +1.51.4H. +1.5v.4H. +8.5I.4H. +8.5K.4H. +7.5M.4H. +1.5Y.4H. +1.5+.4H. +8.60.4H. +5.62.4H. +1.6q.4H. +1.6t.4H. +3.6F.4H. +c.6H.4H. +8.6M.4H. +1.7B.4H. +1.86.4H. +1.8d.4H. +1.51.4I. +1.5v.4I. +1.5I.4I. +1.5M.4I. +1.5Y.4I. +1.5+.4I. +1.60.4I. +h.62.4I. +1.6q.4I. +1.6t.4I. +1.6F.4I. +1.6H.4I. +1.6M.4I. +1.7B.4I. +1.7R.4I. +1.86.4I. +1.8d.4I. +1.51.4J. +1.5v.4J. +8.5I.4J. +8.5K.4J. +7.5M.4J. +1.5Y.4J. +1.5+.4J. +5.60.4J. +5.62.4J. +1.6q.4J. +1.6t.4J. +8.6F.4J. +c.6H.4J. +8.6M.4J. +1.7B.4J. +1.86.4J. +1.8d.4J. +1.51.4K. +1.5v.4K. +8.5I.4K. +8.5K.4K. +7.5M.4K. +1.5Y.4K. +1.5+.4K. +5.60.4K. +8.62.4K. +1.6q.4K. +1.6t.4K. +8.6F.4K. +c.6H.4K. +8.6M.4K. +1.7B.4K. +1.86.4K. +1.8d.4K. +1.51.4L. +1.5v.4L. +8.5I.4L. +8.5K.4L. +7.5M.4L. +1.5Y.4L. +1.5+.4L. +5.60.4L. +5.62.4L. +1.6q.4L. +1.6t.4L. +8.6F.4L. +c.6H.4L. +8.6M.4L. +1.7B.4L. +1.86.4L. +1.8d.4L. +1.51.4M. +1.5v.4M. +5.5I.4M. +6.5K.4M. +7.5M.4M. +1.5Y.4M. +1.5+.4M. +4.60.4M. +5.62.4M. +1.6q.4M. +1.6t.4M. +3.6F.4M. +c.6H.4M. +8.6M.4M. +1.7B.4M. +1.86.4M. +1.8d.4M. +1.51.4N. +1.5v.4N. +5.5I.4N. +6.5K.4N. +7.5M.4N. +1.5Y.4N. +1.5+.4N. +4.60.4N. +5.62.4N. +1.6q.4N. +1.6t.4N. +8.6F.4N. +c.6H.4N. +8.6M.4N. +1.7B.4N. +1.86.4N. +1.8d.4N. +5.4O.4O. +0.4Q.4O. +7.4T.4O. +0.4V.4O. +5.51.4O. +0.52.4O. +a.5a.4O. +2.5b.4O. +5.5d.4O. +5.5B.4O. +5.5C.4O. +5.5F.4O. +0.5H.4O. +0.5I.4O. +f.5K.4O. +7.5M.4O. +5.5O.4O. +5.5R.4O. +0.5U.4O. +0.5W.4O. +1.5Y.4O. +5.5Z.4O. +5.5/.4O. +0.60.4O. +2.62.4O. +2.66.4O. +a.6b.4O. +c.6e.4O. +0.6F.4O. +0.6M.4O. +0.6+.4O. +0.71.4O. +5.79.4O. +0.7D.4O. +0.2p.4O. +0.2p.4O. +1.51.4P. +1.5v.4P. +5.5I.4P. +6.5K.4P. +7.5M.4P. +1.5Y.4P. +1.5+.4P. +4.60.4P. +5.62.4P. +1.6q.4P. +1.6t.4P. +8.6F.4P. +c.6H.4P. +8.6M.4P. +1.7B.4P. +1.86.4P. +1.8d.4P. +3.4Q.4Q. +7.4T.4Q. +0.4Q.4V. +3.4Q.51. +0.52.4Q. +a.5a.4Q. +2.5b.4Q. +5.5d.4Q. +5.5B.4Q. +5.5C.4Q. +5.5F.4Q. +0.5H.4Q. +0.5I.4Q. +0.4Q.5K. +7.5M.4Q. +5.5O.4Q. +0.4Q.5R. +0.4Q.5U. +0.5W.4Q. +1.5Y.4Q. +5.5Z.4Q. +5.5/.4Q. +0.60.4Q. +2.62.4Q. +2.66.4Q. +a.6b.4Q. +c.6e.4Q. +0.4Q.6F. +0.6M.4Q. +0.6+.4Q. +0.71.4Q. +5.79.4Q. +0.4Q.7D. +0.2p.4Q. +0.2p.4Q. +1.51.4R. +1.5v.4R. +1.5I.4R. +1.5M.4R. +1.5Y.4R. +1.5+.4R. +1.60.4R. +1.62.4R. +1.6q.4R. +1.6t.4R. +1.6F.4R. +1.6H.4R. +1.6M.4R. +1.7B.4R. +1.7R.4R. +1.86.4R. +1.8d.4R. +1.51.4S. +1.5v.4S. +6.5I.4S. +6.5K.4S. +7.5M.4S. +1.5Y.4S. +1.5+.4S. +4.60.4S. +5.62.4S. +1.6q.4S. +1.6t.4S. +3.6F.4S. +c.6H.4S. +8.6M.4S. +1.7B.4S. +1.86.4S. +1.8d.4S. +5.4T.4T. +7.4T.4V. +5.4T.51. +5.4T.52. +7.4T.5a. +7.4T.5b. +7.4T.5d. +1.5v.4T. +5.4T.5B. +5.4T.5C. +5.4T.5F. +5.4T.5H. +7.4T.5I. +7.4T.5K. +7.4T.5M. +5.4T.5O. +4.4T.5R. +7.4T.5U. +5.4T.5W. +1.5Y.4T. +5.4T.5Z. +1.5+.4T. +5.4T.5/. +7.4T.60. +7.4T.62. +7.4T.66. +7.4T.6b. +c.6e.4T. +1.6q.4T. +1.6t.4T. +7.4T.6F. +c.6H.4T. +5.4T.6M. +5.4T.6+. +5.4T.71. +7.4T.79. +1.7B.4T. +5.4T.7D. +1.86.4T. +1.8d.4T. +5.4T.2p. +5.4T.2p. +1.51.4U. +1.5v.4U. +8.5I.4U. +8.5K.4U. +7.5M.4U. +1.5Y.4U. +1.5+.4U. +8.60.4U. +5.62.4U. +1.6q.4U. +1.6t.4U. +8.6F.4U. +c.6H.4U. +8.6M.4U. +1.7B.4U. +1.86.4U. +1.8d.4U. +0.4V.4V. +5.51.4V. +0.52.4V. +a.5a.4V. +2.5b.4V. +5.5d.4V. +5.5B.4V. +5.5C.4V. +5.5F.4V. +0.5H.4V. +0.5I.4V. +f.5K.4V. +7.5M.4V. +5.5O.4V. +5.5R.4V. +0.5U.4V. +0.5W.4V. +1.5Y.4V. +5.5Z.4V. +5.5/.4V. +0.60.4V. +2.62.4V. +2.66.4V. +a.6b.4V. +c.6e.4V. +0.6F.4V. +0.6M.4V. +0.6+.4V. +d.71.4V. +5.79.4V. +0.7D.4V. +0.2p.4V. +0.2p.4V. +1.51.4W. +1.5v.4W. +5.5I.4W. +6.5K.4W. +7.5M.4W. +1.5Y.4W. +1.5+.4W. +4.60.4W. +8.62.4W. +1.6q.4W. +1.6t.4W. +3.6F.4W. +c.6H.4W. +8.6M.4W. +1.7B.4W. +1.86.4W. +1.8d.4W. +1.51.4Y. +1.5v.4Y. +8.5I.4Y. +8.5K.4Y. +7.5M.4Y. +1.5Y.4Y. +1.5+.4Y. +5.60.4Y. +5.62.4Y. +1.6q.4Y. +1.6t.4Y. +8.6F.4Y. +c.6H.4Y. +8.6M.4Y. +1.7B.4Y. +1.86.4Y. +1.8d.4Y. +1.51.4Z. +1.5v.4Z. +1.5I.4Z. +1.5M.4Z. +1.5Y.4Z. +1.5+.4Z. +1.60.4Z. +1.62.4Z. +1.6q.4Z. +1.6t.4Z. +1.6F.4Z. +1.6H.4Z. +1.6M.4Z. +1.7B.4Z. +1.7R.4Z. +1.86.4Z. +1.8d.4Z. +1.51.4+. +1.5v.4+. +5.5I.4+. +6.5K.4+. +7.5M.4+. +1.5Y.4+. +1.5+.4+. +4.60.4+. +5.62.4+. +1.6q.4+. +1.6t.4+. +8.6F.4+. +c.6H.4+. +8.6M.4+. +1.7B.4+. +1.86.4+. +1.8d.4+. +1.51.4/. +1.5v.4/. +5.5I.4/. +6.5K.4/. +7.5M.4/. +1.5Y.4/. +1.5+.4/. +4.60.4/. +8.62.4/. +1.6q.4/. +1.6t.4/. +3.6F.4/. +c.6H.4/. +8.6M.4/. +1.7B.4/. +1.86.4/. +1.8d.4/. +1.51.50. +1.5v.50. +1.5I.50. +1.5M.50. +1.5Y.50. +1.5+.50. +1.60.50. +1.62.50. +1.6q.50. +1.6t.50. +1.6F.50. +1.6H.50. +1.6M.50. +1.7B.50. +1.7R.50. +1.86.50. +1.8d.50. +5.51.51. +0.52.51. +1.51.53. +1.51.54. +1.51.55. +1.51.56. +1.51.57. +1.51.58. +1.51.59. +a.5a.51. +2.5b.51. +1.51.5c. +5.5d.51. +1.51.5e. +1.51.5f. +1.51.5g. +1.51.5h. +1.51.5i. +1.51.5j. +1.51.5k. +1.51.5l. +1.51.5m. +1.51.5n. +1.51.5o. +1.51.5p. +1.51.5q. +1.51.5r. +1.51.5s. +1.51.5t. +1.51.5u. +1.51.5v. +1.51.5w. +1.51.5y. +1.51.5z. +1.51.5A. +5.5B.51. +5.5C.51. +1.51.5D. +1.51.5E. +5.5F.51. +1.51.5G. +0.5H.51. +0.5I.51. +f.51.5K. +1.51.5L. +7.5M.51. +1.51.5N. +5.5O.51. +1.51.5P. +1.51.5Q. +5.5R.51. +1.51.5S. +1.51.5T. +5.51.5U. +1.51.5V. +0.5W.51. +1.51.5X. +1.5Y.51. +5.5Z.51. +5.5/.51. +0.60.51. +1.51.61. +2.62.51. +1.51.64. +1.51.65. +2.66.51. +1.51.67. +1.51.68. +1.51.69. +1.51.6a. +a.6b.51. +1.51.6c. +1.51.6d. +c.6e.51. +1.51.6f. +1.51.6g. +1.51.6h. +1.51.6i. +1.51.6j. +1.51.6k. +1.51.6l. +1.51.6m. +1.51.6n. +1.51.6o. +1.51.6p. +1.51.6q. +1.51.6r. +1.51.6s. +1.51.6t. +1.51.6u. +1.51.6v. +1.51.6w. +1.51.6x. +1.51.6y. +1.51.6z. +1.51.6A. +1.51.6B. +1.51.6C. +1.51.6D. +1.51.6E. +5.51.6F. +1.51.6G. +1.51.6H. +1.51.6I. +1.51.6J. +1.51.6K. +1.51.6L. +0.6M.51. +1.51.6N. +1.51.6O. +1.51.6P. +1.51.6Q. +1.51.6R. +1.51.6S. +1.51.6T. +1.51.6U. +1.51.6V. +1.51.6W. +1.51.6X. +1.51.6Y. +1.51.6Z. +0.6+.51. +1.51.6/. +1.51.70. +0.71.51. +1.51.72. +1.51.73. +1.51.74. +1.51.75. +1.51.76. +1.51.77. +1.51.78. +5.79.51. +1.51.7a. +1.51.7b. +1.51.7c. +1.51.7d. +1.51.7e. +1.51.7f. +1.51.7g. +1.51.7h. +1.51.7i. +1.51.7j. +1.51.7k. +1.51.7l. +1.51.7m. +1.51.7o. +1.51.7p. +1.51.7r. +1.51.7s. +1.51.7t. +1.51.7u. +1.51.7v. +1.51.7w. +1.51.7x. +1.51.7y. +1.51.7z. +1.51.7A. +1.51.7B. +1.51.7C. +5.51.7D. +1.51.7E. +1.51.7F. +1.51.7G. +1.51.7H. +1.51.7I. +1.51.7J. +1.51.7K. +1.51.7L. +1.51.7M. +1.51.7N. +1.51.7O. +1.51.7P. +1.51.7Q. +1.51.7R. +1.51.7S. +1.51.7T. +1.51.7U. +1.51.7V. +1.51.7W. +1.51.7X. +1.51.7Y. +1.51.7Z. +1.51.7+. +1.51.7/. +1.51.80. +1.51.81. +1.51.82. +1.51.83. +1.51.84. +1.51.85. +1.51.87. +1.51.88. +1.51.89. +1.51.8a. +1.51.8b. +1.51.8c. +1.51.8d. +1.51.8e. +1.51.8f. +1.51.8g. +1.51.8h. +1.51.8i. +1.51.8j. +1.51.8k. +1.51.8l. +1.51.8m. +1.51.8n. +1.51.8o. +1.51.8p. +1.51.8q. +1.51.8r. +1.51.8s. +1.51.8t. +1.51.8u. +1.51.8v. +1.51.8w. +1.51.8x. +1.51.8y. +1.51.8z. +1.51.8A. +1.51.8B. +1.51.8C. +1.51.8D. +1.51.8E. +1.51.8F. +1.51.8G. +1.51.8H. +1.51.8I. +1.51.8J. +1.51.8K. +1.51.8L. +1.51.8M. +1.51.8N. +1.51.8O. +1.51.8P. +d.2p.51. +d.2p.51. +1.51.3b. +1.51.46. +1.51.4X. +1.51.5x. +1.51.5J. +1.51.7n. +1.51.7q. +0.52.52. +a.5a.52. +2.5b.52. +5.5d.52. +0.52.5B. +0.52.5C. +5.5F.52. +0.52.5H. +0.5I.52. +0.52.5K. +7.5M.52. +5.5O.52. +0.52.5R. +0.52.5U. +0.52.5W. +1.5Y.52. +0.52.5Z. +0.52.5/. +0.60.52. +2.62.52. +2.66.52. +a.6b.52. +c.6e.52. +0.52.6F. +0.6M.52. +0.52.6+. +0.52.71. +5.79.52. +0.52.7D. +0.2p.52. +0.2p.52. +1.5v.53. +8.5I.53. +8.5K.53. +7.5M.53. +1.5Y.53. +1.5+.53. +8.60.53. +5.62.53. +1.6q.53. +1.6t.53. +8.6F.53. +c.6H.53. +8.6M.53. +1.7B.53. +1.86.53. +1.8d.53. +8.5I.54. +8.5K.54. +7.5M.54. +1.5Y.54. +1.5+.54. +8.60.54. +8.62.54. +1.6q.54. +1.6t.54. +8.6F.54. +c.6H.54. +8.6M.54. +1.7B.54. +1.7R.54. +1.86.54. +1.8d.54. +8.5I.55. +8.5K.55. +7.5M.55. +1.5Y.55. +1.5+.55. +8.60.55. +8.62.55. +1.6q.55. +1.6t.55. +8.6F.55. +c.6H.55. +8.6M.55. +1.7B.55. +1.7R.55. +1.86.55. +1.8d.55. +8.5I.56. +8.5K.56. +7.5M.56. +1.5Y.56. +1.5+.56. +5.60.56. +8.62.56. +1.6q.56. +1.6t.56. +8.6F.56. +c.6H.56. +8.6M.56. +1.7B.56. +1.7R.56. +1.86.56. +1.8d.56. +1.5I.57. +1.5M.57. +1.5Y.57. +1.5+.57. +1.60.57. +1.62.57. +1.6q.57. +1.6t.57. +1.6F.57. +1.6H.57. +1.6M.57. +1.7B.57. +1.7R.57. +1.86.57. +1.8d.57. +8.5I.58. +8.5K.58. +7.5M.58. +1.5Y.58. +1.5+.58. +8.60.58. +8.62.58. +1.6q.58. +1.6t.58. +8.6F.58. +c.6H.58. +8.6M.58. +1.7B.58. +1.7R.58. +1.86.58. +1.8d.58. +8.5I.59. +8.5K.59. +7.5M.59. +1.5O.59. +1.5Y.59. +1.5+.59. +8.60.59. +8.62.59. +1.6q.59. +1.6t.59. +8.6F.59. +c.6H.59. +8.6M.59. +1.7B.59. +1.7R.59. +1.86.59. +1.8d.59. +a.5a.5a. +a.5a.5b. +a.5a.5d. +a.5a.5B. +a.5a.5C. +a.5a.5F. +a.5a.5H. +a.5a.5I. +f.5a.5K. +7.5M.5a. +a.5a.5O. +a.5a.5R. +a.5a.5U. +a.5a.5W. +1.5Y.5a. +a.5a.5Z. +a.5a.5/. +a.5a.60. +a.5a.62. +a.5a.66. +a.6b.5a. +c.6e.5a. +a.5a.6F. +a.5a.6M. +a.5a.6+. +a.5a.71. +a.5a.79. +a.5a.7D. +a.5a.2p. +a.5a.2p. +2.5b.5b. +2.5b.5d. +2.5b.5B. +2.5b.5C. +2.5b.5F. +2.5b.5H. +2.5b.5I. +f.5b.5K. +7.5M.5b. +2.5b.5O. +2.5b.5R. +2.5b.5U. +2.5b.5W. +1.5Y.5b. +2.5b.5Z. +2.5b.5/. +2.5b.60. +2.5b.62. +2.5b.66. +c.6e.5b. +2.5b.6F. +2.5b.6M. +2.5b.6+. +2.5b.71. +2.5b.79. +2.5b.7D. +2.5b.2p. +2.5b.2p. +5.5I.5c. +6.5K.5c. +7.5M.5c. +1.5Y.5c. +1.5+.5c. +4.60.5c. +5.62.5c. +1.6q.5c. +1.6t.5c. +8.6F.5c. +c.6H.5c. +8.6M.5c. +1.7B.5c. +1.7R.5c. +1.86.5c. +1.8d.5c. +5.5d.5d. +5.5d.5B. +5.5d.5C. +5.5d.5F. +5.5d.5H. +5.5d.5I. +f.5d.5K. +7.5M.5d. +5.5d.5O. +5.5d.5R. +5.5d.5U. +5.5d.5W. +1.5Y.5d. +5.5d.5Z. +5.5d.5/. +5.5d.60. +2.62.5d. +2.66.5d. +a.6b.5d. +c.6e.5d. +5.5d.6F. +5.5d.6M. +5.5d.6+. +5.5d.71. +5.5d.79. +5.5d.7D. +5.5d.2p. +5.5d.2p. +8.5I.5e. +8.5K.5e. +7.5M.5e. +1.5O.5e. +1.5Y.5e. +1.5+.5e. +5.60.5e. +8.62.5e. +1.6q.5e. +1.6t.5e. +8.6F.5e. +c.6H.5e. +8.6M.5e. +1.7B.5e. +1.7R.5e. +1.86.5e. +1.8d.5e. +8.5I.5f. +8.5K.5f. +7.5M.5f. +1.5O.5f. +1.5Y.5f. +1.5+.5f. +5.60.5f. +8.62.5f. +1.6q.5f. +1.6t.5f. +8.6F.5f. +c.6H.5f. +8.6M.5f. +1.7B.5f. +1.7R.5f. +1.86.5f. +1.8d.5f. +6.5I.5g. +6.5K.5g. +7.5M.5g. +1.5O.5g. +1.5Y.5g. +1.5+.5g. +4.60.5g. +8.62.5g. +1.6q.5g. +1.6t.5g. +8.6F.5g. +c.6H.5g. +8.6M.5g. +1.7B.5g. +1.7R.5g. +1.86.5g. +1.8d.5g. +1.5v.5h. +8.5I.5h. +8.5K.5h. +7.5M.5h. +1.5Y.5h. +1.5+.5h. +8.60.5h. +8.62.5h. +1.6q.5h. +1.6t.5h. +8.6F.5h. +c.6H.5h. +8.6M.5h. +1.7B.5h. +1.86.5h. +1.8d.5h. +1.5v.5i. +5.5I.5i. +6.5K.5i. +7.5M.5i. +1.5Y.5i. +1.5+.5i. +4.60.5i. +8.62.5i. +1.6q.5i. +1.6t.5i. +3.6F.5i. +c.6H.5i. +8.6M.5i. +1.7B.5i. +1.86.5i. +1.8d.5i. +1.5v.5j. +5.5I.5j. +6.5K.5j. +7.5M.5j. +1.5Y.5j. +1.5+.5j. +4.60.5j. +8.62.5j. +1.6q.5j. +1.6t.5j. +3.6F.5j. +c.6H.5j. +8.6M.5j. +1.7B.5j. +1.86.5j. +1.8d.5j. +1.5v.5k. +5.5I.5k. +6.5K.5k. +7.5M.5k. +1.5Y.5k. +1.5+.5k. +4.60.5k. +8.62.5k. +1.6q.5k. +1.6t.5k. +3.6F.5k. +c.6H.5k. +8.6M.5k. +1.7B.5k. +1.86.5k. +1.8d.5k. +5.5I.5l. +6.5K.5l. +7.5M.5l. +1.5Y.5l. +1.5+.5l. +4.60.5l. +5.62.5l. +1.6q.5l. +1.6t.5l. +3.6F.5l. +c.6H.5l. +8.6M.5l. +1.7B.5l. +1.86.5l. +1.8d.5l. +1.5v.5m. +5.5I.5m. +6.5K.5m. +7.5M.5m. +1.5Y.5m. +1.5+.5m. +4.60.5m. +5.62.5m. +1.6q.5m. +1.6t.5m. +8.6F.5m. +c.6H.5m. +8.6M.5m. +1.7B.5m. +1.86.5m. +1.8d.5m. +1.5v.5n. +5.5I.5n. +6.5K.5n. +7.5M.5n. +1.5Y.5n. +1.5+.5n. +4.60.5n. +5.62.5n. +1.6q.5n. +1.6t.5n. +8.6F.5n. +c.6H.5n. +8.6M.5n. +1.7B.5n. +1.86.5n. +1.8d.5n. +1.5v.5o. +6.5I.5o. +6.5K.5o. +7.5M.5o. +1.5Y.5o. +1.5+.5o. +4.60.5o. +5.62.5o. +1.6q.5o. +1.6t.5o. +8.6F.5o. +c.6H.5o. +8.6M.5o. +1.7B.5o. +1.7R.5o. +1.86.5o. +1.8d.5o. +1.5v.5p. +6.5I.5p. +f.5K.5p. +7.5M.5p. +1.5Y.5p. +1.5+.5p. +4.60.5p. +8.62.5p. +1.6q.5p. +1.6t.5p. +8.6F.5p. +c.6H.5p. +8.6M.5p. +1.7B.5p. +1.7R.5p. +1.86.5p. +1.8d.5p. +1.5v.5q. +6.5I.5q. +6.5K.5q. +7.5M.5q. +1.5O.5q. +1.5Y.5q. +1.5+.5q. +4.60.5q. +5.62.5q. +1.6q.5q. +1.6t.5q. +8.6F.5q. +c.6H.5q. +8.6M.5q. +1.7B.5q. +1.7R.5q. +1.86.5q. +1.8d.5q. +1.5v.5r. +5.5I.5r. +6.5K.5r. +7.5M.5r. +1.5Y.5r. +1.5+.5r. +4.60.5r. +8.62.5r. +1.6q.5r. +1.6t.5r. +8.6F.5r. +c.6H.5r. +8.6M.5r. +1.7B.5r. +1.7R.5r. +1.86.5r. +1.8d.5r. +1.5v.5s. +1.5I.5s. +1.5M.5s. +1.5Y.5s. +1.5+.5s. +1.60.5s. +1.62.5s. +1.6q.5s. +1.6t.5s. +1.6F.5s. +1.6H.5s. +1.6M.5s. +1.7B.5s. +1.7R.5s. +1.86.5s. +1.8d.5s. +1.5v.5t. +5.5I.5t. +6.5K.5t. +7.5M.5t. +1.5O.5t. +1.5Y.5t. +1.5+.5t. +4.60.5t. +8.62.5t. +1.6q.5t. +1.6t.5t. +8.6F.5t. +c.6H.5t. +8.6M.5t. +1.7B.5t. +1.7R.5t. +1.86.5t. +1.8d.5t. +1.5v.5u. +5.5I.5u. +6.5K.5u. +7.5M.5u. +1.5Y.5u. +1.5+.5u. +4.60.5u. +5.62.5u. +1.6q.5u. +1.6t.5u. +8.6F.5u. +c.6H.5u. +8.6M.5u. +1.7B.5u. +1.86.5u. +1.8d.5u. +1.5v.5v. +1.5v.5w. +1.5v.5y. +1.5v.5z. +1.5v.5A. +1.5v.5D. +1.5v.5E. +1.5v.5G. +5.5I.5v. +6.5K.5v. +1.5v.5L. +7.5M.5v. +1.5v.5N. +1.5v.5P. +1.5v.5Q. +1.5v.5S. +1.5v.5T. +1.5v.5V. +1.5Y.5v. +1.5+.5v. +4.60.5v. +1.5v.61. +5.62.5v. +1.5v.64. +1.5v.65. +1.5v.68. +1.5v.69. +1.5v.6a. +1.5v.6c. +1.5v.6d. +1.5v.6j. +1.5v.6o. +1.6q.5v. +1.5v.6r. +1.5v.6s. +1.6t.5v. +1.5v.6u. +1.5v.6v. +1.5v.6w. +1.5v.6z. +1.5v.6A. +1.5v.6C. +1.5v.6D. +8.6F.5v. +c.6H.5v. +1.5v.6J. +1.5v.6K. +1.5v.6L. +8.6M.5v. +1.5v.6N. +1.5v.6T. +1.5v.6U. +1.5v.6V. +1.5v.6W. +1.5v.6X. +1.5v.6Y. +1.5v.6Z. +1.5v.6/. +1.5v.70. +1.5v.72. +1.5v.74. +1.5v.75. +1.5v.76. +1.5v.77. +1.5v.7c. +1.5v.7h. +1.5v.7i. +1.5v.7j. +1.5v.7l. +1.5v.7o. +1.5v.7p. +1.5v.7v. +1.5v.7A. +1.5v.7F. +1.5v.7G. +1.5v.7H. +1.5v.7I. +1.5v.7J. +1.5v.7N. +1.5v.7P. +1.5v.7S. +1.5v.7U. +1.5v.7V. +1.5v.7W. +1.5v.7Y. +1.5v.7Z. +1.5v.7+. +1.5v.7/. +1.5v.80. +1.5v.81. +1.5v.82. +1.5v.83. +1.5v.84. +1.5v.85. +1.86.5v. +1.5v.87. +1.5v.88. +1.5v.89. +1.5v.8a. +1.5v.8b. +1.5v.8c. +1.5v.8d. +1.5v.8e. +1.5v.8f. +1.5v.8g. +1.5v.8h. +1.5v.8i. +1.5v.8j. +1.5v.8k. +1.5v.8l. +1.5v.8m. +1.5v.8n. +1.5v.8o. +1.5v.8p. +1.5v.8q. +1.5v.8r. +1.5v.8s. +1.5v.8u. +1.5v.8v. +1.5v.8w. +1.5v.8x. +1.5v.8y. +1.5v.8z. +1.5v.8A. +1.5v.8B. +1.5v.8C. +1.5v.8D. +1.5v.8E. +1.5v.8F. +1.5v.8G. +1.5v.8H. +1.5v.8I. +1.5v.8J. +1.5v.8K. +1.5v.8L. +1.5v.8M. +1.5v.8N. +1.5v.8O. +1.5v.8P. +1.5v.3b. +1.5v.46. +1.5v.4X. +1.5v.5x. +1.5v.5J. +1.5v.7q. +1.5I.5w. +1.5M.5w. +1.5Y.5w. +1.5+.5w. +1.60.5w. +1.62.5w. +1.6q.5w. +1.6t.5w. +1.6F.5w. +1.6H.5w. +1.6M.5w. +1.7B.5w. +1.86.5w. +1.8d.5w. +1.5I.5y. +1.5M.5y. +1.5Y.5y. +1.5+.5y. +1.60.5y. +1.62.5y. +1.6q.5y. +1.6t.5y. +1.6F.5y. +1.6H.5y. +1.6M.5y. +1.7B.5y. +1.7R.5y. +1.86.5y. +1.8d.5y. +1.5I.5z. +1.5M.5z. +1.5Y.5z. +1.5+.5z. +1.60.5z. +1.62.5z. +1.6q.5z. +1.6t.5z. +1.6F.5z. +1.6H.5z. +1.6M.5z. +1.7B.5z. +1.7R.5z. +1.86.5z. +1.8d.5z. +8.5I.5A. +8.5K.5A. +7.5M.5A. +1.5Y.5A. +1.5+.5A. +5.60.5A. +5.62.5A. +1.6q.5A. +1.6t.5A. +8.6F.5A. +c.6H.5A. +8.6M.5A. +1.7B.5A. +1.86.5A. +1.8d.5A. +5.5B.5B. +5.5B.5C. +5.5F.5B. +5.5B.5H. +0.5I.5B. +f.5B.5K. +7.5M.5B. +5.5O.5B. +5.5B.5R. +5.5B.5U. +5.5B.5W. +1.5Y.5B. +5.5B.5Z. +5.5B.5/. +0.60.5B. +2.62.5B. +2.66.5B. +a.6b.5B. +c.6e.5B. +5.5B.6F. +0.6M.5B. +5.5B.6+. +5.5B.71. +5.79.5B. +5.5B.7D. +0.2p.5B. +0.2p.5B. +5.5C.5C. +5.5F.5C. +5.5C.5H. +0.5I.5C. +f.5C.5K. +7.5M.5C. +5.5O.5C. +5.5C.5R. +5.5C.5U. +5.5C.5W. +1.5Y.5C. +5.5C.5Z. +5.5C.5/. +0.60.5C. +2.62.5C. +2.66.5C. +a.6b.5C. +c.6e.5C. +5.5C.6F. +0.6M.5C. +5.5C.6+. +5.5C.71. +5.79.5C. +5.5C.7D. +0.2p.5C. +0.2p.5C. +1.5I.5D. +1.5M.5D. +1.5Y.5D. +1.5+.5D. +1.60.5D. +1.62.5D. +1.6q.5D. +1.6t.5D. +1.6F.5D. +1.6H.5D. +1.6M.5D. +1.7B.5D. +1.86.5D. +1.8d.5D. +8.5I.5E. +8.5K.5E. +7.5M.5E. +1.5Y.5E. +1.5+.5E. +5.60.5E. +5.62.5E. +1.6q.5E. +1.6t.5E. +8.6F.5E. +c.6H.5E. +8.6M.5E. +1.7B.5E. +1.86.5E. +1.8d.5E. +5.5F.5F. +5.5F.5H. +5.5F.5I. +f.5F.5K. +7.5M.5F. +5.5F.5O. +5.5F.5R. +5.5F.5U. +5.5F.5W. +1.5Y.5F. +5.5F.5Z. +5.5F.5/. +5.5F.60. +2.62.5F. +2.66.5F. +a.6b.5F. +c.6e.5F. +5.5F.6F. +5.5F.6M. +5.5F.6+. +5.5F.71. +5.79.5F. +5.5F.7D. +5.5F.2p. +5.5F.2p. +8.5I.5G. +8.5K.5G. +7.5M.5G. +1.5Y.5G. +1.5+.5G. +5.60.5G. +8.62.5G. +1.6q.5G. +1.6t.5G. +8.6F.5G. +c.6H.5G. +8.6M.5G. +1.7B.5G. +1.7R.5G. +1.86.5G. +1.8d.5G. +0.5H.5H. +0.5I.5H. +f.5H.5K. +7.5M.5H. +5.5O.5H. +0.5H.5R. +0.5H.5U. +0.5H.5W. +1.5Y.5H. +5.5Z.5H. +5.5/.5H. +0.60.5H. +2.62.5H. +2.66.5H. +a.6b.5H. +c.6e.5H. +0.5H.6F. +0.6M.5H. +0.6+.5H. +0.5H.71. +5.79.5H. +0.5H.7D. +0.2p.5H. +0.2p.5H. +5.5I.5I. +0.5I.5K. +8.5I.5L. +8.5I.5M. +8.5I.5N. +0.5I.5O. +8.5I.5P. +6.5I.5Q. +0.5I.5R. +5.5I.5S. +5.5I.5T. +0.5I.5U. +8.5I.5V. +0.5I.5W. +8.5I.5X. +8.5I.5Y. +0.5I.5Z. +h.5+.5I. +0.5I.5/. +0.5I.60. +6.5I.61. +2.62.5I. +5.5I.64. +5.5I.65. +2.66.5I. +5.5I.67. +5.5I.68. +5.5I.69. +5.5I.6a. +6.5I.6b. +6.5I.6c. +6.5I.6d. +6.5I.6e. +6.5I.6f. +6.5I.6g. +6.5I.6h. +5.5I.6i. +5.5I.6j. +5.5I.6k. +5.5I.6l. +5.5I.6m. +5.5I.6n. +k.5I.6o. +5.5I.6p. +5.5I.6q. +5.5I.6r. +6.5I.6s. +5.5I.6t. +5.5I.6u. +5.5I.6v. +5.5I.6w. +6.5I.6x. +5.5I.6y. +5.5I.6z. +6.5I.6A. +6.5I.6B. +6.5I.6C. +5.5I.6D. +6.5I.6E. +0.5I.6F. +5.5I.6G. +5.5I.6H. +6.5I.6I. +6.5I.6J. +5.5I.6K. +8.5I.6L. +0.6M.5I. +6.5I.6N. +6.5I.6O. +6.5I.6P. +6.5I.6Q. +5.5I.6R. +5.5I.6S. +6.5I.6T. +6.5I.6U. +5.5I.6V. +5.5I.6W. +1.5I.6X. +6.5I.6Y. +1.5I.6Z. +0.5I.6+. +1.5I.6/. +1.5I.70. +0.5I.71. +5.5I.72. +6.5I.73. +1.5I.74. +5.5I.75. +1.5I.76. +1.5I.77. +5.5I.78. +5.79.5I. +1.5I.7a. +5.5I.7b. +5.5I.7c. +5.5I.7d. +6.5I.7e. +5.5I.7f. +5.5I.7g. +5.5I.7h. +6.5I.7i. +5.5I.7j. +6.5I.7k. +6.5I.7l. +1.5I.7m. +6.5I.7o. +6.5I.7p. +1.5I.7r. +1.5I.7s. +8.5I.7t. +6.5I.7u. +6.5I.7v. +6.5I.7w. +5.5I.7x. +5.5I.7y. +1.5I.7z. +5.5I.7A. +8.5I.7B. +6.5I.7C. +0.5I.7D. +5.5I.7E. +5.5I.7F. +5.5I.7G. +5.5I.7H. +5.5I.7I. +6.5I.7J. +1.5I.7K. +1.5I.7L. +1.5I.7M. +1.5I.7N. +1.5I.7O. +1.5I.7P. +5.5I.7Q. +8.5I.7R. +5.5I.7S. +6.5I.7T. +1.5I.7U. +8.5I.7V. +5.5I.7W. +5.5I.7X. +6.5I.7Y. +5.5I.7Z. +1.5I.7+. +1.5I.7/. +1.5I.80. +1.5I.81. +1.5I.82. +5.5I.83. +1.5I.84. +1.5I.85. +h.86.5I. +1.5I.87. +1.5I.88. +8.5I.89. +5.5I.8a. +8.5I.8b. +6.5I.8c. +8.5I.8d. +6.5I.8e. +6.5I.8f. +6.5I.8g. +6.5I.8h. +6.5I.8i. +6.5I.8j. +6.5I.8k. +6.5I.8l. +6.5I.8m. +6.5I.8n. +6.5I.8o. +6.5I.8p. +6.5I.8q. +6.5I.8r. +6.5I.8s. +6.5I.8t. +6.5I.8u. +6.5I.8v. +6.5I.8w. +6.5I.8x. +6.5I.8y. +6.5I.8z. +6.5I.8A. +6.5I.8B. +5.5I.8C. +6.5I.8D. +6.5I.8E. +6.5I.8F. +6.5I.8G. +4.5I.8H. +4.5I.8I. +6.5I.8J. +6.5I.8K. +6.5I.8L. +6.5I.8M. +6.5I.8N. +6.5I.8O. +6.5I.8P. +0.2p.5I. +0.2p.5I. +8.5I.3b. +1.5I.46. +1.5I.4X. +1.5I.5x. +1.5I.5J. +1.5I.7n. +1.5I.7q. +f.5K.5K. +8.5K.5L. +8.5K.5M. +8.5K.5N. +f.5O.5K. +8.5K.5P. +6.5K.5Q. +f.5R.5K. +6.5K.5S. +6.5K.5T. +f.5U.5K. +8.5K.5V. +f.5W.5K. +8.5K.5X. +8.5K.5Y. +f.5Z.5K. +f.5/.5K. +f.60.5K. +6.5K.61. +f.62.5K. +6.5K.64. +6.5K.65. +f.66.5K. +6.5K.67. +6.5K.68. +6.5K.69. +6.5K.6a. +6.5K.6b. +6.5K.6c. +6.5K.6d. +6.5K.6e. +6.5K.6f. +6.5K.6g. +6.5K.6h. +6.5K.6i. +6.5K.6j. +6.5K.6k. +6.5K.6l. +6.5K.6m. +6.5K.6n. +6.5K.6o. +6.5K.6p. +6.5K.6q. +6.5K.6r. +6.5K.6s. +6.5K.6t. +6.5K.6u. +6.5K.6v. +6.5K.6w. +6.5K.6x. +6.5K.6y. +6.5K.6z. +6.5K.6A. +6.5K.6B. +6.5K.6C. +6.5K.6D. +6.5K.6E. +f.6F.5K. +6.5K.6G. +6.5K.6H. +6.5K.6I. +6.5K.6J. +6.5K.6K. +8.5K.6L. +f.6M.5K. +6.5K.6N. +6.5K.6O. +6.5K.6P. +6.5K.6Q. +6.5K.6R. +6.5K.6S. +6.5K.6T. +6.5K.6U. +6.5K.6V. +6.5K.6W. +6.5K.6Y. +f.6+.5K. +f.71.5K. +6.5K.72. +6.5K.73. +6.5K.75. +6.5K.78. +f.79.5K. +6.5K.7b. +6.5K.7c. +6.5K.7d. +6.5K.7e. +6.5K.7f. +6.5K.7g. +6.5K.7h. +6.5K.7i. +6.5K.7j. +6.5K.7k. +6.5K.7l. +6.5K.7o. +6.5K.7p. +8.5K.7t. +6.5K.7u. +6.5K.7v. +6.5K.7w. +f.5K.7x. +6.5K.7y. +6.5K.7A. +8.5K.7B. +6.5K.7C. +f.5K.7D. +6.5K.7E. +6.5K.7F. +6.5K.7G. +6.5K.7H. +6.5K.7I. +6.5K.7J. +6.5K.7Q. +8.5K.7R. +6.5K.7S. +6.5K.7T. +8.5K.7V. +6.5K.7W. +6.5K.7X. +6.5K.7Y. +6.5K.7Z. +6.5K.83. +8.5K.89. +6.5K.8a. +8.5K.8b. +6.5K.8c. +8.5K.8d. +6.5K.8e. +6.5K.8f. +6.5K.8g. +6.5K.8h. +6.5K.8i. +6.5K.8j. +6.5K.8k. +6.5K.8l. +6.5K.8m. +6.5K.8n. +6.5K.8o. +6.5K.8p. +6.5K.8q. +6.5K.8r. +6.5K.8s. +6.5K.8t. +6.5K.8u. +6.5K.8v. +6.5K.8w. +6.5K.8x. +6.5K.8y. +6.5K.8z. +6.5K.8A. +6.5K.8B. +6.5K.8C. +6.5K.8D. +6.5K.8E. +6.5K.8F. +6.5K.8G. +3.5K.8H. +3.5K.8I. +6.5K.8J. +6.5K.8K. +6.5K.8L. +6.5K.8M. +6.5K.8N. +6.5K.8O. +6.5K.8P. +f.2p.5K. +f.2p.5K. +8.5K.3b. +7.5M.5L. +1.5Y.5L. +1.5+.5L. +5.60.5L. +5.62.5L. +1.6q.5L. +1.6t.5L. +8.6F.5L. +c.6H.5L. +8.6M.5L. +1.7B.5L. +1.7R.5L. +1.86.5L. +1.8d.5L. +7.5M.5M. +7.5M.5N. +7.5M.5O. +7.5M.5P. +7.5M.5Q. +7.5M.5R. +7.5M.5S. +7.5M.5T. +7.5M.5U. +7.5M.5V. +7.5M.5W. +7.5M.5X. +7.5M.5Y. +7.5M.5Z. +1.5+.5M. +7.5M.5/. +7.5M.60. +7.5M.61. +7.5M.62. +7.5M.64. +7.5M.65. +7.5M.66. +7.5M.67. +7.5M.68. +7.5M.69. +7.5M.6a. +7.5M.6b. +7.5M.6c. +7.5M.6d. +7.5M.6e. +7.5M.6f. +7.5M.6g. +7.5M.6h. +7.5M.6i. +7.5M.6j. +7.5M.6k. +7.5M.6l. +7.5M.6m. +7.5M.6n. +7.5M.6o. +7.5M.6p. +7.5M.6q. +7.5M.6r. +7.5M.6s. +7.5M.6t. +7.5M.6u. +7.5M.6v. +7.5M.6w. +7.5M.6x. +7.5M.6y. +7.5M.6z. +7.5M.6A. +7.5M.6B. +7.5M.6C. +7.5M.6D. +7.5M.6E. +7.5M.6F. +7.5M.6G. +7.5M.6H. +7.5M.6I. +7.5M.6J. +7.5M.6K. +7.5M.6L. +7.5M.6M. +7.5M.6N. +7.5M.6O. +7.5M.6P. +7.5M.6Q. +7.5M.6R. +7.5M.6S. +7.5M.6T. +7.5M.6U. +7.5M.6V. +7.5M.6W. +1.5M.6X. +7.5M.6Y. +1.5M.6Z. +7.5M.6+. +1.5M.6/. +1.5M.70. +7.5M.71. +7.5M.72. +7.5M.73. +1.5M.74. +7.5M.75. +1.5M.76. +1.5M.77. +7.5M.78. +7.5M.79. +1.5M.7a. +7.5M.7b. +7.5M.7c. +7.5M.7d. +7.5M.7e. +7.5M.7f. +7.5M.7g. +7.5M.7h. +7.5M.7i. +7.5M.7j. +7.5M.7k. +7.5M.7l. +1.5M.7m. +7.5M.7o. +7.5M.7p. +1.5M.7r. +1.5M.7s. +7.5M.7t. +7.5M.7u. +7.5M.7v. +7.5M.7w. +7.5M.7x. +7.5M.7y. +1.5M.7z. +7.5M.7A. +7.5M.7B. +7.5M.7C. +7.5M.7D. +7.5M.7E. +7.5M.7F. +7.5M.7G. +7.5M.7H. +7.5M.7I. +7.5M.7J. +1.5M.7K. +1.5M.7L. +1.5M.7M. +1.5M.7N. +1.5M.7O. +1.5M.7P. +7.5M.7Q. +7.5M.7R. +7.5M.7S. +7.5M.7T. +1.5M.7U. +7.5M.7V. +7.5M.7W. +7.5M.7X. +7.5M.7Y. +7.5M.7Z. +1.5M.7+. +1.5M.7/. +1.5M.80. +1.5M.81. +1.5M.82. +7.5M.83. +1.5M.84. +1.5M.85. +1.86.5M. +1.5M.87. +1.5M.88. +7.5M.89. +7.5M.8a. +7.5M.8b. +7.5M.8c. +7.5M.8d. +7.5M.8e. +7.5M.8f. +7.5M.8g. +7.5M.8h. +7.5M.8i. +7.5M.8j. +7.5M.8k. +7.5M.8l. +7.5M.8m. +7.5M.8n. +7.5M.8o. +7.5M.8p. +7.5M.8q. +7.5M.8r. +7.5M.8s. +7.5M.8t. +7.5M.8u. +7.5M.8v. +7.5M.8w. +7.5M.8x. +7.5M.8y. +7.5M.8z. +7.5M.8A. +7.5M.8B. +7.5M.8C. +7.5M.8D. +7.5M.8E. +7.5M.8F. +7.5M.8G. +7.5M.8H. +7.5M.8I. +7.5M.8J. +7.5M.8K. +7.5M.8L. +7.5M.8M. +7.5M.8N. +7.5M.8O. +7.5M.8P. +7.5M.2p. +7.5M.2p. +7.5M.3b. +1.5M.46. +1.5M.4X. +1.5M.5x. +1.5M.5J. +1.5M.7n. +1.5M.7q. +1.5Y.5N. +1.5+.5N. +5.60.5N. +5.62.5N. +1.6q.5N. +1.6t.5N. +8.6F.5N. +c.6H.5N. +8.6M.5N. +1.7B.5N. +1.86.5N. +1.8d.5N. +5.5O.5O. +5.5O.5R. +1.5O.5S. +5.5O.5U. +5.5O.5W. +1.5Y.5O. +5.5O.5Z. +5.5O.5/. +5.5O.60. +2.62.5O. +2.66.5O. +a.6b.5O. +c.6e.5O. +1.5O.6f. +1.5O.6g. +1.5O.6h. +1.5O.6i. +1.5O.6k. +1.5O.6m. +1.5O.6n. +1.5O.6p. +1.5O.6u. +1.5O.6v. +1.5O.6x. +1.5O.6E. +5.5O.6F. +1.5O.6G. +1.5O.6I. +1.5O.6L. +0.6M.5O. +1.5O.6N. +1.5O.6O. +1.5O.6Q. +1.5O.6R. +1.5O.6S. +5.5O.6+. +5.5O.71. +1.5O.78. +5.79.5O. +1.5O.7b. +1.5O.7d. +1.5O.7e. +1.5O.7f. +1.5O.7g. +1.5O.7l. +1.5O.7w. +5.5O.7D. +1.5O.7F. +1.5O.7G. +1.5O.7P. +1.5O.7Y. +1.5O.8e. +1.5O.8f. +1.5O.8g. +1.5O.8h. +1.5O.8i. +1.5O.8j. +1.5O.8k. +1.5O.8l. +1.5O.8m. +1.5O.8n. +1.5O.8o. +1.5O.8p. +1.5O.8q. +1.5O.8r. +1.5O.8u. +1.5O.8v. +1.5O.8w. +1.5O.8x. +1.5O.8y. +1.5O.8z. +1.5O.8A. +1.5O.8B. +1.5O.8C. +1.5O.8D. +1.5O.8E. +1.5O.8F. +1.5O.8G. +1.5O.8H. +1.5O.8I. +1.5O.8J. +d.2p.5O. +d.2p.5O. +1.5O.5x. +1.5Y.5P. +1.5+.5P. +8.60.5P. +5.62.5P. +1.6q.5P. +1.6t.5P. +8.6F.5P. +c.6H.5P. +8.6M.5P. +1.7B.5P. +1.86.5P. +1.8d.5P. +1.5Y.5Q. +1.5+.5Q. +4.60.5Q. +5.62.5Q. +1.6q.5Q. +1.6t.5Q. +3.6F.5Q. +c.6H.5Q. +8.6M.5Q. +1.7B.5Q. +1.86.5Q. +1.8d.5Q. +5.5R.5R. +5.5R.5U. +0.5W.5R. +1.5Y.5R. +5.5Z.5R. +5.5/.5R. +0.60.5R. +2.62.5R. +5.63.5R. +2.66.5R. +a.6b.5R. +c.6e.5R. +5.5R.6F. +0.6M.5R. +0.6+.5R. +0.71.5R. +5.79.5R. +5.5R.7D. +0.2p.5R. +0.2p.5R. +1.5Y.5S. +1.5+.5S. +4.60.5S. +5.62.5S. +1.6q.5S. +1.6t.5S. +8.6F.5S. +c.6H.5S. +8.6M.5S. +1.7B.5S. +1.7R.5S. +1.86.5S. +1.8d.5S. +1.5Y.5T. +1.5+.5T. +4.60.5T. +5.62.5T. +1.6q.5T. +1.6t.5T. +3.6F.5T. +c.6H.5T. +8.6M.5T. +1.7B.5T. +1.7R.5T. +1.86.5T. +1.8d.5T. +0.5U.5U. +0.5W.5U. +1.5Y.5U. +5.5Z.5U. +5.5/.5U. +0.60.5U. +2.62.5U. +2.66.5U. +a.6b.5U. +c.6e.5U. +0.6F.5U. +0.6M.5U. +0.6+.5U. +0.71.5U. +5.79.5U. +0.5U.7D. +0.2p.5U. +0.2p.5U. +1.5Y.5V. +1.5+.5V. +5.60.5V. +8.62.5V. +1.6q.5V. +1.6t.5V. +8.6F.5V. +c.6H.5V. +8.6M.5V. +1.7B.5V. +1.7R.5V. +1.86.5V. +1.8d.5V. +0.5W.5W. +1.5Y.5W. +5.5Z.5W. +5.5/.5W. +0.60.5W. +2.62.5W. +2.66.5W. +a.6b.5W. +c.6e.5W. +0.5W.6F. +0.6M.5W. +0.6+.5W. +0.5W.71. +5.79.5W. +0.5W.7D. +1.5W.7R. +0.2p.5W. +0.2p.5W. +1.5Y.5X. +1.5+.5X. +5.60.5X. +8.62.5X. +1.6q.5X. +1.6t.5X. +8.6F.5X. +c.6H.5X. +8.6M.5X. +1.7B.5X. +1.7R.5X. +1.86.5X. +1.8d.5X. +1.5Y.5Y. +1.5Y.5Z. +1.5+.5Y. +1.5Y.5/. +1.5Y.60. +1.5Y.61. +1.5Y.62. +1.5Y.64. +1.5Y.65. +1.5Y.66. +1.5Y.67. +1.5Y.68. +1.5Y.69. +1.5Y.6a. +1.5Y.6b. +1.5Y.6c. +1.5Y.6d. +1.5Y.6e. +1.5Y.6f. +1.5Y.6g. +1.5Y.6h. +1.5Y.6i. +1.5Y.6j. +1.5Y.6k. +1.5Y.6l. +1.5Y.6m. +1.5Y.6n. +1.5Y.6o. +1.5Y.6p. +1.5Y.6q. +1.5Y.6r. +1.5Y.6s. +1.6t.5Y. +1.5Y.6u. +1.5Y.6v. +1.5Y.6w. +1.5Y.6x. +1.5Y.6y. +1.5Y.6z. +1.5Y.6A. +1.5Y.6B. +1.5Y.6C. +1.5Y.6D. +1.5Y.6E. +1.5Y.6F. +1.5Y.6G. +c.6H.5Y. +1.5Y.6I. +1.5Y.6J. +1.5Y.6K. +1.5Y.6L. +1.5Y.6M. +1.5Y.6N. +1.5Y.6O. +1.5Y.6P. +1.5Y.6Q. +1.5Y.6R. +1.5Y.6S. +1.5Y.6T. +1.5Y.6U. +1.5Y.6V. +1.5Y.6W. +1.5Y.6X. +1.5Y.6Y. +1.5Y.6Z. +1.5Y.6+. +1.5Y.6/. +1.5Y.70. +1.5Y.71. +1.5Y.72. +1.5Y.73. +1.5Y.74. +1.5Y.75. +1.5Y.76. +1.5Y.77. +1.5Y.78. +1.5Y.79. +1.5Y.7a. +1.5Y.7b. +1.5Y.7c. +1.5Y.7d. +1.5Y.7e. +1.5Y.7f. +1.5Y.7g. +1.5Y.7h. +1.5Y.7i. +1.5Y.7j. +1.5Y.7k. +1.5Y.7l. +1.5Y.7m. +1.5Y.7o. +1.5Y.7p. +1.5Y.7r. +1.5Y.7s. +1.5Y.7t. +1.5Y.7u. +1.5Y.7v. +1.5Y.7w. +1.5Y.7x. +1.5Y.7y. +1.5Y.7z. +1.5Y.7A. +1.7B.5Y. +1.5Y.7C. +1.5Y.7D. +1.5Y.7E. +1.5Y.7F. +1.5Y.7G. +1.5Y.7H. +1.5Y.7I. +1.5Y.7J. +1.5Y.7K. +1.5Y.7L. +1.5Y.7M. +1.5Y.7N. +1.5Y.7O. +1.5Y.7P. +1.5Y.7Q. +1.5Y.7R. +1.5Y.7S. +1.5Y.7T. +1.5Y.7U. +1.5Y.7V. +1.5Y.7W. +1.5Y.7X. +1.5Y.7Y. +1.5Y.7Z. +1.5Y.7+. +1.5Y.7/. +1.5Y.80. +1.5Y.81. +1.5Y.82. +1.5Y.83. +1.5Y.84. +1.5Y.85. +1.86.5Y. +1.5Y.87. +1.5Y.88. +1.5Y.89. +1.5Y.8a. +1.5Y.8b. +1.5Y.8c. +1.8d.5Y. +1.5Y.8e. +1.5Y.8f. +1.5Y.8g. +1.5Y.8h. +1.5Y.8i. +1.5Y.8j. +1.5Y.8k. +1.5Y.8l. +1.5Y.8m. +1.5Y.8n. +1.5Y.8o. +1.5Y.8p. +1.5Y.8q. +1.5Y.8r. +1.5Y.8s. +1.5Y.8t. +1.5Y.8u. +1.5Y.8v. +1.5Y.8w. +1.5Y.8x. +1.5Y.8y. +1.5Y.8z. +1.5Y.8A. +1.5Y.8B. +1.5Y.8C. +1.5Y.8D. +1.5Y.8E. +1.5Y.8F. +1.5Y.8G. +1.5Y.8H. +1.5Y.8I. +1.5Y.8J. +1.5Y.8K. +1.5Y.8L. +1.5Y.8M. +1.5Y.8N. +1.5Y.8O. +1.5Y.8P. +1.5Y.2p. +1.5Y.2p. +1.5Y.3b. +1.5Y.46. +1.5Y.4X. +1.5Y.5x. +1.5Y.5J. +1.5Y.7n. +1.5Y.7q. +5.5Z.5Z. +5.5Z.5/. +0.60.5Z. +2.62.5Z. +2.66.5Z. +a.6b.5Z. +c.6e.5Z. +5.5Z.6F. +0.6M.5Z. +5.5Z.6+. +5.5Z.71. +5.79.5Z. +5.5Z.7D. +0.2p.5Z. +0.2p.5Z. +h.5+.60. +1.5+.61. +h.5+.62. +1.5+.64. +1.5+.65. +1.5+.67. +1.5+.68. +1.5+.69. +1.5+.6a. +1.5+.6c. +1.5+.6d. +1.5+.6e. +1.5+.6f. +1.5+.6g. +1.5+.6h. +1.5+.6i. +1.5+.6j. +1.5+.6k. +1.5+.6l. +1.5+.6m. +1.5+.6n. +1.5+.6o. +1.5+.6p. +1.5+.6q. +1.5+.6r. +1.5+.6s. +1.5+.6t. +1.5+.6u. +1.5+.6v. +1.5+.6w. +1.5+.6x. +1.5+.6y. +1.5+.6z. +1.5+.6A. +1.5+.6B. +1.5+.6C. +1.5+.6D. +1.5+.6E. +1.5+.6G. +1.5+.6H. +1.5+.6I. +1.5+.6J. +1.5+.6K. +1.5+.6L. +1.5+.6N. +1.5+.6O. +1.5+.6P. +1.5+.6Q. +1.5+.6R. +1.5+.6S. +1.5+.6T. +1.5+.6U. +1.5+.6V. +1.5+.6W. +1.5+.6X. +1.5+.6Y. +1.5+.6Z. +1.5+.6/. +1.5+.70. +1.5+.72. +1.5+.73. +1.5+.74. +1.5+.75. +1.5+.76. +1.5+.77. +1.5+.78. +1.5+.7a. +1.5+.7b. +1.5+.7c. +1.5+.7d. +1.5+.7e. +1.5+.7f. +1.5+.7g. +1.5+.7h. +1.5+.7i. +1.5+.7j. +1.5+.7k. +1.5+.7l. +1.5+.7m. +1.5+.7o. +1.5+.7p. +1.5+.7r. +1.5+.7s. +1.5+.7t. +1.5+.7u. +1.5+.7v. +1.5+.7w. +1.5+.7x. +1.5+.7y. +1.5+.7z. +1.5+.7A. +1.5+.7B. +1.5+.7C. +1.5+.7E. +1.5+.7F. +1.5+.7G. +1.5+.7H. +1.5+.7I. +1.5+.7J. +1.5+.7K. +1.5+.7L. +1.5+.7M. +1.5+.7N. +1.5+.7O. +1.5+.7P. +1.5+.7Q. +1.5+.7R. +1.5+.7S. +1.5+.7T. +1.5+.7U. +1.5+.7V. +1.5+.7W. +1.5+.7X. +1.5+.7Y. +1.5+.7Z. +1.5+.7+. +1.5+.7/. +1.5+.80. +1.5+.81. +1.5+.82. +1.5+.83. +1.5+.84. +1.5+.85. +1.5+.87. +1.5+.88. +1.5+.89. +1.5+.8a. +1.5+.8b. +1.5+.8c. +1.5+.8d. +1.5+.8e. +1.5+.8f. +1.5+.8g. +1.5+.8h. +1.5+.8i. +1.5+.8j. +1.5+.8k. +1.5+.8l. +1.5+.8m. +1.5+.8n. +1.5+.8o. +1.5+.8p. +1.5+.8q. +1.5+.8r. +1.5+.8s. +1.5+.8t. +1.5+.8u. +1.5+.8v. +1.5+.8w. +1.5+.8x. +1.5+.8y. +1.5+.8z. +1.5+.8A. +1.5+.8B. +1.5+.8C. +1.5+.8D. +1.5+.8E. +1.5+.8F. +1.5+.8G. +1.5+.8H. +1.5+.8I. +1.5+.8J. +1.5+.8K. +1.5+.8L. +1.5+.8M. +1.5+.8N. +1.5+.8O. +1.5+.8P. +1.5+.3b. +1.5+.46. +1.5+.4X. +1.5+.5x. +1.5+.5J. +1.5+.7n. +1.5+.7q. +5.5/.5/. +0.60.5/. +2.62.5/. +2.66.5/. +a.6b.5/. +c.6e.5/. +5.5/.6F. +0.6M.5/. +0.6+.5/. +5.5/.71. +5.79.5/. +5.5/.7D. +0.2p.5/. +0.2p.5/. +0.60.60. +4.60.61. +2.62.60. +4.60.64. +4.60.65. +2.66.60. +4.60.67. +4.60.68. +4.60.69. +4.60.6a. +a.6b.60. +4.60.6c. +4.60.6d. +4.60.6e. +4.60.6f. +4.60.6g. +4.60.6h. +4.60.6i. +4.60.6j. +4.60.6k. +4.60.6l. +4.60.6m. +4.60.6n. +4.60.6o. +4.60.6p. +4.60.6q. +4.60.6r. +4.60.6s. +4.60.6t. +4.60.6u. +4.60.6v. +4.60.6w. +4.60.6x. +4.60.6y. +4.60.6z. +4.60.6A. +4.60.6B. +4.60.6C. +4.60.6D. +4.60.6E. +0.60.6F. +4.60.6G. +4.60.6H. +4.60.6I. +4.60.6J. +4.60.6K. +5.60.6L. +0.6M.60. +4.60.6N. +4.60.6O. +4.60.6P. +4.60.6Q. +4.60.6R. +4.60.6S. +4.60.6T. +4.60.6U. +4.60.6V. +4.60.6W. +1.60.6X. +4.60.6Y. +1.60.6Z. +0.60.6+. +1.60.6/. +1.60.70. +0.60.71. +4.60.72. +4.60.73. +1.60.74. +4.60.75. +1.60.76. +1.60.77. +4.60.78. +5.79.60. +1.60.7a. +4.60.7b. +4.60.7c. +4.60.7d. +4.60.7e. +4.60.7f. +4.60.7g. +4.60.7h. +4.60.7i. +4.60.7j. +4.60.7k. +4.60.7l. +1.60.7m. +4.60.7o. +4.60.7p. +1.60.7r. +1.60.7s. +8.60.7t. +4.60.7u. +4.60.7v. +4.60.7w. +4.60.7x. +4.60.7y. +1.60.7z. +4.60.7A. +5.60.7B. +4.60.7C. +0.60.7D. +4.60.7E. +4.60.7F. +4.60.7G. +4.60.7H. +4.60.7I. +4.60.7J. +1.60.7K. +1.60.7L. +1.60.7M. +1.60.7N. +1.60.7O. +1.60.7P. +4.60.7Q. +5.60.7R. +4.60.7S. +4.60.7T. +1.60.7U. +5.60.7V. +4.60.7W. +4.60.7X. +4.60.7Y. +4.60.7Z. +1.60.7+. +1.60.7/. +1.60.80. +1.60.81. +1.60.82. +4.60.83. +1.60.84. +1.60.85. +h.86.60. +1.60.87. +1.60.88. +8.60.89. +4.60.8a. +5.60.8b. +4.60.8c. +8.60.8d. +4.60.8e. +4.60.8f. +4.60.8g. +4.60.8h. +4.60.8i. +4.60.8j. +4.60.8k. +4.60.8l. +4.60.8m. +4.60.8n. +4.60.8o. +4.60.8p. +4.60.8q. +4.60.8r. +4.60.8s. +4.60.8t. +4.60.8u. +4.60.8v. +4.60.8w. +4.60.8x. +4.60.8y. +4.60.8z. +4.60.8A. +4.60.8B. +4.60.8C. +4.60.8D. +4.60.8E. +4.60.8F. +4.60.8G. +4.60.8H. +4.60.8I. +4.60.8J. +4.60.8K. +4.60.8L. +4.60.8M. +4.60.8N. +4.60.8O. +4.60.8P. +0.2p.60. +0.2p.60. +5.60.3b. +1.60.46. +1.60.4X. +1.60.5x. +1.60.5J. +1.60.7n. +1.60.7q. +5.62.61. +1.6q.61. +1.6t.61. +3.6F.61. +c.6H.61. +8.6M.61. +1.7B.61. +1.86.61. +1.8d.61. +2.62.62. +8.62.64. +5.62.65. +2.62.66. +5.62.67. +5.62.68. +5.62.69. +5.62.6a. +a.6b.62. +8.62.6c. +8.62.6d. +8.62.6e. +8.62.6f. +8.62.6g. +8.62.6h. +5.62.6i. +8.62.6j. +8.62.6k. +5.62.6l. +8.62.6m. +5.62.6n. +5.62.6o. +8.62.6p. +5.62.6q. +8.62.6r. +8.62.6s. +8.62.6t. +8.62.6u. +8.62.6v. +5.62.6w. +8.62.6x. +5.62.6y. +5.62.6z. +8.62.6A. +8.62.6B. +5.62.6C. +5.62.6D. +8.62.6E. +2.62.6F. +5.62.6G. +5.62.6H. +5.62.6I. +5.62.6J. +5.62.6K. +8.62.6L. +2.62.6M. +8.62.6N. +8.62.6O. +8.62.6P. +8.62.6Q. +5.62.6R. +5.62.6S. +8.62.6T. +8.62.6U. +5.62.6V. +5.62.6W. +1.62.6X. +8.62.6Y. +1.62.6Z. +2.62.6+. +1.62.6/. +1.62.70. +2.62.71. +5.62.72. +8.62.73. +h.62.74. +5.62.75. +h.62.76. +h.62.77. +5.62.78. +2.62.79. +h.62.7a. +8.62.7b. +8.62.7c. +5.62.7d. +5.62.7e. +8.62.7f. +5.62.7g. +8.62.7h. +8.62.7i. +5.62.7j. +5.62.7k. +8.62.7l. +h.62.7m. +5.62.7o. +5.62.7p. +1.62.7r. +h.62.7s. +8.62.7t. +8.62.7u. +8.62.7v. +5.62.7w. +5.62.7x. +5.62.7y. +1.62.7z. +5.62.7A. +5.62.7B. +5.62.7C. +2.62.7D. +8.62.7E. +5.62.7F. +5.62.7G. +8.62.7H. +8.62.7I. +8.62.7J. +h.62.7K. +h.62.7L. +h.62.7M. +h.62.7N. +h.62.7O. +1.62.7P. +8.62.7Q. +8.62.7R. +5.62.7S. +5.62.7T. +1.62.7U. +8.62.7V. +8.62.7W. +8.62.7X. +5.62.7Y. +5.62.7Z. +h.62.7+. +h.62.7/. +h.62.80. +1.62.81. +1.62.82. +5.62.83. +1.62.84. +1.62.85. +h.62.86. +h.62.87. +1.62.88. +8.62.89. +5.62.8a. +8.62.8b. +8.62.8c. +8.62.8d. +5.62.8e. +5.62.8f. +5.62.8g. +5.62.8h. +5.62.8i. +5.62.8j. +5.62.8k. +5.62.8l. +5.62.8m. +5.62.8n. +5.62.8o. +5.62.8p. +5.62.8q. +5.62.8r. +5.62.8s. +8.62.8t. +8.62.8u. +8.62.8v. +8.62.8w. +8.62.8x. +5.62.8y. +5.62.8z. +5.62.8A. +5.62.8B. +8.62.8C. +5.62.8D. +8.62.8E. +5.62.8F. +5.62.8G. +5.62.8H. +5.62.8I. +5.62.8J. +8.62.8K. +8.62.8L. +8.62.8M. +8.62.8N. +8.62.8O. +8.62.8P. +2.62.2p. +2.62.2p. +8.62.3b. +1.62.46. +1.62.4X. +1.62.5x. +1.62.5J. +h.62.7n. +1.62.7q. +5.63.63. +1.6q.64. +1.6t.64. +8.6F.64. +c.6H.64. +8.6M.64. +1.7B.64. +1.86.64. +1.8d.64. +1.6q.65. +1.6t.65. +8.6F.65. +c.6H.65. +8.6M.65. +1.7B.65. +1.86.65. +1.8d.65. +2.66.66. +a.6b.66. +c.6e.66. +2.66.6F. +2.66.6M. +2.66.6+. +2.66.71. +2.66.79. +2.66.7D. +2.66.2p. +2.66.2p. +1.6q.67. +1.6t.67. +8.6F.67. +c.6H.67. +3.6M.67. +1.7B.67. +1.7R.67. +1.86.67. +1.8d.67. +1.6q.68. +1.6t.68. +8.6F.68. +c.6H.68. +3.6M.68. +1.7B.68. +1.86.68. +1.8d.68. +1.6q.69. +1.6t.69. +8.6F.69. +c.6H.69. +3.6M.69. +1.7B.69. +1.86.69. +1.8d.69. +1.6q.6a. +1.6t.6a. +8.6F.6a. +c.6H.6a. +3.6M.6a. +1.7B.6a. +1.86.6a. +1.8d.6a. +a.6b.6b. +c.6e.6b. +a.6b.6F. +a.6b.6M. +a.6b.6+. +a.6b.71. +a.6b.79. +a.6b.7D. +a.6b.2p. +a.6b.2p. +1.6q.6c. +1.6t.6c. +8.6F.6c. +c.6H.6c. +3.6M.6c. +1.7B.6c. +1.7R.6c. +1.86.6c. +1.8d.6c. +1.6q.6d. +1.6t.6d. +8.6F.6d. +c.6H.6d. +3.6M.6d. +1.7B.6d. +1.7R.6d. +1.86.6d. +1.8d.6d. +c.6e.6e. +1.6q.6e. +1.6t.6e. +8.6F.6e. +c.6H.6e. +3.6M.6e. +c.6e.6+. +c.6e.71. +c.6e.79. +1.7B.6e. +c.6e.7D. +1.7R.6e. +1.86.6e. +1.8d.6e. +c.6e.2p. +c.6e.2p. +1.6q.6f. +1.6t.6f. +8.6F.6f. +c.6H.6f. +3.6M.6f. +1.7B.6f. +1.7R.6f. +1.86.6f. +1.8d.6f. +1.6q.6g. +1.6t.6g. +8.6F.6g. +c.6H.6g. +3.6M.6g. +1.7B.6g. +1.7R.6g. +1.86.6g. +1.8d.6g. +1.6q.6h. +1.6t.6h. +8.6F.6h. +c.6H.6h. +3.6M.6h. +1.7B.6h. +1.7R.6h. +1.86.6h. +1.8d.6h. +1.6q.6i. +1.6t.6i. +8.6F.6i. +k.6H.6i. +3.6M.6i. +1.7B.6i. +1.7R.6i. +1.86.6i. +1.8d.6i. +1.6q.6j. +1.6t.6j. +8.6F.6j. +c.6H.6j. +8.6M.6j. +1.7B.6j. +1.86.6j. +1.8d.6j. +1.6q.6k. +1.6t.6k. +3.6F.6k. +c.6H.6k. +8.6M.6k. +1.7B.6k. +1.7R.6k. +1.86.6k. +1.8d.6k. +1.6q.6l. +1.6t.6l. +8.6F.6l. +c.6H.6l. +8.6M.6l. +1.7B.6l. +1.86.6l. +1.8d.6l. +1.6q.6m. +1.6t.6m. +8.6F.6m. +c.6H.6m. +8.6M.6m. +1.7B.6m. +1.86.6m. +1.8d.6m. +1.6q.6n. +1.6t.6n. +8.6F.6n. +c.6H.6n. +8.6M.6n. +1.7B.6n. +1.86.6n. +1.8d.6n. +1.6q.6o. +1.6t.6o. +3.6F.6o. +c.6H.6o. +8.6M.6o. +1.7B.6o. +1.86.6o. +1.8d.6o. +1.6q.6p. +1.6t.6p. +8.6F.6p. +c.6H.6p. +8.6M.6p. +1.7B.6p. +1.7R.6p. +1.86.6p. +1.8d.6p. +1.6q.6q. +1.6q.6r. +1.6q.6s. +1.6q.6t. +1.6q.6u. +1.6q.6v. +1.6q.6w. +1.6q.6x. +1.6q.6y. +1.6q.6z. +1.6q.6A. +1.6q.6B. +1.6q.6C. +1.6q.6D. +1.6q.6E. +8.6F.6q. +1.6q.6G. +k.6H.6q. +1.6q.6I. +1.6q.6J. +1.6q.6K. +1.6q.6L. +8.6M.6q. +1.6q.6N. +1.6q.6O. +1.6q.6P. +1.6q.6Q. +1.6q.6R. +1.6q.6S. +1.6q.6T. +1.6q.6U. +1.6q.6V. +1.6q.6W. +1.6q.6X. +1.6q.6Y. +1.6q.6/. +1.6q.70. +1.6q.72. +1.6q.73. +1.6q.74. +1.6q.75. +1.6q.76. +1.6q.77. +1.6q.78. +1.6q.7a. +1.6q.7b. +1.6q.7c. +1.6q.7d. +1.6q.7e. +1.6q.7f. +1.6q.7g. +1.6q.7h. +1.6q.7i. +1.6q.7j. +1.6q.7k. +1.6q.7l. +1.6q.7m. +1.6q.7o. +1.6q.7p. +1.6q.7r. +1.6q.7s. +1.6q.7t. +1.6q.7u. +1.6q.7v. +1.6q.7w. +1.6q.7x. +1.6q.7y. +1.6q.7z. +1.6q.7A. +1.6q.7B. +1.6q.7C. +1.6q.7E. +1.6q.7F. +1.6q.7G. +1.6q.7H. +1.6q.7I. +1.6q.7J. +1.6q.7K. +1.6q.7L. +1.6q.7M. +1.6q.7N. +1.6q.7O. +1.6q.7P. +1.6q.7Q. +1.6q.7S. +1.6q.7T. +1.6q.7V. +1.6q.7W. +1.6q.7X. +1.6q.7Y. +1.6q.7+. +1.6q.7/. +1.6q.80. +1.6q.81. +1.6q.82. +1.6q.83. +1.6q.84. +1.6q.85. +1.86.6q. +1.6q.87. +1.6q.88. +1.6q.89. +1.6q.8a. +1.6q.8b. +1.6q.8d. +1.6q.8e. +1.6q.8f. +1.6q.8g. +1.6q.8h. +1.6q.8i. +1.6q.8j. +1.6q.8k. +1.6q.8l. +1.6q.8m. +1.6q.8n. +1.6q.8o. +1.6q.8p. +1.6q.8q. +1.6q.8r. +1.6q.8s. +1.6q.8t. +1.6q.8u. +1.6q.8v. +1.6q.8w. +1.6q.8x. +1.6q.8y. +1.6q.8z. +1.6q.8A. +1.6q.8B. +1.6q.8C. +1.6q.8D. +1.6q.8E. +1.6q.8F. +1.6q.8G. +1.6q.8H. +1.6q.8I. +1.6q.8J. +1.6q.8K. +1.6q.8L. +1.6q.8M. +1.6q.8N. +1.6q.8O. +1.6q.8P. +1.6q.3b. +1.6q.46. +1.6q.4X. +1.6q.5x. +1.6q.5J. +1.6q.7n. +1.6q.7q. +1.6t.6r. +3.6F.6r. +c.6H.6r. +8.6M.6r. +1.7B.6r. +1.86.6r. +1.8d.6r. +1.6t.6s. +8.6F.6s. +c.6H.6s. +8.6M.6s. +1.7B.6s. +1.86.6s. +1.8d.6s. +1.6t.6t. +1.6t.6u. +1.6t.6v. +1.6t.6w. +1.6t.6x. +1.6t.6y. +1.6t.6z. +1.6t.6A. +1.6t.6B. +1.6t.6C. +1.6t.6D. +1.6t.6E. +8.6F.6t. +1.6t.6G. +c.6H.6t. +1.6t.6I. +1.6t.6J. +1.6t.6K. +1.6t.6L. +8.6M.6t. +1.6t.6N. +1.6t.6O. +1.6t.6P. +1.6t.6Q. +1.6t.6R. +1.6t.6S. +1.6t.6T. +1.6t.6U. +1.6t.6V. +1.6t.6W. +1.6t.6X. +1.6t.6Y. +1.6t.6Z. +1.6t.6/. +1.6t.70. +1.6t.72. +1.6t.73. +1.6t.74. +1.6t.75. +1.6t.76. +1.6t.77. +1.6t.78. +1.6t.7a. +1.6t.7b. +1.6t.7c. +1.6t.7d. +1.6t.7e. +1.6t.7f. +1.6t.7g. +1.6t.7h. +1.6t.7i. +1.6t.7j. +1.6t.7k. +1.6t.7l. +1.6t.7o. +1.6t.7p. +1.6t.7r. +1.6t.7s. +1.6t.7t. +1.6t.7u. +1.6t.7v. +1.6t.7w. +1.6t.7x. +1.6t.7y. +1.6t.7z. +1.6t.7A. +1.7B.6t. +1.6t.7C. +1.6t.7E. +1.6t.7F. +1.6t.7G. +1.6t.7H. +1.6t.7I. +1.6t.7J. +1.6t.7K. +1.6t.7L. +1.6t.7M. +1.6t.7N. +1.6t.7O. +1.6t.7P. +1.6t.7Q. +1.6t.7R. +1.6t.7S. +1.6t.7T. +1.6t.7U. +1.6t.7V. +1.6t.7W. +1.6t.7X. +1.6t.7Y. +1.6t.7+. +1.6t.7/. +1.6t.80. +1.6t.81. +1.6t.82. +1.6t.83. +1.6t.84. +1.6t.85. +1.86.6t. +1.6t.87. +1.6t.88. +1.6t.89. +1.6t.8a. +1.6t.8b. +1.6t.8c. +1.6t.8d. +1.6t.8e. +1.6t.8f. +1.6t.8g. +1.6t.8h. +1.6t.8i. +1.6t.8j. +1.6t.8k. +1.6t.8l. +1.6t.8m. +1.6t.8n. +1.6t.8o. +1.6t.8p. +1.6t.8q. +1.6t.8r. +1.6t.8s. +1.6t.8t. +1.6t.8u. +1.6t.8v. +1.6t.8w. +1.6t.8x. +1.6t.8y. +1.6t.8z. +1.6t.8A. +1.6t.8B. +1.6t.8C. +1.6t.8D. +1.6t.8E. +1.6t.8F. +1.6t.8G. +1.6t.8H. +1.6t.8I. +1.6t.8J. +1.6t.8K. +1.6t.8L. +1.6t.8M. +1.6t.8N. +1.6t.8O. +1.6t.8P. +1.6t.3b. +1.6t.46. +1.6t.4X. +1.6t.5x. +1.6t.5J. +1.6t.7n. +1.6t.7q. +8.6F.6u. +c.6H.6u. +8.6M.6u. +1.7B.6u. +1.7R.6u. +1.86.6u. +1.8d.6u. +8.6F.6v. +c.6H.6v. +8.6M.6v. +1.7B.6v. +1.7R.6v. +1.86.6v. +1.8d.6v. +8.6F.6w. +c.6H.6w. +8.6M.6w. +1.7B.6w. +1.86.6w. +1.8d.6w. +8.6F.6x. +c.6H.6x. +8.6M.6x. +1.7B.6x. +1.7R.6x. +1.86.6x. +1.8d.6x. +3.6F.6y. +c.6H.6y. +8.6M.6y. +1.7B.6y. +1.86.6y. +1.8d.6y. +3.6F.6z. +c.6H.6z. +8.6M.6z. +1.7B.6z. +1.7R.6z. +1.86.6z. +1.8d.6z. +8.6F.6A. +c.6H.6A. +8.6M.6A. +1.7B.6A. +1.86.6A. +1.8d.6A. +8.6F.6B. +c.6H.6B. +3.6M.6B. +1.7B.6B. +1.7R.6B. +1.86.6B. +1.8d.6B. +3.6F.6C. +c.6H.6C. +8.6M.6C. +1.7B.6C. +1.7R.6C. +1.86.6C. +1.8d.6C. +8.6F.6D. +c.6H.6D. +8.6M.6D. +1.7B.6D. +1.7R.6D. +1.86.6D. +1.8d.6D. +8.6F.6E. +c.6H.6E. +3.6M.6E. +1.7B.6E. +1.7R.6E. +1.86.6E. +1.8d.6E. +0.6F.6F. +8.6F.6G. +3.6F.6H. +8.6F.6I. +8.6F.6J. +8.6F.6K. +8.6F.6L. +0.6M.6F. +8.6F.6N. +8.6F.6O. +8.6F.6P. +8.6F.6Q. +8.6F.6R. +8.6F.6S. +8.6F.6T. +8.6F.6U. +8.6F.6V. +8.6F.6W. +1.6F.6X. +8.6F.6Y. +1.6F.6Z. +0.6+.6F. +1.6F.6/. +1.6F.70. +0.71.6F. +3.6F.72. +8.6F.73. +1.6F.74. +8.6F.75. +1.6F.76. +1.6F.77. +8.6F.78. +5.79.6F. +1.6F.7a. +8.6F.7b. +3.6F.7c. +8.6F.7d. +8.6F.7e. +8.6F.7f. +8.6F.7g. +3.6F.7h. +8.6F.7i. +8.6F.7j. +8.6F.7k. +8.6F.7l. +1.6F.7m. +8.6F.7o. +8.6F.7p. +1.6F.7r. +1.6F.7s. +8.6F.7t. +8.6F.7u. +3.6F.7v. +8.6F.7w. +8.6F.7x. +8.6F.7y. +1.6F.7z. +3.6F.7A. +3.6F.7B. +8.6F.7C. +0.6F.7D. +8.6F.7E. +8.6F.7F. +8.6F.7G. +8.6F.7H. +8.6F.7I. +3.6F.7J. +1.6F.7K. +1.6F.7L. +1.6F.7M. +1.6F.7N. +1.6F.7O. +1.6F.7P. +8.6F.7Q. +8.6F.7R. +3.6F.7S. +3.6F.7T. +1.6F.7U. +8.6F.7V. +8.6F.7W. +3.6F.7X. +8.6F.7Y. +3.6F.7Z. +1.6F.7+. +1.6F.7/. +1.6F.80. +1.6F.81. +1.6F.82. +8.6F.83. +1.6F.84. +1.6F.85. +1.6F.87. +1.6F.88. +8.6F.89. +3.6F.8a. +8.6F.8b. +8.6F.8c. +8.6F.8d. +8.6F.8e. +8.6F.8f. +3.6F.8g. +8.6F.8h. +8.6F.8i. +3.6F.8j. +8.6F.8k. +3.6F.8l. +3.6F.8m. +8.6F.8n. +8.6F.8o. +8.6F.8p. +8.6F.8q. +8.6F.8r. +8.6F.8s. +8.6F.8t. +8.6F.8u. +8.6F.8v. +8.6F.8w. +8.6F.8x. +8.6F.8y. +8.6F.8z. +8.6F.8A. +3.6F.8B. +8.6F.8C. +8.6F.8D. +8.6F.8E. +8.6F.8F. +8.6F.8G. +3.6F.8H. +8.6F.8I. +8.6F.8J. +8.6F.8K. +8.6F.8L. +8.6F.8M. +3.6F.8N. +8.6F.8O. +8.6F.8P. +0.2p.6F. +0.2p.6F. +8.6F.3b. +1.6F.46. +1.6F.4X. +1.6F.5x. +1.6F.5J. +1.6F.7n. +1.6F.7q. +c.6H.6G. +8.6M.6G. +1.7B.6G. +1.7R.6G. +1.86.6G. +1.8d.6G. +c.6H.6H. +c.6H.6I. +c.6H.6J. +c.6H.6K. +c.6H.6L. +8.6M.6H. +c.6H.6N. +c.6H.6O. +c.6H.6P. +c.6H.6Q. +c.6H.6R. +c.6H.6S. +c.6H.6T. +c.6H.6U. +c.6H.6V. +c.6H.6W. +1.6H.6X. +c.6H.6Y. +1.6H.6Z. +1.6H.6/. +1.6H.70. +c.6H.72. +c.6H.73. +1.6H.74. +c.6H.75. +1.6H.76. +1.6H.77. +c.6H.78. +1.6H.7a. +c.6H.7b. +c.6H.7c. +c.6H.7d. +c.6H.7e. +c.6H.7f. +c.6H.7g. +c.6H.7h. +c.6H.7i. +c.6H.7j. +c.6H.7k. +c.6H.7l. +1.6H.7m. +c.6H.7o. +c.6H.7p. +1.6H.7r. +1.6H.7s. +c.6H.7t. +c.6H.7u. +c.6H.7v. +c.6H.7w. +c.6H.7x. +c.6H.7y. +1.6H.7z. +c.6H.7A. +c.6H.7B. +c.6H.7C. +c.6H.7E. +c.6H.7F. +c.6H.7G. +c.6H.7H. +c.6H.7I. +c.6H.7J. +1.6H.7K. +1.6H.7L. +1.6H.7M. +1.6H.7N. +1.6H.7O. +1.6H.7P. +c.6H.7Q. +c.6H.7R. +c.6H.7S. +c.6H.7T. +1.6H.7U. +c.6H.7V. +c.6H.7W. +c.6H.7X. +c.6H.7Y. +c.6H.7Z. +1.6H.7+. +1.6H.7/. +1.6H.80. +1.6H.81. +1.6H.82. +c.6H.83. +1.6H.84. +1.6H.85. +1.86.6H. +1.6H.87. +1.6H.88. +c.6H.89. +c.6H.8a. +c.6H.8b. +c.6H.8c. +c.6H.8d. +c.6H.8e. +c.6H.8f. +c.6H.8g. +c.6H.8h. +c.6H.8i. +c.6H.8j. +c.6H.8k. +c.6H.8l. +c.6H.8m. +c.6H.8n. +c.6H.8o. +c.6H.8p. +c.6H.8q. +c.6H.8r. +c.6H.8s. +c.6H.8t. +c.6H.8u. +c.6H.8v. +c.6H.8w. +c.6H.8x. +c.6H.8y. +c.6H.8z. +c.6H.8A. +c.6H.8B. +c.6H.8C. +c.6H.8D. +c.6H.8E. +c.6H.8F. +c.6H.8G. +c.6H.8H. +c.6H.8I. +c.6H.8J. +c.6H.8K. +c.6H.8L. +c.6H.8M. +c.6H.8N. +c.6H.8O. +c.6H.8P. +c.6H.3b. +1.6H.46. +1.6H.4X. +1.6H.5x. +1.6H.5J. +1.6H.7n. +1.6H.7q. +3.6M.6I. +1.7B.6I. +1.7R.6I. +1.86.6I. +1.8d.6I. +8.6M.6J. +1.7B.6J. +1.86.6J. +1.8d.6J. +8.6M.6K. +1.7B.6K. +1.7R.6K. +1.86.6K. +1.8d.6K. +8.6M.6L. +1.7B.6L. +1.7R.6L. +1.86.6L. +1.8d.6L. +j.6M.6M. +8.6M.6N. +8.6M.6O. +8.6M.6P. +8.6M.6Q. +8.6M.6R. +8.6M.6S. +8.6M.6T. +8.6M.6U. +8.6M.6V. +8.6M.6W. +1.6M.6X. +8.6M.6Y. +1.6M.6Z. +0.6M.6+. +1.6M.6/. +1.6M.70. +0.6M.71. +8.6M.72. +8.6M.73. +1.6M.74. +8.6M.75. +1.6M.76. +1.6M.77. +8.6M.78. +5.79.6M. +1.6M.7a. +8.6M.7b. +8.6M.7c. +8.6M.7d. +8.6M.7e. +8.6M.7f. +8.6M.7g. +8.6M.7h. +8.6M.7i. +8.6M.7j. +8.6M.7k. +8.6M.7l. +1.6M.7m. +8.6M.7o. +8.6M.7p. +1.6M.7r. +1.6M.7s. +8.6M.7t. +8.6M.7u. +8.6M.7v. +8.6M.7w. +8.6M.7x. +8.6M.7y. +1.6M.7z. +8.6M.7A. +8.6M.7B. +8.6M.7C. +0.6M.7D. +8.6M.7E. +8.6M.7F. +8.6M.7G. +8.6M.7H. +8.6M.7I. +8.6M.7J. +1.6M.7K. +1.6M.7L. +1.6M.7M. +1.6M.7N. +1.6M.7O. +1.6M.7P. +8.6M.7Q. +8.6M.7R. +3.6M.7S. +3.6M.7T. +1.6M.7U. +8.6M.7V. +8.6M.7W. +8.6M.7X. +8.6M.7Y. +8.6M.7Z. +1.6M.7+. +1.6M.7/. +1.6M.80. +1.6M.81. +1.6M.82. +8.6M.83. +1.6M.84. +1.6M.85. +1.6M.87. +1.6M.88. +8.6M.89. +8.6M.8a. +8.6M.8b. +3.6M.8c. +8.6M.8d. +3.6M.8e. +3.6M.8f. +3.6M.8g. +3.6M.8h. +3.6M.8i. +3.6M.8j. +3.6M.8k. +3.6M.8l. +3.6M.8m. +3.6M.8n. +3.6M.8o. +3.6M.8p. +3.6M.8q. +3.6M.8r. +3.6M.8s. +8.6M.8t. +8.6M.8u. +8.6M.8v. +8.6M.8w. +8.6M.8x. +3.6M.8y. +8.6M.8z. +8.6M.8A. +8.6M.8B. +8.6M.8C. +8.6M.8D. +8.6M.8E. +8.6M.8F. +8.6M.8G. +8.6M.8H. +8.6M.8I. +8.6M.8J. +3.6M.8K. +3.6M.8L. +3.6M.8M. +3.6M.8N. +3.6M.8O. +3.6M.8P. +0.6M.2p. +0.6M.2p. +8.6M.3b. +1.6M.46. +1.6M.4X. +1.6M.5x. +1.6M.5J. +1.6M.7n. +1.6M.7q. +1.7B.6N. +1.7R.6N. +1.86.6N. +1.8d.6N. +1.7B.6O. +1.7R.6O. +1.86.6O. +1.8d.6O. +1.7B.6P. +1.7R.6P. +1.86.6P. +1.8d.6P. +1.7B.6Q. +1.7R.6Q. +1.86.6Q. +1.8d.6Q. +1.7B.6R. +1.7R.6R. +1.86.6R. +1.8d.6R. +1.7B.6S. +1.7R.6S. +1.86.6S. +1.8d.6S. +1.7B.6T. +1.86.6T. +1.8d.6T. +1.7B.6U. +1.7R.6U. +1.86.6U. +1.8d.6U. +1.7B.6V. +1.86.6V. +1.8d.6V. +1.7B.6W. +1.86.6W. +1.8d.6W. +1.7B.6X. +1.86.6X. +1.8d.6X. +1.7B.6Y. +1.7R.6Y. +1.86.6Y. +1.8d.6Y. +1.7B.6Z. +1.8d.6Z. +d.6+.6+. +d.6+.71. +5.79.6+. +0.6+.7D. +0.2p.6+. +0.2p.6+. +1.7B.6/. +1.86.6/. +1.8d.6/. +1.7B.70. +1.86.70. +1.8d.70. +d.71.71. +5.79.71. +0.71.7D. +0.2p.71. +0.2p.71. +1.7B.72. +1.86.72. +1.8d.72. +1.7B.73. +1.7R.73. +1.86.73. +1.8d.73. +1.7B.74. +1.86.74. +1.8d.74. +1.7B.75. +1.86.75. +1.8d.75. +1.7B.76. +1.86.76. +1.8d.76. +1.7B.77. +1.86.77. +1.8d.77. +1.7B.78. +1.7R.78. +1.86.78. +1.8d.78. +5.79.79. +5.79.7D. +5.79.2p. +5.79.2p. +1.7B.7a. +1.7R.7a. +1.86.7a. +1.8d.7a. +1.7B.7b. +1.86.7b. +1.8d.7b. +1.7B.7c. +1.7R.7c. +1.86.7c. +1.8d.7c. +1.7B.7d. +1.86.7d. +1.8d.7d. +1.7B.7e. +1.7R.7e. +1.86.7e. +1.8d.7e. +1.7B.7f. +1.86.7f. +1.8d.7f. +1.7B.7g. +1.86.7g. +1.8d.7g. +1.7B.7h. +1.7R.7h. +1.86.7h. +1.8d.7h. +1.7B.7i. +1.86.7i. +1.8d.7i. +1.7B.7j. +1.7R.7j. +1.86.7j. +1.8d.7j. +1.7B.7k. +1.7R.7k. +1.86.7k. +1.8d.7k. +1.7B.7l. +1.7R.7l. +1.86.7l. +1.8d.7l. +1.7B.7m. +1.7R.7m. +1.86.7m. +1.8d.7m. +1.7B.7o. +1.86.7o. +1.8d.7o. +1.7B.7p. +1.7R.7p. +1.86.7p. +1.8d.7p. +1.7B.7r. +1.7R.7r. +1.86.7r. +1.8d.7r. +1.7B.7s. +1.7R.7s. +1.86.7s. +1.8d.7s. +1.7B.7t. +1.7R.7t. +1.86.7t. +1.8d.7t. +1.7B.7u. +1.7R.7u. +1.86.7u. +1.8d.7u. +1.7B.7v. +1.86.7v. +1.8d.7v. +1.7B.7w. +1.7R.7w. +1.86.7w. +1.8d.7w. +1.7B.7x. +1.7R.7x. +1.86.7x. +1.8d.7x. +1.7B.7y. +1.7R.7y. +1.86.7y. +1.8d.7y. +1.7B.7z. +1.7R.7z. +1.86.7z. +1.8d.7z. +1.86.7A. +1.7B.7C. +1.7B.7E. +1.7B.7F. +1.7B.7G. +1.7B.7H. +1.7B.7I. +1.7B.7J. +1.7B.7K. +1.7B.7L. +1.7B.7M. +1.7B.7N. +1.7B.7O. +1.7B.7P. +1.7B.7Q. +1.7B.7R. +1.7B.7S. +1.7B.7T. +1.7B.7U. +1.7B.7V. +1.7B.7W. +1.7B.7X. +1.7B.7Y. +1.7B.7Z. +1.7B.7+. +1.7B.7/. +1.7B.80. +1.7B.81. +1.7B.82. +1.7B.83. +1.7B.84. +1.86.7B. +1.7B.87. +1.7B.88. +1.7B.89. +1.7B.8a. +1.7B.8b. +1.8d.7B. +1.7B.8e. +1.7B.8f. +1.7B.8g. +1.7B.8h. +1.7B.8i. +1.7B.8j. +1.7B.8k. +1.7B.8l. +1.7B.8m. +1.7B.8n. +1.7B.8o. +1.7B.8p. +1.7B.8q. +1.7B.8r. +1.7B.8s. +1.7B.8t. +1.7B.8u. +1.7B.8v. +1.7B.8w. +1.7B.8x. +1.7B.8y. +1.7B.8z. +1.7B.8A. +1.7B.8B. +1.7B.8C. +1.7B.8D. +1.7B.8E. +1.7B.8F. +1.7B.8G. +1.7B.8H. +1.7B.8I. +1.7B.8J. +1.7B.8K. +1.7B.8L. +1.7B.8M. +1.7B.8N. +1.7B.8O. +1.7B.8P. +1.7B.3b. +1.7B.46. +1.7B.4X. +1.7B.5x. +1.7B.5J. +1.7B.7n. +1.7B.7q. +1.7R.7C. +1.86.7C. +1.8d.7C. +0.7D.7D. +0.2p.7D. +0.2p.7D. +1.7R.7E. +1.86.7E. +1.8d.7E. +1.7R.7F. +1.86.7F. +1.8d.7F. +1.7R.7G. +1.86.7G. +1.8d.7G. +1.86.7H. +1.8d.7H. +1.86.7I. +1.8d.7I. +1.86.7J. +1.8d.7J. +1.7R.7K. +1.86.7K. +1.8d.7K. +1.7R.7L. +1.86.7L. +1.8d.7L. +1.7R.7M. +1.86.7M. +1.8d.7M. +1.86.7N. +1.8d.7N. +1.7R.7O. +1.86.7O. +1.8d.7O. +1.7R.7P. +1.86.7P. +1.8d.7P. +1.7R.7Q. +1.86.7Q. +1.8d.7Q. +1.7R.7R. +1.7R.7T. +1.7R.7V. +1.7R.7W. +1.7R.7X. +1.7R.7Y. +1.7R.83. +1.7R.84. +1.7R.85. +1.86.7R. +1.7R.87. +1.7R.8a. +1.7R.8c. +1.7R.8d. +1.7R.8e. +1.7R.8f. +1.7R.8g. +1.7R.8h. +1.7R.8i. +1.7R.8j. +1.7R.8k. +1.7R.8l. +1.7R.8m. +1.7R.8n. +1.7R.8o. +1.7R.8p. +1.7R.8q. +1.7R.8r. +1.7R.8s. +1.7R.8t. +1.7R.8u. +1.7R.8v. +1.7R.8w. +1.7R.8x. +1.7R.8y. +1.7R.8z. +1.7R.8A. +1.7R.8B. +1.7R.8C. +1.7R.8D. +1.7R.8E. +1.7R.8F. +1.7R.8G. +1.7R.8H. +1.7R.8I. +1.7R.8J. +1.7R.3b. +1.7R.5x. +1.7R.7n. +1.86.7S. +1.8d.7S. +1.86.7T. +1.8d.7T. +1.86.7U. +1.8d.7U. +1.86.7V. +1.8d.7V. +1.86.7W. +1.8d.7W. +1.86.7X. +1.8d.7X. +1.86.7Y. +1.8d.7Y. +1.86.7Z. +1.8d.7Z. +1.86.7+. +1.8d.7+. +1.86.7/. +1.8d.7/. +1.86.80. +1.8d.80. +1.86.81. +1.8d.81. +1.86.82. +1.8d.82. +1.86.83. +1.8d.83. +1.86.84. +1.8d.84. +1.86.85. +1.8d.85. +1.86.87. +1.86.88. +1.86.89. +1.86.8a. +1.86.8b. +1.86.8c. +1.86.8d. +1.86.8e. +1.86.8f. +1.86.8g. +1.86.8h. +1.86.8i. +1.86.8j. +1.86.8k. +1.86.8l. +1.86.8m. +1.86.8n. +1.86.8o. +1.86.8p. +1.86.8q. +1.86.8r. +1.86.8s. +1.86.8t. +1.86.8u. +1.86.8v. +1.86.8w. +1.86.8x. +1.86.8y. +1.86.8z. +1.86.8A. +1.86.8B. +1.86.8C. +1.86.8D. +1.86.8E. +1.86.8F. +1.86.8G. +1.86.8H. +1.86.8I. +1.86.8J. +1.86.8K. +1.86.8L. +1.86.8M. +1.86.8N. +1.86.8O. +1.86.8P. +1.86.3b. +1.86.46. +1.86.4X. +1.86.5x. +1.86.5J. +1.86.7n. +1.86.7q. +1.8d.87. +1.8d.88. +1.8d.89. +1.8d.8a. +1.8d.8b. +1.8d.8d. +1.8d.8e. +1.8d.8f. +1.8d.8g. +1.8d.8h. +1.8d.8i. +1.8d.8j. +1.8d.8k. +1.8d.8l. +1.8d.8m. +1.8d.8n. +1.8d.8o. +1.8d.8p. +1.8d.8q. +1.8d.8r. +1.8d.8s. +1.8d.8t. +1.8d.8u. +1.8d.8v. +1.8d.8w. +1.8d.8x. +1.8d.8y. +1.8d.8z. +1.8d.8A. +1.8d.8B. +1.8d.8C. +1.8d.8D. +1.8d.8E. +1.8d.8F. +1.8d.8G. +1.8d.8H. +1.8d.8I. +1.8d.8J. +1.8d.8K. +1.8d.8L. +1.8d.8M. +1.8d.8N. +1.8d.8O. +1.8d.8P. +1.8d.3b. +1.8d.46. +1.8d.4X. +1.8d.5x. +1.8d.5J. +1.8d.7n. +1.8d.7q. +0.2p.2p. +0.2p.2p. +0.2p.2p. +2.M.J. +6.5I.3v. +4.5I.4T. +5.60.5Y. +8.60.5M. +4.60.4T. +8.6M.5Y. +8.6M.5M. +a.6b.5b. +1.5Y.5K. +4.5K.4T. +1.4s.3I. +1.51.3I. +c.6H.2d. +` \ No newline at end of file diff --git a/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts b/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts new file mode 100644 index 0000000000..654eb357f7 --- /dev/null +++ b/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts @@ -0,0 +1,60 @@ +import * as data from './emojiData'; + +const mixEmojiUrl = (r, c) => { + let padZeros = r < 20220500; // Revisions before 0522 had preceding zeros + c[0] = c[0].split(/-/g).map(s => padZeros ? s.padStart(4, "0") : s).join("-u"); + c[1] = c[1].split(/-/g).map(s => padZeros ? s.padStart(4, "0") : s).join("-u"); + return `https://www.gstatic.com/android/keyboard/emojikitchen/${r}/u${c[0]}/u${c[0]}_u${c[1]}.png`; +}; + +const convertBase = (value, from_base, to_base) => { + value = value.toString(); + var range = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/'.split(''); + var from_range = range.slice(0, from_base); + var to_range = range.slice(0, to_base); + + var dec_value = value.split('').reverse().reduce(function (carry, digit, index) { + if (from_range.indexOf(digit) === -1) throw new Error('Invalid digit `' + digit + '` for base ' + from_base + '.'); + return carry += from_range.indexOf(digit) * (Math.pow(from_base, index)); + }, 0); + + var new_value = ''; + while (dec_value > 0) { + new_value = to_range[dec_value % to_base] + new_value; + dec_value = (dec_value - (dec_value % to_base)) / to_base; + } + return new_value || '0'; +}; + +const hexEncodeEmoji = (chr) => { + if (chr.length !== 1) { + const hi = chr.charCodeAt(0); + const lo = chr.charCodeAt(1); + if (0xD800 <= hi && hi < 0xDC00 && 0xDC00 <= lo && lo < 0xE000) { + return (0x10000 + (hi - 0xD800) * 0x400 + (lo - 0xDC00)).toString(16); + } + return ("000" + hi.toString(16)).slice(-4) + '-' + ("000" + lo.toString(16)).slice(-4); + } + else { + return ("000" + chr.charCodeAt(0).toString(16)).slice(-4); + } +}; + +const pairsMatchingMap = match => { + const mv = match[0]; + let [d, c1, c2] = mv.split('.'); + c1 = data.points[convertBase(c1, 64, 10)]; + c2 = data.points[convertBase(c2, 64, 10)]; + d = data.revisions[convertBase(d, 64, 10)]; + + return mixEmojiUrl(d, [c1, c2]); +}; + +export const mixEmoji = (emoji1, emoji2) => { + const encordedEmoji1 = convertBase(data.points.indexOf(hexEncodeEmoji(emoji1)), 10, 64); + const encordedEmoji2 = convertBase(data.points.indexOf(hexEncodeEmoji(emoji2)), 10, 64); + return [ + ...data.pairs.matchAll(new RegExp("^.*\\." + encordedEmoji1 + "\\." + encordedEmoji2 + "\\.$", "gm")), + ...data.pairs.matchAll(new RegExp("^.*\\." + encordedEmoji2 + "\\." + encordedEmoji1 + "\\.$", "gm")) + ].map(pairsMatchingMap).pop(); +}; diff --git a/packages/frontend/src/scripts/mfm-tags.ts b/packages/frontend/src/scripts/mfm-tags.ts index dc78e42238..5968a56639 100644 --- a/packages/frontend/src/scripts/mfm-tags.ts +++ b/packages/frontend/src/scripts/mfm-tags.ts @@ -1,6 +1 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate']; +export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'skew', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'fgg', 'bgg', 'clip', 'move', 'mix']; From 4847dc8756066d7ce9c2fd2fb008e3d267ca1909 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 18:25:04 +0900 Subject: [PATCH 027/501] 2023.9.0-beta.8-mattyaski.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8c318695ec..79710e4fb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.8-mattyaski.1", + "version": "2023.9.0-beta.8-mattyaski.2", "codename": "nasubi", "repository": { "type": "git", From cd45dfcc1a0a02b7116ce696bb7f6d790a2fa34c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 19:20:48 +0900 Subject: [PATCH 028/501] =?UTF-8?q?fix:=20=E3=83=AA=E3=83=A2=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92=E9=87=8D?= =?UTF-8?q?=E8=A4=87=E5=88=A4=E5=AE=9A=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/api/endpoints/admin/emoji/copy.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts index 40f9f9e1e3..83b093e931 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/copy.ts @@ -13,6 +13,7 @@ import { DriveService } from '@/core/DriveService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { ApiError } from '../../../error.js'; +import {IsNull} from "typeorm"; export const meta = { tags: ['admin'], @@ -78,6 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const duplicationEmoji = await this.emojisRepository.find({ where: { name: emoji.name, + host: IsNull() }, }); From 523bdc295c37b277c8fb3b0236afec2763f2f2ad Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 19:31:03 +0900 Subject: [PATCH 029/501] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AE=E3=82=B5=E3=82=A4=E3=82=BA=E5=89=8A=E6=B8=9B=E3=81=97?= =?UTF-8?q?=E3=81=9F=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/server/FileServerService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 11721263d3..8c7d92fc61 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -284,7 +284,7 @@ export class FileServerService { } else { const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) })) .resize({ - height: 'emoji' in request.query ? 128 : 320, + height: 'emoji' in request.query ? 64 : 128, withoutEnlargement: true, }) .webp(webpDefault); From 7ead6b04fa63f37dc55451d5f49c8e8cdd432f8e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 17 Sep 2023 19:33:27 +0900 Subject: [PATCH 030/501] 2023.9.0-beta.8-mattyaski.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79710e4fb4..520c327b30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.8-mattyaski.2", + "version": "2023.9.0-beta.8-mattyaski.3", "codename": "nasubi", "repository": { "type": "git", From 875216064527deaf7876b4d6c48b479d65bef678 Mon Sep 17 00:00:00 2001 From: Fairy-Phy <phy.public@gmail.com> Date: Sun, 17 Sep 2023 01:34:45 +0900 Subject: [PATCH 031/501] =?UTF-8?q?=E4=B8=80=E9=83=A8=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=81=AE=E3=83=90=E3=82=A4=E3=83=88=E5=A4=89=E6=8F=9B?= =?UTF-8?q?=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=8F=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E5=95=8F=E9=A1=8C=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/scripts/emojiKitchen/emojiMixer.ts | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts b/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts index 654eb357f7..89ade45b24 100644 --- a/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts +++ b/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts @@ -26,17 +26,24 @@ const convertBase = (value, from_base, to_base) => { return new_value || '0'; }; +const emojiSplit = String.fromCodePoint(0x200d); const hexEncodeEmoji = (chr) => { - if (chr.length !== 1) { + if (chr.length === 3) return hexEncodeEmoji(chr.slice(0, 2)) + '-' + hexEncodeEmoji(chr.slice(2, chr.length)); + else if (chr.length === 2) { const hi = chr.charCodeAt(0); const lo = chr.charCodeAt(1); if (0xD800 <= hi && hi < 0xDC00 && 0xDC00 <= lo && lo < 0xE000) { return (0x10000 + (hi - 0xD800) * 0x400 + (lo - 0xDC00)).toString(16); } - return ("000" + hi.toString(16)).slice(-4) + '-' + ("000" + lo.toString(16)).slice(-4); + return hi.toString(16) + '-' + lo.toString(16); + } + else if (chr.length === 1) { + return chr.charCodeAt(0).toString(16); } else { - return ("000" + chr.charCodeAt(0).toString(16)).slice(-4); + const sp = chr.split(emojiSplit); + if (sp.length !== 2) return ''; + return hexEncodeEmoji(sp[0]) + '-200d-' + hexEncodeEmoji(sp[1]); } }; From 368afeba34e2954e75baeae348b38c486650909d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 08:24:40 +0900 Subject: [PATCH 032/501] =?UTF-8?q?feat:=20=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=83=90=E3=83=BC=E5=BC=B7=E5=8C=96(?= =?UTF-8?q?=E3=81=97=E3=81=9F=E3=81=8B=E3=81=A3=E3=81=9F)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 2 ++ locales/ja-JP.yml | 1 + .../frontend/src/components/global/MkAvatar.vue | 2 +- packages/frontend/src/pages/settings/general.vue | 4 +++- packages/frontend/src/store.ts | 4 ++++ packages/frontend/src/ui/universal.vue | 14 ++++++++++++++ 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index ac714258e2..2f8437f52a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1032,6 +1032,7 @@ export interface Locale { "video": string; "videos": string; "dataSaver": string; + "cellularWithDataSaver": string; "accountMigration": string; "accountMoved": string; "accountMovedShort": string; @@ -1656,6 +1657,7 @@ export interface Locale { "contributors": string; "allContributors": string; "source": string; + "forksource": string; "translation": string; "donate": string; "morePatrons": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 71c04873ad..b92aa0ec81 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1029,6 +1029,7 @@ noteIdOrUrl: "ノートIDまたはURL" video: "動画" videos: "動画" dataSaver: "データセーバー" +cellularWithDataSaver: "モバイルデータ通信でデータセーバーをオンにする" accountMigration: "アカウントの移行" accountMoved: "このユーザーは新しいアカウントに移行しました:" accountMovedShort: "このアカウントは移行されています" diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 7c344ccf7c..788e4ec9af 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick"> - <MkImgWithBlurhash :class="$style.inner" :src="url" :hash="user?.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> + <MkImgWithBlurhash :class="$style.inner" :src="defaultStore.state.enableDataSaverMode ? undefined : url" :hash="user?.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> <div v-if="user.isCat" :class="[$style.ears]"> <div :class="$style.earLeft"> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b486e6d80f..e5b0d7b63f 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -114,6 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch> <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> <MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> + <MkSwitch v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> </div> <div> <MkRadios v-model="emojiStyle"> @@ -227,7 +228,8 @@ const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); -const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode')); +const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode')) ; +const enableCellularWithDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithDataSaver')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 787a584f83..06aabba7f6 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -204,6 +204,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + enableCellularWithDataSaver: { + where: 'device', + default: false, + }, disableShowingAnimatedImages: { where: 'device', default: window.matchMedia('(prefers-reduced-motion)').matches, diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index d9cb81b5ef..73d78bc3b6 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -115,6 +115,20 @@ const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announce const DESKTOP_THRESHOLD = 1100; const MOBILE_THRESHOLD = 500; +onMounted(() => { + if ( + window.navigator.connection.type === "cellular" && + !defaultStore.state.enableDataSaverMode && + defaultStore.state.enableCellularWithDataSaver + ) { + defaultStore.state.enableDataSaverMode = true; + } else if ( + window.navigator.connection.type !== "cellular" && + defaultStore.state.enableDataSaverMode && + !defaultStore.state.enableCellularWithDataSaver) { + defaultStore.state.enableDataSaverMode = false; + } +}); // デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); From e970ddef2c084306ad99ff8b4cef135b35d8d03d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 08:25:23 +0900 Subject: [PATCH 033/501] =?UTF-8?q?feat:=20frontend=E3=81=AEbuild=E3=81=AB?= =?UTF-8?q?=E5=9C=A7=E7=B8=AE=E3=82=92=E3=81=8B=E3=81=91=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB(=E6=84=8F=E5=91=B3=E3=81=8C=E3=81=82?= =?UTF-8?q?=E3=82=8B=E3=81=AE=E3=81=8B=E3=81=AF=E3=82=8F=E3=81=8B=E3=82=89?= =?UTF-8?q?=E3=81=AA=E3=81=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/package.json | 1 + packages/frontend/vite.config.ts | 5 +++-- pnpm-lock.yaml | 11 +++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index d38611c2fc..6cffc44fb7 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -130,6 +130,7 @@ "storybook": "7.4.1", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "summaly": "github:misskey-dev/summaly", + "vite-plugin-compression2": "^0.10.4", "vite-plugin-turbosnap": "1.0.3", "vitest": "0.34.4", "vitest-fetch-mock": "0.2.2", diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index da976b7917..637ae677b8 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -1,7 +1,7 @@ import path from 'path'; import pluginReplace from '@rollup/plugin-replace'; import pluginVue from '@vitejs/plugin-vue'; -import { type UserConfig, defineConfig } from 'vite'; +import {type UserConfig, defineConfig} from 'vite'; // @ts-expect-error https://github.com/sxzz/unplugin-vue-macros/issues/257#issuecomment-1410752890 import ReactivityTransform from '@vue-macros/reactivity-transform/vite'; @@ -9,6 +9,7 @@ import locales from '../../locales'; import meta from '../../package.json'; import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name'; import pluginJson5 from './vite.json5'; +import compression from "vite-plugin-compression2"; const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue']; @@ -44,12 +45,12 @@ function toBase62(n: number): string { export function getConfig(): UserConfig { return { base: '/vite/', - server: { port: 5173, }, plugins: [ + compression(), pluginVue({ reactivityTransform: true, }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 61fcb6aef5..313d261f8a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -980,6 +980,9 @@ importers: summaly: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 + vite-plugin-compression2: + specifier: ^0.10.4 + version: 0.10.4(rollup@3.29.1) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 @@ -19126,6 +19129,14 @@ packages: - terser dev: true + /vite-plugin-compression2@0.10.4(rollup@3.29.1): + resolution: {integrity: sha512-9YcESw0n1j8KxxY1NJKEcItlT0bLS+K/NKa/xPqZGEHW/qwgigIeRF/bCTUdZ/bn/mg2+PeERWgRmK8G1L0tyg==} + dependencies: + '@rollup/pluginutils': 5.0.4(rollup@3.29.1) + transitivePeerDependencies: + - rollup + dev: true + /vite-plugin-turbosnap@1.0.3: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true From c7085f5adef69f929d5df271cf879cef73d6c134 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 08:38:57 +0900 Subject: [PATCH 034/501] =?UTF-8?q?CHANGELOG.md=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bbcbdf58da..b7c66a25fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ --> -## 2023.9.0 (unreleased) +## 2023.9.0-mattyaski (unreleased) ### General - OAuth 2.0のサポート @@ -48,7 +48,10 @@ - Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように - Enhance: Renote自体を通報できるように - Enhance: データセーバーモードの強化 + - Safariは非対応だけど、LTEだと自動的にオンにする機能も追加 (https://developer.mozilla.org/ja/docs/Web/API/Navigator/connection) + - アイコンをblurで表示させるように - Enhance: Renoteを管理者権限で削除可能に +- Enhance: `$[mix ]`(emojiKitchen) 記法を追加 # mattyaski独自 - `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました - Playの操作を行うAPI TokenをAPIコンソールから発行できるように - リアクションの表示サイズをより大きくできるように @@ -70,7 +73,9 @@ - Webhookのペイロードにサーバーのurlが含まれるようになりました - Webhook設定でsecretを空に出来るように - 使われていないアンテナの自動停止を設定可能に +- リプライをホーム投稿に # mattyaski独自 - nodeinfo 2.1対応 +- Enhance: 絵文字の重複登録を不可に # mattyaski独自 - 自分へのメンション一覧を取得する際のパフォーマンスを向上 - Docker環境でjemallocを使用することでメモリ使用量を削減 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正 From c8d0c99d61f0aa30915e324143574c7da83aaf70 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 08:39:20 +0900 Subject: [PATCH 035/501] 2023.9.0-beta.8-mattyaski.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 520c327b30..036cabb97b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.8-mattyaski.3", + "version": "2023.9.0-beta.8-mattyaski.4", "codename": "nasubi", "repository": { "type": "git", From f2ab7a56260aad3695338f0593237c761338a793 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 09:11:20 +0900 Subject: [PATCH 036/501] =?UTF-8?q?feat:=20=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=83=90=E3=83=BC=E5=BC=B7=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkInstanceTicker.vue | 3 +- packages/frontend/src/ui/universal.vue | 618 +++++++++--------- 2 files changed, 319 insertions(+), 302 deletions(-) diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue index afa0004cc7..3215d87ddf 100644 --- a/packages/frontend/src/components/MkInstanceTicker.vue +++ b/packages/frontend/src/components/MkInstanceTicker.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root" :style="bg"> - <img v-if="faviconUrl" :class="$style.icon" :src="faviconUrl"/> + <img v-if="faviconUrl && !defaultStore.state.enableDataSaverMode" :class="$style.icon" :src="faviconUrl"/> <div :class="$style.name">{{ instance.name }}</div> </div> </template> @@ -15,6 +15,7 @@ import { } from 'vue'; import { instanceName } from '@/config'; import { instance as Instance } from '@/instance'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy'; +import {defaultStore} from "@/store"; const props = defineProps<{ instance?: { diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 73d78bc3b6..1bede0c14c 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -4,108 +4,122 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="$style.root"> - <XSidebar v-if="!isMobile" :class="$style.sidebar"/> + <div :class="$style.root"> + <XSidebar v-if="!isMobile" :class="$style.sidebar"/> - <MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" @contextmenu.stop="onContextmenu"> - <template #header> - <div> - <XAnnouncements v-if="$i" :class="$style.announcements"/> - <XStatusBars :class="$style.statusbars"/> - </div> - </template> - <RouterView/> - <div :class="$style.spacer"></div> - </MkStickyContainer> + <MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" + @contextmenu.stop="onContextmenu"> + <template #header> + <div> + <XAnnouncements v-if="$i" :class="$style.announcements"/> + <XStatusBars :class="$style.statusbars"/> + </div> + </template> + <RouterView/> + <div :class="$style.spacer"></div> + </MkStickyContainer> - <div v-if="isDesktop" :class="$style.widgets"> - <XWidgets/> - </div> + <div v-if="isDesktop" :class="$style.widgets"> + <XWidgets/> + </div> - <button v-if="!isDesktop && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button> + <button v-if="!isDesktop && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"> + <i class="ti ti-apps"></i></button> - <div v-if="isMobile" ref="navFooter" :class="$style.nav"> - <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button> - <button :class="$style.navButton" class="_button" @click="mainRouter.currentRoute.value.name === 'index' ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button> - <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button> - <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i></button> - <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button> - </div> + <div v-if="isMobile" ref="navFooter" :class="$style.nav"> + <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i + :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" + :class="$style.navButtonIndicator"><i + class="_indicatorCircle"></i></span></button> + <button :class="$style.navButton" class="_button" + @click="mainRouter.currentRoute.value.name === 'index' ? top() : mainRouter.push('/')"><i + :class="$style.navButtonIcon" class="ti ti-home"></i></button> + <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i + :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" + :class="$style.navButtonIndicator"><i + class="_indicatorCircle"></i></span></button> + <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" + class="ti ti-apps"></i> + </button> + <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" + class="ti ti-pencil"></i></button> + </div> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''" - > - <div - v-if="drawerMenuShowing" - :class="$style.menuDrawerBg" - class="_modalBg" - @click="drawerMenuShowing = false" - @touchstart.passive="drawerMenuShowing = false" - ></div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''" + > + <div + v-if="drawerMenuShowing" + :class="$style.menuDrawerBg" + class="_modalBg" + @click="drawerMenuShowing = false" + @touchstart.passive="drawerMenuShowing = false" + ></div> + </Transition> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''" - > - <div v-if="drawerMenuShowing" :class="$style.menuDrawer"> - <XDrawerMenu/> - </div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''" + > + <div v-if="drawerMenuShowing" :class="$style.menuDrawer"> + <XDrawerMenu/> + </div> + </Transition> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''" - > - <div - v-if="widgetsShowing" - :class="$style.widgetsDrawerBg" - class="_modalBg" - @click="widgetsShowing = false" - @touchstart.passive="widgetsShowing = false" - ></div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''" + > + <div + v-if="widgetsShowing" + :class="$style.widgetsDrawerBg" + class="_modalBg" + @click="widgetsShowing = false" + @touchstart.passive="widgetsShowing = false" + ></div> + </Transition> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''" - > - <div v-if="widgetsShowing" :class="$style.widgetsDrawer"> - <button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button> - <XWidgets/> - </div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''" + > + <div v-if="widgetsShowing" :class="$style.widgetsDrawer"> + <button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i + class="ti ti-x"></i></button> + <XWidgets/> + </div> + </Transition> - <XCommon/> -</div> + <XCommon/> + </div> </template> <script lang="ts" setup> -import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef, watch, shallowRef, Ref } from 'vue'; +import {defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef, watch, shallowRef, Ref} from 'vue'; import XCommon from './_common_/common.vue'; import type MkStickyContainer from '@/components/global/MkStickyContainer.vue'; -import { instanceName } from '@/config'; +import {instanceName} from '@/config'; import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue'; import * as os from '@/os'; -import { defaultStore } from '@/store'; -import { navbarItemDef } from '@/navbar'; -import { i18n } from '@/i18n'; -import { $i } from '@/account'; -import { mainRouter } from '@/router'; -import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata'; -import { deviceKind } from '@/scripts/device-kind'; -import { miLocalStorage } from '@/local-storage'; -import { CURRENT_STICKY_BOTTOM } from '@/const'; -import { useScrollPositionManager } from '@/nirax'; +import {defaultStore} from '@/store'; +import {navbarItemDef} from '@/navbar'; +import {i18n} from '@/i18n'; +import {$i} from '@/account'; +import {mainRouter} from '@/router'; +import {PageMetadata, provideMetadataReceiver} from '@/scripts/page-metadata'; +import {deviceKind} from '@/scripts/device-kind'; +import {miLocalStorage} from '@/local-storage'; +import {CURRENT_STICKY_BOTTOM} from '@/const'; +import {useScrollPositionManager} from '@/nirax'; const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); @@ -122,10 +136,8 @@ onMounted(() => { defaultStore.state.enableCellularWithDataSaver ) { defaultStore.state.enableDataSaverMode = true; - } else if ( - window.navigator.connection.type !== "cellular" && - defaultStore.state.enableDataSaverMode && - !defaultStore.state.enableCellularWithDataSaver) { + + } else if (window.navigator.connection.type !== "cellular" && window.navigator.connection.type !== "undefined" && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { defaultStore.state.enableDataSaverMode = false; } }); @@ -133,7 +145,7 @@ onMounted(() => { const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); window.addEventListener('resize', () => { - isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD; + isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD; }); let pageMetadata = $ref<null | ComputedRef<PageMetadata>>(); @@ -143,103 +155,103 @@ const contents = shallowRef<InstanceType<typeof MkStickyContainer>>(); provide('router', mainRouter); provideMetadataReceiver((info) => { - pageMetadata = info; - if (pageMetadata.value) { - document.title = `${pageMetadata.value.title} | ${instanceName}`; - } + pageMetadata = info; + if (pageMetadata.value) { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } }); const menuIndicated = computed(() => { - for (const def in navbarItemDef) { - if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから - if (navbarItemDef[def].indicated) return true; - } - return false; + for (const def in navbarItemDef) { + if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから + if (navbarItemDef[def].indicated) return true; + } + return false; }); const drawerMenuShowing = ref(false); mainRouter.on('change', () => { - drawerMenuShowing.value = false; + drawerMenuShowing.value = false; }); if (window.innerWidth > 1024) { - const tempUI = miLocalStorage.getItem('ui_temp'); - if (tempUI) { - miLocalStorage.setItem('ui', tempUI); - miLocalStorage.removeItem('ui_temp'); - location.reload(); - } + const tempUI = miLocalStorage.getItem('ui_temp'); + if (tempUI) { + miLocalStorage.setItem('ui', tempUI); + miLocalStorage.removeItem('ui_temp'); + location.reload(); + } } defaultStore.loaded.then(() => { - if (defaultStore.state.widgets.length === 0) { - defaultStore.set('widgets', [{ - name: 'calendar', - id: 'a', place: 'right', data: {}, - }, { - name: 'notifications', - id: 'b', place: 'right', data: {}, - }, { - name: 'trends', - id: 'c', place: 'right', data: {}, - }]); - } + if (defaultStore.state.widgets.length === 0) { + defaultStore.set('widgets', [{ + name: 'calendar', + id: 'a', place: 'right', data: {}, + }, { + name: 'notifications', + id: 'b', place: 'right', data: {}, + }, { + name: 'trends', + id: 'c', place: 'right', data: {}, + }]); + } }); onMounted(() => { - if (!isDesktop.value) { - window.addEventListener('resize', () => { - if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true; - }, { passive: true }); - } + if (!isDesktop.value) { + window.addEventListener('resize', () => { + if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true; + }, {passive: true}); + } }); const onContextmenu = (ev) => { - const isLink = (el: HTMLElement) => { - if (el.tagName === 'A') return true; - if (el.parentElement) { - return isLink(el.parentElement); - } - }; - if (isLink(ev.target)) return; - if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; - if (window.getSelection()?.toString() !== '') return; - const path = mainRouter.getCurrentPath(); - os.contextMenu([{ - type: 'label', - text: path, - }, { - icon: 'ti ti-window-maximize', - text: i18n.ts.openInWindow, - action: () => { - os.pageWindow(path); - }, - }], ev); + const isLink = (el: HTMLElement) => { + if (el.tagName === 'A') return true; + if (el.parentElement) { + return isLink(el.parentElement); + } + }; + if (isLink(ev.target)) return; + if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; + if (window.getSelection()?.toString() !== '') return; + const path = mainRouter.getCurrentPath(); + os.contextMenu([{ + type: 'label', + text: path, + }, { + icon: 'ti ti-window-maximize', + text: i18n.ts.openInWindow, + action: () => { + os.pageWindow(path); + }, + }], ev); }; function top() { - contents.value.rootEl.scrollTo({ - top: 0, - behavior: 'smooth', - }); + contents.value.rootEl.scrollTo({ + top: 0, + behavior: 'smooth', + }); } let navFooterHeight = $ref(0); provide<Ref<number>>(CURRENT_STICKY_BOTTOM, $$(navFooterHeight)); watch($$(navFooter), () => { - if (navFooter) { - navFooterHeight = navFooter.offsetHeight; - document.body.style.setProperty('--stickyBottom', `${navFooterHeight}px`); - document.body.style.setProperty('--minBottomSpacing', 'var(--minBottomSpacingMobile)'); - } else { - navFooterHeight = 0; - document.body.style.setProperty('--stickyBottom', '0px'); - document.body.style.setProperty('--minBottomSpacing', '0px'); - } + if (navFooter) { + navFooterHeight = navFooter.offsetHeight; + document.body.style.setProperty('--stickyBottom', `${navFooterHeight}px`); + document.body.style.setProperty('--minBottomSpacing', 'var(--minBottomSpacingMobile)'); + } else { + navFooterHeight = 0; + document.body.style.setProperty('--stickyBottom', '0px'); + document.body.style.setProperty('--minBottomSpacing', '0px'); + } }, { - immediate: true, + immediate: true, }); useScrollPositionManager(() => contents.value.rootEl, mainRouter); @@ -248,22 +260,22 @@ useScrollPositionManager(() => contents.value.rootEl, mainRouter); <style> html, body { - width: 100%; - height: 100%; - overflow: clip; - position: fixed; - top: 0; - left: 0; - overscroll-behavior: none; + width: 100%; + height: 100%; + overflow: clip; + position: fixed; + top: 0; + left: 0; + overscroll-behavior: none; } #misskey_app { - width: 100%; - height: 100%; - overflow: clip; - position: absolute; - top: 0; - left: 0; + width: 100%; + height: 100%; + overflow: clip; + position: absolute; + top: 0; + left: 0; } </style> @@ -273,218 +285,222 @@ $widgets-hide-threshold: 1090px; .transition_menuDrawerBg_enterActive, .transition_menuDrawerBg_leaveActive { - opacity: 1; - transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); + opacity: 1; + transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); } + .transition_menuDrawerBg_enterFrom, .transition_menuDrawerBg_leaveTo { - opacity: 0; + opacity: 0; } .transition_menuDrawer_enterActive, .transition_menuDrawer_leaveActive { - opacity: 1; - transform: translateX(0); - transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); + opacity: 1; + transform: translateX(0); + transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); } + .transition_menuDrawer_enterFrom, .transition_menuDrawer_leaveTo { - opacity: 0; - transform: translateX(-240px); + opacity: 0; + transform: translateX(-240px); } .transition_widgetsDrawerBg_enterActive, .transition_widgetsDrawerBg_leaveActive { - opacity: 1; - transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); + opacity: 1; + transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); } + .transition_widgetsDrawerBg_enterFrom, .transition_widgetsDrawerBg_leaveTo { - opacity: 0; + opacity: 0; } .transition_widgetsDrawer_enterActive, .transition_widgetsDrawer_leaveActive { - opacity: 1; - transform: translateX(0); - transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); + opacity: 1; + transform: translateX(0); + transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); } + .transition_widgetsDrawer_enterFrom, .transition_widgetsDrawer_leaveTo { - opacity: 0; - transform: translateX(240px); + opacity: 0; + transform: translateX(240px); } .root { - height: 100dvh; - overflow: clip; - contain: strict; - box-sizing: border-box; - display: flex; + height: 100dvh; + overflow: clip; + contain: strict; + box-sizing: border-box; + display: flex; } .sidebar { - border-right: solid 0.5px var(--divider); + border-right: solid 0.5px var(--divider); } .contents { - flex: 1; - height: 100%; - min-width: 0; - overflow: auto; - overflow-y: scroll; - overscroll-behavior: contain; - background: var(--bg); + flex: 1; + height: 100%; + min-width: 0; + overflow: auto; + overflow-y: scroll; + overscroll-behavior: contain; + background: var(--bg); } .widgets { - width: 350px; - height: 100%; - box-sizing: border-box; - overflow: auto; - padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)); - border-left: solid 0.5px var(--divider); - background: var(--bg); + width: 350px; + height: 100%; + box-sizing: border-box; + overflow: auto; + padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)); + border-left: solid 0.5px var(--divider); + background: var(--bg); - @media (max-width: $widgets-hide-threshold) { - display: none; - } + @media (max-width: $widgets-hide-threshold) { + display: none; + } } .widgetButton { - display: block; - position: fixed; - z-index: 1000; - bottom: 32px; - right: 32px; - width: 64px; - height: 64px; - border-radius: 100%; - box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12); - font-size: 22px; - background: var(--panel); + display: block; + position: fixed; + z-index: 1000; + bottom: 32px; + right: 32px; + width: 64px; + height: 64px; + border-radius: 100%; + box-shadow: 0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12); + font-size: 22px; + background: var(--panel); } .widgetsDrawerBg { - z-index: 1001; + z-index: 1001; } .widgetsDrawer { - position: fixed; - top: 0; - right: 0; - z-index: 1001; - width: 310px; - height: 100dvh; - padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)) !important; - box-sizing: border-box; - overflow: auto; - overscroll-behavior: contain; - background: var(--bg); + position: fixed; + top: 0; + right: 0; + z-index: 1001; + width: 310px; + height: 100dvh; + padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)) !important; + box-sizing: border-box; + overflow: auto; + overscroll-behavior: contain; + background: var(--bg); } .widgetsCloseButton { - padding: 8px; - display: block; - margin: 0 auto; + padding: 8px; + display: block; + margin: 0 auto; } @media (min-width: 370px) { - .widgetsCloseButton { - display: none; - } + .widgetsCloseButton { + display: none; + } } .nav { - position: fixed; - z-index: 1000; - bottom: 0; - left: 0; - padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px; - display: grid; - grid-template-columns: 1fr 1fr 1fr 1fr 1fr; - grid-gap: 8px; - width: 100%; - box-sizing: border-box; - -webkit-backdrop-filter: var(--blur, blur(24px)); - backdrop-filter: var(--blur, blur(24px)); - background-color: var(--header); - border-top: solid 0.5px var(--divider); + position: fixed; + z-index: 1000; + bottom: 0; + left: 0; + padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px; + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + grid-gap: 8px; + width: 100%; + box-sizing: border-box; + -webkit-backdrop-filter: var(--blur, blur(24px)); + backdrop-filter: var(--blur, blur(24px)); + background-color: var(--header); + border-top: solid 0.5px var(--divider); } .navButton { - position: relative; - padding: 0; - aspect-ratio: 1; - width: 100%; - max-width: 60px; - margin: auto; - border-radius: 100%; - background: var(--panel); - color: var(--fg); + position: relative; + padding: 0; + aspect-ratio: 1; + width: 100%; + max-width: 60px; + margin: auto; + border-radius: 100%; + background: var(--panel); + color: var(--fg); - &:hover { - background: var(--panelHighlight); - } + &:hover { + background: var(--panelHighlight); + } - &:active { - background: var(--X2); - } + &:active { + background: var(--X2); + } } .postButton { - composes: navButton; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - color: var(--fgOnAccent); + composes: navButton; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + color: var(--fgOnAccent); - &:hover { - background: linear-gradient(90deg, var(--X8), var(--X8)); - } + &:hover { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } - &:active { - background: linear-gradient(90deg, var(--X8), var(--X8)); - } + &:active { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } } .navButtonIcon { - font-size: 18px; - vertical-align: middle; + font-size: 18px; + vertical-align: middle; } .navButtonIndicator { - position: absolute; - top: 0; - left: 0; - color: var(--indicator); - font-size: 16px; - animation: blink 1s infinite; + position: absolute; + top: 0; + left: 0; + color: var(--indicator); + font-size: 16px; + animation: blink 1s infinite; } .menuDrawerBg { - z-index: 1001; + z-index: 1001; } .menuDrawer { - position: fixed; - top: 0; - left: 0; - z-index: 1001; - height: 100dvh; - width: 240px; - box-sizing: border-box; - contain: strict; - overflow: auto; - overscroll-behavior: contain; - background: var(--navBg); + position: fixed; + top: 0; + left: 0; + z-index: 1001; + height: 100dvh; + width: 240px; + box-sizing: border-box; + contain: strict; + overflow: auto; + overscroll-behavior: contain; + background: var(--navBg); } .statusbars { - position: sticky; - top: 0; - left: 0; + position: sticky; + top: 0; + left: 0; } .spacer { - height: calc(var(--minBottomSpacing)); + height: calc(var(--minBottomSpacing)); } </style> From c9b97d5e0d1e6497925c6c260ec6d5b75d1aeeba Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 09:26:25 +0900 Subject: [PATCH 037/501] =?UTF-8?q?feat:=20=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=82=BB=E3=83=BC=E3=83=90=E3=83=BC=E5=BC=B7=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/FileServerService.ts | 26 +++++++++++++++++-- .../src/components/global/MkCustomEmoji.vue | 11 ++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 8c7d92fc61..54b50041fd 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -265,7 +265,8 @@ export class FileServerService { 'avatar' in request.query || 'static' in request.query || 'preview' in request.query || - 'badge' in request.query + 'badge' in request.query || + 'datasaver' in request.query ) { if (!isConvertibleImage) { // 画像でないなら404でお茶を濁す @@ -330,7 +331,28 @@ export class FileServerService { ext: 'png', type: 'image/png', }; - } else if (file.mime === 'image/svg+xml') { + } else if ('datasaver' in request.query){ + if (!isAnimationConvertibleImage && !('static' in request.query)) { + image = { + data: fs.createReadStream(file.path), + ext: file.ext, + type: file.mime, + }; + } else { + const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) })) + .resize({ + height: 64, + withoutEnlargement: true, + }) + .webp(webpDefault); + + image = { + data, + ext: 'webp', + type: 'image/webp', + }; + } + }else if (file.mime === 'image/svg+xml') { image = this.imageProcessingService.convertToWebpStream(file.path, 2048, 2048); } else if (!file.mime.startsWith('image/') || !FILE_TYPE_BROWSERSAFE.includes(file.mime)) { throw new StatusError('Rejected type', 403, 'Rejected type'); diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index bef7c59d77..b01a172a60 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -38,13 +38,20 @@ const rawUrl = computed(() => { const url = computed(() => { if (rawUrl.value == null) return null; - + const useOriginalSize = props.useOriginalSize; + const enableDataSaverMode = defaultStore.state.enableDataSaverMode; + let datasaver_result ; + if (enableDataSaverMode) { + datasaver_result = useOriginalSize ? undefined : 'datasaver'; + } else { + datasaver_result = useOriginalSize ? undefined : 'emoji'; + } const proxied = (rawUrl.value.startsWith('/emoji/') || (props.useOriginalSize && isLocal.value)) ? rawUrl.value : getProxiedImageUrl( rawUrl.value, - props.useOriginalSize ? undefined : 'emoji', + datasaver_result, false, true, ); From 984c8b73cc7f62cdb37512ede93534fe8fd56047 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 09:26:44 +0900 Subject: [PATCH 038/501] =?UTF-8?q?CHANGELOG.md=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b7c66a25fa..f00a78cd5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -76,6 +76,7 @@ - リプライをホーム投稿に # mattyaski独自 - nodeinfo 2.1対応 - Enhance: 絵文字の重複登録を不可に # mattyaski独自 +- Enhance: frontendのbuildに圧縮をかけるように # mattyaski独自 - 自分へのメンション一覧を取得する際のパフォーマンスを向上 - Docker環境でjemallocを使用することでメモリ使用量を削減 - Fix: MK_ONLY_SERVERオプションを指定した際にクラッシュする問題を修正 From 62b70f85f092c2369b4fb3c0ed9dc48146dff41b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 21:47:39 +0900 Subject: [PATCH 039/501] =?UTF-8?q?=E3=81=AA=E3=82=93=E3=81=8B=E3=82=82?= =?UTF-8?q?=E3=81=86=E8=89=B2=E3=80=85=E5=A4=89=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 3 +++ locales/index.d.ts | 2 ++ locales/ja-JP.yml | 2 ++ packages/backend/src/server/FileServerService.ts | 2 +- .../frontend/src/components/MkInstanceTicker.vue | 2 +- .../frontend/src/components/global/MkAvatar.vue | 2 +- .../src/components/global/MkCustomEmoji.vue | 2 +- packages/frontend/src/pages/settings/general.vue | 10 ++++++++-- packages/frontend/src/store.ts | 8 ++++++++ packages/frontend/src/ui/universal.vue | 15 +++++++++++++++ packages/frontend/vite.config.ts | 2 +- 11 files changed, 43 insertions(+), 7 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 9ce6ee5cdb..772e6f35da 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1029,6 +1029,9 @@ noteIdOrUrl: "Note ID or URL" video: "Video" videos: "Videos" dataSaver: "Data Saver" +cellularWithDataSaver: "Turn on Data Saver in Mobile Data Communications" +UltimatedataSaver: "Ultimate Data Saver" +cellularWithUltimateDataSaver: "Turn on Ultimate Data Saver in Mobile Data Communications" accountMigration: "Account Migration" accountMoved: "This user has moved to a new account:" accountMovedShort: "This account has been migrated." diff --git a/locales/index.d.ts b/locales/index.d.ts index 2f8437f52a..32df226912 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1033,6 +1033,8 @@ export interface Locale { "videos": string; "dataSaver": string; "cellularWithDataSaver": string; + "UltimateDataSaver": string; + "cellularWithUltimateDataSaver": string; "accountMigration": string; "accountMoved": string; "accountMovedShort": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index b92aa0ec81..7cad2361bf 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1030,6 +1030,8 @@ video: "動画" videos: "動画" dataSaver: "データセーバー" cellularWithDataSaver: "モバイルデータ通信でデータセーバーをオンにする" +UltimateDataSaver: "究極のデータセーバー" +cellularWithUltimateDataSaver: "モバイルデータ通信で究極のデータセーバーをオンにする" accountMigration: "アカウントの移行" accountMoved: "このユーザーは新しいアカウントに移行しました:" accountMovedShort: "このアカウントは移行されています" diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 54b50041fd..247ef73de8 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -341,7 +341,7 @@ export class FileServerService { } else { const data = (await sharpBmp(file.path, file.mime, { animated: !('static' in request.query) })) .resize({ - height: 64, + height: 32, withoutEnlargement: true, }) .webp(webpDefault); diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue index 3215d87ddf..c702b6403b 100644 --- a/packages/frontend/src/components/MkInstanceTicker.vue +++ b/packages/frontend/src/components/MkInstanceTicker.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root" :style="bg"> - <img v-if="faviconUrl && !defaultStore.state.enableDataSaverMode" :class="$style.icon" :src="faviconUrl"/> + <img v-if="faviconUrl && !defaultStore.state.enableUltimateDataSaverMode" :class="$style.icon" :src="faviconUrl"/> <div :class="$style.name">{{ instance.name }}</div> </div> </template> diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 788e4ec9af..72768dd171 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick"> - <MkImgWithBlurhash :class="$style.inner" :src="defaultStore.state.enableDataSaverMode ? undefined : url" :hash="user?.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> + <MkImgWithBlurhash :class="$style.inner" :src="defaultStore.state.enableUltimateDataSaverMode ? undefined : url" :hash="user?.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> <div v-if="user.isCat" :class="[$style.ears]"> <div :class="$style.earLeft"> diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index b01a172a60..4029bfa855 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -39,7 +39,7 @@ const rawUrl = computed(() => { const url = computed(() => { if (rawUrl.value == null) return null; const useOriginalSize = props.useOriginalSize; - const enableDataSaverMode = defaultStore.state.enableDataSaverMode; + const enableDataSaverMode = defaultStore.state.enableUltimateDataSaverMode; let datasaver_result ; if (enableDataSaverMode) { datasaver_result = useOriginalSize ? undefined : 'datasaver'; diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index e5b0d7b63f..962b800d40 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -113,8 +113,10 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch> <MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch> <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> - <MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> - <MkSwitch v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> + <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> + <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> + <MkSwitch v-model="enableUltimateDataSaverMode">{{ i18n.ts.UltimateDataSaver }}</MkSwitch> + <MkSwitch v-model="enableCellularWithUltimateDataSaver">{{ i18n.ts.cellularWithUltimateDataSaver }}</MkSwitch> </div> <div> <MkRadios v-model="emojiStyle"> @@ -230,6 +232,8 @@ const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode')) ; const enableCellularWithDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithDataSaver')); +const enableUltimateDataSaverMode = computed(defaultStore.makeGetterSetter('enableUltimateDataSaverMode')) +const enableCellularWithUltimateDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithUltimateDataSaver')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); @@ -244,6 +248,8 @@ const notificationPosition = computed(defaultStore.makeGetterSetter('notificatio const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies')); + + watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 06aabba7f6..0b16391777 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -204,10 +204,18 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + enableUltimateDataSaverMode: { + where: 'device', + default: false, + }, enableCellularWithDataSaver: { where: 'device', default: false, }, + enableCellularWithUltimateDataSaver: { + where: 'device', + default: false, + }, disableShowingAnimatedImages: { where: 'device', default: window.matchMedia('(prefers-reduced-motion)').matches, diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 1bede0c14c..7fc8043dd8 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -130,6 +130,18 @@ const DESKTOP_THRESHOLD = 1100; const MOBILE_THRESHOLD = 500; onMounted(() => { + if ( + window.navigator.connection.type === "cellular" && + !defaultStore.state.enableUltimateDataSaverMode && + defaultStore.state.enableCellularWithUltimateDataSaver + ) { + defaultStore.state.enableDataSaverMode = true; + defaultStore.state.enableUltimateDataSaverMode = true; + } else if (window.navigator.connection.type !== "cellular" && window.navigator.connection.type !== "undefined" && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { + defaultStore.state.enableDataSaverMode = false; + defaultStore.state.enableUltimateDataSaverMode = true; + } + if ( window.navigator.connection.type === "cellular" && !defaultStore.state.enableDataSaverMode && @@ -140,6 +152,9 @@ onMounted(() => { } else if (window.navigator.connection.type !== "cellular" && window.navigator.connection.type !== "undefined" && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { defaultStore.state.enableDataSaverMode = false; } + if (defaultStore.state.enableUltimateDataSaverMode) { + defaultStore.state.enableDataSaverMode = true; + } }); // デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index 637ae677b8..abd483bbe8 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -50,7 +50,7 @@ export function getConfig(): UserConfig { }, plugins: [ - compression(), + compression({ algorithm: 'brotliCompress'}), pluginVue({ reactivityTransform: true, }), From d778ff68d3f03f1bc5fd3a0bed23e468aa9a3716 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 18 Sep 2023 21:50:50 +0900 Subject: [PATCH 040/501] 2023.9.0-beta.8-mattyaski.5 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 036cabb97b..70a5727fe0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.8-mattyaski.4", + "version": "2023.9.0-beta.8-mattyaski.5", "codename": "nasubi", "repository": { "type": "git", From bec8237cb34997980ca2a268623a83adcdf3af5f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 20 Sep 2023 09:02:50 +0900 Subject: [PATCH 041/501] =?UTF-8?q?feat:=20prismisskey=E7=94=A8=E3=81=AB?= =?UTF-8?q?=E8=83=8C=E6=99=AF=E7=94=BB=E5=83=8F=E3=81=AA=E3=81=A9=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/about.vue | 14 +- .../frontend/src/pages/settings/theme.vue | 176 ++-- packages/frontend/src/store.ts | 7 + .../src/ui/_common_/navbar-for-mobile.vue | 15 +- packages/frontend/src/ui/_common_/navbar.vue | 794 +++++++++--------- .../src/widgets/WidgetInstanceInfo.vue | 13 +- 6 files changed, 554 insertions(+), 465 deletions(-) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 0ff9463841..0cd6e32264 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="tab === 'overview'" :contentMax="600" :marginMin="20"> <div class="_gaps_m"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"> <div style="overflow: clip;"> <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> <div :class="$style.bannerName"> @@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch } from 'vue'; +import {computed, ref, watch} from 'vue'; import XEmojis from './about.emojis.vue'; import XFederation from './about.federation.vue'; import { version, host } from '@/config'; @@ -115,6 +115,7 @@ import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { claimAchievement } from '@/scripts/achievements'; import { instance } from '@/instance'; +import {bannerDark, bannerLight, defaultStore} from "@/store"; const props = withDefaults(defineProps<{ initialTab?: string; @@ -130,7 +131,16 @@ watch($$(tab), () => { claimAchievement('viewInstanceChart'); } }); +let bannerUrl = ref(defaultStore.state.bannerUrl); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +watch(darkMode, () => { + if (darkMode.value){ + bannerUrl.value = bannerDark; + }else{ + bannerUrl.value = bannerLight; + } +}) const initStats = () => os.api('stats', { }).then((res) => { stats = res; diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index 6517f24acb..c1a904708e 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -4,90 +4,107 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m rsljpzjq"> - <div v-adaptive-border class="rfqxtzch _panel"> - <div class="toggle"> - <div class="toggleWrapper"> - <input id="dn" v-model="darkMode" type="checkbox" class="dn"/> - <label for="dn" class="toggle"> - <span class="before">{{ i18n.ts.light }}</span> - <span class="after">{{ i18n.ts.dark }}</span> - <span class="toggle__handler"> + <div class="_gaps_m rsljpzjq"> + <div v-adaptive-border class="rfqxtzch _panel"> + <div class="toggle"> + <div class="toggleWrapper"> + <input id="dn" v-model="darkMode" type="checkbox" class="dn"/> + <label for="dn" class="toggle"> + <span class="before">{{ i18n.ts.light }}</span> + <span class="after">{{ i18n.ts.dark }}</span> + <span class="toggle__handler"> <span class="crater crater--1"></span> <span class="crater crater--2"></span> <span class="crater crater--3"></span> </span> - <span class="star star--1"></span> - <span class="star star--2"></span> - <span class="star star--3"></span> - <span class="star star--4"></span> - <span class="star star--5"></span> - <span class="star star--6"></span> - </label> + <span class="star star--1"></span> + <span class="star star--2"></span> + <span class="star star--3"></span> + <span class="star star--4"></span> + <span class="star star--5"></span> + <span class="star star--6"></span> + </label> + </div> + </div> + <div class="sync"> + <MkSwitch v-model="syncDeviceDarkMode">{{ i18n.ts.syncDeviceDarkMode }}</MkSwitch> </div> </div> - <div class="sync"> - <MkSwitch v-model="syncDeviceDarkMode">{{ i18n.ts.syncDeviceDarkMode }}</MkSwitch> + + <div class="selects"> + <MkSelect v-model="lightThemeId" large class="select"> + <template #label>{{ i18n.ts.themeForLightMode }}</template> + <template #prefix><i class="ti ti-sun"></i></template> + <option v-if="instanceLightTheme" :key="'instance:' + instanceLightTheme.id" :value="instanceLightTheme.id"> + {{ instanceLightTheme.name }} + </option> + <optgroup v-if="installedLightThemes.length > 0" :label="i18n.ts._theme.installedThemes"> + <option v-for="x in installedLightThemes" :key="'installed:' + x.id" :value="x.id">{{ x.name }}</option> + </optgroup> + <optgroup :label="i18n.ts._theme.builtinThemes"> + <option v-for="x in builtinLightThemes" :key="'builtin:' + x.id" :value="x.id">{{ x.name }}</option> + </optgroup> + </MkSelect> + <MkSelect v-model="darkThemeId" large class="select"> + <template #label>{{ i18n.ts.themeForDarkMode }}</template> + <template #prefix><i class="ti ti-moon"></i></template> + <option v-if="instanceDarkTheme" :key="'instance:' + instanceDarkTheme.id" :value="instanceDarkTheme.id"> + {{ instanceDarkTheme.name }} + </option> + <optgroup v-if="installedDarkThemes.length > 0" :label="i18n.ts._theme.installedThemes"> + <option v-for="x in installedDarkThemes" :key="'installed:' + x.id" :value="x.id">{{ x.name }}</option> + </optgroup> + <optgroup :label="i18n.ts._theme.builtinThemes"> + <option v-for="x in builtinDarkThemes" :key="'builtin:' + x.id" :value="x.id">{{ x.name }}</option> + </optgroup> + </MkSelect> </div> + + <FormSection> + <div class="_formLinksGrid"> + <FormLink to="/settings/theme/manage"> + <template #icon><i class="ti ti-tool"></i></template> + {{ i18n.ts._theme.manage }} + <template #suffix>{{ themesCount }}</template> + </FormLink> + <FormLink to="https://assets.misskey.io/theme/list" external> + <template #icon><i class="ti ti-world"></i></template> + {{ i18n.ts._theme.explore }} + </FormLink> + <FormLink to="/settings/theme/install"> + <template #icon><i class="ti ti-download"></i></template> + {{ i18n.ts._theme.install }} + </FormLink> + <FormLink to="/theme-editor"> + <template #icon><i class="ti ti-paint"></i></template> + {{ i18n.ts._theme.make }} + </FormLink> + </div> + </FormSection> + + <MkButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</MkButton> + <MkButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</MkButton> </div> - - <div class="selects"> - <MkSelect v-model="lightThemeId" large class="select"> - <template #label>{{ i18n.ts.themeForLightMode }}</template> - <template #prefix><i class="ti ti-sun"></i></template> - <option v-if="instanceLightTheme" :key="'instance:' + instanceLightTheme.id" :value="instanceLightTheme.id">{{ instanceLightTheme.name }}</option> - <optgroup v-if="installedLightThemes.length > 0" :label="i18n.ts._theme.installedThemes"> - <option v-for="x in installedLightThemes" :key="'installed:' + x.id" :value="x.id">{{ x.name }}</option> - </optgroup> - <optgroup :label="i18n.ts._theme.builtinThemes"> - <option v-for="x in builtinLightThemes" :key="'builtin:' + x.id" :value="x.id">{{ x.name }}</option> - </optgroup> - </MkSelect> - <MkSelect v-model="darkThemeId" large class="select"> - <template #label>{{ i18n.ts.themeForDarkMode }}</template> - <template #prefix><i class="ti ti-moon"></i></template> - <option v-if="instanceDarkTheme" :key="'instance:' + instanceDarkTheme.id" :value="instanceDarkTheme.id">{{ instanceDarkTheme.name }}</option> - <optgroup v-if="installedDarkThemes.length > 0" :label="i18n.ts._theme.installedThemes"> - <option v-for="x in installedDarkThemes" :key="'installed:' + x.id" :value="x.id">{{ x.name }}</option> - </optgroup> - <optgroup :label="i18n.ts._theme.builtinThemes"> - <option v-for="x in builtinDarkThemes" :key="'builtin:' + x.id" :value="x.id">{{ x.name }}</option> - </optgroup> - </MkSelect> - </div> - - <FormSection> - <div class="_formLinksGrid"> - <FormLink to="/settings/theme/manage"><template #icon><i class="ti ti-tool"></i></template>{{ i18n.ts._theme.manage }}<template #suffix>{{ themesCount }}</template></FormLink> - <FormLink to="https://assets.misskey.io/theme/list" external><template #icon><i class="ti ti-world"></i></template>{{ i18n.ts._theme.explore }}</FormLink> - <FormLink to="/settings/theme/install"><template #icon><i class="ti ti-download"></i></template>{{ i18n.ts._theme.install }}</FormLink> - <FormLink to="/theme-editor"><template #icon><i class="ti ti-paint"></i></template>{{ i18n.ts._theme.make }}</FormLink> - </div> - </FormSection> - - <MkButton v-if="wallpaper == null" @click="setWallpaper">{{ i18n.ts.setWallpaper }}</MkButton> - <MkButton v-else @click="wallpaper = null">{{ i18n.ts.removeWallpaper }}</MkButton> -</div> </template> <script lang="ts" setup> -import { computed, onActivated, ref, watch } from 'vue'; +import {computed, onActivated, ref, watch} from 'vue'; import JSON5 from 'json5'; import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; import FormSection from '@/components/form/section.vue'; import FormLink from '@/components/form/link.vue'; import MkButton from '@/components/MkButton.vue'; -import { getBuiltinThemesRef } from '@/scripts/theme'; -import { selectFile } from '@/scripts/select-file'; -import { isDeviceDarkmode } from '@/scripts/is-device-darkmode'; -import { ColdDeviceStorage, defaultStore } from '@/store'; -import { i18n } from '@/i18n'; -import { instance } from '@/instance'; -import { uniqueBy } from '@/scripts/array'; -import { fetchThemes, getThemes } from '@/theme-store'; -import { definePageMetadata } from '@/scripts/page-metadata'; -import { miLocalStorage } from '@/local-storage'; +import {getBuiltinThemesRef} from '@/scripts/theme'; +import {selectFile} from '@/scripts/select-file'; +import {isDeviceDarkmode} from '@/scripts/is-device-darkmode'; +import { ColdDeviceStorage, defaultStore , bannerDark,bannerLight} from '@/store'; +import {i18n} from '@/i18n'; +import {instance} from '@/instance'; +import {uniqueBy} from '@/scripts/array'; +import {fetchThemes, getThemes} from '@/theme-store'; +import {definePageMetadata} from '@/scripts/page-metadata'; +import {miLocalStorage} from '@/local-storage'; const installedThemes = ref(getThemes()); const builtinThemes = getBuiltinThemesRef(); @@ -128,6 +145,15 @@ const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const syncDeviceDarkMode = computed(ColdDeviceStorage.makeGetterSetter('syncDeviceDarkMode')); const wallpaper = ref(miLocalStorage.getItem('wallpaper')); const themesCount = installedThemes.value.length; +watch(darkMode, () => { + if (darkMode.value) { + defaultStore.set('bannerUrl', bannerDark) + }else if(!darkMode.value) { + defaultStore.set('bannerUrl', bannerLight) + }else{ + defaultStore.set('bannerUrl', bannerDark) + } +}) watch(syncDeviceDarkMode, () => { if (syncDeviceDarkMode.value) { @@ -237,9 +263,9 @@ definePageMetadata({ height: 50px - 6; background-color: #FFCF96; border-radius: 50px; - box-shadow: 0 2px 6px rgba(0,0,0,.3); + box-shadow: 0 2px 6px rgba(0, 0, 0, .3); transition: all 400ms cubic-bezier(0.68, -0.55, 0.265, 1.55) !important; - transform: rotate(-45deg); + transform: rotate(-45deg); .crater { position: absolute; @@ -315,7 +341,7 @@ definePageMetadata({ z-index: 0; width: 2px; height: 2px; - transform: translate3d(3px,0,0); + transform: translate3d(3px, 0, 0); } .star--5 { @@ -324,7 +350,7 @@ definePageMetadata({ z-index: 0; width: 3px; height: 3px; - transform: translate3d(3px,0,0); + transform: translate3d(3px, 0, 0); } .star--6 { @@ -333,7 +359,7 @@ definePageMetadata({ z-index: 0; width: 2px; height: 2px; - transform: translate3d(3px,0,0); + transform: translate3d(3px, 0, 0); } input:checked { @@ -352,7 +378,9 @@ definePageMetadata({ background-color: #FFE5B5; transform: translate3d(40px, 0, 0) rotate(0); - .crater { opacity: 1; } + .crater { + opacity: 1; + } } .star--1 { @@ -376,7 +404,7 @@ definePageMetadata({ .star--5, .star--6 { opacity: 1; - transform: translate3d(0,0,0); + transform: translate3d(0, 0, 0); } .star--4 { diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 787a584f83..51f4d37754 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -40,6 +40,9 @@ export const noteActions: NoteAction[] = []; export const noteViewInterruptors: NoteViewInterruptor[] = []; export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; +export const bannerDark='https://files.prismisskey.space/misskey/ac141052-7a16-4608-bc08-263566326a6d.jpg' +export const bannerLight ='https://files.prismisskey.space/misskey/e8d13afc-2348-4b13-a64a-f55a19e8d7aa.jpg' + // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) // あと、現行の定義の仕方なら「whereが何であるかに関わらずキー名の重複不可」という制約を付けられるメリットもあるからそのメリットを引き継ぐ方法も考えないといけない @@ -248,6 +251,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + bannerUrl:{ + where: 'device', + default: bannerDark + }, instanceTicker: { where: 'device', default: 'remote' as 'none' | 'remote' | 'always', diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index f2783ebb77..4221d8781b 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root"> <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> <button class="_button" :class="$style.instance" @click="openInstanceMenu"> <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> </button> @@ -46,15 +46,24 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, toRef } from 'vue'; +import {computed, defineAsyncComponent, ref, toRef, watch} from 'vue'; import { openInstanceMenu } from './common'; import * as os from '@/os'; import { navbarItemDef } from '@/navbar'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; -import { defaultStore } from '@/store'; +import {bannerDark, bannerLight, defaultStore} from '@/store'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; +let bannerUrl = ref(defaultStore.state.bannerUrl); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +watch(darkMode, () => { + if (darkMode.value){ + bannerUrl.value = bannerDark; + }else{ + bannerUrl.value = bannerLight; + } +}) const menu = toRef(defaultStore.state, 'menu'); const otherMenuItemIndicated = computed(() => { for (const def in navbarItemDef) { diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index d0162c8330..e711918e54 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -4,81 +4,106 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.iconOnly]: iconOnly }]"> - <div :class="$style.body"> - <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> - <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> - </button> - </div> - <div :class="$style.middle"> - <MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact> - <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span> - </MkA> - <template v-for="item in menu"> - <div v-if="item === '-'" :class="$style.divider"></div> - <component - :is="navbarItemDef[item].to ? 'MkA' : 'button'" - v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" - v-tooltip.noDelay.right="navbarItemDef[item].title" - class="_button" - :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" - :activeClass="$style.active" - :to="navbarItemDef[item].to" - v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" - > - <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> - <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> - </component> - </template> - <div :class="$style.divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="$style.item" :activeClass="$style.active" to="/admin"> - <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> - </MkA> - <button class="_button" :class="$style.item" @click="more"> - <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> - <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> - </button> - <MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" to="/settings"> - <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span> - </MkA> - </div> - <div :class="$style.bottom"> - <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form @click="os.post"> - <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note }}</span> - </button> - <button v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" :class="[$style.account]" @click="openAccountMenu"> - <MkAvatar :user="$i" :class="$style.avatar"/><MkAcct class="_nowrap" :class="$style.acct" :user="$i"/> - </button> - </div> - </div> -</div> + <div :class="[$style.root, { [$style.iconOnly]: iconOnly }]"> + <div :class="$style.body"> + <div :class="$style.top"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> + <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" + @click="openInstanceMenu"> + <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + </button> + </div> + <div :class="$style.middle"> + <MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact> + <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.timeline + }}</span> + </MkA> + <template v-for="item in menu"> + <div v-if="item === '-'" :class="$style.divider"></div> + <component + :is="navbarItemDef[item].to ? 'MkA' : 'button'" + v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" + v-tooltip.noDelay.right="navbarItemDef[item].title" + class="_button" + :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" + :activeClass="$style.active" + :to="navbarItemDef[item].to" + v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" + > + <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span + :class="$style.itemText">{{ navbarItemDef[item].title }}</span> + <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator"><i + class="_indicatorCircle"></i></span> + </component> + </template> + <div :class="$style.divider"></div> + <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="$style.item" + :activeClass="$style.active" to="/admin"> + <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span + :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> + </MkA> + <button class="_button" :class="$style.item" @click="more"> + <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.more + }}</span> + <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> + </button> + <MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" + to="/settings"> + <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span + :class="$style.itemText">{{ i18n.ts.settings }}</span> + </MkA> + </div> + <div :class="$style.bottom"> + <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form + @click="os.post"> + <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ + i18n.ts.note + }}</span> + </button> + <button v-tooltip.noDelay.right="`${i18n.ts.account}: @${$i.username}`" class="_button" + :class="[$style.account]" @click="openAccountMenu"> + <MkAvatar :user="$i" :class="$style.avatar"/> + <MkAcct class="_nowrap" :class="$style.acct" :user="$i"/> + </button> + </div> + </div> + </div> </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, watch } from 'vue'; -import { openInstanceMenu } from './common'; +import {computed, defineAsyncComponent, ref, watch} from 'vue'; +import {openInstanceMenu} from './common'; import * as os from '@/os'; -import { navbarItemDef } from '@/navbar'; -import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; -import { defaultStore } from '@/store'; -import { i18n } from '@/i18n'; -import { instance } from '@/instance'; +import {navbarItemDef} from '@/navbar'; +import {$i, openAccountMenu as openAccountMenu_} from '@/account'; +import {bannerDark, bannerLight, defaultStore} from '@/store'; +import {i18n} from '@/i18n'; +import {instance} from '@/instance'; const iconOnly = ref(false); +let bannerUrl = ref(defaultStore.state.bannerUrl); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +watch(darkMode, () => { + if (darkMode.value){ + bannerUrl.value = bannerDark; + }else{ + bannerUrl.value = bannerLight; + } +}) const menu = computed(() => defaultStore.state.menu); const otherMenuItemIndicated = computed(() => { - for (const def in navbarItemDef) { - if (menu.value.includes(def)) continue; - if (navbarItemDef[def].indicated) return true; - } - return false; + for (const def in navbarItemDef) { + if (menu.value.includes(def)) continue; + if (navbarItemDef[def].indicated) return true; + } + return false; }); const calcViewState = () => { - iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon'); + iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon'); }; calcViewState(); @@ -86,393 +111,392 @@ calcViewState(); window.addEventListener('resize', calcViewState); watch(defaultStore.reactiveState.menuDisplay, () => { - calcViewState(); + calcViewState(); }); function openAccountMenu(ev: MouseEvent) { - openAccountMenu_({ - withExtraOperation: true, - }, ev); + openAccountMenu_({ + withExtraOperation: true, + }, ev); } function more(ev: MouseEvent) { - os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { - src: ev.currentTarget ?? ev.target, - }, { - }, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { + src: ev.currentTarget ?? ev.target, + }, {}, 'closed'); } </script> <style lang="scss" module> .root { - --nav-width: 250px; - --nav-icon-only-width: 72px; + --nav-width: 250px; + --nav-icon-only-width: 72px; - flex: 0 0 var(--nav-width); - width: var(--nav-width); - box-sizing: border-box; + flex: 0 0 var(--nav-width); + width: var(--nav-width); + box-sizing: border-box; } .body { - position: fixed; - top: 0; - left: 0; - z-index: 1001; - width: var(--nav-icon-only-width); - height: 100dvh; - box-sizing: border-box; - overflow: auto; - overflow-x: clip; - overscroll-behavior: contain; - background: var(--navBg); - contain: strict; - display: flex; - flex-direction: column; + position: fixed; + top: 0; + left: 0; + z-index: 1001; + width: var(--nav-icon-only-width); + height: 100dvh; + box-sizing: border-box; + overflow: auto; + overflow-x: clip; + overscroll-behavior: contain; + background: var(--navBg); + contain: strict; + display: flex; + flex-direction: column; } .root:not(.iconOnly) { - .body { - width: var(--nav-width); - } + .body { + width: var(--nav-width); + } - .top { - position: sticky; - top: 0; - z-index: 1; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - } + .top { + position: sticky; + top: 0; + z-index: 1; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } - .banner { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-size: cover; - background-position: center center; - -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - } + .banner { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: cover; + background-position: center center; + -webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 15%, rgba(0, 0, 0, 0.75) 100%); + mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 15%, rgba(0, 0, 0, 0.75) 100%); + } - .instance { - position: relative; - display: block; - text-align: center; - width: 100%; - } + .instance { + position: relative; + display: block; + text-align: center; + width: 100%; + } - .instanceIcon { - display: inline-block; - width: 38px; - aspect-ratio: 1; - } + .instanceIcon { + display: inline-block; + width: 38px; + aspect-ratio: 1; + } - .bottom { - position: sticky; - bottom: 0; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - } + .bottom { + position: sticky; + bottom: 0; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } - .post { - position: relative; - display: block; - width: 100%; - height: 40px; - color: var(--fgOnAccent); - font-weight: bold; - text-align: left; + .post { + position: relative; + display: block; + width: 100%; + height: 40px; + color: var(--fgOnAccent); + font-weight: bold; + text-align: left; - &:before { - content: ""; - display: block; - width: calc(100% - 38px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - } + &:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + } - &:hover, &.active { - &:before { - background: var(--accentLighten); - } - } - } + &:hover, &.active { + &:before { + background: var(--accentLighten); + } + } + } - .postIcon { - position: relative; - margin-left: 30px; - margin-right: 8px; - width: 32px; - } + .postIcon { + position: relative; + margin-left: 30px; + margin-right: 8px; + width: 32px; + } - .postText { - position: relative; - } + .postText { + position: relative; + } - .account { - position: relative; - display: flex; - align-items: center; - padding-left: 30px; - width: 100%; - text-align: left; - box-sizing: border-box; - margin-top: 16px; - } + .account { + position: relative; + display: flex; + align-items: center; + padding-left: 30px; + width: 100%; + text-align: left; + box-sizing: border-box; + margin-top: 16px; + } - .avatar { - display: block; - flex-shrink: 0; - position: relative; - width: 32px; - aspect-ratio: 1; - margin-right: 8px; - } + .avatar { + display: block; + flex-shrink: 0; + position: relative; + width: 32px; + aspect-ratio: 1; + margin-right: 8px; + } - .acct { - display: block; - flex-shrink: 1; - padding-right: 8px; - } + .acct { + display: block; + flex-shrink: 1; + padding-right: 8px; + } - .middle { - flex: 1; - } + .middle { + flex: 1; + } - .divider { - margin: 16px 16px; - border-top: solid 0.5px var(--divider); - } + .divider { + margin: 16px 16px; + border-top: solid 0.5px var(--divider); + } - .item { - position: relative; - display: block; - padding-left: 30px; - line-height: 2.85rem; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - width: 100%; - text-align: left; - box-sizing: border-box; - color: var(--navFg); + .item { + position: relative; + display: block; + padding-left: 30px; + line-height: 2.85rem; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + text-align: left; + box-sizing: border-box; + color: var(--navFg); - &:hover { - text-decoration: none; - color: var(--navHoverFg); - } + &:hover { + text-decoration: none; + color: var(--navHoverFg); + } - &.active { - color: var(--navActive); - } + &.active { + color: var(--navActive); + } - &:hover, &.active { - color: var(--accent); + &:hover, &.active { + color: var(--accent); - &:before { - content: ""; - display: block; - width: calc(100% - 34px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: var(--accentedBg); - } - } - } + &:before { + content: ""; + display: block; + width: calc(100% - 34px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: var(--accentedBg); + } + } + } - .itemIcon { - position: relative; - width: 32px; - margin-right: 8px; - } + .itemIcon { + position: relative; + width: 32px; + margin-right: 8px; + } - .itemIndicator { - position: absolute; - top: 0; - left: 20px; - color: var(--navIndicator); - font-size: 8px; - animation: blink 1s infinite; - } + .itemIndicator { + position: absolute; + top: 0; + left: 20px; + color: var(--navIndicator); + font-size: 8px; + animation: blink 1s infinite; + } - .itemText { - position: relative; - font-size: 0.9em; - } + .itemText { + position: relative; + font-size: 0.9em; + } } .root.iconOnly { - flex: 0 0 var(--nav-icon-only-width); - width: var(--nav-icon-only-width); + flex: 0 0 var(--nav-icon-only-width); + width: var(--nav-icon-only-width); - .body { - width: var(--nav-icon-only-width); - } + .body { + width: var(--nav-icon-only-width); + } - .top { - position: sticky; - top: 0; - z-index: 1; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - } + .top { + position: sticky; + top: 0; + z-index: 1; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } - .instance { - display: block; - text-align: center; - width: 100%; - } + .instance { + display: block; + text-align: center; + width: 100%; + } - .instanceIcon { - display: inline-block; - width: 30px; - aspect-ratio: 1; - } + .instanceIcon { + display: inline-block; + width: 30px; + aspect-ratio: 1; + } - .bottom { - position: sticky; - bottom: 0; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - } + .bottom { + position: sticky; + bottom: 0; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + } - .post { - display: block; - position: relative; - width: 100%; - height: 52px; - margin-bottom: 16px; - text-align: center; + .post { + display: block; + position: relative; + width: 100%; + height: 52px; + margin-bottom: 16px; + text-align: center; - &:before { - content: ""; - display: block; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - margin: auto; - width: 52px; - aspect-ratio: 1/1; - border-radius: 100%; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - } + &:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 52px; + aspect-ratio: 1/1; + border-radius: 100%; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + } - &:hover, &.active { - &:before { - background: var(--accentLighten); - } - } - } + &:hover, &.active { + &:before { + background: var(--accentLighten); + } + } + } - .postIcon { - position: relative; - color: var(--fgOnAccent); - } + .postIcon { + position: relative; + color: var(--fgOnAccent); + } - .postText { - display: none; - } + .postText { + display: none; + } - .account { - display: block; - text-align: center; - width: 100%; - } + .account { + display: block; + text-align: center; + width: 100%; + } - .avatar { - display: inline-block; - width: 38px; - aspect-ratio: 1; - } + .avatar { + display: inline-block; + width: 38px; + aspect-ratio: 1; + } - .acct { - display: none; - } + .acct { + display: none; + } - .middle { - flex: 1; - } + .middle { + flex: 1; + } - .divider { - margin: 8px auto; - width: calc(100% - 32px); - border-top: solid 0.5px var(--divider); - } + .divider { + margin: 8px auto; + width: calc(100% - 32px); + border-top: solid 0.5px var(--divider); + } - .item { - display: block; - position: relative; - padding: 18px 0; - width: 100%; - text-align: center; + .item { + display: block; + position: relative; + padding: 18px 0; + width: 100%; + text-align: center; - &:hover, &.active { - text-decoration: none; - color: var(--accent); + &:hover, &.active { + text-decoration: none; + color: var(--accent); - &:before { - content: ""; - display: block; - height: 100%; - aspect-ratio: 1; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: var(--accentedBg); - } + &:before { + content: ""; + display: block; + height: 100%; + aspect-ratio: 1; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: var(--accentedBg); + } - > .icon, - > .text { - opacity: 1; - } - } - } + > .icon, + > .text { + opacity: 1; + } + } + } - .itemIcon { - display: block; - margin: 0 auto; - opacity: 0.7; - } + .itemIcon { + display: block; + margin: 0 auto; + opacity: 0.7; + } - .itemText { - display: none; - } + .itemText { + display: none; + } - .itemIndicator { - position: absolute; - top: 6px; - left: 24px; - color: var(--navIndicator); - font-size: 8px; - animation: blink 1s infinite; - } + .itemIndicator { + position: absolute; + top: 6px; + left: 24px; + color: var(--navIndicator); + font-size: 8px; + animation: blink 1s infinite; + } } </style> diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index a7413d4b6a..2adeaaef59 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_panel"> - <div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ instance.bannerUrl })` : null }"> + <div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ bannerUrl })` : null }"> <div :class="$style.iconContainer"> <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/> </div> @@ -24,9 +24,20 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp import { GetFormResultType } from '@/scripts/form'; import { host } from '@/config'; import { instance } from '@/instance'; +import {bannerDark, bannerLight, defaultStore} from "@/store"; +import {computed, ref, watch} from "vue"; const name = 'instanceInfo'; +let bannerUrl = ref(defaultStore.state.bannerUrl); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +watch(darkMode, () => { + if (darkMode.value){ + bannerUrl.value = bannerDark; + }else{ + bannerUrl.value = bannerLight; + } +}) const widgetPropsDef = { }; From df9f847f697a01d5b89acd313afa25a578fae9b3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 20 Sep 2023 09:07:06 +0900 Subject: [PATCH 042/501] 2023.9.0-beta.8-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 70a5727fe0..33f8066c94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.8-mattyaski.5", + "version": "2023.9.0-beta.8-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 4979c5180cdff43f931d70b3ce77c97e106610c8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 20 Sep 2023 09:25:19 +0900 Subject: [PATCH 043/501] =?UTF-8?q?feat:=20prismisskey=E7=94=A8=E3=81=ABic?= =?UTF-8?q?on=E5=A4=89=E6=9B=B4=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/about.vue | 8 +++++--- packages/frontend/src/pages/settings/theme.vue | 5 ++++- packages/frontend/src/store.ts | 6 ++++++ packages/frontend/src/ui/_common_/navbar-for-mobile.vue | 8 +++++--- packages/frontend/src/ui/_common_/navbar.vue | 9 ++++++--- packages/frontend/src/widgets/WidgetInstanceInfo.vue | 9 ++++++--- 6 files changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 0cd6e32264..d263fe8940 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"> <div style="overflow: clip;"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> + <img :src="iconUrl" alt="" :class="$style.bannerIcon"/> <div :class="$style.bannerName"> <b>{{ instance.name ?? host }}</b> </div> @@ -115,7 +115,7 @@ import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; import { claimAchievement } from '@/scripts/achievements'; import { instance } from '@/instance'; -import {bannerDark, bannerLight, defaultStore} from "@/store"; +import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store"; const props = withDefaults(defineProps<{ initialTab?: string; @@ -132,13 +132,15 @@ watch($$(tab), () => { } }); let bannerUrl = ref(defaultStore.state.bannerUrl); - +let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; + iconUrl.value = iconDark; }else{ bannerUrl.value = bannerLight; + iconUrl.value = iconLight; } }) const initStats = () => os.api('stats', { diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index c1a904708e..58485a442d 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -98,7 +98,7 @@ import MkButton from '@/components/MkButton.vue'; import {getBuiltinThemesRef} from '@/scripts/theme'; import {selectFile} from '@/scripts/select-file'; import {isDeviceDarkmode} from '@/scripts/is-device-darkmode'; -import { ColdDeviceStorage, defaultStore , bannerDark,bannerLight} from '@/store'; +import {ColdDeviceStorage, defaultStore, bannerDark, bannerLight, iconDark, iconLight} from '@/store'; import {i18n} from '@/i18n'; import {instance} from '@/instance'; import {uniqueBy} from '@/scripts/array'; @@ -148,10 +148,13 @@ const themesCount = installedThemes.value.length; watch(darkMode, () => { if (darkMode.value) { defaultStore.set('bannerUrl', bannerDark) + defaultStore.set('iconUrl', iconDark) }else if(!darkMode.value) { defaultStore.set('bannerUrl', bannerLight) + defaultStore.set('iconUrl', iconLight) }else{ defaultStore.set('bannerUrl', bannerDark) + defaultStore.set('iconUrl', iconDark) } }) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 984cf98b04..7635175289 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -42,6 +42,8 @@ export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; export const bannerDark='https://files.prismisskey.space/misskey/ac141052-7a16-4608-bc08-263566326a6d.jpg' export const bannerLight ='https://files.prismisskey.space/misskey/e8d13afc-2348-4b13-a64a-f55a19e8d7aa.jpg' +export const iconDark='https://files.prismisskey.space/misskey/c7e56b1d-4c4f-408f-bf73-3175f4eb26ca.png' +export const iconLight='https://files.prismisskey.space/misskey/f3b3c9f8-ff2a-474d-a858-64ffe9023e22.png' // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) @@ -267,6 +269,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: bannerDark }, + iconUrl:{ + where: 'device', + default: iconDark + }, instanceTicker: { where: 'device', default: 'remote' as 'none' | 'remote' | 'always', diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 4221d8781b..020a97d45c 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.top"> <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> <button class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> </button> </div> <div :class="$style.middle"> @@ -51,17 +51,19 @@ import { openInstanceMenu } from './common'; import * as os from '@/os'; import { navbarItemDef } from '@/navbar'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; -import {bannerDark, bannerLight, defaultStore} from '@/store'; +import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; let bannerUrl = ref(defaultStore.state.bannerUrl); - +let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; + iconUrl.value = iconDark; }else{ bannerUrl.value = bannerLight; + iconUrl.value = iconLight; } }) const menu = toRef(defaultStore.state, 'menu'); diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index e711918e54..cbfcc1dd14 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> </button> </div> <div :class="$style.middle"> @@ -78,21 +78,24 @@ import {openInstanceMenu} from './common'; import * as os from '@/os'; import {navbarItemDef} from '@/navbar'; import {$i, openAccountMenu as openAccountMenu_} from '@/account'; -import {bannerDark, bannerLight, defaultStore} from '@/store'; +import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; import {i18n} from '@/i18n'; import {instance} from '@/instance'; const iconOnly = ref(false); let bannerUrl = ref(defaultStore.state.bannerUrl); - +let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; + iconUrl.value = iconDark; }else{ bannerUrl.value = bannerLight; + iconUrl.value = iconLight; } }) + const menu = computed(() => defaultStore.state.menu); const otherMenuItemIndicated = computed(() => { for (const def in navbarItemDef) { diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index 2adeaaef59..36db872e9c 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_panel"> <div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ bannerUrl })` : null }"> <div :class="$style.iconContainer"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/> + <img :src="iconUrl" alt="" :class="$style.icon"/> </div> <div :class="$style.bodyContainer"> <div :class="$style.body"> @@ -24,20 +24,23 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp import { GetFormResultType } from '@/scripts/form'; import { host } from '@/config'; import { instance } from '@/instance'; -import {bannerDark, bannerLight, defaultStore} from "@/store"; +import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store"; import {computed, ref, watch} from "vue"; const name = 'instanceInfo'; let bannerUrl = ref(defaultStore.state.bannerUrl); - +let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; + iconUrl.value = iconDark; }else{ bannerUrl.value = bannerLight; + iconUrl.value = iconLight; } }) + const widgetPropsDef = { }; From ded8135d361593a0ee9609ec09a880836e7f5eec Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 20 Sep 2023 09:48:52 +0900 Subject: [PATCH 044/501] =?UTF-8?q?feat:=20prismisskey=E7=94=A8=E3=81=ABic?= =?UTF-8?q?on=E5=A4=89=E6=9B=B4=E3=81=AA=E3=81=A9=E3=81=AA=E3=81=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/about.vue | 7 +++++++ packages/frontend/src/store.ts | 4 ++-- .../src/ui/_common_/navbar-for-mobile.vue | 7 +++++++ packages/frontend/src/ui/_common_/navbar.vue | 16 ++++++++++++---- .../frontend/src/widgets/WidgetInstanceInfo.vue | 7 +++++++ 5 files changed, 35 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index d263fe8940..27cf807901 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -134,6 +134,13 @@ watch($$(tab), () => { let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value){ + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; +}else{ + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; +} watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 7635175289..23af670266 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -42,8 +42,8 @@ export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; export const bannerDark='https://files.prismisskey.space/misskey/ac141052-7a16-4608-bc08-263566326a6d.jpg' export const bannerLight ='https://files.prismisskey.space/misskey/e8d13afc-2348-4b13-a64a-f55a19e8d7aa.jpg' -export const iconDark='https://files.prismisskey.space/misskey/c7e56b1d-4c4f-408f-bf73-3175f4eb26ca.png' -export const iconLight='https://files.prismisskey.space/misskey/f3b3c9f8-ff2a-474d-a858-64ffe9023e22.png' +export const iconDark='https://files.prismisskey.space/misskey/f3b3c9f8-ff2a-474d-a858-64ffe9023e22.png' +export const iconLight='https://files.prismisskey.space/misskey/c7e56b1d-4c4f-408f-bf73-3175f4eb26ca.png' // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 020a97d45c..597cfff73b 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -57,6 +57,13 @@ import { instance } from '@/instance'; let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value){ + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; +}else{ + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; +} watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index cbfcc1dd14..7c76c5fe6c 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -84,8 +84,16 @@ import {instance} from '@/instance'; const iconOnly = ref(false); let bannerUrl = ref(defaultStore.state.bannerUrl); -let iconUrl = ref(defaultStore.state.iconUrl); +let iconUrl = ref(); + const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value){ + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; +}else{ + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; +} watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; @@ -167,7 +175,7 @@ function more(ev: MouseEvent) { top: 0; z-index: 1; padding: 20px 0; - background: var(--X14); + //background: var(--X14); -webkit-backdrop-filter: var(--blur, blur(8px)); backdrop-filter: var(--blur, blur(8px)); } @@ -180,8 +188,8 @@ function more(ev: MouseEvent) { height: 100%; background-size: cover; background-position: center center; - -webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 15%, rgba(0, 0, 0, 0.75) 100%); - mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 15%, rgba(0, 0, 0, 0.75) 100%); + -webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%); + mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%); } .instance { diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index 36db872e9c..bef71dceff 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -31,6 +31,13 @@ const name = 'instanceInfo'; let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value){ + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; +}else{ + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; +} watch(darkMode, () => { if (darkMode.value){ bannerUrl.value = bannerDark; From f9574b028bb5a54322e821647173c47a9ab21574 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 20 Sep 2023 09:56:59 +0900 Subject: [PATCH 045/501] =?UTF-8?q?fix:=20=E3=81=84=E3=82=8D=E3=81=84?= =?UTF-8?q?=E3=82=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/about-misskey.vue | 2 +- packages/frontend/src/ui/_common_/navbar-for-mobile.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index a7cbf0da05..eeb9b0da48 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._aboutMisskey.source }} <template #suffix>GitHub</template> </FormLink> - <FormLink to="https://github.com/mattyatea/misskey" external> + <FormLink to="https://github.com/prismisskey/misskey" external> <template #icon><i class="ti ti-code"></i></template> {{ i18n.ts._aboutMisskey.forksource }} <template #suffix>GitHub</template> diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 597cfff73b..a6380c1c57 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -118,8 +118,8 @@ function more() { height: 100%; background-size: cover; background-position: center center; - -webkit-mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); - mask-image: linear-gradient(0deg, rgba(0,0,0,0) 15%, rgba(0,0,0,0.75) 100%); + -webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%); + mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%); } .instance { From b9341aa2c8a6fc75b0d2b9deb92f510ecf7d9e9a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 00:20:58 +0900 Subject: [PATCH 046/501] 2023.9.0-beta.9-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed4ef25abb..fdd36fc200 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.9", + "version": "2023.9.0-beta.9-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 9f3b3bbffa9de5171c07a785e20da2754ffdbab8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 21 Sep 2023 06:02:47 +0900 Subject: [PATCH 047/501] 2023.9.0-beta.9-mattyaski.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ed4ef25abb..1c6f68f53d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.9", + "version": "2023.9.0-beta.9-mattyaski.1", "codename": "nasubi", "repository": { "type": "git", From f56ffdf0d559bb65538277d6538fd480e98087f3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 06:22:40 +0900 Subject: [PATCH 048/501] =?UTF-8?q?Revert=20"feat:=20frontend=E3=81=AEbuil?= =?UTF-8?q?d=E3=81=AB=E5=9C=A7=E7=B8=AE=E3=82=92=E3=81=8B=E3=81=91?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB(=E6=84=8F=E5=91=B3?= =?UTF-8?q?=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=81=8B=E3=81=AF=E3=82=8F?= =?UTF-8?q?=E3=81=8B=E3=82=89=E3=81=AA=E3=81=84)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit e970ddef --- packages/frontend/package.json | 1 - packages/frontend/vite.config.ts | 1 - pnpm-lock.yaml | 12 +----------- 3 files changed, 1 insertion(+), 13 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 45dd740e53..e32be43a22 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -131,7 +131,6 @@ "storybook": "7.4.2", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "summaly": "github:misskey-dev/summaly", - "vite-plugin-compression2": "^0.10.4", "vite-plugin-turbosnap": "1.0.3", "vitest": "0.34.4", "vitest-fetch-mock": "0.2.2", diff --git a/packages/frontend/vite.config.ts b/packages/frontend/vite.config.ts index abd483bbe8..5bb76f009e 100644 --- a/packages/frontend/vite.config.ts +++ b/packages/frontend/vite.config.ts @@ -50,7 +50,6 @@ export function getConfig(): UserConfig { }, plugins: [ - compression({ algorithm: 'brotliCompress'}), pluginVue({ reactivityTransform: true, }), diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e21c0cd8a..ce476cc418 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -658,6 +658,7 @@ importers: '@rollup/pluginutils': specifier: 5.0.4 version: 5.0.4(rollup@3.29.2) + '@syuilo/aiscript': specifier: 0.16.0 version: 0.16.0 @@ -980,9 +981,6 @@ importers: summaly: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 - vite-plugin-compression2: - specifier: ^0.10.4 - version: 0.10.4(rollup@3.29.1) vite-plugin-turbosnap: specifier: 1.0.3 version: 1.0.3 @@ -18961,14 +18959,6 @@ packages: - terser dev: true - /vite-plugin-compression2@0.10.4(rollup@3.29.1): - resolution: {integrity: sha512-9YcESw0n1j8KxxY1NJKEcItlT0bLS+K/NKa/xPqZGEHW/qwgigIeRF/bCTUdZ/bn/mg2+PeERWgRmK8G1L0tyg==} - dependencies: - '@rollup/pluginutils': 5.0.4(rollup@3.29.1) - transitivePeerDependencies: - - rollup - dev: true - /vite-plugin-turbosnap@1.0.3: resolution: {integrity: sha512-p4D8CFVhZS412SyQX125qxyzOgIFouwOcvjZWk6bQbNPR1wtaEzFT6jZxAjf1dejlGqa6fqHcuCvQea6EWUkUA==} dev: true From 8da4b396442cde35585982affc755169e0fa7603 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 06:29:30 +0900 Subject: [PATCH 049/501] 2023.9.0-beta.9-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1c6f68f53d..fdd36fc200 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.9-mattyaski.1", + "version": "2023.9.0-beta.9-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From bbd5282dc6cc31fb37f4fe326e59d881c09ba1aa Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 16:39:18 +0900 Subject: [PATCH 050/501] =?UTF-8?q?feat:=20=E3=82=B2=E3=83=BC=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=83=A2=E3=83=BC=E3=83=89=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/components/MkButton.vue | 575 +++++++++++------- .../frontend/src/pages/settings/general.vue | 3 +- packages/frontend/src/pages/timeline.vue | 5 + packages/frontend/src/store.ts | 4 + packages/frontend/src/ui/_common_/navbar.vue | 372 ++++++++++- 8 files changed, 740 insertions(+), 222 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index d8d6855d62..dfbab848fa 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -292,6 +292,7 @@ location: "Location" theme: "Themes" themeForLightMode: "Theme to use in Light Mode" themeForDarkMode: "Theme to use in Dark Mode" +gamingMode: "Gaming Mode" light: "Light" dark: "Dark" lightThemes: "Light themes" diff --git a/locales/index.d.ts b/locales/index.d.ts index 59295664cb..88183b810a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -295,6 +295,7 @@ export interface Locale { "theme": string; "themeForLightMode": string; "themeForDarkMode": string; + "gamingMode": string; "light": string; "dark": string; "lightThemes": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a9c11bbba2..13c2498a81 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -292,6 +292,7 @@ location: "場所" theme: "テーマ" themeForLightMode: "ライトモードで使うテーマ" themeForDarkMode: "ダークモードで使うテーマ" +gamingMode: 'ゲーミングモード' light: "ライト" dark: "ダーク" lightThemes: "明るいテーマ" diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index bcd58ae516..2647ad1490 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -4,285 +4,440 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<button - v-if="!link" - ref="el" class="_button" - :class="[$style.root, { [$style.inline]: inline, [$style.primary]: primary, [$style.gradate]: gradate, [$style.danger]: danger, [$style.rounded]: rounded, [$style.full]: full, [$style.small]: small, [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike }]" - :type="type" - :name="name" - :value="value" - @click="emit('click', $event)" - @mousedown="onMousedown" -> - <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> - <div :class="$style.content"> - <slot></slot> - </div> -</button> -<MkA - v-else class="_button" - :class="[$style.root, { [$style.inline]: inline, [$style.primary]: primary, [$style.gradate]: gradate, [$style.danger]: danger, [$style.rounded]: rounded, [$style.full]: full, [$style.small]: small, [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike }]" - :to="to" - @mousedown="onMousedown" -> - <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> - <div :class="$style.content"> - <slot></slot> - </div> -</MkA> + <button + v-if="!link" + ref="el" class="_button" + :class="[ + $style.root, + { + [$style.inline]: inline, + [$style.primary]: primary, + [$style.gradate]: gradate, + [$style.danger]: danger, + [$style.rounded]: rounded, + [$style.full]: full, + [$style.small]: small, + [$style.large]: large, + [$style.transparent]: transparent, + [$style.asLike]: asLike, + [$style.gamingDark]: gaming === 'dark', // gamingが1の場合にgamingDarkクラスを追加 + [$style.gamingLight]: gaming === 'light', // gamingが2の場合にgamingLightクラスを追加 + } + ]" + :type="type" + :name="name" + :value="value" + @click="emit('click', $event)" + @mousedown="onMousedown" + > + <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> + <div :class="$style.content"> + <slot></slot> + </div> + </button> + <MkA + v-else class="_button" + :class="[ + $style.root, + { + [$style.inline]: inline, + [$style.primary]: primary, + [$style.gradate]: gradate, + [$style.danger]: danger, + [$style.rounded]: rounded, + [$style.full]: full, + [$style.small]: small, + [$style.large]: large, + [$style.transparent]: transparent, + [$style.asLike]: asLike, + [$style.gamingDark]: gaming === 'dark', // gamingが1の場合にgamingDarkクラスを追加 + [$style.gamingLight]: gaming === 'light', // gamingが2の場合にgamingLightクラスを追加 + } + ]" + :to="to" + @mousedown="onMousedown" + > + <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> + <div :class="$style.content"> + <slot></slot> + </div> + </MkA> </template> <script lang="ts" setup> -import { nextTick, onMounted } from 'vue'; +import {computed, nextTick, onMounted, reactive, ref, watch} from 'vue'; +import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store.js"; +import {unisonReload} from "@/scripts/unison-reload.js"; const props = defineProps<{ - type?: 'button' | 'submit' | 'reset'; - primary?: boolean; - gradate?: boolean; - rounded?: boolean; - inline?: boolean; - link?: boolean; - to?: string; - autofocus?: boolean; - wait?: boolean; - danger?: boolean; - full?: boolean; - small?: boolean; - large?: boolean; - transparent?: boolean; - asLike?: boolean; - name?: string; - value?: string; + type?: 'button' | 'submit' | 'reset'; + primary?: boolean; + gradate?: boolean; + rounded?: boolean; + inline?: boolean; + link?: boolean; + to?: string; + autofocus?: boolean; + wait?: boolean; + danger?: boolean; + full?: boolean; + small?: boolean; + large?: boolean; + transparent?: boolean; + gamingdark?: boolean; + gaminglight?: boolean; + asLike?: boolean; + name?: string; + value?: string; }>(); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +// gamingをrefで初期化する +let gaming = ref(''); // 0-off , 1-dark , 2-light + +// gaming.valueに新しい値を代入する +if (darkMode.value && gamingMode.value && props.primary) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value && props.primary) { + gaming.value = 'light'; +}else{ + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value && props.primary) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value && props.primary) { + gaming.value = 'light'; + }else{ + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value && props.primary) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value && props.primary) { + gaming.value = 'light'; + }else{ + gaming.value = ''; + } +}) const emit = defineEmits<{ - (ev: 'click', payload: MouseEvent): void; + (ev: 'click', payload: MouseEvent): void; }>(); let el = $shallowRef<HTMLElement | null>(null); let ripples = $shallowRef<HTMLElement | null>(null); onMounted(() => { - if (props.autofocus) { - nextTick(() => { - el!.focus(); - }); - } + if (props.autofocus) { + nextTick(() => { + el!.focus(); + }); + } }); function distance(p, q): number { - return Math.hypot(p.x - q.x, p.y - q.y); + return Math.hypot(p.x - q.x, p.y - q.y); } function calcCircleScale(boxW, boxH, circleCenterX, circleCenterY): number { - const origin = { x: circleCenterX, y: circleCenterY }; - const dist1 = distance({ x: 0, y: 0 }, origin); - const dist2 = distance({ x: boxW, y: 0 }, origin); - const dist3 = distance({ x: 0, y: boxH }, origin); - const dist4 = distance({ x: boxW, y: boxH }, origin); - return Math.max(dist1, dist2, dist3, dist4) * 2; + const origin = {x: circleCenterX, y: circleCenterY}; + const dist1 = distance({x: 0, y: 0}, origin); + const dist2 = distance({x: boxW, y: 0}, origin); + const dist3 = distance({x: 0, y: boxH}, origin); + const dist4 = distance({x: boxW, y: boxH}, origin); + return Math.max(dist1, dist2, dist3, dist4) * 2; } function onMousedown(evt: MouseEvent): void { - const target = evt.target! as HTMLElement; - const rect = target.getBoundingClientRect(); + const target = evt.target! as HTMLElement; + const rect = target.getBoundingClientRect(); - const ripple = document.createElement('div'); - ripple.classList.add(ripples!.dataset.childrenClass!); - ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px'; - ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px'; + const ripple = document.createElement('div'); + ripple.classList.add(ripples!.dataset.childrenClass!); + ripple.style.top = (evt.clientY - rect.top - 1).toString() + 'px'; + ripple.style.left = (evt.clientX - rect.left - 1).toString() + 'px'; - ripples!.appendChild(ripple); + ripples!.appendChild(ripple); - const circleCenterX = evt.clientX - rect.left; - const circleCenterY = evt.clientY - rect.top; + const circleCenterX = evt.clientX - rect.left; + const circleCenterY = evt.clientY - rect.top; - const scale = calcCircleScale(target.clientWidth, target.clientHeight, circleCenterX, circleCenterY); + const scale = calcCircleScale(target.clientWidth, target.clientHeight, circleCenterX, circleCenterY); - window.setTimeout(() => { - ripple.style.transform = 'scale(' + (scale / 2) + ')'; - }, 1); - window.setTimeout(() => { - ripple.style.transition = 'all 1s ease'; - ripple.style.opacity = '0'; - }, 1000); - window.setTimeout(() => { - if (ripples) ripples.removeChild(ripple); - }, 2000); + window.setTimeout(() => { + ripple.style.transform = 'scale(' + (scale / 2) + ')'; + }, 1); + window.setTimeout(() => { + ripple.style.transition = 'all 1s cubic-bezier(0, 0.44, 0.39, 1.1)'; + ripple.style.opacity = '0'; + }, 1000); + window.setTimeout(() => { + if (ripples) ripples.removeChild(ripple); + }, 2000); } </script> <style lang="scss" module> .root { - position: relative; - z-index: 1; // 他コンポーネントのbox-shadowに隠されないようにするため - display: block; - min-width: 100px; - width: max-content; - padding: 7px 14px; - text-align: center; - font-weight: normal; - font-size: 95%; - box-shadow: none; - text-decoration: none; - background: var(--buttonBg); - border-radius: 5px; - overflow: clip; - box-sizing: border-box; - transition: background 0.1s ease; + position: relative; + z-index: 1; // 他コンポーネントのbox-shadowに隠されないようにするため + display: block; + min-width: 100px; + width: max-content; + padding: 7px 14px; + text-align: center; + font-weight: normal; + font-size: 95%; + box-shadow: none; + text-decoration: none; + background: var(--buttonBg); + border-radius: 5px; + overflow: clip; + box-sizing: border-box; + transition: background 0.1s cubic-bezier(0, 0.44, 0.39, 1.1); - &:not(:disabled):hover { - background: var(--buttonHoverBg); - } + &:not(:disabled):hover { + background: var(--buttonHoverBg); + } - &:not(:disabled):active { - background: var(--buttonHoverBg); - } + &:not(:disabled):active { + background: var(--buttonHoverBg); + } - &.small { - font-size: 90%; - padding: 6px 12px; - } + &.small { + font-size: 90%; + padding: 6px 12px; + } - &.large { - font-size: 100%; - padding: 8px 16px; - } + &.large { + font-size: 100%; + padding: 8px 16px; + } - &.full { - width: 100%; - } + &.full { + width: 100%; + } - &.rounded { - border-radius: 999px; - } + &.rounded { + border-radius: 999px; + } - &.primary { - font-weight: bold; - color: var(--fgOnAccent) !important; - background: var(--accent); + &.primary { + font-weight: bold; + color: var(--fgOnAccent) !important; + background: var(--accent); - &:not(:disabled):hover { - background: var(--X8); - } + &:not(:disabled):hover { + background: var(--X8); + } - &:not(:disabled):active { - background: var(--X8); - } - } + &:not(:disabled):active { + background: var(--X8); + } + } - &.asLike { - background: rgba(255, 86, 125, 0.07); - color: #ff002f; + &.asLike { + background: rgba(255, 86, 125, 0.07); + color: #ff002f; - &:not(:disabled):hover { - background: rgba(255, 74, 116, 0.11); - } + &:not(:disabled):hover { + background: rgba(255, 74, 116, 0.11); + } - &:not(:disabled):active { - background: rgba(224, 57, 96, 0.125); - } + &:not(:disabled):active { + background: rgba(224, 57, 96, 0.125); + } - > .ripples { - > .ripple { - background: rgba(255, 60, 106, 0.15); - } - } + > .ripples { + > .ripple { + background: rgba(255, 60, 106, 0.15); + } + } - &.primary { - background: rgb(241 97 132); + &.primary { + background: rgb(241 97 132); - &:not(:disabled):hover { - background: rgb(241 92 128); - } + &:not(:disabled):hover { + background: rgb(241 92 128); + } - &:not(:disabled):active { - background: rgb(241 92 128); - } - } - } + &:not(:disabled):active { + background: rgb(241 92 128); + } + } + } - &.transparent { - background: transparent; - } + &.transparent { + background: transparent; + } - &.gradate { - font-weight: bold; - color: var(--fgOnAccent) !important; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + &.gradate { + font-weight: bold; + color: var(--fgOnAccent) !important; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - &:not(:disabled):hover { - background: linear-gradient(90deg, var(--X8), var(--X8)); - } + &:not(:disabled):hover { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } - &:not(:disabled):active { - background: linear-gradient(90deg, var(--X8), var(--X8)); - } - } + &:not(:disabled):active { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } + } - &.danger { - color: #ff2a2a; + &.gamingLight { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; - &.primary { - color: #fff; - background: #ff2a2a; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - &:not(:disabled):hover { - background: #ff4242; - } + &:not(:disabled):hover { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800%; - &:not(:disabled):active { - background: #d42e2e; - } - } - } + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + } - &:disabled { - opacity: 0.7; - } + &:not(:disabled):active { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; - &:focus-visible { - outline: solid 2px var(--focus); - outline-offset: 2px; - } + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + } + } - &.inline { - display: inline-block; - width: auto; - min-width: 100px; - } + &.gamingDark { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; - &.primary > .ripples > .ripple { - background: rgba(0, 0, 0, 0.15); - } + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + + &:not(:disabled):hover { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% ; + + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + } + + &:not(:disabled):active { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + } + } + + &.danger { + color: #ff2a2a; + + &.primary { + color: #fff; + background: #ff2a2a; + + &:not(:disabled):hover { + background: #ff4242; + } + + &:not(:disabled):active { + background: #d42e2e; + } + } + } + + &:disabled { + opacity: 0.7; + } + + &:focus-visible { + outline: solid 2px var(--focus); + outline-offset: 2px; + } + + &.inline { + display: inline-block; + width: auto; + min-width: 100px; + } + + &.primary > .ripples > .ripple { + background: rgba(0, 0, 0, 0.15); + } } .ripples { - position: absolute; - z-index: 0; - top: 0; - left: 0; - width: 100%; - height: 100%; - border-radius: 6px; - overflow: clip; - pointer-events: none; + position: absolute; + z-index: 0; + top: 0; + left: 0; + width: 100%; + height: 100%; + border-radius: 6px; + overflow: clip; + pointer-events: none; } .ripple { - position: absolute; - width: 2px; - height: 2px; - border-radius: 100%; - background: rgba(0, 0, 0, 0.1); - opacity: 1; - transform: scale(1); - transition: all 0.5s cubic-bezier(0,.5,0,1); + position: absolute; + width: 2px; + height: 2px; + border-radius: 100%; + background: rgba(0, 0, 0, 0.1); + opacity: 1; + transform: scale(1); + transition: all 0.5s cubic-bezier(0, .5, 0, 1); } .content { - position: relative; - z-index: 1; - pointer-events: none; + position: relative; + z-index: 1; + pointer-events: none; } + +@-webkit-keyframes AnimationLight { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@-moz-keyframes AnimationLight { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@keyframes AnimationLight { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@-webkit-keyframes AnimationDark { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@-moz-keyframes AnimationDark { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@keyframes AnimationDark { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} + </style> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b1229cd1ba..709fd2d516 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -123,6 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> <MkSwitch v-model="enableUltimateDataSaverMode">{{ i18n.ts.UltimateDataSaver }}</MkSwitch> <MkSwitch v-model="enableCellularWithUltimateDataSaver">{{ i18n.ts.cellularWithUltimateDataSaver }}</MkSwitch> + <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }}</MkSwitch> </div> <div> <MkRadios v-model="emojiStyle"> @@ -255,7 +256,7 @@ const notificationPosition = computed(defaultStore.makeGetterSetter('notificatio const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies')); const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); - +const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); watch(lang, () => { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 712f8605a2..0b4f826919 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -136,6 +136,11 @@ const headerTabs = $computed(() => [{ title: i18n.ts._timelines.local, icon: 'ti ti-planet', iconOnly: true, +}, { + key: 'media', + title: i18n.ts._timelines.media, + icon: 'ti ti-photo', + iconOnly: true, }, { key: 'social', title: i18n.ts._timelines.social, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index f9ee966797..27de6d150c 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -270,6 +270,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + gamingMode: { + where: 'device', + default: false, + }, bannerUrl:{ where: 'device', default: bannerDark diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 7c76c5fe6c..89c16473ae 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -10,11 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> + <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> </button> </div> <div :class="$style.middle"> - <MkA v-tooltip.noDelay.right="i18n.ts.timeline" :class="$style.item" :activeClass="$style.active" to="/" exact> + <MkA v-tooltip.noDelay.right="i18n.ts.timeline" + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/" exact> <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span> @@ -26,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-tooltip.noDelay.right="navbarItemDef[item].title" class="_button" - :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" + :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" @@ -38,25 +40,27 @@ SPDX-License-Identifier: AGPL-3.0-only </component> </template> <div :class="$style.divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="$style.item" + <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/admin"> <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> </MkA> - <button class="_button" :class="$style.item" @click="more"> + <button class="_button" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click="more"> <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> </button> - <MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="$style.item" :activeClass="$style.active" + <MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/settings"> <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span> </MkA> </div> <div :class="$style.bottom"> - <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" :class="[$style.post]" data-cy-open-post-form + <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" + :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" + data-cy-open-post-form @click="os.post"> <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ i18n.ts.note @@ -85,25 +89,59 @@ import {instance} from '@/instance'; const iconOnly = ref(false); let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(); +let gaming = ref(''); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value){ + +if (darkMode.value) { bannerUrl.value = bannerDark; iconUrl.value = iconDark; -}else{ +} else { bannerUrl.value = bannerLight; iconUrl.value = iconLight; } + watch(darkMode, () => { - if (darkMode.value){ + if (darkMode.value) { bannerUrl.value = bannerDark; iconUrl.value = iconDark; - }else{ + } else { bannerUrl.value = bannerLight; iconUrl.value = iconLight; } }) +// gaming.valueに新しい値を代入する +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + const menu = computed(() => defaultStore.state.menu); const otherMenuItemIndicated = computed(() => { for (const def in navbarItemDef) { @@ -243,6 +281,65 @@ function more(ev: MouseEvent) { background: var(--accentLighten); } } + + &.gamingLight:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingLight:hover, &.gamingLight.active { + &.gamingLight:before { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + + &.gamingDark:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingDark:hover, &.gamingDark.active { + &.gamingDark:before { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + } .postIcon { @@ -331,6 +428,85 @@ function more(ev: MouseEvent) { background: var(--accentedBg); } } + + &.gamingDark:hover { + color: white; + background-size: 1800% 1800%; + text-decoration: none; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingDark.active { + color: white; + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingDark:hover, &.gamingDark.active { + color: white; + + &.gamingDark:before { + content: ""; + display: block; + width: calc(100% - 34px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + + &.gamingLight:hover { + color: black; + background-size: 1800% 1800% !important; + text-decoration: none; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingLight:active { + color: black; + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingLight:hover, &.gamingLight.active { + color: black; + &.gamingLight:before { + content: ""; + display: block; + width: calc(100% - 34px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } } .itemIcon { @@ -421,6 +597,66 @@ function more(ev: MouseEvent) { background: var(--accentLighten); } } + + &.gamingLight:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 52px; + aspect-ratio: 1/1; + border-radius: 100%; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingLight:hover, &.gamingLight.active { + &.gamingLight:before { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + + &.gamingDark:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 52px; + aspect-ratio: 1/1; + border-radius: 100%; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingDark:hover, &.gamingDark.active { + &.gamingDark:before { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + + } .postIcon { @@ -489,6 +725,53 @@ function more(ev: MouseEvent) { opacity: 1; } } + + &.gamingDark:hover, &.gamingDark.active { + text-decoration: none; + color: var(--accent); + + &.gamingDark:before { + content: ""; + display: block; + height: 100%; + aspect-ratio: 1; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + &.gamingLight:hover, &.gamingLight.active { + text-decoration: none; + color: var(--accent); + + &.gamingLight:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 52px; + aspect-ratio: 1/1; + border-radius: 100%; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } } .itemIcon { @@ -509,5 +792,72 @@ function more(ev: MouseEvent) { font-size: 8px; animation: blink 1s infinite; } + + @-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } } </style> From 1516de1feeeeff19a8b204a9b273f83c3390c99d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Mon, 24 Jul 2023 02:48:01 +0900 Subject: [PATCH 051/501] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BC=B8=E5=85=A5=20(#103)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * メディアタイムラインの実装 * refactor(stream): ストリーミングにwithFilesオプションを実装 --------- Co-authored-by: tar_bin <tar.bin.master@gmail.com> --- locales/en-US.yml | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + misskey-assets | 2 +- .../api/stream/channels/global-timeline.ts | 5 ++++ .../api/stream/channels/home-timeline.ts | 5 ++++ .../api/stream/channels/hybrid-timeline.ts | 5 ++++ .../api/stream/channels/local-timeline.ts | 5 ++++ .../frontend/src/components/MkTimeline.vue | 11 ++++++++ packages/frontend/src/pages/timeline.vue | 27 +++++-------------- packages/frontend/src/ui/deck/deck-store.ts | 2 +- packages/frontend/src/ui/deck/tl-column.vue | 3 +++ .../frontend/src/widgets/WidgetTimeline.vue | 4 +++ 13 files changed, 50 insertions(+), 22 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index dfbab848fa..46d77611b5 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1933,6 +1933,7 @@ _instanceCharts: _timelines: home: "Home" local: "Local" + media: "Media" social: "Social" global: "Global" _play: diff --git a/locales/index.d.ts b/locales/index.d.ts index 88183b810a..a1285d21c4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2067,6 +2067,7 @@ export interface Locale { "_timelines": { "home": string; "local": string; + "media": string; "social": string; "global": string; }; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 13c2498a81..591fefb061 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1982,6 +1982,7 @@ _instanceCharts: _timelines: home: "ホーム" local: "ローカル" + media: "メディア" social: "ソーシャル" global: "グローバル" diff --git a/misskey-assets b/misskey-assets index 0179793ec8..cf3ce27b2e 160000 --- a/misskey-assets +++ b/misskey-assets @@ -1 +1 @@ -Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5 +Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364 diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index a33f1a956a..3fb2f81fac 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -19,6 +19,7 @@ class GlobalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class GlobalTimelineChannel extends Channel { if (!policies.gtlAvailable) return; this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -48,6 +50,9 @@ class GlobalTimelineChannel extends Channel { if (note.visibility !== 'public') return; if (note.channelId != null) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + // リプライなら再pack if (note.replyId != null) { note.reply = await this.noteEntityService.pack(note.replyId, this.user, { diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index bd8888f679..f4971d832b 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -17,6 +17,7 @@ class HomeTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withFiles: boolean; constructor( private noteEntityService: NoteEntityService, @@ -31,6 +32,7 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; this.subscriber.on('notesStream', this.onNote); } @@ -47,6 +49,9 @@ class HomeTimelineChannel extends Channel { // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances ?? []))) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + if (['followers', 'specified'].includes(note.visibility)) { note = await this.noteEntityService.pack(note.id, this.user!, { detail: true, diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 760fb8d19f..36fc23cb16 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -19,6 +19,7 @@ class HybridTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = true; private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -38,6 +39,7 @@ class HybridTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -56,6 +58,9 @@ class HybridTimelineChannel extends Channel { (note.channelId != null && this.followingChannels.has(note.channelId)) )) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + if (['followers', 'specified'].includes(note.visibility)) { note = await this.noteEntityService.pack(note.id, this.user!, { detail: true, diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index f32f8c5cec..6fb1f456a6 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -18,6 +18,7 @@ class LocalTimelineChannel extends Channel { public static shouldShare = true; public static requireCredential = false; private withReplies: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -37,6 +38,7 @@ class LocalTimelineChannel extends Channel { if (!policies.ltlAvailable) return; this.withReplies = params.withReplies as boolean; + this.withFiles = params.withFiles as boolean; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -48,6 +50,9 @@ class LocalTimelineChannel extends Channel { if (note.visibility !== 'public') return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; + // ファイルを含まない投稿は除外 + if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; + // リプライなら再pack if (note.replyId != null) { note.reply = await this.noteEntityService.pack(note.replyId, this.user, { diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index e7a595b7d8..8c6029c053 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -79,6 +79,17 @@ if (props.src === 'antenna') { withReplies: defaultStore.state.showTimelineReplies, }); connection.on('note', prepend); +} else if (props.src === 'media') { + endpoint = 'notes/hybrid-timeline'; + query = { + withFiles: true, + withReplies: defaultStore.state.showTimelineReplies, + }; + connection = stream.useChannel('hybridTimeline', { + withFiles: true, + withReplies: defaultStore.state.showTimelineReplies, + }); + connection.on('note', prepend); } else if (props.src === 'social') { endpoint = 'notes/hybrid-timeline'; query = { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 0b4f826919..d085443290 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -1,8 +1,3 @@ -<!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors -SPDX-License-Identifier: AGPL-3.0-only ---> - <template> <MkStickyContainer> <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> @@ -38,8 +33,6 @@ import { i18n } from '@/i18n'; import { instance } from '@/instance'; import { $i } from '@/account'; import { definePageMetadata } from '@/scripts/page-metadata'; -import { miLocalStorage } from '@/local-storage'; -import { antennasCache, userListsCache } from '@/cache'; provide('shouldOmitHeaderTitle', true); @@ -47,7 +40,6 @@ const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); -const isAdmin = ($i != null && $i.isAdmin); const keymap = { 't': focus, }; @@ -70,7 +62,7 @@ function top(): void { } async function chooseList(ev: MouseEvent): Promise<void> { - const lists = await userListsCache.fetch(); + const lists = await os.api('users/lists/list'); const items = lists.map(list => ({ type: 'link' as const, text: list.name, @@ -80,7 +72,7 @@ async function chooseList(ev: MouseEvent): Promise<void> { } async function chooseAntenna(ev: MouseEvent): Promise<void> { - const antennas = await antennasCache.fetch(); + const antennas = await os.api('antennas/list'); const items = antennas.map(antenna => ({ type: 'link' as const, text: antenna.name, @@ -103,7 +95,7 @@ async function chooseChannel(ev: MouseEvent): Promise<void> { os.popupMenu(items, ev.currentTarget ?? ev.target); } -function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | 'all'): void { +function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global'): void { defaultStore.set('tl', { ...defaultStore.state.tl, src: newSrc, @@ -137,10 +129,10 @@ const headerTabs = $computed(() => [{ icon: 'ti ti-planet', iconOnly: true, }, { - key: 'media', - title: i18n.ts._timelines.media, - icon: 'ti ti-photo', - iconOnly: true, + key: 'media', + title: i18n.ts._timelines.media, + icon: 'ti ti-photo', + iconOnly: true, }, { key: 'social', title: i18n.ts._timelines.social, @@ -151,11 +143,6 @@ const headerTabs = $computed(() => [{ title: i18n.ts._timelines.global, icon: 'ti ti-whirl', iconOnly: true, -}] : []), ...(isAdmin ? [{ - key: 'all', - title: 'all', - icon: 'ti ti-whirl', - iconOnly: true, }] : []), { icon: 'ti ti-list', title: i18n.ts.lists, diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts index f910c5181d..e896f4e722 100644 --- a/packages/frontend/src/ui/deck/deck-store.ts +++ b/packages/frontend/src/ui/deck/deck-store.ts @@ -29,7 +29,7 @@ export type Column = { channelId?: string; roleId?: string; includingTypes?: typeof notificationTypes[number][]; - tl?: 'home' | 'local' | 'social' | 'global'; + tl?: 'home' | 'local' | 'media' | 'social' | 'global'; }; export const deckStore = markRaw(new Storage('deck', { diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index 813b801d21..3ae189e473 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-if="column.tl === 'home'" class="ti ti-home"></i> <i v-else-if="column.tl === 'local'" class="ti ti-planet"></i> <i v-else-if="column.tl === 'social'" class="ti ti-rocket"></i> + <i v-else-if="column.tl === 'media'" class="ti ti-photo"></i> <i v-else-if="column.tl === 'global'" class="ti ti-whirl"></i> <span style="margin-left: 8px;">{{ column.name }}</span> </template> @@ -61,6 +62,8 @@ async function setType() { value: 'home' as const, text: i18n.ts._timelines.home, }, { value: 'local' as const, text: i18n.ts._timelines.local, + }, { + value: 'media' as const, text: i18n.ts._timelines.media, }, { value: 'social' as const, text: i18n.ts._timelines.social, }, { diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 51623023c7..3eede6e7be 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -122,6 +122,10 @@ const choose = async (ev) => { text: i18n.ts._timelines.local, icon: 'ti ti-planet', action: () => { setSrc('local'); }, + }, { + text: i18n.ts._timelines.media, + icon: 'ti ti-photo', + action: () => { setSrc('media'); }, }, { text: i18n.ts._timelines.social, icon: 'ti ti-rocket', From f05f91191602e58c34046e8e35d2647dc072b617 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 17:31:23 +0900 Subject: [PATCH 052/501] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=87=E3=82=A3?= =?UTF-8?q?=E3=82=A2=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 4 ++++ locales/index.d.ts | 4 ++++ locales/ja-JP.yml | 4 ++++ package.json | 2 +- packages/frontend/src/pages/settings/general.vue | 7 ++++--- packages/frontend/src/pages/timeline.tutorial.vue | 1 + packages/frontend/src/pages/timeline.vue | 15 +++++++++------ packages/frontend/src/store.ts | 6 +++++- 8 files changed, 32 insertions(+), 11 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 46d77611b5..db61b10250 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -166,6 +166,8 @@ flagAsCat: "Mark this account as a cat" flagAsCatDescription: "Enable this option to mark this account as a cat." flagShowTimelineReplies: "Show replies in timeline" flagShowTimelineRepliesDescription: "Shows replies of users to notes of other users in the timeline if turned on." +showMediaTimeline: "Show Media timeline" +showMediaTimelineInfo: "When on, the media timeline is displayed on the top bar. When turned off, it will not be displayed." autoAcceptFollowed: "Automatically approve follow requests from users you're following" addAccount: "Add account" reloadAccountsList: "Reload account list" @@ -293,6 +295,7 @@ theme: "Themes" themeForLightMode: "Theme to use in Light Mode" themeForDarkMode: "Theme to use in Dark Mode" gamingMode: "Gaming Mode" +gamingModeInfo: "It makes a nice gradation of buttons and other decorations. There is no intense blinking, etc." light: "Light" dark: "Dark" lightThemes: "Light themes" @@ -1708,6 +1711,7 @@ _timelineTutorial: title: "How to use Misskey" step1_1: "This is the \"timeline\". All \"notes\" submitted on {name} will be chronologically displayed here." step1_2: "There are a few different timelines. For example, the \"Home timeline\" will contain notes of users you follow, and the \"Local timeline\" will contain notes from all users of {name}." + step1_3: 'Besides these two, "Social Timeline" is like Home TL + Local TL, and "Media Timeline" is a stream of notes posted with some file at {name}.' step2_1: "Let's try posting a note next. You can do so by pressing the button with a pencil icon." step2_2: "How about writing a self-introduction, or just \"Hello {name}!\" if you don't feel like it?" step3_1: "Finished posting your first note?" diff --git a/locales/index.d.ts b/locales/index.d.ts index a1285d21c4..9c4ed6505c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -168,6 +168,8 @@ export interface Locale { "flagAsCat": string; "flagAsCatDescription": string; "flagShowTimelineReplies": string; + "showMediaTimeline": string; + "showMediaTimelineInfo": string; "flagShowTimelineRepliesDescription": string; "autoAcceptFollowed": string; "addAccount": string; @@ -296,6 +298,7 @@ export interface Locale { "themeForLightMode": string; "themeForDarkMode": string; "gamingMode": string; + "gamingModeInfo": string; "light": string; "dark": string; "lightThemes": string; @@ -1825,6 +1828,7 @@ export interface Locale { "title": string; "step1_1": string; "step1_2": string; + "step1_3": string; "step2_1": string; "step2_2": string; "step3_1": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 591fefb061..a8285cfed3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -165,6 +165,8 @@ flagAsBotDescription: "このアカウントがプログラムによって運用 flagAsCat: "にゃああああああああああああああ!!!!!!!!!!!!" flagAsCatDescription: "にゃにゃにゃ??" flagShowTimelineReplies: "タイムラインにノートへの返信を表示する" +showMediaTimeline: "メディアタイムラインを表示する" +showMediaTimelineInfo: "オンにするとメディアタイムラインを上のバーに表示します。 オフにすると表示しなくなります。" flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。" autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認" addAccount: "アカウントを追加" @@ -293,6 +295,7 @@ theme: "テーマ" themeForLightMode: "ライトモードで使うテーマ" themeForDarkMode: "ダークモードで使うテーマ" gamingMode: 'ゲーミングモード' +gamingModeInfo: "ボタンなどの装飾をいい感じのグラデーションにします。 激しい点滅などはございません。" light: "ライト" dark: "ダーク" lightThemes: "明るいテーマ" @@ -1742,6 +1745,7 @@ _timelineTutorial: title: "Misskeyの使い方" step1_1: "この画面は「タイムライン」です。{name}に投稿された「ノート」が時系列で表示されます。" step1_2: "タイムラインにはいくつか種類があり、例えば「ホームタイムライン」にはあなたがフォローしている人のノートが流れ、「ローカルタイムライン」には{name}全体のノートが流れます。" + step1_3: "この2つ以外にも、「ソーシャルタイムライン」は ホームTL + ローカルTL のようなもので、 「メディアタイムライン」 には{name}で何かしらのファイル付きで投稿されたノートが流れます。" step2_1: "試しに、何かノートを投稿してみましょう。画面上にある鉛筆マークのボタンを押すとフォームが開きます。" step2_2: "初めてのノートの内容は、あなたの自己紹介や「{name}始めました」などがおすすめです。" step3_1: "投稿できましたか?" diff --git a/package.json b/package.json index fdd36fc200..c6083c4a2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.9-prismisskey.1", + "version": "2023.9.0-beta.9-prismisskey.3", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 709fd2d516..29f0ad9e18 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -30,7 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch> <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> <MkSwitch v-model="showTimelineReplies">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></MkSwitch> - <MkFolder> + <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> + <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> @@ -123,7 +124,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> <MkSwitch v-model="enableUltimateDataSaverMode">{{ i18n.ts.UltimateDataSaver }}</MkSwitch> <MkSwitch v-model="enableCellularWithUltimateDataSaver">{{ i18n.ts.cellularWithUltimateDataSaver }}</MkSwitch> - <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }}</MkSwitch> + <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> </div> <div> <MkRadios v-model="emojiStyle"> @@ -257,7 +258,7 @@ const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificati const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimelineReplies')); const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); - +const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); diff --git a/packages/frontend/src/pages/timeline.tutorial.vue b/packages/frontend/src/pages/timeline.tutorial.vue index 66b8e796e5..95b0a79adb 100644 --- a/packages/frontend/src/pages/timeline.tutorial.vue +++ b/packages/frontend/src/pages/timeline.tutorial.vue @@ -21,6 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="tutorial === 0" :class="$style.body"> <div>{{ i18n.t('_timelineTutorial.step1_1', { name: instance.name ?? host }) }}</div> <div>{{ i18n.t('_timelineTutorial.step1_2', { name: instance.name ?? host }) }}</div> + <div>{{ i18n.t('_timelineTutorial.step1_3', { name: instance.name ?? host }) }}</div> </div> <div v-else-if="tutorial === 1" :class="$style.body"> <div>{{ i18n.ts._timelineTutorial.step2_1 }}</div> diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index d085443290..81a8824697 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -40,6 +40,9 @@ const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); +const isShowMediaTimeline = defaultStore.state.showMediaTimeline; +console.log(isShowMediaTimeline) + const keymap = { 't': focus, }; @@ -128,12 +131,12 @@ const headerTabs = $computed(() => [{ title: i18n.ts._timelines.local, icon: 'ti ti-planet', iconOnly: true, -}, { - key: 'media', - title: i18n.ts._timelines.media, - icon: 'ti ti-photo', - iconOnly: true, -}, { +}, ...(isShowMediaTimeline ? [{ + key: 'media', + title: i18n.ts._timelines.media, + icon: 'ti ti-photo', + iconOnly: true, +}] : []), { key: 'social', title: i18n.ts._timelines.social, icon: 'ti ti-rocket', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 27de6d150c..2ba80cd7fd 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -272,7 +272,7 @@ export const defaultStore = markRaw(new Storage('base', { }, gamingMode: { where: 'device', - default: false, + default: true, }, bannerUrl:{ where: 'device', @@ -350,6 +350,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + showMediaTimeline:{ + where: 'device', + default: true, + }, reactionsDisplaySize: { where: 'device', default: 'medium' as 'small' | 'medium' | 'large', From abb192e61f6eaaa65dd8810377b6dcd45f4aa9f2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 19:05:31 +0900 Subject: [PATCH 053/501] =?UTF-8?q?feat:=20=E3=82=B2=E3=83=BC=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=83=A2=E3=83=BC=E3=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkButton.vue | 40 ++-- .../src/ui/_common_/navbar-for-mobile.vue | 219 +++++++++++++++++- packages/frontend/src/ui/universal.vue | 158 ++++++++++++- 3 files changed, 389 insertions(+), 28 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 2647ad1490..993cf7a39e 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -172,7 +172,7 @@ function onMousedown(evt: MouseEvent): void { ripple.style.transform = 'scale(' + (scale / 2) + ')'; }, 1); window.setTimeout(() => { - ripple.style.transition = 'all 1s cubic-bezier(0, 0.44, 0.39, 1.1)'; + ripple.style.transition = 'all 1s cubic-bezier(0, 0.45, 0.30, 1)'; ripple.style.opacity = '0'; }, 1000); window.setTimeout(() => { @@ -198,7 +198,7 @@ function onMousedown(evt: MouseEvent): void { border-radius: 5px; overflow: clip; box-sizing: border-box; - transition: background 0.1s cubic-bezier(0, 0.44, 0.39, 1.1); + transition: background 0.1s cubic-bezier(0, 0.45, 0.30, 1); &:not(:disabled):hover { background: var(--buttonHoverBg); @@ -293,26 +293,26 @@ function onMousedown(evt: MouseEvent): void { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; &:not(:disabled):hover { background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); background-size: 1800% 1800%; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; } &:not(:disabled):active { background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; - animation: AnimationLight 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; } } @@ -320,26 +320,26 @@ function onMousedown(evt: MouseEvent): void { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; &:not(:disabled):hover { background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); background-size: 1800% 1800% ; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; } &:not(:disabled):active { background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite ; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; - animation: AnimationDark 45s cubic-bezier(0, 0.44, 0.39, 1.1) infinite; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; } } diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index a6380c1c57..e8cdcef741 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -12,30 +12,32 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </div> <div :class="$style.middle"> - <MkA :class="$style.item" :activeClass="$style.active" to="/" exact> + <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/" exact> <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span> </MkA> <template v-for="item in menu"> <div v-if="item === '-'" :class="$style.divider"></div> - <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" :class="[$style.item, { [$style.active]: navbarItemDef[item].active }]" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> + <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" + :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> </component> </template> <div :class="$style.divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" :class="$style.item" :activeClass="$style.active" to="/admin"> + <MkA v-if="$i.isAdmin || $i.isModerator" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/admin"> <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> </MkA> - <button :class="$style.item" class="_button" @click="more"> + <button :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" class="_button" @click="more"> <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> </button> - <MkA :class="$style.item" :activeClass="$style.active" to="/settings"> + <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/settings"> <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span> </MkA> </div> <div :class="$style.bottom"> - <button class="_button" :class="$style.post" data-cy-open-post-form @click="os.post"> + <button class="_button" :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" data-cy-open-post-form @click="os.post"> <i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ i18n.ts.note }}</span> </button> <button class="_button" :class="$style.account" @click="openAccountMenu"> @@ -56,7 +58,11 @@ import { i18n } from '@/i18n'; import { instance } from '@/instance'; let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); + const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); + +let gaming = ref() if (darkMode.value){ bannerUrl.value = bannerDark; iconUrl.value = iconDark; @@ -73,6 +79,35 @@ watch(darkMode, () => { iconUrl.value = iconLight; } }) +// gaming.valueに新しい値を代入する +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const menu = toRef(defaultStore.state, 'menu'); const otherMenuItemIndicated = computed(() => { for (const def in navbarItemDef) { @@ -173,6 +208,66 @@ function more() { background: var(--accentLighten); } } + + &.gamingLight:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingLight:hover, &.gamingLight.active { + &.gamingLight:before { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + + &.gamingDark:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingDark:hover, &.gamingDark.active { + &.gamingDark:before { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + + } .postIcon { @@ -255,6 +350,52 @@ function more() { background: var(--accentedBg); } } + &.gamingDark:hover, &.gamingDark.active { + text-decoration: none; + color: white; + + &.gamingDark:before { + content: ""; + display: block; + height: 100%; + aspect-ratio: 1; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } + &.gamingLight:hover, &.gamingLight.active { + text-decoration: none; + color: black; + + &.gamingLight:before { + content: ""; + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: 52px; + aspect-ratio: 1/1; + border-radius: 100%; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + } } .itemIcon { @@ -276,4 +417,70 @@ function more() { position: relative; font-size: 0.9em; } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 7fc8043dd8..4f37e917bb 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -41,8 +41,10 @@ SPDX-License-Identifier: AGPL-3.0-only <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i> </button> - <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" - class="ti ti-pencil"></i></button> + <button + :class="[{[$style.postButton_gamingDark]: gaming === 'dark',[$style.postButton_gamingLight]: gaming === 'light',[$style.postButton]: gaming === ''}]" + class="_button" @click="os.post()"><i :class="$style.navButtonIcon" + class="ti ti-pencil"></i></button> </div> <Transition @@ -121,6 +123,39 @@ import {miLocalStorage} from '@/local-storage'; import {CURRENT_STICKY_BOTTOM} from '@/const'; import {useScrollPositionManager} from '@/nirax'; +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); + +let gaming = ref() +// gaming.valueに新しい値を代入する +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); @@ -463,6 +498,57 @@ $widgets-hide-threshold: 1090px; } } +.postButton_gamingDark { + composes: navButton; + color: var(--fgOnAccent); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + + &.gamingDark:hover { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingDark:active { + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } +} + +.postButton_gamingLight { + composes: navButton; + color: var(--fgOnAccent); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + + &.gamingLight:hover { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + &.gamingLight:active { + background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } +} + .postButton { composes: navButton; background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); @@ -475,6 +561,8 @@ $widgets-hide-threshold: 1090px; &:active { background: linear-gradient(90deg, var(--X8), var(--X8)); } + + } .navButtonIcon { @@ -518,4 +606,70 @@ $widgets-hide-threshold: 1090px; .spacer { height: calc(var(--minBottomSpacing)); } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> From 3c6794bc21b438619f68c50aaf6a154444e54880 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 19:09:16 +0900 Subject: [PATCH 054/501] 2023.9.0-beta.10.prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8b5f574947..0a27b5d6a9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.10", + "version": "2023.9.0-beta.10.prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 9a161962de3c85d8a2d3caf724a57db87aa37775 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 19:38:07 +0900 Subject: [PATCH 055/501] fix: icon --- packages/frontend/src/ui/_common_/navbar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 89c16473ae..d58fe6273f 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.root, { [$style.iconOnly]: iconOnly }]"> <div :class="$style.body"> <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> From 20f811b49ea034681bc7b245e825f746caa2e6aa Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 21 Sep 2023 20:28:12 +0900 Subject: [PATCH 056/501] fix: icon --- packages/frontend/src/ui/_common_/navbar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index d58fe6273f..aecf0c1b92 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> </button> </div> <div :class="$style.middle"> From 093540375df430bc7534dec050317cb418a33967 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 22 Sep 2023 20:07:39 +0900 Subject: [PATCH 057/501] fix: icon --- .../src/components/MkFollowButton.vue | 502 +++-- .../frontend/src/components/MkPostForm.vue | 1845 +++++++++-------- .../src/components/MkSwitch.button.vue | 152 +- packages/frontend/src/components/MkSwitch.vue | 78 +- .../components/global/MkPageHeader.tabs.vue | 116 +- packages/frontend/src/pages/admin/index.vue | 20 +- .../src/ui/_common_/navbar-for-mobile.vue | 24 +- packages/frontend/src/ui/_common_/navbar.vue | 133 +- 8 files changed, 1787 insertions(+), 1083 deletions(-) diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index 15043fcd0b..11c36b8034 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -2,54 +2,108 @@ SPDX-FileCopyrightText: syuilo and other misskey contributors SPDX-License-Identifier: AGPL-3.0-only --> - <template> -<button - class="_button" - :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large }]" - :disabled="wait" - @click="onClick" -> - <template v-if="!wait"> - <template v-if="hasPendingFollowRequestFromYou && user.isLocked"> - <span v-if="full" :class="$style.text">{{ i18n.ts.followRequestPending }}</span><i class="ti ti-hourglass-empty"></i> - </template> - <template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> - <!-- つまりリモートフォローの場合。 --> - <span v-if="full" :class="$style.text">{{ i18n.ts.processing }}</span><MkLoading :em="true" :colored="false"/> - </template> - <template v-else-if="isFollowing"> - <span v-if="full" :class="$style.text">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> - </template> - <template v-else-if="!isFollowing && user.isLocked"> - <span v-if="full" :class="$style.text">{{ i18n.ts.followRequest }}</span><i class="ti ti-plus"></i> - </template> - <template v-else-if="!isFollowing && !user.isLocked"> - <span v-if="full" :class="$style.text">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> - </template> - </template> - <template v-else> - <span v-if="full" :class="$style.text">{{ i18n.ts.processing }}</span><MkLoading :em="true" :colored="false"/> - </template> -</button> + <button + class="_button" + :class="[$style.root, ,{ [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou,[$style.gamingDark]: gaming === 'dark' || isFollowing || hasPendingFollowRequestFromYou,[$style.gamingLight]: gaming === 'light' || isFollowing || hasPendingFollowRequestFromYou +, [$style.full]: full, [$style.large]: large }]" + :disabled="wait" + @click="onClick" + > + <template v-if="!wait"> + <template v-if="hasPendingFollowRequestFromYou && user.isLocked"> + <span v-if="full" + :class="$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'} ">{{ + i18n.ts.followRequestPending + }}</span><i class="ti ti-hourglass-empty"></i> + </template> + <template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> + <!-- つまりリモートフォローの場合。 --> + <span v-if="full" + :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + i18n.ts.processing + }}</span> + <MkLoading :em="true" :colored="false"/> + </template> + <template v-else-if="isFollowing"> + <span v-if="full" + :class="$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' } ">{{ + i18n.ts.unfollow + }}</span><i class="ti ti-minus"></i> + </template> + <template v-else-if="!isFollowing && user.isLocked"> + <span v-if="full" + :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + i18n.ts.followRequest + }}</span><i class="ti ti-plus"></i> + </template> + <template v-else-if="!isFollowing && !user.isLocked"> + <span v-if="full" + :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + i18n.ts.follow + }}</span><i class="ti ti-plus"></i> + </template> + </template> + <template v-else> + <span v-if="full" + :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + i18n.ts.processing + }}</span> + <MkLoading :em="true" :colored="false"/> + </template> + </button> </template> <script lang="ts" setup> -import { onBeforeUnmount, onMounted } from 'vue'; +import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; -import { useStream } from '@/stream.js'; -import { i18n } from '@/i18n.js'; -import { claimAchievement } from '@/scripts/achievements.js'; -import { $i } from '@/account.js'; +import {useStream} from '@/stream.js'; +import {i18n} from '@/i18n.js'; +import {claimAchievement} from '@/scripts/achievements.js'; +import {$i} from '@/account.js'; +import {defaultStore} from "@/store.js"; + +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ - user: Misskey.entities.UserDetailed, - full?: boolean, - large?: boolean, + user: Misskey.entities.UserDetailed, + full?: boolean, + large?: boolean, }>(), { - full: false, - large: false, + full: false, + large: false, }); let isFollowing = $ref(props.user.isFollowing); @@ -58,151 +112,299 @@ let wait = $ref(false); const connection = useStream().useChannel('main'); if (props.user.isFollowing == null) { - os.api('users/show', { - userId: props.user.id, - }) - .then(onFollowChange); + os.api('users/show', { + userId: props.user.id, + }) + .then(onFollowChange); } function onFollowChange(user: Misskey.entities.UserDetailed) { - if (user.id === props.user.id) { - isFollowing = user.isFollowing; - hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou; - } + if (user.id === props.user.id) { + isFollowing = user.isFollowing; + hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou; + } } async function onClick() { - wait = true; + wait = true; - try { - if (isFollowing) { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.t('unfollowConfirm', { name: props.user.name || props.user.username }), - }); + try { + if (isFollowing) { + const {canceled} = await os.confirm({ + type: 'warning', + text: i18n.t('unfollowConfirm', {name: props.user.name || props.user.username}), + }); - if (canceled) return; + if (canceled) return; - await os.api('following/delete', { - userId: props.user.id, - }); - } else { - if (hasPendingFollowRequestFromYou) { - await os.api('following/requests/cancel', { - userId: props.user.id, - }); - hasPendingFollowRequestFromYou = false; - } else { - await os.api('following/create', { - userId: props.user.id, - }); - hasPendingFollowRequestFromYou = true; + await os.api('following/delete', { + userId: props.user.id, + }); + } else { + if (hasPendingFollowRequestFromYou) { + await os.api('following/requests/cancel', { + userId: props.user.id, + }); + hasPendingFollowRequestFromYou = false; + } else { + await os.api('following/create', { + userId: props.user.id, + }); + hasPendingFollowRequestFromYou = true; - claimAchievement('following1'); + claimAchievement('following1'); - if ($i.followingCount >= 10) { - claimAchievement('following10'); - } - if ($i.followingCount >= 50) { - claimAchievement('following50'); - } - if ($i.followingCount >= 100) { - claimAchievement('following100'); - } - if ($i.followingCount >= 300) { - claimAchievement('following300'); - } - } - } - } catch (err) { - console.error(err); - } finally { - wait = false; - } + if ($i.followingCount >= 10) { + claimAchievement('following10'); + } + if ($i.followingCount >= 50) { + claimAchievement('following50'); + } + if ($i.followingCount >= 100) { + claimAchievement('following100'); + } + if ($i.followingCount >= 300) { + claimAchievement('following300'); + } + } + } + } catch (err) { + console.error(err); + } finally { + wait = false; + } } onMounted(() => { - connection.on('follow', onFollowChange); - connection.on('unfollow', onFollowChange); + connection.on('follow', onFollowChange); + connection.on('unfollow', onFollowChange); }); onBeforeUnmount(() => { - connection.dispose(); + connection.dispose(); }); </script> <style lang="scss" module> .root { - position: relative; - display: inline-block; - font-weight: bold; - color: var(--fgOnWhite); - border: solid 1px var(--accent); - padding: 0; - height: 31px; - font-size: 16px; - border-radius: 32px; - background: #fff; + position: relative; + display: inline-block; + font-weight: bold; + color: var(--fgOnWhite); + border: solid 1px; + padding: 0; + height: 31px; + font-size: 16px; + border-radius: 32px; + background: #fff; - &.full { - padding: 0 8px 0 12px; - font-size: 14px; - } + &.full { + padding: 0 8px 0 12px; + font-size: 14px; + } - &.large { - font-size: 16px; - height: 38px; - padding: 0 12px 0 16px; - } + &.large { + font-size: 16px; + height: 38px; + padding: 0 12px 0 16px; + } - &:not(.full) { - width: 31px; - } + &:not(.full) { + width: 31px; + } - &:focus-visible { - &:after { - content: ""; - pointer-events: none; - position: absolute; - top: -5px; - right: -5px; - bottom: -5px; - left: -5px; - border: 2px solid var(--focus); - border-radius: 32px; - } - } + &:focus-visible { + &:after { + content: ""; + pointer-events: none; + position: absolute; + top: -5px; + right: -5px; + bottom: -5px; + left: -5px; + border: 2px solid var(--focus); + border-radius: 32px; + } + } - &:hover { - //background: mix($primary, #fff, 20); - } + &:hover { + //background: mix($primary, #fff, 20); + } - &:active { - //background: mix($primary, #fff, 40); - } + &:active { + //background: mix($primary, #fff, 40); + } - &.active { - color: var(--fgOnAccent); - background: var(--accent); + &.active { + color: var(--fgOnAccent); + background: var(--accent); - &:hover { - background: var(--accentLighten); - border-color: var(--accentLighten); - } + &:hover { + background: var(--accentLighten); + border-color: var(--accentLighten); + } - &:active { - background: var(--accentDarken); - border-color: var(--accentDarken); - } - } + &:active { + background: var(--accentDarken); + border-color: var(--accentDarken); + } - &.wait { - cursor: wait !important; - opacity: 0.7; - } + &.gamingDark:hover { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingDark:active { + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + border-color: white; + } + + &.gamingLight:hover { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + border-color: white; + } + + &.gamingLight:active { + color: white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + border-color: white; + } + + &.gamingDark { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + + &.gamingLight { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + + } + + } + + &.wait { + cursor: wait !important; + opacity: 0.7; + } } .text { - margin-right: 6px; + margin-right: 6px; + + &.gamingDark { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + &.gamingLight { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + +} + +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } </style> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 2b4dcc8ed4..a3c0596402 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -4,156 +4,223 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div - :class="[$style.root, { [$style.modal]: modal, _popup: modal }]" - @dragover.stop="onDragover" - @dragenter="onDragenter" - @dragleave="onDragleave" - @drop.stop="onDrop" -> - <header :class="$style.header"> - <div :class="$style.headerLeft"> - <button v-if="!fixed" :class="$style.cancel" class="_button" @click="cancel"><i class="ti ti-x"></i></button> - <button v-click-anime v-tooltip="i18n.ts.switchAccount" :class="$style.account" class="_button" @click="openAccountMenu"> - <MkAvatar :user="postAccount ?? $i" :class="$style.avatar"/> - </button> - </div> - <div :class="$style.headerRight"> - <template v-if="!(channel != null && fixed)"> - <button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility"> - <span v-if="visibility === 'public'"><i class="ti ti-world"></i></span> - <span v-if="visibility === 'home'"><i class="ti ti-home"></i></span> - <span v-if="visibility === 'followers'"><i class="ti ti-lock"></i></span> - <span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span> - <span :class="$style.headerRightButtonText">{{ i18n.ts._visibility[visibility] }}</span> - </button> - <button v-else class="_button" :class="[$style.headerRightItem, $style.visibility]" disabled> - <span><i class="ti ti-device-tv"></i></span> - <span :class="$style.headerRightButtonText">{{ channel.name }}</span> - </button> - </template> - <button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly"> - <span v-if="!localOnly"><i class="ti ti-rocket"></i></span> - <span v-else><i class="ti ti-rocket-off"></i></span> - </button> - <button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" class="_button" :class="[$style.headerRightItem, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" @click="toggleReactionAcceptance"> - <span v-if="reactionAcceptance === 'likeOnly'"><i class="ti ti-heart"></i></span> - <span v-else-if="reactionAcceptance === 'likeOnlyForRemote'"><i class="ti ti-heart-plus"></i></span> - <span v-else><i class="ti ti-icons"></i></span> - </button> - <button v-click-anime class="_button" :class="$style.submit" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> - <div :class="$style.submitInner"> - <template v-if="posted"></template> - <template v-else-if="posting"><MkEllipsis/></template> - <template v-else>{{ submitText }}</template> - <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : 'ti ti-send'"></i> - </div> - </button> - </div> - </header> - <MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/> - <MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/> - <div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="ti ti-x"></i></button></div> - <div v-if="visibility === 'specified'" :class="$style.toSpecified"> - <span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span> - <div :class="$style.visibleUsers"> + <div + :class="[$style.root, { [$style.modal]: modal, _popup: modal }]" + @dragover.stop="onDragover" + @dragenter="onDragenter" + @dragleave="onDragleave" + @drop.stop="onDrop" + > + <header :class="$style.header"> + <div :class="$style.headerLeft"> + <button v-if="!fixed" :class="$style.cancel" class="_button" @click="cancel"><i class="ti ti-x"></i></button> + <button v-click-anime v-tooltip="i18n.ts.switchAccount" :class="$style.account" class="_button" + @click="openAccountMenu"> + <MkAvatar :user="postAccount ?? $i" :class="$style.avatar"/> + </button> + </div> + <div :class="$style.headerRight"> + <template v-if="!(channel != null && fixed)"> + <button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" + :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility"> + <span v-if="visibility === 'public'"><i class="ti ti-world"></i></span> + <span v-if="visibility === 'home'"><i class="ti ti-home"></i></span> + <span v-if="visibility === 'followers'"><i class="ti ti-lock"></i></span> + <span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span> + <span :class="$style.headerRightButtonText">{{ i18n.ts._visibility[visibility] }}</span> + </button> + <button v-else class="_button" :class="[$style.headerRightItem, $style.visibility]" disabled> + <span><i class="ti ti-device-tv"></i></span> + <span :class="$style.headerRightButtonText">{{ channel.name }}</span> + </button> + </template> + <button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" + :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" + :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly"> + <span v-if="!localOnly"><i class="ti ti-rocket"></i></span> + <span v-else><i class="ti ti-rocket-off"></i></span> + </button> + <button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" class="_button" + :class="[$style.headerRightItem, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" + @click="toggleReactionAcceptance"> + <span v-if="reactionAcceptance === 'likeOnly'"><i class="ti ti-heart"></i></span> + <span v-else-if="reactionAcceptance === 'likeOnlyForRemote'"><i class="ti ti-heart-plus"></i></span> + <span v-else><i class="ti ti-icons"></i></span> + </button> + <button v-click-anime class="_button" :class="$style.submit" :disabled="!canPost" data-cy-open-post-form-submit + @click="post"> + <div + :class="[$style.submitInner , { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> + <template v-if="posted"></template> + <template v-else-if="posting"> + <MkEllipsis/> + </template> + <template v-else>{{ submitText }}</template> + <i style="margin-left: 6px;" + :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : 'ti ti-send'"></i> + </div> + </button> + </div> + </header> + <MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/> + <MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/> + <div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }} + <button @click="quoteId = null"><i class="ti ti-x"></i></button> + </div> + <div v-if="visibility === 'specified'" :class="$style.toSpecified"> + <span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span> + <div :class="$style.visibleUsers"> <span v-for="u in visibleUsers" :key="u.id" :class="$style.visibleUser"> <MkAcct :user="u"/> <button class="_button" style="padding: 4px 8px;" @click="removeVisibleUser(u)"><i class="ti ti-x"></i></button> </span> - <button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button> - </div> - </div> - <MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> - <input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> - <div :class="[$style.textOuter, { [$style.withCw]: useCw }]"> - <textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> - <div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div> - </div> - <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> - <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/> - <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> - <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text"/> - <div v-if="showingOptions" style="padding: 8px 16px;"> - </div> - <footer :class="$style.footer"> - <div :class="$style.footerLeft"> - <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> - <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> - <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> - <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> - <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> - <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> - <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> - </div> - <div :class="$style.footerRight"> - <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> - <!--<button v-tooltip="i18n.ts.more" class="_button" :class="$style.footerButton" @click="showingOptions = !showingOptions"><i class="ti ti-dots"></i></button>--> - </div> - </footer> - <datalist id="hashtags"> - <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> - </datalist> -</div> + <button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i + class="ti ti-plus ti-fw"></i></button> + </div> + </div> + <MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions"> + {{ i18n.ts.notSpecifiedMentionWarning }} - + <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button> + </MkInfo> + <input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" + @keydown="onKeydown"> + <div :class="[$style.textOuter, { [$style.withCw]: useCw }]"> + <textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" + :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" + @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> + <div v-if="maxTextLength - textLength < 100" + :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]"> + {{ maxTextLength - textLength }} + </div> + </div> + <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" + :placeholder="i18n.ts.hashtags" list="hashtags"> + <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" + @changeName="updateFileName" @replaceFile="replaceFile"/> + <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> + <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text"/> + <div v-if="showingOptions" style="padding: 8px 16px;"> + </div> + <footer :class="$style.footer"> + <div :class="$style.footerLeft"> + <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i + class="ti ti-photo-plus"></i></button> + <button v-tooltip="i18n.ts.poll" class="_button" + :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i + class="ti ti-chart-arrows"></i></button> + <button v-tooltip="i18n.ts.useCw" class="_button" + :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i + class="ti ti-eye-off"></i></button> + <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i + class="ti ti-at"></i></button> + <button v-tooltip="i18n.ts.hashtags" class="_button" + :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" + @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> + <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" + :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> + <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i + class="ti ti-mood-happy"></i></button> + </div> + <div :class="$style.footerRight"> + <button v-tooltip="i18n.ts.previewNoteText" class="_button" + :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" + @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> + <!--<button v-tooltip="i18n.ts.more" class="_button" :class="$style.footerButton" @click="showingOptions = !showingOptions"><i class="ti ti-dots"></i></button>--> + </div> + </footer> + <datalist id="hashtags"> + <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> + </datalist> + </div> </template> <script lang="ts" setup> -import { inject, watch, nextTick, onMounted, defineAsyncComponent } from 'vue'; +import {inject, watch, nextTick, onMounted, defineAsyncComponent, computed, ref} from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; -import { toASCII } from 'punycode/'; +import {toASCII} from 'punycode/'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; import MkNotePreview from '@/components/MkNotePreview.vue'; import XPostFormAttaches from '@/components/MkPostFormAttaches.vue'; import MkPollEditor from '@/components/MkPollEditor.vue'; -import { host, url } from '@/config.js'; -import { erase, unique } from '@/scripts/array.js'; -import { extractMentions } from '@/scripts/extract-mentions.js'; -import { formatTimeString } from '@/scripts/format-time-string.js'; -import { Autocomplete } from '@/scripts/autocomplete.js'; +import {host, url} from '@/config.js'; +import {erase, unique} from '@/scripts/array.js'; +import {extractMentions} from '@/scripts/extract-mentions.js'; +import {formatTimeString} from '@/scripts/format-time-string.js'; +import {Autocomplete} from '@/scripts/autocomplete.js'; import * as os from '@/os.js'; -import { selectFiles } from '@/scripts/select-file.js'; -import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; +import {selectFiles} from '@/scripts/select-file.js'; +import {defaultStore, notePostInterruptors, postFormActions} from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; -import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; -import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; -import { uploadFile } from '@/scripts/upload.js'; -import { deepClone } from '@/scripts/clone.js'; +import {i18n} from '@/i18n.js'; +import {instance} from '@/instance.js'; +import {$i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_} from '@/account.js'; +import {uploadFile} from '@/scripts/upload.js'; +import {deepClone} from '@/scripts/clone.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; -import { miLocalStorage } from '@/local-storage.js'; -import { claimAchievement } from '@/scripts/achievements.js'; +import {miLocalStorage} from '@/local-storage.js'; +import {claimAchievement} from '@/scripts/achievements.js'; const modal = inject('modal'); const props = withDefaults(defineProps<{ - reply?: Misskey.entities.Note; - renote?: Misskey.entities.Note; - channel?: Misskey.entities.Channel; // TODO - mention?: Misskey.entities.User; - specified?: Misskey.entities.User; - initialText?: string; - initialVisibility?: (typeof Misskey.noteVisibilities)[number]; - initialFiles?: Misskey.entities.DriveFile[]; - initialLocalOnly?: boolean; - initialVisibleUsers?: Misskey.entities.User[]; - initialNote?: Misskey.entities.Note; - instant?: boolean; - fixed?: boolean; - autofocus?: boolean; - freezeAfterPosted?: boolean; + reply?: Misskey.entities.Note; + renote?: Misskey.entities.Note; + channel?: Misskey.entities.Channel; // TODO + mention?: Misskey.entities.User; + specified?: Misskey.entities.User; + initialText?: string; + initialVisibility?: (typeof Misskey.noteVisibilities)[number]; + initialFiles?: Misskey.entities.DriveFile[]; + initialLocalOnly?: boolean; + initialVisibleUsers?: Misskey.entities.User[]; + initialNote?: Misskey.entities.Note; + instant?: boolean; + fixed?: boolean; + autofocus?: boolean; + freezeAfterPosted?: boolean; }>(), { - initialVisibleUsers: () => [], - autofocus: true, + initialVisibleUsers: () => [], + autofocus: true, }); - +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +let gaming: any; +gaming = ref(); const emit = defineEmits<{ - (ev: 'posted'): void; - (ev: 'cancel'): void; - (ev: 'esc'): void; + (ev: 'posted'): void; + (ev: 'cancel'): void; + (ev: 'esc'): void; }>(); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const textareaEl = $shallowRef<HTMLTextAreaElement | null>(null); const cwInputEl = $shallowRef<HTMLInputElement | null>(null); const hashtagsInputEl = $shallowRef<HTMLInputElement | null>(null); @@ -164,10 +231,10 @@ let posted = $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; + choices: string[]; + multiple: boolean; + expiresAt: string | null; + expiredAfter: string | null; } | null>(null); let useCw = $ref(false); let showPreview = $ref(defaultStore.state.showPreview); @@ -177,7 +244,7 @@ let localOnly = $ref<boolean>(props.initialLocalOnly ?? defaultStore.state.remem 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); + props.initialVisibleUsers.forEach(pushVisibleUser); } let reactionAcceptance = $ref(defaultStore.state.reactionAcceptance); let autocomplete = $ref(null); @@ -189,1057 +256,1145 @@ let imeText = $ref(''); let showingOptions = $ref(false); const draftKey = $computed((): string => { - let key = props.channel ? `channel:${props.channel.id}` : ''; + let key = props.channel ? `channel:${props.channel.id}` : ''; - if (props.renote) { - key += `renote:${props.renote.id}`; - } else if (props.reply) { - key += `reply:${props.reply.id}`; - } else { - key += `note:${$i.id}`; - } + if (props.renote) { + key += `renote:${props.renote.id}`; + } else if (props.reply) { + key += `reply:${props.reply.id}`; + } else { + key += `note:${$i.id}`; + } - return key; + return key; }); const placeholder = $computed((): string => { - if (props.renote) { - return i18n.ts._postForm.quotePlaceholder; - } else if (props.reply) { - return i18n.ts._postForm.replyPlaceholder; - } else if (props.channel) { - return i18n.ts._postForm.channelPlaceholder; - } else { - const xs = [ - i18n.ts._postForm._placeholders.a, - i18n.ts._postForm._placeholders.b, - i18n.ts._postForm._placeholders.c, - i18n.ts._postForm._placeholders.d, - i18n.ts._postForm._placeholders.e, - i18n.ts._postForm._placeholders.f, - ]; - return xs[Math.floor(Math.random() * xs.length)]; - } + if (props.renote) { + return i18n.ts._postForm.quotePlaceholder; + } else if (props.reply) { + return i18n.ts._postForm.replyPlaceholder; + } else if (props.channel) { + return i18n.ts._postForm.channelPlaceholder; + } else { + const xs = [ + i18n.ts._postForm._placeholders.a, + i18n.ts._postForm._placeholders.b, + i18n.ts._postForm._placeholders.c, + i18n.ts._postForm._placeholders.d, + i18n.ts._postForm._placeholders.e, + i18n.ts._postForm._placeholders.f, + ]; + return xs[Math.floor(Math.random() * xs.length)]; + } }); const submitText = $computed((): string => { - return props.renote - ? i18n.ts.quote - : props.reply - ? i18n.ts.reply - : i18n.ts.note; + return props.renote + ? i18n.ts.quote + : props.reply + ? i18n.ts.reply + : i18n.ts.note; }); const textLength = $computed((): number => { - return (text + imeText).trim().length; + return (text + imeText).trim().length; }); const maxTextLength = $computed((): number => { - return instance ? instance.maxNoteTextLength : 1000; + return instance ? instance.maxNoteTextLength : 1000; }); const canPost = $computed((): boolean => { - return !posting && !posted && - (1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && - (textLength <= maxTextLength) && - (!poll || poll.choices.length >= 2); + return !posting && !posted && + (1 <= textLength || 1 <= files.length || !!poll || !!props.renote) && + (textLength <= maxTextLength) && + (!poll || poll.choices.length >= 2); }); const withHashtags = $computed(defaultStore.makeGetterSetter('postFormWithHashtags')); const hashtags = $computed(defaultStore.makeGetterSetter('postFormHashtags')); watch($$(text), () => { - checkMissingMention(); -}, { immediate: true }); + checkMissingMention(); +}, {immediate: true}); watch($$(visibility), () => { - checkMissingMention(); -}, { immediate: true }); + checkMissingMention(); +}, {immediate: true}); watch($$(visibleUsers), () => { - checkMissingMention(); + checkMissingMention(); }, { - deep: true, + deep: true, }); if (props.mention) { - text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; - text += ' '; + text = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; + text += ' '; } 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 = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `; } if (props.reply && props.reply.text != null) { - const ast = mfm.parse(props.reply.text); - const otherHost = props.reply.user.host; + const ast = mfm.parse(props.reply.text); + const otherHost = props.reply.user.host; - for (const x of extractMentions(ast)) { - const mention = x.host ? - `@${x.username}@${toASCII(x.host)}` : - (otherHost == null || otherHost === host) ? - `@${x.username}` : - `@${x.username}@${toASCII(otherHost)}`; + for (const x of extractMentions(ast)) { + const mention = x.host ? + `@${x.username}@${toASCII(x.host)}` : + (otherHost == null || otherHost === host) ? + `@${x.username}` : + `@${x.username}@${toASCII(otherHost)}`; - // 自分は除外 - if ($i.username === x.username && (x.host == null || x.host === host)) continue; + // 自分は除外 + if ($i.username === x.username && (x.host == null || x.host === host)) continue; - // 重複は除外 - if (text.includes(`${mention} `)) continue; + // 重複は除外 + if (text.includes(`${mention} `)) continue; - text += `${mention} `; - } + text += `${mention} `; + } } if (props.channel) { - visibility = 'public'; - localOnly = true; // TODO: チャンネルが連合するようになった折には消す + visibility = 'public'; + localOnly = true; // TODO: チャンネルが連合するようになった折には消す } // 公開以外へのリプライ時は元の公開範囲を引き継ぐ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { - if (props.reply.visibility === 'home' && visibility === 'followers') { - visibility = 'followers'; - } else if (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') { - visibility = 'specified'; - } else { - visibility = props.reply.visibility; - } + if (props.reply.visibility === 'home' && visibility === 'followers') { + visibility = 'followers'; + } else if (['home', 'followers'].includes(props.reply.visibility) && visibility === 'specified') { + visibility = 'specified'; + } else { + visibility = props.reply.visibility; + } - if (visibility === 'specified') { - if (props.reply.visibleUserIds) { - os.api('users/show', { - userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), - }).then(users => { - users.forEach(pushVisibleUser); - }); - } + if (visibility === 'specified') { + if (props.reply.visibleUserIds) { + os.api('users/show', { + userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId), + }).then(users => { + users.forEach(pushVisibleUser); + }); + } - if (props.reply.userId !== $i.id) { - os.api('users/show', { userId: props.reply.userId }).then(user => { - pushVisibleUser(user); - }); - } - } + if (props.reply.userId !== $i.id) { + os.api('users/show', {userId: props.reply.userId}).then(user => { + pushVisibleUser(user); + }); + } + } } if (props.specified) { - visibility = 'specified'; - pushVisibleUser(props.specified); + visibility = 'specified'; + pushVisibleUser(props.specified); } // keep cw when reply if (defaultStore.state.keepCw && props.reply && props.reply.cw) { - useCw = true; - cw = props.reply.cw; + useCw = true; + cw = 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 === 'specified') { + const ast = mfm.parse(text); - for (const x of extractMentions(ast)) { - if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) { - hasNotSpecifiedMentions = true; - return; - } - } - hasNotSpecifiedMentions = false; - } + for (const x of extractMentions(ast)) { + if (!visibleUsers.some(u => (u.username === x.username) && (u.host === x.host))) { + hasNotSpecifiedMentions = true; + return; + } + } + hasNotSpecifiedMentions = false; + } } function addMissingMention() { - const ast = mfm.parse(text); + const ast = mfm.parse(text); - for (const x of extractMentions(ast)) { - if (!visibleUsers.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); - }); - } - } + for (const x of extractMentions(ast)) { + if (!visibleUsers.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); + }); + } + } } function togglePoll() { - if (poll) { - poll = null; - } else { - poll = { - choices: ['', ''], - multiple: false, - expiresAt: null, - expiredAfter: null, - }; - } + if (poll) { + poll = null; + } else { + poll = { + choices: ['', ''], + multiple: false, + expiresAt: null, + expiredAfter: null, + }; + } } function addTag(tag: string) { - insertTextAtCursor(textareaEl, ` #${tag} `); + insertTextAtCursor(textareaEl, ` #${tag} `); } function focus() { - if (textareaEl) { - textareaEl.focus(); - textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length); - } + if (textareaEl) { + textareaEl.focus(); + textareaEl.setSelectionRange(textareaEl.value.length, textareaEl.value.length); + } } function chooseFileFrom(ev) { - selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { - for (const file of files_) { - files.push(file); - } - }); + selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { + for (const file of files_) { + files.push(file); + } + }); } function detachFile(id) { - files = files.filter(x => x.id !== id); + files = files.filter(x => x.id !== id); } function updateFileSensitive(file, sensitive) { - files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive; + files[files.findIndex(x => x.id === file.id)].isSensitive = sensitive; } function updateFileName(file, name) { - files[files.findIndex(x => x.id === file.id)].name = name; + files[files.findIndex(x => x.id === file.id)].name = name; } function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void { - files[files.findIndex(x => x.id === file.id)] = newFile; + files[files.findIndex(x => x.id === file.id)] = newFile; } function upload(file: File, name?: string): void { - uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { - files.push(res); - }); + uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { + files.push(res); + }); } function setVisibility() { - if (props.channel) { - visibility = 'public'; - localOnly = true; // TODO: チャンネルが連合するようになった折には消す - return; - } + if (props.channel) { + visibility = 'public'; + localOnly = true; // TODO: チャンネルが連合するようになった折には消す + return; + } - os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { - currentVisibility: visibility, - localOnly: localOnly, - src: visibilityButton, - }, { - changeVisibility: v => { - visibility = v; - if (defaultStore.state.rememberNoteVisibility) { - defaultStore.set('visibility', visibility); - } - }, - }, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { + currentVisibility: visibility, + localOnly: localOnly, + src: visibilityButton, + }, { + changeVisibility: v => { + visibility = v; + if (defaultStore.state.rememberNoteVisibility) { + defaultStore.set('visibility', visibility); + } + }, + }, 'closed'); } async function toggleLocalOnly() { - if (props.channel) { - visibility = 'public'; - localOnly = true; // TODO: チャンネルが連合するようになった折には消す - return; - } + if (props.channel) { + visibility = 'public'; + localOnly = true; // TODO: チャンネルが連合するようになった折には消す + return; + } - const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); + const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); - if (!localOnly && neverShowInfo !== 'true') { - const confirm = await os.actions({ - type: 'question', - title: i18n.ts.disableFederationConfirm, - text: i18n.ts.disableFederationConfirmWarn, - actions: [ - { - value: 'yes' as const, - text: i18n.ts.disableFederationOk, - primary: true, - }, - { - value: 'neverShow' as const, - text: `${i18n.ts.disableFederationOk} (${i18n.ts.neverShow})`, - danger: true, - }, - { - value: 'no' as const, - text: i18n.ts.cancel, - }, - ], - }); - if (confirm.canceled) return; - if (confirm.result === 'no') return; + if (!localOnly && neverShowInfo !== 'true') { + const confirm = await os.actions({ + type: 'question', + title: i18n.ts.disableFederationConfirm, + text: i18n.ts.disableFederationConfirmWarn, + actions: [ + { + value: 'yes' as const, + text: i18n.ts.disableFederationOk, + primary: true, + }, + { + value: 'neverShow' as const, + text: `${i18n.ts.disableFederationOk} (${i18n.ts.neverShow})`, + danger: true, + }, + { + value: 'no' as const, + text: i18n.ts.cancel, + }, + ], + }); + if (confirm.canceled) return; + if (confirm.result === 'no') return; - if (confirm.result === 'neverShow') { - miLocalStorage.setItem('neverShowLocalOnlyInfo', 'true'); - } - } + if (confirm.result === 'neverShow') { + miLocalStorage.setItem('neverShowLocalOnlyInfo', 'true'); + } + } - localOnly = !localOnly; + localOnly = !localOnly; } async function toggleReactionAcceptance() { - const select = await os.select({ - title: i18n.ts.reactionAcceptance, - items: [ - { value: null, text: i18n.ts.all }, - { value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote }, - { value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly }, - { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, - { value: 'likeOnly' as const, text: i18n.ts.likeOnly }, - ], - default: reactionAcceptance, - }); - if (select.canceled) return; - reactionAcceptance = select.result; + const select = await os.select({ + title: i18n.ts.reactionAcceptance, + items: [ + {value: null, text: i18n.ts.all}, + {value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote}, + {value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly}, + {value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote}, + {value: 'likeOnly' as const, text: i18n.ts.likeOnly}, + ], + default: reactionAcceptance, + }); + if (select.canceled) return; + reactionAcceptance = select.result; } function pushVisibleUser(user) { - if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) { - visibleUsers.push(user); - } + if (!visibleUsers.some(u => u.username === user.username && u.host === user.host)) { + visibleUsers.push(user); + } } function addVisibleUser() { - os.selectUser().then(user => { - pushVisibleUser(user); + os.selectUser().then(user => { + pushVisibleUser(user); - if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { - text = `@${Misskey.acct.toString(user)} ${text}`; - } - }); + if (!text.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { + text = `@${Misskey.acct.toString(user)} ${text}`; + } + }); } function removeVisibleUser(user) { - visibleUsers = erase(user, visibleUsers); + visibleUsers = erase(user, visibleUsers); } function clear() { - text = ''; - files = []; - poll = null; - quoteId = null; + text = ''; + files = []; + poll = null; + quoteId = null; } function onKeydown(ev: KeyboardEvent) { - if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost) post(); - if (ev.key === 'Escape') emit('esc'); + if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost) post(); + if (ev.key === 'Escape') emit('esc'); } function onCompositionUpdate(ev: CompositionEvent) { - imeText = ev.data; + imeText = ev.data; } function onCompositionEnd(ev: CompositionEvent) { - imeText = ''; + imeText = ''; } async function onPaste(ev: ClipboardEvent) { - for (const { item, i } of Array.from(ev.clipboardData.items, (item, i) => ({ item, i }))) { - if (item.kind === 'file') { - const file = item.getAsFile(); - const lio = file.name.lastIndexOf('.'); - const ext = lio >= 0 ? file.name.slice(lio) : ''; - const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; - upload(file, formatted); - } - } + for (const {item, i} of Array.from(ev.clipboardData.items, (item, i) => ({item, i}))) { + if (item.kind === 'file') { + const file = item.getAsFile(); + const lio = file.name.lastIndexOf('.'); + const ext = lio >= 0 ? file.name.slice(lio) : ''; + const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; + upload(file, formatted); + } + } - const paste = ev.clipboardData.getData('text'); + const paste = ev.clipboardData.getData('text'); - if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) { - ev.preventDefault(); + if (!props.renote && !quoteId && paste.startsWith(url + '/notes/')) { + ev.preventDefault(); - os.confirm({ - type: 'info', - text: i18n.ts.quoteQuestion, - }).then(({ canceled }) => { - if (canceled) { - insertTextAtCursor(textareaEl, paste); - return; - } + os.confirm({ + type: 'info', + text: i18n.ts.quoteQuestion, + }).then(({canceled}) => { + if (canceled) { + insertTextAtCursor(textareaEl, paste); + return; + } - quoteId = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; - }); - } + quoteId = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)[1]; + }); + } } function onDragover(ev) { - if (!ev.dataTransfer.items[0]) return; - const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; - if (isFile || isDriveFile) { - ev.preventDefault(); - draghover = true; - switch (ev.dataTransfer.effectAllowed) { - case 'all': - case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': - ev.dataTransfer.dropEffect = 'copy'; - break; - case 'linkMove': - case 'move': - ev.dataTransfer.dropEffect = 'move'; - break; - default: - ev.dataTransfer.dropEffect = 'none'; - break; - } - } + if (!ev.dataTransfer.items[0]) return; + const isFile = ev.dataTransfer.items[0].kind === 'file'; + const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; + if (isFile || isDriveFile) { + ev.preventDefault(); + draghover = true; + switch (ev.dataTransfer.effectAllowed) { + case 'all': + case 'uninitialized': + case 'copy': + case 'copyLink': + case 'copyMove': + ev.dataTransfer.dropEffect = 'copy'; + break; + case 'linkMove': + case 'move': + ev.dataTransfer.dropEffect = 'move'; + break; + default: + ev.dataTransfer.dropEffect = 'none'; + break; + } + } } function onDragenter(ev) { - draghover = true; + draghover = true; } function onDragleave(ev) { - draghover = false; + draghover = false; } function onDrop(ev): void { - draghover = false; + draghover = false; - // ファイルだったら - if (ev.dataTransfer.files.length > 0) { - ev.preventDefault(); - for (const x of Array.from(ev.dataTransfer.files)) upload(x); - return; - } + // ファイルだったら + if (ev.dataTransfer.files.length > 0) { + ev.preventDefault(); + for (const x of Array.from(ev.dataTransfer.files)) upload(x); + return; + } - //#region ドライブのファイル - const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); - if (driveFile != null && driveFile !== '') { - const file = JSON.parse(driveFile); - files.push(file); - ev.preventDefault(); - } - //#endregion + //#region ドライブのファイル + const driveFile = ev.dataTransfer.getData(_DATA_TRANSFER_DRIVE_FILE_); + if (driveFile != null && driveFile !== '') { + const file = JSON.parse(driveFile); + files.push(file); + ev.preventDefault(); + } + //#endregion } function saveDraft() { - if (props.instant) return; + if (props.instant) return; - const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); - draftData[draftKey] = { - updatedAt: new Date(), - data: { - text: text, - useCw: useCw, - cw: cw, - visibility: visibility, - localOnly: localOnly, - files: files, - poll: poll, - }, - }; + draftData[draftKey] = { + updatedAt: new Date(), + data: { + text: text, + useCw: useCw, + cw: cw, + visibility: visibility, + localOnly: localOnly, + files: files, + poll: poll, + }, + }; - miLocalStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } function deleteDraft() { - const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); - delete draftData[draftKey]; + delete draftData[draftKey]; - miLocalStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } async function post(ev?: MouseEvent) { - if (ev) { - const el = ev.currentTarget ?? ev.target; - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); - } + if (ev) { + const el = ev.currentTarget ?? ev.target; + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, {x, y}, {}, 'end'); + } - const annoying = - text.includes('$[x2') || - text.includes('$[x3') || - text.includes('$[x4') || - text.includes('$[scale') || - text.includes('$[position'); + const annoying = + text.includes('$[x2') || + text.includes('$[x3') || + text.includes('$[x4') || + text.includes('$[scale') || + text.includes('$[position'); - if (annoying && visibility === 'public') { - const { canceled, result } = await os.actions({ - type: 'warning', - text: i18n.ts.thisPostMayBeAnnoying, - actions: [{ - value: 'home', - text: i18n.ts.thisPostMayBeAnnoyingHome, - primary: true, - }, { - value: 'cancel', - text: i18n.ts.thisPostMayBeAnnoyingCancel, - }, { - value: 'ignore', - text: i18n.ts.thisPostMayBeAnnoyingIgnore, - }], - }); + if (annoying && visibility === 'public') { + const {canceled, result} = await os.actions({ + type: 'warning', + text: i18n.ts.thisPostMayBeAnnoying, + actions: [{ + value: 'home', + text: i18n.ts.thisPostMayBeAnnoyingHome, + primary: true, + }, { + value: 'cancel', + text: i18n.ts.thisPostMayBeAnnoyingCancel, + }, { + value: 'ignore', + text: i18n.ts.thisPostMayBeAnnoyingIgnore, + }], + }); - if (canceled) return; - if (result === 'cancel') return; - if (result === 'home') { - visibility = 'home'; - } - } + if (canceled) return; + if (result === 'cancel') return; + if (result === 'home') { + visibility = 'home'; + } + } - let postData = { - text: text === '' ? undefined : text, - fileIds: files.length > 0 ? files.map(f => f.id) : undefined, - replyId: props.reply ? props.reply.id : undefined, - renoteId: props.renote ? props.renote.id : quoteId ? quoteId : 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, - reactionAcceptance, - }; + let postData = { + text: text === '' ? undefined : text, + fileIds: files.length > 0 ? files.map(f => f.id) : undefined, + replyId: props.reply ? props.reply.id : undefined, + renoteId: props.renote ? props.renote.id : quoteId ? quoteId : 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, + reactionAcceptance, + }; - if (withHashtags && hashtags && hashtags.trim() !== '') { - const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); - postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; - } + if (withHashtags && hashtags && hashtags.trim() !== '') { + const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); + postData.text = postData.text ? `${postData.text} ${hashtags_}` : hashtags_; + } - // plugin - if (notePostInterruptors.length > 0) { - for (const interruptor of notePostInterruptors) { - postData = await interruptor.handler(deepClone(postData)); - } - } + // plugin + if (notePostInterruptors.length > 0) { + for (const interruptor of notePostInterruptors) { + postData = await interruptor.handler(deepClone(postData)); + } + } - let token = undefined; + let token = undefined; - if (postAccount) { - const storedAccounts = await getAccounts(); - token = storedAccounts.find(x => x.id === postAccount.id)?.token; - } + if (postAccount) { + const storedAccounts = await getAccounts(); + token = storedAccounts.find(x => x.id === postAccount.id)?.token; + } - posting = true; - os.api('notes/create', postData, token).then(() => { - if (props.freezeAfterPosted) { - posted = true; - } else { - clear(); - } - nextTick(() => { - deleteDraft(); - emit('posted'); - if (postData.text && postData.text !== '') { - const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); - const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; - miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); - } - posting = false; - postAccount = null; + posting = true; + os.api('notes/create', postData, token).then(() => { + if (props.freezeAfterPosted) { + posted = true; + } else { + clear(); + } + nextTick(() => { + deleteDraft(); + emit('posted'); + if (postData.text && postData.text !== '') { + const hashtags_ = mfm.parse(postData.text).filter(x => x.type === 'hashtag').map(x => x.props.hashtag); + const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; + miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); + } + posting = false; + postAccount = null; - incNotesCount(); - if (notesCount === 1) { - claimAchievement('notes1'); - } + incNotesCount(); + if (notesCount === 1) { + claimAchievement('notes1'); + } - const text = postData.text ?? ''; - const lowerCase = text.toLowerCase(); - if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { - claimAchievement('iLoveMisskey'); - } - if ([ - 'https://youtu.be/Efrlqw8ytg4', - 'https://www.youtube.com/watch?v=Efrlqw8ytg4', - 'https://m.youtube.com/watch?v=Efrlqw8ytg4', + const text = postData.text ?? ''; + const lowerCase = text.toLowerCase(); + if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { + claimAchievement('iLoveMisskey'); + } + if ([ + 'https://youtu.be/Efrlqw8ytg4', + 'https://www.youtube.com/watch?v=Efrlqw8ytg4', + 'https://m.youtube.com/watch?v=Efrlqw8ytg4', - 'https://youtu.be/XVCwzwxdHuA', - 'https://www.youtube.com/watch?v=XVCwzwxdHuA', - 'https://m.youtube.com/watch?v=XVCwzwxdHuA', + 'https://youtu.be/XVCwzwxdHuA', + 'https://www.youtube.com/watch?v=XVCwzwxdHuA', + 'https://m.youtube.com/watch?v=XVCwzwxdHuA', - 'https://open.spotify.com/track/3Cuj0mZrlLoXx9nydNi7RB', - 'https://open.spotify.com/track/7anfcaNPQWlWCwyCHmZqNy', - 'https://open.spotify.com/track/5Odr16TvEN4my22K9nbH7l', - 'https://open.spotify.com/album/5bOlxyl4igOrp2DwVQxBco', - ].some(url => text.includes(url))) { - claimAchievement('brainDiver'); - } + 'https://open.spotify.com/track/3Cuj0mZrlLoXx9nydNi7RB', + 'https://open.spotify.com/track/7anfcaNPQWlWCwyCHmZqNy', + 'https://open.spotify.com/track/5Odr16TvEN4my22K9nbH7l', + 'https://open.spotify.com/album/5bOlxyl4igOrp2DwVQxBco', + ].some(url => text.includes(url))) { + claimAchievement('brainDiver'); + } - if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { - claimAchievement('selfQuote'); - } + if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { + claimAchievement('selfQuote'); + } - const date = new Date(); - const h = date.getHours(); - const m = date.getMinutes(); - const s = date.getSeconds(); - if (h >= 0 && h <= 3) { - claimAchievement('postedAtLateNight'); - } - if (m === 0 && s === 0) { - claimAchievement('postedAt0min0sec'); - } - }); - }).catch(err => { - posting = false; - os.alert({ - type: 'error', - text: err.message + '\n' + (err as any).id, - }); - }); + const date = new Date(); + const h = date.getHours(); + const m = date.getMinutes(); + const s = date.getSeconds(); + if (h >= 0 && h <= 3) { + claimAchievement('postedAtLateNight'); + } + if (m === 0 && s === 0) { + claimAchievement('postedAt0min0sec'); + } + }); + }).catch(err => { + posting = false; + os.alert({ + type: 'error', + text: err.message + '\n' + (err as any).id, + }); + }); } function cancel() { - emit('cancel'); + emit('cancel'); } function insertMention() { - os.selectUser().then(user => { - insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' '); - }); + os.selectUser().then(user => { + insertTextAtCursor(textareaEl, '@' + Misskey.acct.toString(user) + ' '); + }); } async function insertEmoji(ev: MouseEvent) { - os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); + os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); } function showActions(ev) { - os.popupMenu(postFormActions.map(action => ({ - text: action.title, - action: () => { - action.handler({ - text: text, - }, (key, value) => { - if (key === 'text') { text = value; } - }); - }, - })), ev.currentTarget ?? ev.target); + os.popupMenu(postFormActions.map(action => ({ + text: action.title, + action: () => { + action.handler({ + text: text, + }, (key, value) => { + if (key === 'text') { + text = value; + } + }); + }, + })), ev.currentTarget ?? ev.target); } let postAccount = $ref<Misskey.entities.UserDetailed | null>(null); function openAccountMenu(ev: MouseEvent) { - openAccountMenu_({ - withExtraOperation: false, - includeCurrentAccount: true, - active: postAccount != null ? postAccount.id : $i.id, - onChoose: (account) => { - if (account.id === $i.id) { - postAccount = null; - } else { - postAccount = account; - } - }, - }, ev); + openAccountMenu_({ + withExtraOperation: false, + includeCurrentAccount: true, + active: postAccount != null ? postAccount.id : $i.id, + onChoose: (account) => { + if (account.id === $i.id) { + postAccount = null; + } else { + postAccount = account; + } + }, + }, ev); } onMounted(() => { - if (props.autofocus) { - focus(); + if (props.autofocus) { + focus(); - nextTick(() => { - focus(); - }); - } + nextTick(() => { + focus(); + }); + } - // TODO: detach when unmount - new Autocomplete(textareaEl, $$(text)); - new Autocomplete(cwInputEl, $$(cw)); - new Autocomplete(hashtagsInputEl, $$(hashtags)); + // TODO: detach when unmount + new Autocomplete(textareaEl, $$(text)); + new Autocomplete(cwInputEl, $$(cw)); + new Autocomplete(hashtagsInputEl, $$(hashtags)); - nextTick(() => { - // 書きかけの投稿を復元 - if (!props.instant && !props.mention && !props.specified) { - const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey]; - 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); - if (draft.data.poll) { - poll = draft.data.poll; - } - } - } + nextTick(() => { + // 書きかけの投稿を復元 + if (!props.instant && !props.mention && !props.specified) { + const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey]; + 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); + if (draft.data.poll) { + poll = draft.data.poll; + } + } + } - // 削除して編集 - if (props.initialNote) { - const init = props.initialNote; - text = init.text ? init.text : ''; - files = init.files; - cw = init.cw; - useCw = init.cw != null; - if (init.poll) { - poll = { - 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; - } + // 削除して編集 + if (props.initialNote) { + const init = props.initialNote; + text = init.text ? init.text : ''; + files = init.files; + cw = init.cw; + useCw = init.cw != null; + if (init.poll) { + poll = { + 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; + } - nextTick(() => watchForDraft()); - }); + nextTick(() => watchForDraft()); + }); }); defineExpose({ - clear, + clear, }); </script> <style lang="scss" module> .root { - position: relative; - container-type: inline-size; + position: relative; + container-type: inline-size; - &.modal { - width: 100%; - max-width: 520px; - } + &.modal { + width: 100%; + max-width: 520px; + } } //#region header .header { - z-index: 1000; - min-height: 50px; - display: flex; - flex-wrap: nowrap; - gap: 4px; + z-index: 1000; + min-height: 50px; + display: flex; + flex-wrap: nowrap; + gap: 4px; } .headerLeft { - display: flex; - flex: 0 1 100px; + display: flex; + flex: 0 1 100px; } .cancel { - padding: 0; - font-size: 1em; - height: 100%; - flex: 0 1 50px; + padding: 0; + font-size: 1em; + height: 100%; + flex: 0 1 50px; } .account { - height: 100%; - display: inline-flex; - vertical-align: bottom; - flex: 0 1 50px; + height: 100%; + display: inline-flex; + vertical-align: bottom; + flex: 0 1 50px; } .avatar { - width: 28px; - height: 28px; - margin: auto; + width: 28px; + height: 28px; + margin: auto; } .headerRight { - display: flex; - min-height: 48px; - font-size: 0.9em; - flex-wrap: nowrap; - align-items: center; - margin-left: auto; - gap: 4px; - overflow: clip; - padding-left: 4px; + display: flex; + min-height: 48px; + font-size: 0.9em; + flex-wrap: nowrap; + align-items: center; + margin-left: auto; + gap: 4px; + overflow: clip; + padding-left: 4px; } .submit { - margin: 12px 12px 12px 6px; - vertical-align: bottom; + margin: 12px 12px 12px 6px; + vertical-align: bottom; - &:disabled { - opacity: 0.7; - } + &:disabled { + opacity: 0.7; + } - &.posting { - cursor: wait; - } + &.posting { + cursor: wait; + } - &:not(:disabled):hover { - > .inner { - background: linear-gradient(90deg, var(--X8), var(--X8)); - } - } + &:not(:disabled):hover { + > .inner { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } + } - &:not(:disabled):active { - > .inner { - background: linear-gradient(90deg, var(--X8), var(--X8)); - } - } + &:not(:disabled):active { + > .inner { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } + } } .submitInner { - padding: 0 12px; - line-height: 34px; - font-weight: bold; - border-radius: 6px; - min-width: 90px; - box-sizing: border-box; - color: var(--fgOnAccent); - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + padding: 0 12px; + line-height: 34px; + font-weight: bold; + border-radius: 6px; + min-width: 90px; + box-sizing: border-box; + color: var(--fgOnAccent); + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + + &.gamingLight { + + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + &.gamingDark{ + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; + } } .headerRightItem { - margin: 0; - padding: 8px; - border-radius: 6px; + margin: 0; + padding: 8px; + border-radius: 6px; - &:hover { - background: var(--X5); - } + &:hover { + background: var(--X5); + } - &:disabled { - background: none; - } + &:disabled { + background: none; + } - &.danger { - color: #ff2a2a; - } + &.danger { + color: #ff2a2a; + } } .headerRightButtonText { - padding-left: 6px; + padding-left: 6px; } .visibility { - overflow: clip; - text-overflow: ellipsis; - white-space: nowrap; + overflow: clip; + text-overflow: ellipsis; + white-space: nowrap; - &:enabled { - > .headerRightButtonText { - opacity: 0.8; - } - } + &:enabled { + > .headerRightButtonText { + opacity: 0.8; + } + } } + //#endregion .preview { - padding: 16px 20px 0 20px; - max-height: 150px; - overflow: auto; + padding: 16px 20px 0 20px; + max-height: 150px; + overflow: auto; } .targetNote { - padding: 0 20px 16px 20px; + padding: 0 20px 16px 20px; } .withQuote { - margin: 0 0 8px 0; - color: var(--accent); + margin: 0 0 8px 0; + color: var(--accent); } .toSpecified { - padding: 6px 24px; - margin-bottom: 8px; - overflow: auto; - white-space: nowrap; + padding: 6px 24px; + margin-bottom: 8px; + overflow: auto; + white-space: nowrap; } .visibleUsers { - display: inline; - top: -1px; - font-size: 14px; + display: inline; + top: -1px; + font-size: 14px; } .visibleUser { - margin-right: 14px; - padding: 8px 0 8px 8px; - border-radius: 8px; - background: var(--X4); + margin-right: 14px; + padding: 8px 0 8px 8px; + border-radius: 8px; + background: var(--X4); } .hasNotSpecifiedMentions { - margin: 0 20px 16px 20px; + margin: 0 20px 16px 20px; } .cw, .hashtags, .text { - display: block; - box-sizing: border-box; - padding: 0 24px; - margin: 0; - width: 100%; - font-size: 16px; - border: none; - border-radius: 0; - background: transparent; - color: var(--fg); - font-family: inherit; + display: block; + box-sizing: border-box; + padding: 0 24px; + margin: 0; + width: 100%; + font-size: 16px; + border: none; + border-radius: 0; + background: transparent; + color: var(--fg); + font-family: inherit; - &:focus { - outline: none; - } + &:focus { + outline: none; + } - &:disabled { - opacity: 0.5; - } + &:disabled { + opacity: 0.5; + } } .cw { - z-index: 1; - padding-bottom: 8px; - border-bottom: solid 0.5px var(--divider); + z-index: 1; + padding-bottom: 8px; + border-bottom: solid 0.5px var(--divider); } .hashtags { - z-index: 1; - padding-top: 8px; - padding-bottom: 8px; - border-top: solid 0.5px var(--divider); + z-index: 1; + padding-top: 8px; + padding-bottom: 8px; + border-top: solid 0.5px var(--divider); } .textOuter { - width: 100%; - position: relative; + width: 100%; + position: relative; - &.withCw { - padding-top: 8px; - } + &.withCw { + padding-top: 8px; + } } .text { - max-width: 100%; - min-width: 100%; - width: 100%; - min-height: 90px; - height: 100%; + max-width: 100%; + min-width: 100%; + width: 100%; + min-height: 90px; + height: 100%; } .textCount { - position: absolute; - top: 0; - right: 2px; - padding: 4px 6px; - font-size: .9em; - color: var(--warn); - border-radius: 6px; - min-width: 1.6em; - text-align: center; + position: absolute; + top: 0; + right: 2px; + padding: 4px 6px; + font-size: .9em; + color: var(--warn); + border-radius: 6px; + min-width: 1.6em; + text-align: center; - &.textOver { - color: #ff2a2a; - } + &.textOver { + color: #ff2a2a; + } } .footer { - display: flex; - padding: 0 16px 16px 16px; - font-size: 1em; + display: flex; + padding: 0 16px 16px 16px; + font-size: 1em; } .footerLeft { - flex: 1; - display: grid; - grid-auto-flow: row; - grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); - grid-auto-rows: 40px; + flex: 1; + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); + grid-auto-rows: 40px; } .footerRight { - flex: 0; - margin-left: auto; - display: grid; - grid-auto-flow: row; - grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); - grid-auto-rows: 40px; - direction: rtl; + flex: 0; + margin-left: auto; + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); + grid-auto-rows: 40px; + direction: rtl; } .footerButton { - display: inline-block; - padding: 0; - margin: 0; - font-size: 1em; - width: auto; - height: 100%; - border-radius: 6px; + display: inline-block; + padding: 0; + margin: 0; + font-size: 1em; + width: auto; + height: 100%; + border-radius: 6px; - &:hover { - background: var(--X5); - } + &:hover { + background: var(--X5); + } - &.footerButtonActive { - color: var(--accent); - } + &.footerButtonActive { + color: var(--accent); + } } .previewButtonActive { - color: var(--accent); + color: var(--accent); } @container (max-width: 500px) { - .headerRight { - font-size: .9em; - } + .headerRight { + font-size: .9em; + } - .headerRightButtonText { - display: none; - } + .headerRightButtonText { + display: none; + } - .visibility { - overflow: initial; - } + .visibility { + overflow: initial; + } - .submit { - margin: 8px 8px 8px 4px; - } + .submit { + margin: 8px 8px 8px 4px; + } - .toSpecified { - padding: 6px 16px; - } + .toSpecified { + padding: 6px 16px; + } - .preview { - padding: 16px 14px 0 14px; - } - .cw, - .hashtags, - .text { - padding: 0 16px; - } + .preview { + padding: 16px 14px 0 14px; + } - .text { - min-height: 80px; - } + .cw, + .hashtags, + .text { + padding: 0 16px; + } - .footer { - padding: 0 8px 8px 8px; - } + .text { + min-height: 80px; + } + + .footer { + padding: 0 8px 8px 8px; + } } @container (max-width: 350px) { - .footer { - font-size: 0.9em; - } + .footer { + font-size: 0.9em; + } - .footerLeft { - grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); - } + .footerLeft { + grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); + } - .footerRight { - grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); - } + .footerRight { + grid-template-columns: repeat(auto-fill, minmax(38px, 1fr)); + } - .headerRight { - gap: 0; - } + .headerRight { + gap: 0; + } + @-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } + @keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } } </style> diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue index a7e91acc39..9cbcb82bcb 100644 --- a/packages/frontend/src/components/MkSwitch.button.vue +++ b/packages/frontend/src/components/MkSwitch.button.vue @@ -9,19 +9,55 @@ SPDX-License-Identifier: AGPL-3.0-only :class="{ [$style.button]: true, [$style.buttonChecked]: checked, - [$style.buttonDisabled]: props.disabled + [$style.buttonDisabled]: props.disabled, + [$style.gamingDark]: gaming === 'dark' && checked, + [$style.gamingLight]: gaming === 'light' && checked }" data-cy-switch-toggle @click.prevent.stop="toggle" > - <div :class="{ [$style.knob]: true, [$style.knobChecked]: checked }"></div> + <div + :class="{ [$style.knob]: true, [$style.knobChecked]: checked, [$style.gamingDark]: gaming === 'dark' && checked,[$style.gamingLight]: gaming === 'light' && checked}"></div> </span> </template> <script lang="ts" setup> -import { toRefs, Ref } from 'vue'; -import { i18n } from '@/i18n.js'; +import {toRefs, Ref, ref, computed, watch} from 'vue'; +import {i18n} from '@/i18n.js'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ checked: boolean | Ref<boolean>; disabled?: boolean; @@ -56,13 +92,40 @@ const toggle = () => { cursor: pointer; transition: inherit; user-select: none; + + &.gamingLight { + border-image: conic-gradient(#e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd) 1; + border: solid 1px; + } + + &.gamingDark { + border-image: conic-gradient(#c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4) 1; + border: solid 1px; + } } .buttonChecked { background-color: var(--switchOnBg) !important; border-color: var(--switchOnBg) !important; + + &.gamingLight { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + + &.gamingDark { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } } + .buttonDisabled { cursor: not-allowed; } @@ -78,11 +141,92 @@ const toggle = () => { &:not(.knobChecked) { left: 3px; background: var(--switchOffFg); + } } .knobChecked { left: 12px; background: var(--switchOnFg); + + &.gamingDark { + background: white !important; + } + + &.gamingLight { + background: white !important; + } +} + +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } </style> diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 8e946e7437..4a14121c70 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked }]"> +<div :class="[$style.root, { [$style.disabled]: disabled && gaming === '', [$style.checked]: checked && gaming === '' , [$style.gamingdarkDisabled]: disabled && gaming === 'dark', [$style.gamingLightDisabled]: disabled && gaming === 'light'}]"> <input ref="input" type="checkbox" @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only @keydown.enter="toggle" > <XButton :checked="checked" :disabled="disabled" @toggle="toggle"/> - <span :class="$style.body"> + <span :class="$style.body,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}"> <!-- TODO: 無名slotの方は廃止 --> <span :class="$style.label"> <span @click="toggle"> @@ -27,8 +27,42 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { toRefs, Ref } from 'vue'; +import {toRefs, Ref, ref, computed, watch} from 'vue'; import XButton from '@/components/MkSwitch.button.vue'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); + +// gaming.valueに新しい値を代入する +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = defineProps<{ modelValue: boolean | Ref<boolean>; @@ -65,6 +99,26 @@ const toggle = () => { cursor: not-allowed; } + &.gamingDarkDisabled{ + opacity: 0.6; + cursor: not-allowed; + background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + + } + &.gamingLightDisabled{ + opacity: 0.6; + cursor: not-allowed; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + + } //&.checked { //} } @@ -82,6 +136,22 @@ const toggle = () => { display: block; transition: inherit; color: var(--fg); + &.gamingDark { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + + } + &.gamingLight{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + + } } .label { @@ -106,4 +176,6 @@ const toggle = () => { font-size: 85%; vertical-align: top; } + + </style> diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index e62967963f..52dcc05548 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div ref="tabHighlightEl" - :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value }]" + :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value , [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" ></div> </div> </template> @@ -53,9 +53,40 @@ export type Tab = { </script> <script lang="ts" setup> -import { onMounted, onUnmounted, watch, nextTick, shallowRef } from 'vue'; +import {onMounted, onUnmounted, watch, nextTick, shallowRef, ref, computed} from 'vue'; import { defaultStore } from '@/store.js'; +let gaming = ref(''); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ tabs?: Tab[]; tab?: string; @@ -245,9 +276,88 @@ onUnmounted(() => { border-radius: 999px; transition: none; pointer-events: none; - + &.gamingLight{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } + &.gamingDark{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } &.animate { transition: width 0.15s ease, left 0.15s ease; } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index ab4e7620dd..833d87bc71 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="700" :marginMin="16"> <div class="lxpfedzu"> <div class="banner"> - <img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> + <img :src="iconUrl" alt="" class="icon"/> </div> <MkInfo v-if="thereIsUnresolvedAbuseReport" warn class="info">{{ i18n.ts.thereIsUnresolvedAbuseReportWarning }} <MkA to="/admin/abuses" class="_link">{{ i18n.ts.check }}</MkA></MkInfo> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onActivated, onMounted, onUnmounted, provide, watch } from 'vue'; +import {computed, onActivated, onMounted, onUnmounted, provide, ref, watch} from 'vue'; import { i18n } from '@/i18n.js'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; @@ -37,6 +37,7 @@ import * as os from '@/os.js'; import { lookupUser } from '@/scripts/lookup-user.js'; import { useRouter } from '@/router.js'; import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; +import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store.js"; const isEmpty = (x: string | null) => x == null || x === ''; @@ -61,7 +62,20 @@ let noBotProtection = !instance.disableRegistration && !instance.enableHcaptcha let noEmailServer = !instance.enableEmail; let thereIsUnresolvedAbuseReport = $ref(false); let currentPage = $computed(() => router.currentRef.value.child); - +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +let iconUrl = ref(); +if (darkMode.value) { + iconUrl.value = iconDark; +} else { + iconUrl.value = iconLight; +} +watch(darkMode, () => { + if (darkMode.value) { + iconUrl.value = iconDark; + } else { + iconUrl.value = iconLight; + } +}) os.api('admin/abuse-user-reports', { state: 'unresolved', limit: 1, diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index e8cdcef741..d6105416bb 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -375,20 +375,20 @@ function more() { } &.gamingLight:hover, &.gamingLight.active { text-decoration: none; - color: black; + color: white; &.gamingLight:before { - content: ""; - display: block; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - margin: auto; - width: 52px; - aspect-ratio: 1/1; - border-radius: 100%; + content: ""; + display: block; + height: 100%; + aspect-ratio: 1; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index aecf0c1b92..e99abf79ec 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -283,6 +283,7 @@ function more(ev: MouseEvent) { } &.gamingLight:before { + color: white; content: ""; display: block; width: calc(100% - 38px); @@ -302,6 +303,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover, &.gamingLight.active { + color: white; &.gamingLight:before { background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); background-size: 1800% 1800% !important; @@ -312,6 +314,7 @@ function more(ev: MouseEvent) { } &.gamingDark:before { + color: white; content: ""; display: block; width: calc(100% - 38px); @@ -331,6 +334,7 @@ function more(ev: MouseEvent) { } &.gamingDark:hover, &.gamingDark.active { + color: white; &.gamingDark:before { background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); background-size: 1800% 1800% !important; @@ -470,7 +474,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover { - color: black; + color: white; background-size: 1800% 1800% !important; text-decoration: none; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; @@ -479,7 +483,7 @@ function more(ev: MouseEvent) { } &.gamingLight:active { - color: black; + color: white; background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; @@ -487,7 +491,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover, &.gamingLight.active { - color: black; + color: white; &.gamingLight:before { content: ""; display: block; @@ -618,6 +622,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover, &.gamingLight.active { + &.gamingLight:before { background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); background-size: 1800% 1800% !important; @@ -628,6 +633,7 @@ function more(ev: MouseEvent) { } &.gamingDark:before { + color: white; content: ""; display: block; position: absolute; @@ -647,6 +653,7 @@ function more(ev: MouseEvent) { } &.gamingDark:hover, &.gamingDark.active { + color: white; &.gamingDark:before { background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); background-size: 1800% 1800% !important; @@ -793,71 +800,71 @@ function more(ev: MouseEvent) { animation: blink 1s infinite; } - @-webkit-keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + +} +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% } - @-moz-keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 50% { + background-position: 100% 50% } - @keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 100% { + background-position: 0% 50% } - @-webkit-keyframes AnimationDark { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% } - @-moz-keyframes AnimationDark { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 50% { + background-position: 100% 50% } - @keyframes AnimationDark { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% } } </style> From 13de10463410e2ff7ea68846d83eaca28f9323f8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 09:01:20 +0900 Subject: [PATCH 058/501] =?UTF-8?q?feat:=20=E3=82=B2=E3=83=BC=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=83=A2=E3=83=BC=E3=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/notes/create.ts | 5 +- packages/backend/src/server/web/style.css | 73 ++- packages/frontend/src/components/MkButton.vue | 62 +- .../src/components/MkChannelFollowButton.vue | 155 ++++- .../src/components/MkFollowButton.vue | 102 +-- .../frontend/src/components/MkLaunchPad.vue | 117 +++- .../frontend/src/components/MkMention.vue | 119 +++- packages/frontend/src/components/MkMenu.vue | 207 ++++++- .../src/components/MkNoteDetailed.vue | 83 ++- packages/frontend/src/components/MkRadio.vue | 141 ++++- packages/frontend/src/components/MkRange.vue | 146 ++++- .../components/MkReactionsViewer.reaction.vue | 123 +++- .../src/components/MkSignupDialog.form.vue | 579 +++++++++++------- .../src/components/MkSignupDialog.rules.vue | 128 +++- .../frontend/src/components/MkSuperMenu.vue | 121 +++- .../src/components/MkSwitch.button.vue | 288 ++++----- packages/frontend/src/components/MkTab.vue | 116 +++- .../src/components/MkUserSetupDialog.vue | 115 +++- .../src/components/MkVisitorDashboard.vue | 75 ++- .../src/components/global/MkLoading.vue | 2 +- .../components/global/MkPageHeader.tabs.vue | 17 +- .../frontend/src/pages/explore.featured.vue | 2 +- .../frontend/src/pages/welcome.entrance.a.vue | 75 ++- packages/frontend/src/style.scss | 8 + .../src/ui/_common_/navbar-for-mobile.vue | 480 ++++++++------- packages/frontend/src/ui/_common_/navbar.vue | 284 +++++---- packages/frontend/src/ui/universal.vue | 24 +- .../frontend/src/ui/universal.widgets.vue | 135 +++- 28 files changed, 2917 insertions(+), 865 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 8deb7ebf38..519243c688 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -244,10 +244,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } else if (reply.renoteId && !reply.text && !reply.fileIds && !reply.hasPoll) { throw new ApiError(meta.errors.cannotReplyToPureRenote); } - // ノートがリプライでパブリック投稿の場合はホームにする - if (ps.visibility != 'home' && ps.visibility!== 'followers' && ps.visibility!=='specified' ){ - visibility = 'home'; - } + // Check blocking if (reply.userId !== me.id) { const blockExist = await this.blockingsRepository.exist({ diff --git a/packages/backend/src/server/web/style.css b/packages/backend/src/server/web/style.css index 952be9bf0b..6919bf39aa 100644 --- a/packages/backend/src/server/web/style.css +++ b/packages/backend/src/server/web/style.css @@ -45,7 +45,13 @@ html { width: 28px; height: 28px; transform: translateY(70px); - color: var(--accent); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; } #splashSpinner > .spinner { position: absolute; @@ -74,3 +80,68 @@ html { transform: rotate(360deg); } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 993cf7a39e..82b94140aa 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -20,8 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike, - [$style.gamingDark]: gaming === 'dark', // gamingが1の場合にgamingDarkクラスを追加 - [$style.gamingLight]: gaming === 'light', // gamingが2の場合にgamingLightクラスを追加 + [$style.gamingDark]: gaming === 'dark', + [$style.gamingLight]: gaming === 'light', } ]" :type="type" @@ -50,8 +50,8 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike, - [$style.gamingDark]: gaming === 'dark', // gamingが1の場合にgamingDarkクラスを追加 - [$style.gamingLight]: gaming === 'light', // gamingが2の場合にgamingLightクラスを追加 + [$style.gamingDark]: gaming === 'dark', + [$style.gamingLight]: gaming === 'light', } ]" :to="to" @@ -96,18 +96,18 @@ const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); let gaming = ref(''); // 0-off , 1-dark , 2-light // gaming.valueに新しい値を代入する -if (darkMode.value && gamingMode.value && props.primary) { +if (darkMode.value && gamingMode.value && props.primary || props.gradate ) { gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value && props.primary) { +} else if (!darkMode.value && gamingMode.value && props.primary|| props.gradate ) { gaming.value = 'light'; }else{ gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value && props.primary) { + if (darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary) { + } else if (!darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { gaming.value = 'light'; }else{ gaming.value = ''; @@ -115,9 +115,9 @@ watch(darkMode, () => { }) watch(gamingMode, () => { - if (darkMode.value && gamingMode.value && props.primary) { + if (darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary) { + } else if (!darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { gaming.value = 'light'; }else{ gaming.value = ''; @@ -290,53 +290,53 @@ function onMousedown(evt: MouseEvent): void { } &.gamingLight { - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + color: white !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; &:not(:disabled):hover { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + color: white !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &:not(:disabled):active { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; - animation: AnimationLight 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + color: white !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; } } &.gamingDark { - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - + color: white !important; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; &:not(:disabled):hover { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% ; - + color: white !important; -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; } &:not(:disabled):active { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - + color: white !important; -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue index 41b02a7e3f..cf485e7892 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.vue +++ b/packages/frontend/src/components/MkChannelFollowButton.vue @@ -6,28 +6,29 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <button class="_button" - :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing, [$style.full]: full }]" + :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing, [$style.full]: full },[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]]" :disabled="wait" @click="onClick" > <template v-if="!wait"> <template v-if="isFollowing"> - <span v-if="full" :class="$style.text">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> + <span v-if="full" :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> </template> <template v-else> - <span v-if="full" :class="$style.text">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> + <span v-if="full" :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> </template> </template> <template v-else> - <span v-if="full" :class="$style.text">{{ i18n.ts.processing }}</span><MkLoading :em="true"/> + <span v-if="full" :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ i18n.ts.processing }}</span><MkLoading :em="true"/> </template> </button> </template> <script lang="ts" setup> -import { ref } from 'vue'; +import {computed, ref, watch} from 'vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; +import {defaultStore} from "@/store.js"; const props = withDefaults(defineProps<{ channel: Record<string, any>; @@ -36,6 +37,39 @@ const props = withDefaults(defineProps<{ full: false, }); + +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const isFollowing = ref<boolean>(props.channel.isFollowing); const wait = ref(false); @@ -75,7 +109,28 @@ async function onClick() { font-size: 16px; border-radius: 32px; background: #fff; + &.gamingDark { + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + &.gamingLight { + color: #fff; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + + } &.full { padding: 0 8px 0 12px; font-size: 14px; @@ -107,25 +162,83 @@ async function onClick() { //background: mix($primary, #fff, 40); } - &.active { - color: var(--fgOnAccent); - background: var(--accent); + &.active { + color: var(--fgOnAccent); + background: var(--accent); - &:hover { - background: var(--accentLighten); - border-color: var(--accentLighten); - } + &:hover { + background: var(--accentLighten); + border-color: var(--accentLighten); + } - &:active { - background: var(--accentDarken); - border-color: var(--accentDarken); - } - } + &:active { + background: var(--accentDarken); + border-color: var(--accentDarken); + } - &.wait { - cursor: wait !important; - opacity: 0.7; - } + &.gamingDark:hover { + -webkit-text-fill-color: unset; + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingDark:active { + -webkit-text-fill-color: unset !important; + color: white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + border-color: white; + } + + &.gamingLight:hover { + -webkit-text-fill-color: unset !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + border-color: white; + } + + &.gamingLight:active { + -webkit-text-fill-color: unset !important; + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + border-color: white; + } + + &.gamingDark { + -webkit-text-fill-color: unset !important; + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingLight { + -webkit-text-fill-color: unset !important; + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + + } + + &.wait { + cursor: wait !important; + opacity: 0.7; + } + } } .text { diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index 11c36b8034..f64e0da737 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -77,7 +77,6 @@ if (darkMode.value && gamingMode.value == true) { } watch(darkMode, () => { - console.log(gaming) if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -198,6 +197,27 @@ onBeforeUnmount(() => { border-radius: 32px; background: #fff; + &.gamingDark { + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + + &.gamingLight { + color: #dee7e4; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + + } &.full { padding: 0 8px 0 12px; font-size: 14px; @@ -250,56 +270,60 @@ onBeforeUnmount(() => { } &.gamingDark:hover { - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-text-fill-color: unset; + color: white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:active { + -webkit-text-fill-color: unset !important; color: white; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; border-color: white; } &.gamingLight:hover { - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + -webkit-text-fill-color: unset !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border-color: white; } &.gamingLight:active { + -webkit-text-fill-color: unset !important; color: white; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border-color: white; } &.gamingDark { - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-text-fill-color: unset !important; + color: white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight { - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-text-fill-color: unset !important; + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } @@ -315,23 +339,13 @@ onBeforeUnmount(() => { margin-right: 6px; &.gamingDark { - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + color: black; + -webkit-text-fill-color: unset !important; } &.gamingLight { - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + color: white; + -webkit-text-fill-color: unset !important; } } diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index 321acc0fbc..c6684ccb87 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -8,12 +8,12 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }"> <div class="main"> <template v-for="item in items"> - <button v-if="item.action" v-click-anime class="_button item" @click="$event => { item.action($event); close(); }"> + <button v-if="item.action" v-click-anime class="_button item" :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }" @click="$event => { item.action($event); close(); }"> <i class="icon" :class="item.icon"></i> <div class="text">{{ item.text }}</div> <span v-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span> </button> - <MkA v-else v-click-anime :to="item.to" class="item" @click.passive="close()"> + <MkA v-else v-click-anime :to="item.to" class="item" :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }" @click.passive="close()"> <i class="icon" :class="item.icon"></i> <div class="text">{{ item.text }}</div> <span v-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span> @@ -25,12 +25,43 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref , computed , watch} from 'vue'; import MkModal from '@/components/MkModal.vue'; import { navbarItemDef } from '@/navbar'; import { defaultStore } from '@/store.js'; import { deviceKind } from '@/scripts/device-kind.js'; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ src?: HTMLElement; anchor?: { x: string; y: string; }; @@ -98,7 +129,20 @@ function close() { vertical-align: bottom; height: 100px; border-radius: 10px; - + &.gamingDark:hover{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + color: black; + } + &.gamingLight:hover{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + color: white; + } &:hover { color: var(--accent); background: var(--accentedBg); @@ -132,4 +176,69 @@ function close() { } } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index ffc9ca3f4d..d877565a33 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe }]" :to="url" :style="{ background: bgCss }"> +<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe && gaming === '' , [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :to="url" :style="{ background: bgCss }"> <img :class="$style.icon" :src="`/avatar/@${username}@${host}`" alt=""> <span> <span>@{{ username }}</span> @@ -15,12 +15,43 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { toUnicode } from 'punycode'; +import {computed, ref, watch} from 'vue'; import { } from 'vue'; import tinycolor from 'tinycolor2'; import { host as localHost } from '@/config.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; +let gaming = ref(''); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = defineProps<{ username: string; host: string; @@ -36,7 +67,8 @@ const isMe = $i && ( const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(isMe ? '--mentionMe' : '--mention')); bg.setAlpha(0.1); -const bgCss = bg.toRgbString(); +const bgCss = (gaming.value === '') ? bg.toRgbString() : ""; +//const bgCss = `background:${bg.toRgbString()}; ${result}` ; </script> <style lang="scss" module> @@ -46,8 +78,26 @@ const bgCss = bg.toRgbString(); border-radius: 999px; color: var(--mention); + &.gamingLight{ + color: white; + opacity: 0.9; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } + &.gamingDark{ + opacity: 0.9; + color: white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + &.isMe { color: var(--mentionMe); + } } @@ -63,4 +113,69 @@ const bgCss = bg.toRgbString(); .host { opacity: 0.5; } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index ec8383fe84..2d123385f5 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -14,44 +14,44 @@ SPDX-License-Identifier: AGPL-3.0-only > <template v-for="(item, i) in items2"> <div v-if="item === null" role="separator" :class="$style.divider"></div> - <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]"> + <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> <span>{{ item.text }}</span> </span> - <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]"> + <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> <span><MkEllipsis/></span> </span> - <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </MkA> - <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </a> - <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </button> - <button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } ]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } , { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)" /> <span :class="$style.switchText">{{ item.text }}</span> </button> - <div v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)"> + <div v-else-if="item.type === 'parent'" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item } , { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <span>{{ item.text }}</span> <span :class="$style.caret"><i class="ti ti-chevron-right ti-fw"></i></span> </div> - <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> - <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> + <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </button> </template> - <span v-if="items2.length === 0" :class="[$style.none, $style.item]"> + <span v-if="items2.length === 0" :class="[$style.none, $style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> <span>{{ i18n.ts.none }}</span> </span> </div> @@ -62,14 +62,51 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts"> -import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; +import {Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch, computed} from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus.js'; import MkSwitchButton from '@/components/MkSwitch.button.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { isTouchUsing } from '@/scripts/touch.js'; +import {defaultStore} from '@/store.js' +let gaming = ref(''); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + document.documentElement.style.setProperty('--accent', '#fff'); + document.documentElement.style.setProperty('--accentLighten', '#fff'); +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + document.documentElement.style.setProperty('--accent', '#fff'); + document.documentElement.style.setProperty('--accentLighten', '#fff'); + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + document.documentElement.style.setProperty('--accent', '#fff'); + document.documentElement.style.setProperty('--accentLighten', '#fff'); + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const childrenCache = new WeakMap<MenuParent, MenuItem[]>(); </script> @@ -294,10 +331,32 @@ onBeforeUnmount(() => { &:not(:disabled):hover { color: var(--accent); text-decoration: none; + &:before { + background: var(--accentedBg); + } + &.gamingDark{ + color:black !important; + } + &.gamingLight{ + color:white !important; + } + &.gamingDark:before{ + color:black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + + } + &.gamingLight:before{ + color:white !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + + } - &:before { - background: var(--accentedBg); - } } &.danger { @@ -322,12 +381,32 @@ onBeforeUnmount(() => { &:active, &.active { - color: var(--fgOnAccent) !important; + color: var(--fgOnAccent); opacity: 1; - + &.gamingDark{ + color:black !important; + } + &.gamingLight{ + color:white !important; + } &:before { - background: var(--accent) !important; + background: var(--accent); } + &.gamingDark:before{ + color:black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingLight:before{ + color:white !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } } &:not(:active):focus-visible { @@ -364,9 +443,32 @@ onBeforeUnmount(() => { color: var(--accent); text-decoration: none; - &:before { - background: var(--accentedBg); - } + &:before { + background: var(--accentedBg); + } + &.gamingDark{ + color:black !important; + } + &.gamingLight{ + color:white !important; + } + &.gamingDark:before{ + color:black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingLight:before{ + color:white !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + + } + } } } @@ -429,4 +531,69 @@ onBeforeUnmount(() => { margin: 8px 0; border-top: solid 0.5px var(--divider); } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 0bcf98cca2..cab62c9308 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -131,9 +131,9 @@ SPDX-License-Identifier: AGPL-3.0-only </footer> </article> <div :class="$style.tabs"> - <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' }]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button> - <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes' }]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button> - <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions' }]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button> + <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'replies'}]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button> + <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'renotes'}]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button> + <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'reactions'}]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button> </div> <div> <div v-if="tab === 'replies'" :class="$style.tab_replies"> @@ -180,7 +180,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, inject, onMounted, ref, shallowRef } from 'vue'; +import {computed, inject, onMounted, ref, shallowRef, watch} from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; @@ -223,6 +223,39 @@ const inChannel = inject('inChannel', null); let note = $ref(deepClone(props.note)); + +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { @@ -730,6 +763,9 @@ function loadConversation() { .tabActive { border-bottom: solid 2px var(--accent); + &.gamingLight{ + border-bottom: solid 2px black; + } } .tab_renotes { @@ -755,6 +791,12 @@ function loadConversation() { .reactionTabActive { border-color: var(--accent); + &.gamingLight{ + border-bottom: solid 2px black; + } + &.gamingDark{ + border-bottom: solid 2px black; + } } @container (max-width: 500px) { @@ -808,4 +850,37 @@ function loadConversation() { text-align: center; opacity: 0.7; } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index c4df3e991b..18195a0067 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div v-adaptive-border - :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked }]" + :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked ,[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' } ]" :aria-checked="checked" :aria-disabled="disabled" @click="toggle" @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only :disabled="disabled" :class="$style.input" > - <span :class="$style.button"> + <span :class="[$style.button , {[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> <span></span> </span> <span :class="$style.label"><slot></slot></span> @@ -24,8 +24,40 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import { ref,computed,watch } from 'vue'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = defineProps<{ modelValue: any; value: any; @@ -74,14 +106,48 @@ function toggle(): void { border-color: var(--accentedBg) !important; color: var(--accent); cursor: default !important; - + &.gamingDark{ + color:black !important; + border-color: black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + &.gamingLight{ + color:white; + border-color: white !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + } > .button { border-color: var(--accent); - + &.gamingDark{ + border-color:black; + color:black !important; + } + &.gamingLight{ + border-color: white; + color:white; + } + &.gamingDark:after{ + background-color: black; + transform: scale(1); + opacity: 1; + } + &.gamingLight:after{ + background-color:white !important; + transform: scale(1); + opacity: 1; + } &:after { background-color: var(--accent); transform: scale(1); opacity: 1; + } } } @@ -125,4 +191,69 @@ function toggle(): void { line-height: 20px; cursor: pointer; } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 2cfc27ceee..4322eb2dfb 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -9,12 +9,12 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-adaptive-border class="body"> <div ref="containerEl" class="container"> <div class="track"> - <div class="highlight" :style="{ width: (steppedRawValue * 100) + '%' }"></div> + <div :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light'}" class="highlight" :style="{ width: (steppedRawValue * 100) + '%' }"></div> </div> <div v-if="steps && showTicks" class="ticks"> <div v-for="i in (steps + 1)" class="tick" :style="{ left: (((i - 1) / steps) * 100) + '%' }"></div> </div> - <div ref="thumbEl" v-tooltip="textConverter(finalValue)" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div> + <div ref="thumbEl" v-tooltip="textConverter(finalValue)" :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light'}" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div> </div> </div> <div class="caption"><slot name="caption"></slot></div> @@ -24,7 +24,41 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch, shallowRef } from 'vue'; import * as os from '@/os.js'; +import {defaultStore} from "@/store.js"; + +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ modelValue: number; disabled?: boolean; @@ -207,6 +241,20 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { height: 100%; background: var(--accent); opacity: 0.5; + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + &.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } } } @@ -239,9 +287,36 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { cursor: grab; background: var(--accent); border-radius: 999px; - + &.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } &:hover { background: var(--accentLighten); + &.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } } } } @@ -263,4 +338,69 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { } } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index d0db515219..d9c8a8a86c 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -8,16 +8,16 @@ SPDX-License-Identifier: AGPL-3.0-only ref="buttonEl" v-ripple="canToggle" class="_button" - :class="[$style.root, { [$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" + :class="[$style.root, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' ,[$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" @click="toggleReaction()" > <MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/> - <span :class="$style.count">{{ count }}</span> + <span :class="[$style.count,{ [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ count }}</span> </button> </template> <script lang="ts" setup> -import { computed, onMounted, shallowRef, watch } from 'vue'; +import {computed, onMounted, ref, shallowRef, watch} from 'vue'; import * as Misskey from 'misskey-js'; import XDetails from '@/components/MkReactionsViewer.details.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; @@ -29,6 +29,37 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = defineProps<{ reaction: string; count: number; @@ -160,8 +191,29 @@ useTooltip(buttonEl, async (showing) => { color: var(--accent); box-shadow: 0 0 0px 1px var(--accent) inset; + &.gamingDark{ + color:white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + box-shadow: 0 0 0px 1px white inset; + } + + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + box-shadow: 0 0 0px 1px white inset; + color: white !important; + } + > .count { color: var(--accent); + &.gamingLight{ + color: white; + } } > .icon { @@ -179,4 +231,69 @@ useTooltip(buttonEl, async (showing) => { line-height: 42px; margin: 0 0 0 4px; } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index a67251eda1..14cb91c85c 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -4,99 +4,166 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <div :class="$style.banner"> - <i class="ti ti-user-edit"></i> - </div> - <MkSpacer :marginMin="20" :marginMax="32"> - <form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit"> - <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> - <template #label>{{ i18n.ts.invitationCode }}</template> - <template #prefix><i class="ti ti-key"></i></template> - </MkInput> - <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" autocomplete="username" required data-cy-signup-username @update:modelValue="onChangeUsername"> - <template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="ti ti-help-circle"></i></div></template> - <template #prefix>@</template> - <template #suffix>@{{ host }}</template> - <template #caption> - <div><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.cannotBeChangedLater }}</div> - <span v-if="usernameState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span> - <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span> - <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span> - <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span> - <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span> - <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooShort }}</span> - <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span> - </template> - </MkInput> - <MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail"> - <template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="ti ti-help-circle"></i></div></template> - <template #prefix><i class="ti ti-mail"></i></template> - <template #caption> - <span v-if="emailState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span> - <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span> - <span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span> - <span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span> - <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span> - <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span> - <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span> - <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span> - <span v-else-if="emailState === 'error'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span> - </template> - </MkInput> - <MkInput v-model="password" type="password" autocomplete="new-password" required data-cy-signup-password @update:modelValue="onChangePassword"> - <template #label>{{ i18n.ts.password }}</template> - <template #prefix><i class="ti ti-lock"></i></template> - <template #caption> - <span v-if="passwordStrength == 'low'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.weakPassword }}</span> - <span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.normalPassword }}</span> - <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span> - </template> - </MkInput> - <MkInput v-model="retypedPassword" type="password" autocomplete="new-password" required data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype"> - <template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template> - <template #prefix><i class="ti ti-lock"></i></template> - <template #caption> - <span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ i18n.ts.passwordMatched }}</span> - <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span> - </template> - </MkInput> - <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> - <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;"> - <template v-if="submitting"> - <MkLoading :em="true" :colored="false"/> - </template> - <template v-else>{{ i18n.ts.start }}</template> - </MkButton> - </form> - </MkSpacer> -</div> + <div> + <div :class="[$style.banner ,{[$style.gamingDark]: gaming==='dark' , [$style.gamingLight]: gaming==='light'}]"> + <i class="ti ti-user-edit"></i> + </div> + <MkSpacer :marginMin="20" :marginMax="32"> + <form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit"> + <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> + <template #label>{{ i18n.ts.invitationCode }}</template> + <template #prefix><i class="ti ti-key"></i></template> + </MkInput> + <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" + autocomplete="username" required data-cy-signup-username @update:modelValue="onChangeUsername"> + <template #label>{{ i18n.ts.username }} + <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="ti ti-help-circle"></i></div> + </template> + <template #prefix>@</template> + <template #suffix>@{{ host }}</template> + <template #caption> + <div><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.cannotBeChangedLater }}</div> + <span v-if="usernameState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ + i18n.ts.checking + }}</span> + <span v-else-if="usernameState === 'ok'" style="color: var(--success)"><i + class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span> + <span v-else-if="usernameState === 'unavailable'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span> + <span v-else-if="usernameState === 'error'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span> + <span v-else-if="usernameState === 'invalid-format'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.usernameInvalidFormat }}</span> + <span v-else-if="usernameState === 'min-range'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooShort }}</span> + <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span> + </template> + </MkInput> + <MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" + :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail"> + <template #label>{{ i18n.ts.emailAddress }} + <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i + class="ti ti-help-circle"></i></div> + </template> + <template #prefix><i class="ti ti-mail"></i></template> + <template #caption> + <span v-if="emailState === 'wait'" style="color:#999"><MkLoading :em="true"/> {{ i18n.ts.checking }}</span> + <span v-else-if="emailState === 'ok'" style="color: var(--success)"><i + class="ti ti-check ti-fw"></i> {{ i18n.ts.available }}</span> + <span v-else-if="emailState === 'unavailable:used'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.used }}</span> + <span v-else-if="emailState === 'unavailable:format'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.format }}</span> + <span v-else-if="emailState === 'unavailable:disposable'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.disposable }}</span> + <span v-else-if="emailState === 'unavailable:mx'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.mx }}</span> + <span v-else-if="emailState === 'unavailable:smtp'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts._emailUnavailable.smtp }}</span> + <span v-else-if="emailState === 'unavailable'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.unavailable }}</span> + <span v-else-if="emailState === 'error'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.error }}</span> + </template> + </MkInput> + <MkInput v-model="password" type="password" autocomplete="new-password" required data-cy-signup-password + @update:modelValue="onChangePassword"> + <template #label>{{ i18n.ts.password }}</template> + <template #prefix><i class="ti ti-lock"></i></template> + <template #caption> + <span v-if="passwordStrength == 'low'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.weakPassword }}</span> + <span v-if="passwordStrength == 'medium'" style="color: var(--warn)"><i + class="ti ti-check ti-fw"></i> {{ i18n.ts.normalPassword }}</span> + <span v-if="passwordStrength == 'high'" style="color: var(--success)"><i + class="ti ti-check ti-fw"></i> {{ i18n.ts.strongPassword }}</span> + </template> + </MkInput> + <MkInput v-model="retypedPassword" type="password" autocomplete="new-password" required + data-cy-signup-password-retype @update:modelValue="onChangePasswordRetype"> + <template #label>{{ i18n.ts.password }} ({{ i18n.ts.retype }})</template> + <template #prefix><i class="ti ti-lock"></i></template> + <template #caption> + <span v-if="passwordRetypeState == 'match'" style="color: var(--success)"><i class="ti ti-check ti-fw"></i> {{ + i18n.ts.passwordMatched + }}</span> + <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i + class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span> + </template> + </MkInput> + <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" + provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> + <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" + provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> + <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" + provider="turnstile" :sitekey="instance.turnstileSiteKey"/> + <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit + style="margin: 0 auto;"> + <template v-if="submitting"> + <MkLoading :em="true" :colored="false"/> + </template> + <template v-else>{{ i18n.ts.start }}</template> + </MkButton> + </form> + </MkSpacer> + </div> </template> <script lang="ts" setup> -import { } from 'vue'; -import { toUnicode } from 'punycode/'; +import {computed, ref, watch} from 'vue'; +import {toUnicode} from 'punycode/'; import MkButton from './MkButton.vue'; import MkInput from './MkInput.vue'; import MkSwitch from './MkSwitch.vue'; -import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue'; +import MkCaptcha, {type Captcha} from '@/components/MkCaptcha.vue'; import * as config from '@/config.js'; import * as os from '@/os.js'; -import { login } from '@/account.js'; -import { instance } from '@/instance.js'; -import { i18n } from '@/i18n.js'; +import {login} from '@/account.js'; +import {instance} from '@/instance.js'; +import {i18n} from '@/i18n.js'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ - autoSet?: boolean; + autoSet?: boolean; }>(), { - autoSet: false, + autoSet: false, }); const emit = defineEmits<{ - (ev: 'signup', user: Record<string, any>): void; - (ev: 'signupEmailPending'): void; + (ev: 'signup', user: Record<string, any>): void; + (ev: 'signupEmailPending'): void; }>(); const host = toUnicode(config.host); @@ -122,179 +189,269 @@ let usernameAbortController: null | AbortController = $ref(null); let emailAbortController: null | AbortController = $ref(null); const shouldDisableSubmitting = $computed((): boolean => { - return submitting || - instance.enableHcaptcha && !hCaptchaResponse || - instance.enableRecaptcha && !reCaptchaResponse || - instance.enableTurnstile && !turnstileResponse || - instance.emailRequiredForSignup && emailState !== 'ok' || - usernameState !== 'ok' || - passwordRetypeState !== 'match'; + return submitting || + instance.enableHcaptcha && !hCaptchaResponse || + instance.enableRecaptcha && !reCaptchaResponse || + instance.enableTurnstile && !turnstileResponse || + instance.emailRequiredForSignup && emailState !== 'ok' || + usernameState !== 'ok' || + passwordRetypeState !== 'match'; }); function getPasswordStrength(source: string): number { - let strength = 0; - let power = 0.018; + let strength = 0; + let power = 0.018; - // 英数字 - if (/[a-zA-Z]/.test(source) && /[0-9]/.test(source)) { - power += 0.020; - } + // 英数字 + if (/[a-zA-Z]/.test(source) && /[0-9]/.test(source)) { + power += 0.020; + } - // 大文字と小文字が混ざってたら - if (/[a-z]/.test(source) && /[A-Z]/.test(source)) { - power += 0.015; - } + // 大文字と小文字が混ざってたら + if (/[a-z]/.test(source) && /[A-Z]/.test(source)) { + power += 0.015; + } - // 記号が混ざってたら - if (/[!\x22\#$%&@'()*+,-./_]/.test(source)) { - power += 0.02; - } + // 記号が混ざってたら + if (/[!\x22\#$%&@'()*+,-./_]/.test(source)) { + power += 0.02; + } - strength = power * source.length; + strength = power * source.length; - return Math.max(0, Math.min(1, strength)); + return Math.max(0, Math.min(1, strength)); } function onChangeUsername(): void { - if (username === '') { - usernameState = null; - return; - } + if (username === '') { + usernameState = null; + return; + } - { - const err = - !username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : - username.length < 1 ? 'min-range' : - username.length > 20 ? 'max-range' : - null; + { + const err = + !username.match(/^[a-zA-Z0-9_]+$/) ? 'invalid-format' : + username.length < 1 ? 'min-range' : + username.length > 20 ? 'max-range' : + null; - if (err) { - usernameState = err; - return; - } - } + if (err) { + usernameState = err; + return; + } + } - if (usernameAbortController != null) { - usernameAbortController.abort(); - } - usernameState = 'wait'; - usernameAbortController = new AbortController(); + if (usernameAbortController != null) { + usernameAbortController.abort(); + } + usernameState = 'wait'; + usernameAbortController = new AbortController(); - os.api('username/available', { - username, - }, undefined, usernameAbortController.signal).then(result => { - usernameState = result.available ? 'ok' : 'unavailable'; - }).catch((err) => { - if (err.name !== 'AbortError') { - usernameState = 'error'; - } - }); + os.api('username/available', { + username, + }, undefined, usernameAbortController.signal).then(result => { + usernameState = result.available ? 'ok' : 'unavailable'; + }).catch((err) => { + if (err.name !== 'AbortError') { + usernameState = 'error'; + } + }); } function onChangeEmail(): void { - if (email === '') { - emailState = null; - return; - } + if (email === '') { + emailState = null; + return; + } - if (emailAbortController != null) { - emailAbortController.abort(); - } - emailState = 'wait'; - emailAbortController = new AbortController(); + if (emailAbortController != null) { + emailAbortController.abort(); + } + emailState = 'wait'; + emailAbortController = new AbortController(); - os.api('email-address/available', { - emailAddress: email, - }, undefined, emailAbortController.signal).then(result => { - emailState = result.available ? 'ok' : - result.reason === 'used' ? 'unavailable:used' : - result.reason === 'format' ? 'unavailable:format' : - result.reason === 'disposable' ? 'unavailable:disposable' : - result.reason === 'mx' ? 'unavailable:mx' : - result.reason === 'smtp' ? 'unavailable:smtp' : - 'unavailable'; - }).catch((err) => { - if (err.name !== 'AbortError') { - emailState = 'error'; - } - }); + os.api('email-address/available', { + emailAddress: email, + }, undefined, emailAbortController.signal).then(result => { + emailState = result.available ? 'ok' : + result.reason === 'used' ? 'unavailable:used' : + result.reason === 'format' ? 'unavailable:format' : + result.reason === 'disposable' ? 'unavailable:disposable' : + result.reason === 'mx' ? 'unavailable:mx' : + result.reason === 'smtp' ? 'unavailable:smtp' : + 'unavailable'; + }).catch((err) => { + if (err.name !== 'AbortError') { + emailState = 'error'; + } + }); } function onChangePassword(): void { - if (password === '') { - passwordStrength = ''; - return; - } + if (password === '') { + passwordStrength = ''; + return; + } - const strength = getPasswordStrength(password); - passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; + const strength = getPasswordStrength(password); + passwordStrength = strength > 0.7 ? 'high' : strength > 0.3 ? 'medium' : 'low'; } function onChangePasswordRetype(): void { - if (retypedPassword === '') { - passwordRetypeState = null; - return; - } + if (retypedPassword === '') { + passwordRetypeState = null; + return; + } - passwordRetypeState = password === retypedPassword ? 'match' : 'not-match'; + passwordRetypeState = password === retypedPassword ? 'match' : 'not-match'; } async function onSubmit(): Promise<void> { - if (submitting) return; - submitting = true; + if (submitting) return; + submitting = true; - try { - await os.api('signup', { - username, - password, - emailAddress: email, - invitationCode, - 'hcaptcha-response': hCaptchaResponse, - 'g-recaptcha-response': reCaptchaResponse, - 'turnstile-response': turnstileResponse, - }); - if (instance.emailRequiredForSignup) { - os.alert({ - type: 'success', - title: i18n.ts._signup.almostThere, - text: i18n.t('_signup.emailSent', { email }), - }); - emit('signupEmailPending'); - } else { - const res = await os.api('signin', { - username, - password, - }); - emit('signup', res); + try { + await os.api('signup', { + username, + password, + emailAddress: email, + invitationCode, + 'hcaptcha-response': hCaptchaResponse, + 'g-recaptcha-response': reCaptchaResponse, + 'turnstile-response': turnstileResponse, + }); + if (instance.emailRequiredForSignup) { + os.alert({ + type: 'success', + title: i18n.ts._signup.almostThere, + text: i18n.t('_signup.emailSent', {email}), + }); + emit('signupEmailPending'); + } else { + const res = await os.api('signin', { + username, + password, + }); + emit('signup', res); - if (props.autoSet) { - return login(res.i); - } - } - } catch { - submitting = false; - hcaptcha?.reset?.(); - recaptcha?.reset?.(); - turnstile?.reset?.(); + if (props.autoSet) { + return login(res.i); + } + } + } catch { + submitting = false; + hcaptcha?.reset?.(); + recaptcha?.reset?.(); + turnstile?.reset?.(); - os.alert({ - type: 'error', - text: i18n.ts.somethingHappened, - }); - } + os.alert({ + type: 'error', + text: i18n.ts.somethingHappened, + }); + } } </script> <style lang="scss" module> .banner { - padding: 16px; - text-align: center; - font-size: 26px; - background-color: var(--accentedBg); - color: var(--accent); + padding: 16px; + text-align: center; + font-size: 26px; + background-color: var(--accentedBg); + color: var(--accent); + + &.gamingDark { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingLight { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + + -webkit-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + } .captcha { - margin: 16px 0; + margin: 16px 0; +} + +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } </style> diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index aa4a184d7b..b4633f419d 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> - <div :class="$style.banner"> + <div :class="[$style.banner ,{[$style.gamingDark]: gaming==='dark' , [$style.gamingLight]: gaming==='light'}]"> <i class="ti ti-checklist"></i> </div> <MkSpacer :marginMin="20" :marginMax="28"> @@ -68,7 +68,39 @@ import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const availableServerRules = instance.serverRules.length > 0; const availableTos = instance.tosUrl != null; @@ -130,11 +162,29 @@ async function updateAgreeNote(v: boolean) { <style lang="scss" module> .banner { - padding: 16px; - text-align: center; - font-size: 26px; - background-color: var(--accentedBg); - color: var(--accent); + padding: 16px; + text-align: center; + font-size: 26px; + background-color: var(--accentedBg); + color: var(--accent); + + &.gamingDark { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingLight { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + + -webkit-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + } .rules { @@ -172,4 +222,70 @@ async function updateAgreeNote(v: boolean) { .ruleText { padding-top: 6px; } + +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index efd5665396..c4903419fc 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -10,15 +10,15 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="items"> <template v-for="(item, i) in group.items"> - <a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }"> + <a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active, gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }"> <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> <span class="text">{{ item.text }}</span> </a> - <button v-else-if="item.type === 'button'" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }" :disabled="item.active" @click="ev => item.action(ev)"> + <button v-else-if="item.type === 'button'" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active , gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }" :disabled="item.active" @click="ev => item.action(ev)"> <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> <span class="text">{{ item.text }}</span> </button> - <MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active }"> + <MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active , gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }"> <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> <span class="text">{{ item.text }}</span> </MkA> @@ -29,8 +29,40 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import {ref , computed , watch } from 'vue'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) defineProps<{ def: any[]; grid?: boolean; @@ -70,6 +102,21 @@ defineProps<{ &.active { color: var(--accent); background: var(--accentedBg); + &.gamingDark{ + color: black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + &.gamingLight{ + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } } &.danger { @@ -154,4 +201,70 @@ defineProps<{ } } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> + diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue index 9cbcb82bcb..1cf6bd86a9 100644 --- a/packages/frontend/src/components/MkSwitch.button.vue +++ b/packages/frontend/src/components/MkSwitch.button.vue @@ -5,19 +5,20 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <span - v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" - :class="{ + v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" + :class="{ [$style.button]: true, + [$style.gamingDark]: gaming === 'dark' && checked, + [$style.gamingLight]: gaming === 'light' && checked, [$style.buttonChecked]: checked, [$style.buttonDisabled]: props.disabled, - [$style.gamingDark]: gaming === 'dark' && checked, - [$style.gamingLight]: gaming === 'light' && checked + }" - data-cy-switch-toggle - @click.prevent.stop="toggle" + data-cy-switch-toggle + @click.prevent.stop="toggle" > <div - :class="{ [$style.knob]: true, [$style.knobChecked]: checked, [$style.gamingDark]: gaming === 'dark' && checked,[$style.gamingLight]: gaming === 'light' && checked}"></div> + :class="{ [$style.knob]: true, [$style.knobChecked]: checked, [$style.gamingDark]: gaming === 'dark' && checked,[$style.gamingLight]: gaming === 'light' && checked}"></div> </span> </template> @@ -31,202 +32,201 @@ let gaming = ref(''); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gaming.value = 'light'; } else { - gaming.value = ''; + gaming.value = ''; } watch(darkMode, () => { - console.log(gaming) - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } + console.log(gaming) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } }) watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } }) const props = withDefaults(defineProps<{ - checked: boolean | Ref<boolean>; - disabled?: boolean; + checked: boolean | Ref<boolean>; + disabled?: boolean; }>(), { - disabled: false, + disabled: false, }); const emit = defineEmits<{ - (ev: 'toggle'): void; + (ev: 'toggle'): void; }>(); const checked = toRefs(props).checked; const toggle = () => { - emit('toggle'); + emit('toggle'); }; </script> <style lang="scss" module> .button { - position: relative; - display: inline-flex; - flex-shrink: 0; - margin: 0; - box-sizing: border-box; - width: 32px; - height: 23px; - outline: none; - background: var(--switchOffBg); - background-clip: content-box; - border: solid 1px var(--switchOffBg); - border-radius: 999px; - cursor: pointer; - transition: inherit; - user-select: none; + position: relative; + display: inline-flex; + flex-shrink: 0; + margin: 0; + box-sizing: border-box; + width: 32px; + height: 23px; + outline: none; + background: var(--switchOffBg); + background-clip: content-box; + border: solid 1px var(--switchOffBg); + border-radius: 999px; + cursor: pointer; + transition: inherit; + user-select: none; - &.gamingLight { - border-image: conic-gradient(#e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd) 1; - border: solid 1px; - } + &.gamingLight { + border-image: conic-gradient(#e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd) 1; + border: solid 1px; + } - &.gamingDark { - border-image: conic-gradient(#c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4) 1; - border: solid 1px; - } + &.gamingDark { + border-image: conic-gradient(#c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4) 1; + border: solid 1px; + } } .buttonChecked { - background-color: var(--switchOnBg) !important; - border-color: var(--switchOnBg) !important; - - &.gamingLight { - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - } - - &.gamingDark { - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - } + background-color: var(--switchOnBg); + border-color: var(--switchOnBg); } +.gamingLight { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; +} + +.gamingDark { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; +} .buttonDisabled { - cursor: not-allowed; + cursor: not-allowed; } .knob { - position: absolute; - top: 3px; - width: 15px; - height: 15px; - border-radius: 999px; - transition: all 0.2s ease; + position: absolute; + top: 3px; + width: 15px; + height: 15px; + border-radius: 999px; + transition: all 0.2s ease; - &:not(.knobChecked) { - left: 3px; - background: var(--switchOffFg); + &:not(.knobChecked) { + left: 3px; + background: var(--switchOffFg); - } + } } .knobChecked { - left: 12px; - background: var(--switchOnFg); + left: 12px; + background: var(--switchOnFg); - &.gamingDark { - background: white !important; - } + &.gamingDark { + background: white !important; + } - &.gamingLight { - background: white !important; - } + &.gamingLight { + background: white !important; + } } @-webkit-keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } @-moz-keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } @keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } @-webkit-keyframes AnimationDark { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } @-moz-keyframes AnimationDark { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } @keyframes AnimationDark { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } </style> diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue index 9785d89403..1fef2d020f 100644 --- a/packages/frontend/src/components/MkTab.vue +++ b/packages/frontend/src/components/MkTab.vue @@ -4,8 +4,40 @@ SPDX-License-Identifier: AGPL-3.0-only --> <script lang="ts"> -import { defineComponent, h, resolveDirective, withDirectives } from 'vue'; +import {computed, defineComponent, h, resolveDirective, withDirectives , ref , watch} from 'vue'; +import {defaultStore} from "@/store.js"; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) export default defineComponent({ props: { modelValue: { @@ -18,7 +50,7 @@ export default defineComponent({ return () => h('div', { class: 'pxhvhrfw', }, options.map(option => withDirectives(h('button', { - class: ['_button', { active: props.modelValue === option.props.value }], + class: ['_button', { active: props.modelValue === option.props.value , gamingDark: gaming.value == 'dark' && props.modelValue === option.props.value,gamingLight: gaming.value == 'light' && props.modelValue === option.props.value } ], key: option.key, disabled: props.modelValue === option.props.value, onClick: () => { @@ -49,6 +81,21 @@ export default defineComponent({ &.active { color: var(--accent); background: var(--accentedBg); + + &.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } } &:not(.active):hover { @@ -75,4 +122,69 @@ export default defineComponent({ } } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index d60e01c44d..45317f3bda 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div style="overflow-x: clip;"> <div :class="$style.progressBar"> - <div :class="$style.progressBarValue" :style="{ width: `${(page / 5) * 100}%` }"></div> + <div :class="[$style.progressBarValue , {[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :style="{ width: `${(page / 5) * 100}%` }"></div> </div> <Transition mode="out-in" @@ -123,7 +123,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, shallowRef, watch } from 'vue'; +import {computed, ref, shallowRef, watch} from 'vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import XProfile from '@/components/MkUserSetupDialog.Profile.vue'; @@ -137,6 +137,38 @@ import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowB import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const emit = defineEmits<{ (ev: 'closed'): void; }>(); @@ -203,6 +235,20 @@ async function later(later: boolean) { height: 100%; background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); transition: all 0.5s cubic-bezier(0,.5,.5,1); + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + &.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } } .centerPage { @@ -223,4 +269,69 @@ async function later(later: boolean) { -webkit-backdrop-filter: blur(15px); backdrop-filter: blur(15px); } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 7a8d7e6109..ae54fa1428 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo> </div> <div class="_gaps_s" :class="$style.mainActions"> - <MkButton :class="$style.mainAction" full rounded gradate data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton> + <MkButton :class="[$style.mainAction , $style.gamingDark]" full rounded data-cy-signup style="margin-right: 12px;" @click="signup()">{{ i18n.ts.joinThisServer }}</MkButton> <MkButton :class="$style.mainAction" full rounded @click="exploreOtherServers()">{{ i18n.ts.exploreOtherServers }}</MkButton> <MkButton :class="$style.mainAction" full rounded data-cy-signin @click="signin()">{{ i18n.ts.login }}</MkButton> </div> @@ -190,6 +190,14 @@ function exploreOtherServers() { line-height: 28px; } +.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; +} + .stats { display: grid; grid-template-columns: 1fr 1fr; @@ -225,4 +233,69 @@ function exploreOtherServers() { height: 350px; overflow: auto; } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> diff --git a/packages/frontend/src/components/global/MkLoading.vue b/packages/frontend/src/components/global/MkLoading.vue index 3f34e83f58..606cde32dd 100644 --- a/packages/frontend/src/components/global/MkLoading.vue +++ b/packages/frontend/src/components/global/MkLoading.vue @@ -56,7 +56,7 @@ const props = withDefaults(defineProps<{ --size: 38px; &.colored { - color: var(--accent); + color: #5f5f5f; } &.inline { diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 52dcc05548..e40d26db76 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -68,7 +68,6 @@ if (darkMode.value && gamingMode.value == true) { } watch(darkMode, () => { - console.log(gaming) if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -277,18 +276,18 @@ onUnmounted(() => { transition: none; pointer-events: none; &.gamingLight{ - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingDark{ - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.animate { transition: width 0.15s ease, left 0.15s ease; diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue index 0558faec16..d109847692 100644 --- a/packages/frontend/src/pages/explore.featured.vue +++ b/packages/frontend/src/pages/explore.featured.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkSpacer :contentMax="800"> - <MkTab v-model="tab" style="margin-bottom: var(--margin);"> + <MkTab v-model="tab" style="margin-bottom: var(--margin);"> <option value="notes">{{ i18n.ts.notes }}</option> <option value="polls">{{ i18n.ts.poll }}</option> </MkTab> diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue index 998d942488..d720d172ce 100644 --- a/packages/frontend/src/pages/welcome.entrance.a.vue +++ b/packages/frontend/src/pages/welcome.entrance.a.vue @@ -96,7 +96,10 @@ os.apiGet('federation/instances', { left: 0; width: 100vw; height: 100vh; - background: var(--accent); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.75, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.75, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.75, 1) infinite; clip-path: polygon(0% 0%, 45% 0%, 20% 100%, 0% 100%); } > .shape2 { @@ -105,7 +108,10 @@ os.apiGet('federation/instances', { left: 0; width: 100vw; height: 100vh; - background: var(--accent); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.75, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.75, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.75, 1) infinite; clip-path: polygon(0% 0%, 25% 0%, 35% 100%, 0% 100%); opacity: 0.5; } @@ -165,6 +171,71 @@ os.apiGet('federation/instances', { } } } +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} </style> <style lang="scss" module> diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index c644fc76da..a2c860403a 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -153,6 +153,14 @@ hr { height: 1em; border-radius: 100%; background: currentColor; + &.gamingdark{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + } ._noSelect { diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index d6105416bb..54fbf1606a 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -4,58 +4,84 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="$style.root"> - <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> - <button class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> - </button> - </div> - <div :class="$style.middle"> - <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/" exact> - <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline }}</span> - </MkA> - <template v-for="item in menu"> - <div v-if="item === '-'" :class="$style.divider"></div> - <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" - :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" - :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> - <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> - <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> - </component> - </template> - <div :class="$style.divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/admin"> - <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> - </MkA> - <button :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" class="_button" @click="more"> - <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> - <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> - </button> - <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/settings"> - <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span> - </MkA> - </div> - <div :class="$style.bottom"> - <button class="_button" :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" data-cy-open-post-form @click="os.post"> - <i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ i18n.ts.note }}</span> - </button> - <button class="_button" :class="$style.account" @click="openAccountMenu"> - <MkAvatar :user="$i" :class="$style.avatar"/><MkAcct :class="$style.acct" class="_nowrap" :user="$i"/> - </button> - </div> -</div> + <div :class="$style.root"> + <div :class="$style.top"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> + <button class="_button" :class="$style.instance" @click="openInstanceMenu"> + <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> + </button> + </div> + <div :class="$style.middle"> + <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/" exact> + <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.timeline + }}</span> + </MkA> + <template v-for="item in menu"> + <div v-if="item === '-'" :class="$style.divider"></div> + <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" + v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" + :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" :to="navbarItemDef[item].to" + v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> + <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span + :class="$style.itemText">{{ navbarItemDef[item].title }}</span> + <span v-if="navbarItemDef[item].indicated" + :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i + class="_indicatorCircle"></i></span> + </component> + </template> + <div :class="$style.divider"></div> + <MkA v-if="$i.isAdmin || $i.isModerator" + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/admin"> + <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span + :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> + </MkA> + <button + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + class="_button" @click="more"> + <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.more + }}</span> + <span v-if="otherMenuItemIndicated" + :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i + class="_indicatorCircle"></i></span> + </button> + <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/settings"> + <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.settings + }}</span> + </MkA> + </div> + <div :class="$style.bottom"> + <button class="_button" + :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" + data-cy-open-post-form @click="os.post"> + <i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ + i18n.ts.note + }}</span> + </button> + <button class="_button" :class="$style.account" @click="openAccountMenu"> + <MkAvatar :user="$i" :class="$style.avatar"/> + <MkAcct :class="$style.acct" class="_nowrap" :user="$i"/> + </button> + </div> + </div> </template> <script lang="ts" setup> import {computed, defineAsyncComponent, ref, toRef, watch} from 'vue'; -import { openInstanceMenu } from './common'; +import {openInstanceMenu} from './common'; import * as os from '@/os'; -import { navbarItemDef } from '@/navbar'; -import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; +import {navbarItemDef} from '@/navbar'; +import {$i, openAccountMenu as openAccountMenu_} from '@/account'; import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; -import { i18n } from '@/i18n'; -import { instance } from '@/instance'; +import {i18n} from '@/i18n'; +import {instance} from '@/instance'; + let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); @@ -63,18 +89,18 @@ const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); let gaming = ref() -if (darkMode.value){ +if (darkMode.value) { bannerUrl.value = bannerDark; iconUrl.value = iconDark; -}else{ +} else { bannerUrl.value = bannerLight; iconUrl.value = iconLight; } watch(darkMode, () => { - if (darkMode.value){ + if (darkMode.value) { bannerUrl.value = bannerDark; iconUrl.value = iconDark; - }else{ + } else { bannerUrl.value = bannerLight; iconUrl.value = iconLight; } @@ -89,7 +115,6 @@ if (darkMode.value && gamingMode.value == true) { } watch(darkMode, () => { - console.log(gaming) if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -110,104 +135,103 @@ watch(gamingMode, () => { }) const menu = toRef(defaultStore.state, 'menu'); const otherMenuItemIndicated = computed(() => { - for (const def in navbarItemDef) { - if (menu.value.includes(def)) continue; - if (navbarItemDef[def].indicated) return true; - } - return false; + for (const def in navbarItemDef) { + if (menu.value.includes(def)) continue; + if (navbarItemDef[def].indicated) return true; + } + return false; }); function openAccountMenu(ev: MouseEvent) { - openAccountMenu_({ - withExtraOperation: true, - }, ev); + openAccountMenu_({ + withExtraOperation: true, + }, ev); } function more() { - os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, { - }, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, {}, 'closed'); } </script> <style lang="scss" module> .root { - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; } .top { - position: sticky; - top: 0; - z-index: 1; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); + position: sticky; + top: 0; + z-index: 1; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); } .banner { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-size: cover; - background-position: center center; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-size: cover; + background-position: center center; -webkit-mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%); mask-image: linear-gradient(0deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.75) 20%); } .instance { - position: relative; - display: block; - text-align: center; - width: 100%; + position: relative; + display: block; + text-align: center; + width: 100%; } .instanceIcon { - display: inline-block; - width: 38px; - aspect-ratio: 1; + display: inline-block; + width: 38px; + aspect-ratio: 1; } .bottom { - position: sticky; - bottom: 0; - padding: 20px 0; - background: var(--X14); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); + position: sticky; + bottom: 0; + padding: 20px 0; + background: var(--X14); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); } .post { - position: relative; - display: block; - width: 100%; - height: 40px; - color: var(--fgOnAccent); - font-weight: bold; - text-align: left; + position: relative; + display: block; + width: 100%; + height: 40px; + color: var(--fgOnAccent); + font-weight: bold; + text-align: left; - &:before { - content: ""; - display: block; - width: calc(100% - 38px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - } + &:before { + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); + } - &:hover, &.active { - &:before { - background: var(--accentLighten); - } - } + &:hover, &.active { + &:before { + background: var(--accentLighten); + } + } &.gamingLight:before { content: ""; @@ -221,20 +245,20 @@ function more() { right: 0; bottom: 0; border-radius: 999px; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:hover, &.gamingLight.active { &.gamingLight:before { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -250,20 +274,20 @@ function more() { right: 0; bottom: 0; border-radius: 999px; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { &.gamingDark:before { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -271,85 +295,86 @@ function more() { } .postIcon { - position: relative; - margin-left: 30px; - margin-right: 8px; - width: 32px; + position: relative; + margin-left: 30px; + margin-right: 8px; + width: 32px; } .account { - position: relative; - display: flex; - align-items: center; - padding-left: 30px; - width: 100%; - text-align: left; - box-sizing: border-box; - margin-top: 16px; + position: relative; + display: flex; + align-items: center; + padding-left: 30px; + width: 100%; + text-align: left; + box-sizing: border-box; + margin-top: 16px; } .avatar { - display: block; - flex-shrink: 0; - position: relative; - width: 32px; - aspect-ratio: 1; - margin-right: 8px; + display: block; + flex-shrink: 0; + position: relative; + width: 32px; + aspect-ratio: 1; + margin-right: 8px; } .acct { - display: block; - flex-shrink: 1; - padding-right: 8px; + display: block; + flex-shrink: 1; + padding-right: 8px; } .middle { - flex: 1; + flex: 1; } .divider { - margin: 16px 16px; - border-top: solid 0.5px var(--divider); + margin: 16px 16px; + border-top: solid 0.5px var(--divider); } .item { - position: relative; - display: block; - padding-left: 24px; - line-height: 2.85rem; - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - width: 100%; - text-align: left; - box-sizing: border-box; - color: var(--navFg); + position: relative; + display: block; + padding-left: 24px; + line-height: 2.85rem; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + text-align: left; + box-sizing: border-box; + color: var(--navFg); - &:hover { - text-decoration: none; - color: var(--navHoverFg); - } + &:hover { + text-decoration: none; + color: var(--navHoverFg); + } - &.active { - color: var(--navActive); - } + &.active { + color: var(--navActive); + } + + &:hover, &.active { + &:before { + content: ""; + display: block; + width: calc(100% - 24px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: var(--accentedBg); + } + } - &:hover, &.active { - &:before { - content: ""; - display: block; - width: calc(100% - 24px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: var(--accentedBg); - } - } &.gamingDark:hover, &.gamingDark.active { text-decoration: none; color: white; @@ -366,57 +391,67 @@ function more() { right: 0; bottom: 0; border-radius: 999px; - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } + &.gamingLight:hover, &.gamingLight.active { text-decoration: none; color: white; &.gamingLight:before { - content: ""; - display: block; - height: 100%; - aspect-ratio: 1; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + content: ""; + display: block; + height: 100%; + aspect-ratio: 1; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } .itemIcon { - position: relative; - width: 32px; - margin-right: 8px; + position: relative; + width: 32px; + margin-right: 8px; } .itemIndicator { - position: absolute; - top: 0; - left: 20px; - color: var(--navIndicator); - font-size: 8px; - animation: blink 1s infinite; + position: absolute; + top: 0; + left: 20px; + color: var(--navIndicator); + font-size: 8px; + animation: blink 1s infinite; + + &.gamingDark { + color: white; + } + + &.gamingLight { + color: black; + } } .itemText { - position: relative; - font-size: 0.9em; + position: relative; + font-size: 0.9em; } + @-webkit-keyframes AnimationLight { 0% { background-position: 0% 50% @@ -428,6 +463,7 @@ function more() { background-position: 0% 50% } } + @-moz-keyframes AnimationLight { 0% { background-position: 0% 50% @@ -439,6 +475,7 @@ function more() { background-position: 0% 50% } } + @keyframes AnimationLight { 0% { background-position: 0% 50% @@ -450,6 +487,7 @@ function more() { background-position: 0% 50% } } + @-webkit-keyframes AnimationDark { 0% { background-position: 0% 50% @@ -461,6 +499,7 @@ function more() { background-position: 0% 50% } } + @-moz-keyframes AnimationDark { 0% { background-position: 0% 50% @@ -472,6 +511,7 @@ function more() { background-position: 0% 50% } } + @keyframes AnimationDark { 0% { background-position: 0% 50% diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index e99abf79ec..481f79ed1f 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -35,23 +35,31 @@ SPDX-License-Identifier: AGPL-3.0-only > <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> - <span v-if="navbarItemDef[item].indicated" :class="$style.itemIndicator"><i + <span v-if="navbarItemDef[item].indicated" + :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i class="_indicatorCircle"></i></span> </component> </template> <div :class="$style.divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" to="/admin"> <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> </MkA> - <button class="_button" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click="more"> + <button class="_button" + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + @click="more"> <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> - <span v-if="otherMenuItemIndicated" :class="$style.itemIndicator"><i class="_indicatorCircle"></i></span> + <span v-if="otherMenuItemIndicated" + :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i + class="_indicatorCircle"></i></span> </button> - <MkA v-tooltip.noDelay.right="i18n.ts.settings" :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :activeClass="$style.active" + <MkA v-tooltip.noDelay.right="i18n.ts.settings" + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/settings"> <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.settings }}</span> @@ -62,7 +70,8 @@ SPDX-License-Identifier: AGPL-3.0-only :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" data-cy-open-post-form @click="os.post"> - <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span :class="$style.postText">{{ + <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span + :class="$style.postText,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}">{{ i18n.ts.note }}</span> </button> @@ -122,7 +131,7 @@ if (darkMode.value && gamingMode.value == true) { } watch(darkMode, () => { - console.log(gaming) + if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -282,39 +291,16 @@ function more(ev: MouseEvent) { } } + &.gamingLight { + color: white !important; + } + + &.gamingDark { + color: black !important; + } + &.gamingLight:before { - color: white; - content: ""; - display: block; - width: calc(100% - 38px); - height: 100%; - margin: auto; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border-radius: 999px; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - } - - &.gamingLight:hover, &.gamingLight.active { - color: white; - &.gamingLight:before { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - } - } - - &.gamingDark:before { - color: white; + color: white !important; content: ""; display: block; width: calc(100% - 38px); @@ -327,20 +313,53 @@ function more(ev: MouseEvent) { bottom: 0; border-radius: 999px; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } + + &.gamingLight:hover, &.gamingLight.active { + color: white !important; + + &.gamingLight:before { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } + } + + &.gamingDark:before { + color: black !important; + content: ""; + display: block; + width: calc(100% - 38px); + height: 100%; + margin: auto; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 999px; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { - color: white; + color: black !important; + &.gamingDark:before { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -405,6 +424,14 @@ function more(ev: MouseEvent) { box-sizing: border-box; color: var(--navFg); + &.gamingDark { + color: white; + } + + &.gamingLight { + color: black; + } + &:hover { text-decoration: none; color: var(--navHoverFg); @@ -435,19 +462,21 @@ function more(ev: MouseEvent) { &.gamingDark:hover { color: white; + background-size: 1800% 1800%; text-decoration: none; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark.active { color: white; + background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { @@ -464,35 +493,38 @@ function more(ev: MouseEvent) { left: 0; right: 0; bottom: 0; - border-radius: 999px; - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } &.gamingLight:hover { - color: white; + color: black; background-size: 1800% 1800% !important; text-decoration: none; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:active { - color: white; + color: black; + background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:hover, &.gamingLight.active { - color: white; + color: black; + + &.gamingLight:before { + color: white !important; content: ""; display: block; width: calc(100% - 34px); @@ -504,11 +536,11 @@ function more(ev: MouseEvent) { right: 0; bottom: 0; border-radius: 999px; - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } @@ -526,6 +558,14 @@ function more(ev: MouseEvent) { color: var(--navIndicator); font-size: 8px; animation: blink 1s infinite; + + &.gamingDark { + color: white; + } + + &.gamingLight { + color: black; + } } .itemText { @@ -614,21 +654,23 @@ function more(ev: MouseEvent) { width: 52px; aspect-ratio: 1/1; border-radius: 100%; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + color: black; } &.gamingLight:hover, &.gamingLight.active { &.gamingLight:before { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + color: black; } } @@ -647,19 +689,20 @@ function more(ev: MouseEvent) { border-radius: 100%; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { - color: white; + color: black; + &.gamingDark:before { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -712,6 +755,14 @@ function more(ev: MouseEvent) { text-decoration: none; color: var(--accent); + &.gamingDark { + color: white; + } + + &.gamingLight { + color: #DEE7E4; + } + &:before { content: ""; display: block; @@ -735,9 +786,10 @@ function more(ev: MouseEvent) { &.gamingDark:hover, &.gamingDark.active { text-decoration: none; - color: var(--accent); + color: black; &.gamingDark:before { + color: black; content: ""; display: block; height: 100%; @@ -749,16 +801,17 @@ function more(ev: MouseEvent) { right: 0; bottom: 0; border-radius: 999px; - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } + &.gamingLight:hover, &.gamingLight.active { text-decoration: none; - color: var(--accent); + color: black; &.gamingLight:before { content: ""; @@ -772,11 +825,11 @@ function more(ev: MouseEvent) { width: 52px; aspect-ratio: 1/1; border-radius: 100%; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } @@ -798,10 +851,19 @@ function more(ev: MouseEvent) { color: var(--navIndicator); font-size: 8px; animation: blink 1s infinite; + + &.gamingDark { + color: white; + } + + &.gamingLight { + color: black; + } } } + @-webkit-keyframes AnimationLight { 0% { background-position: 0% 50% @@ -813,6 +875,7 @@ function more(ev: MouseEvent) { background-position: 0% 50% } } + @-moz-keyframes AnimationLight { 0% { background-position: 0% 50% @@ -823,17 +886,20 @@ function more(ev: MouseEvent) { 100% { background-position: 0% 50% } -} @keyframes AnimationLight { - 0% { - background-position: 0% 50% - } - 50% { - background-position: 100% 50% - } - 100% { - background-position: 0% 50% - } - } +} + +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + @-webkit-keyframes AnimationDark { 0% { background-position: 0% 50% @@ -845,6 +911,7 @@ function more(ev: MouseEvent) { background-position: 0% 50% } } + @-moz-keyframes AnimationDark { 0% { background-position: 0% 50% @@ -856,6 +923,7 @@ function more(ev: MouseEvent) { background-position: 0% 50% } } + @keyframes AnimationDark { 0% { background-position: 0% 50% diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 4f37e917bb..81a4257be4 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -29,14 +29,14 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="isMobile" ref="navFooter" :class="$style.nav"> <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" - :class="$style.navButtonIndicator"><i + :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i class="_indicatorCircle"></i></span></button> <button :class="$style.navButton" class="_button" @click="mainRouter.currentRoute.value.name === 'index' ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button> <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" - :class="$style.navButtonIndicator"><i + :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i class="_indicatorCircle"></i></span></button> <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i> @@ -137,7 +137,6 @@ if (darkMode.value && gamingMode.value == true) { } watch(darkMode, () => { - console.log(gaming) if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -501,14 +500,14 @@ $widgets-hide-threshold: 1090px; .postButton_gamingDark { composes: navButton; color: var(--fgOnAccent); - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; &.gamingDark:hover { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; @@ -516,7 +515,7 @@ $widgets-hide-threshold: 1090px; } &.gamingDark:active { - background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; @@ -527,21 +526,21 @@ $widgets-hide-threshold: 1090px; .postButton_gamingLight { composes: navButton; color: var(--fgOnAccent); - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; &.gamingLight:hover { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; } &.gamingLight:active { - background: linear-gradient(270deg, #d08c8c, #cfb28c, #dbdb8b, #95d08e, #8bdbdb, #94a9cf, #b09ecf, #cfa0cf, #e0a0bd); + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; @@ -577,6 +576,13 @@ $widgets-hide-threshold: 1090px; color: var(--indicator); font-size: 16px; animation: blink 1s infinite; + &.gamingDark { + color: white; + } + + &.gamingLight { + color: black; + } } .menuDrawerBg { diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue index 44988e6df3..b5685fd54e 100644 --- a/packages/frontend/src/ui/universal.widgets.vue +++ b/packages/frontend/src/ui/universal.widgets.vue @@ -7,8 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <XWidgets :edit="editMode" :widgets="widgets" @addWidget="addWidget" @removeWidget="removeWidget" @updateWidget="updateWidget" @updateWidgets="updateWidgets" @exit="editMode = false"/> - <button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button> - <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button> + <button v-if="editMode" class="_textButton" style="font-size: 0.9em;" :class="{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button> + <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit, {[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button> </div> </template> @@ -16,11 +16,42 @@ SPDX-License-Identifier: AGPL-3.0-only let editMode = $ref(false); </script> <script lang="ts" setup> -import { } from 'vue'; +import { ref , computed , watch} from 'vue'; import XWidgets from '@/components/MkWidgets.vue'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) const props = withDefaults(defineProps<{ // null = 全てのウィジェットを表示 // left = place: leftだけを表示 @@ -77,5 +108,103 @@ function updateWidgets(thisWidgets) { <style lang="scss" module> .edit { width: 100%; + &.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + } +} +.gamingDark{ + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; +} +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} @keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } + } +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } </style> From ae13d713ffc7849967bf067473c02e85c7865994 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 09:04:16 +0900 Subject: [PATCH 059/501] 2023.9.0-beta.11-prismisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b92ca1479..3538bc52aa 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.11-prismisskey.1", + "version": "2023.9.0-beta.11-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From df4d040f1e382bdf094e93dabe5e207bedc20226 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 09:31:01 +0900 Subject: [PATCH 060/501] 2023.9.0-beta.11-prismisskey.3 --- packages/frontend/src/components/MkFollowButton.vue | 12 ++++++------ packages/frontend/src/pages/settings/general.vue | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index f64e0da737..4a81e8a7c7 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <button class="_button" - :class="[$style.root, ,{ [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou,[$style.gamingDark]: gaming === 'dark' || isFollowing || hasPendingFollowRequestFromYou,[$style.gamingLight]: gaming === 'light' || isFollowing || hasPendingFollowRequestFromYou + :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou,[$style.gamingDark]: gaming === 'dark' && isFollowing || gaming === 'dark' && hasPendingFollowRequestFromYou,[$style.gamingLight]: gaming === 'light' && isFollowing || gaming === 'light' && hasPendingFollowRequestFromYou , [$style.full]: full, [$style.large]: large }]" :disabled="wait" @click="onClick" @@ -13,33 +13,33 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="!wait"> <template v-if="hasPendingFollowRequestFromYou && user.isLocked"> <span v-if="full" - :class="$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'} ">{{ + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]">{{ i18n.ts.followRequestPending }}</span><i class="ti ti-hourglass-empty"></i> </template> <template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> <!-- つまりリモートフォローの場合。 --> <span v-if="full" - :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] ">{{ i18n.ts.processing }}</span> <MkLoading :em="true" :colored="false"/> </template> <template v-else-if="isFollowing"> <span v-if="full" - :class="$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' } ">{{ + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] ">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> </template> <template v-else-if="!isFollowing && user.isLocked"> <span v-if="full" - :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]">{{ i18n.ts.followRequest }}</span><i class="ti ti-plus"></i> </template> <template v-else-if="!isFollowing && !user.isLocked"> <span v-if="full" - :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> </template> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 7644daa34b..1fbc31b15c 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -297,6 +297,8 @@ watch([ reactionsDisplaySize, highlightSensitiveMedia, keepScreenOn, + showMediaTimeline, + enableGamingMode ], async () => { await reloadAsk(); }); From 578b11b54bc5ffb56a561871927bd8d4ec8aecf0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 09:31:49 +0900 Subject: [PATCH 061/501] 2023.9.0-beta.11-prismisskey.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3538bc52aa..c9f3c4d5a8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-beta.11-prismisskey.2", + "version": "2023.9.0-beta.11-prismisskey.3", "codename": "nasubi", "repository": { "type": "git", From db7e4138d7d532c0e3d6ea2ddc9568e96fbba0e0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 09:45:10 +0900 Subject: [PATCH 062/501] 2023.9.0-beta.11-prismisskey.4 --- packages/frontend/src/components/MkButton.vue | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 82b94140aa..3078ca241e 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -96,7 +96,7 @@ const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); let gaming = ref(''); // 0-off , 1-dark , 2-light // gaming.valueに新しい値を代入する -if (darkMode.value && gamingMode.value && props.primary || props.gradate ) { +if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value && props.primary|| props.gradate ) { gaming.value = 'light'; @@ -105,9 +105,9 @@ if (darkMode.value && gamingMode.value && props.primary || props.gradate ) { } watch(darkMode, () => { - if (darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { + if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { + } else if (!darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate) { gaming.value = 'light'; }else{ gaming.value = ''; @@ -115,9 +115,9 @@ watch(darkMode, () => { }) watch(gamingMode, () => { - if (darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { + if (darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary || props.gradate || props.large || props.small || props.full ) { + } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'light'; }else{ gaming.value = ''; From 4e846b29b97ee97a42c268be85db0ed9276c82b0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 09:58:48 +0900 Subject: [PATCH 063/501] bugfix --- packages/frontend/src/components/MkButton.vue | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 3078ca241e..6bd59b3906 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -98,7 +98,7 @@ let gaming = ref(''); // 0-off , 1-dark , 2-light // gaming.valueに新しい値を代入する if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value && props.primary|| props.gradate ) { +} else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'light'; }else{ gaming.value = ''; @@ -299,20 +299,20 @@ function onMousedown(evt: MouseEvent): void { &:not(:disabled):hover { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; + background-size: 1800% 1800%; color: white !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &:not(:disabled):active { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800% !important; + background-size: 1800% 1800% !important; color: white !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; } } @@ -326,20 +326,20 @@ function onMousedown(evt: MouseEvent): void { &:not(:disabled):hover { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% ; + background-size: 1800% 1800% ; color: white !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; } &:not(:disabled):active { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; + background-size: 1800% 1800% !important; color: white !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; } } From 6e4b8d88fd9bd9e3638f3ca2e408754c04ed5cf5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 17:15:53 +0900 Subject: [PATCH 064/501] bugfix --- .../src/components/MkChannelFollowButton.vue | 267 ++++++++++++------ .../src/components/MkFollowButton.vue | 78 +++-- packages/frontend/src/components/MkMenu.vue | 2 +- packages/frontend/src/ui/_common_/navbar.vue | 22 +- 4 files changed, 232 insertions(+), 137 deletions(-) diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue index cf485e7892..c95fad0789 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.vue +++ b/packages/frontend/src/components/MkChannelFollowButton.vue @@ -4,37 +4,47 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<button - class="_button" - :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing, [$style.full]: full },[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]]" - :disabled="wait" - @click="onClick" -> - <template v-if="!wait"> - <template v-if="isFollowing"> - <span v-if="full" :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> - </template> - <template v-else> - <span v-if="full" :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> - </template> - </template> - <template v-else> - <span v-if="full" :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ i18n.ts.processing }}</span><MkLoading :em="true"/> - </template> -</button> + <button + class="_button" + :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing, [$style.full]: full },[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]]" + :disabled="wait" + @click="onClick" + > + <template v-if="!wait"> + <template v-if="isFollowing"> + <span v-if="full" + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ + i18n.ts.unfollow + }}</span><i class="ti ti-minus"></i> + </template> + <template v-else> + <span v-if="full" + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ + i18n.ts.follow + }}</span><i class="ti ti-plus"></i> + </template> + </template> + <template v-else> + <span v-if="full" + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ + i18n.ts.processing + }}</span> + <MkLoading :em="true"/> + </template> + </button> </template> <script lang="ts" setup> import {computed, ref, watch} from 'vue'; import * as os from '@/os.js'; -import { i18n } from '@/i18n.js'; +import {i18n} from '@/i18n.js'; import {defaultStore} from "@/store.js"; const props = withDefaults(defineProps<{ - channel: Record<string, any>; - full?: boolean; + channel: Record<string, any>; + full?: boolean; }>(), { - full: false, + full: false, }); @@ -74,41 +84,41 @@ const isFollowing = ref<boolean>(props.channel.isFollowing); const wait = ref(false); async function onClick() { - wait.value = true; + wait.value = true; - try { - if (isFollowing.value) { - await os.api('channels/unfollow', { - channelId: props.channel.id, - }); - isFollowing.value = false; - } else { - await os.api('channels/follow', { - channelId: props.channel.id, - }); - isFollowing.value = true; - } - } catch (err) { - console.error(err); - } finally { - wait.value = false; - } + try { + if (isFollowing.value) { + await os.api('channels/unfollow', { + channelId: props.channel.id, + }); + isFollowing.value = false; + } else { + await os.api('channels/follow', { + channelId: props.channel.id, + }); + isFollowing.value = true; + } + } catch (err) { + console.error(err); + } finally { + wait.value = false; + } } </script> <style lang="scss" module> .root { - position: relative; - display: inline-block; - font-weight: bold; - color: var(--accent); - background: transparent; - border: solid 1px var(--accent); - padding: 0; - height: 31px; - font-size: 16px; - border-radius: 32px; - background: #fff; + position: relative; + display: inline-block; + font-weight: bold; + color: var(--accent); + border: solid 1px var(--accent); + padding: 0; + height: 31px; + font-size: 16px; + border-radius: 32px; + background: #fff; + &.gamingDark { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); @@ -116,8 +126,6 @@ async function onClick() { -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; } &.gamingLight { @@ -127,40 +135,39 @@ async function onClick() { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; } - &.full { - padding: 0 8px 0 12px; - font-size: 14px; - } - &:not(.full) { - width: 31px; - } + &.full { + padding: 0 8px 0 12px; + font-size: 14px; + } - &:focus-visible { - &:after { - content: ""; - pointer-events: none; - position: absolute; - top: -5px; - right: -5px; - bottom: -5px; - left: -5px; - border: 2px solid var(--focus); - border-radius: 32px; - } - } + &:not(.full) { + width: 31px; + } - &:hover { - //background: mix($primary, #fff, 20); - } + &:focus-visible { + &:after { + content: ""; + pointer-events: none; + position: absolute; + top: -5px; + right: -5px; + bottom: -5px; + left: -5px; + border: 2px solid var(--focus); + border-radius: 32px; + } + } - &:active { - //background: mix($primary, #fff, 40); - } + &:hover { + //background: mix($primary, #fff, 20); + } + + &:active { + //background: mix($primary, #fff, 40); + } &.active { color: var(--fgOnAccent); @@ -177,18 +184,19 @@ async function onClick() { } &.gamingDark:hover { - -webkit-text-fill-color: unset; + color: black; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:active { - -webkit-text-fill-color: unset !important; - color: white; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -196,7 +204,6 @@ async function onClick() { } &.gamingLight:hover { - -webkit-text-fill-color: unset !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -206,9 +213,9 @@ async function onClick() { } &.gamingLight:active { - -webkit-text-fill-color: unset !important; color: white; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -216,18 +223,18 @@ async function onClick() { } &.gamingDark { - -webkit-text-fill-color: unset !important; color: black; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight { - -webkit-text-fill-color: unset !important; color: white; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -242,6 +249,78 @@ async function onClick() { } .text { - margin-right: 6px; + margin-right: 6px; +} + +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } } </style> diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index 4a81e8a7c7..f7cf3f2abb 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -5,8 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <button class="_button" - :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou,[$style.gamingDark]: gaming === 'dark' && isFollowing || gaming === 'dark' && hasPendingFollowRequestFromYou,[$style.gamingLight]: gaming === 'light' && isFollowing || gaming === 'light' && hasPendingFollowRequestFromYou -, [$style.full]: full, [$style.large]: large }]" + :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' +,}]" :disabled="wait" @click="onClick" > @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <template v-else> <span v-if="full" - :class="$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ">{{ + :class="[$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ]">{{ i18n.ts.processing }}</span> <MkLoading :em="true" :colored="false"/> @@ -190,37 +190,54 @@ onBeforeUnmount(() => { display: inline-block; font-weight: bold; color: var(--fgOnWhite); - border: solid 1px; + border: solid 1px var(--accent); padding: 0; height: 31px; font-size: 16px; border-radius: 32px; background: #fff; + &.gamingDark { - color: black; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + color: black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; + } &.gamingLight { - color: #dee7e4; + color: white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -webkit-background-clip: text; - -webkit-text-fill-color: transparent; - } + &.full { padding: 0 8px 0 12px; font-size: 14px; + &.gamingDark { + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + + } + + &.gamingLight { + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } } &.large { @@ -270,48 +287,48 @@ onBeforeUnmount(() => { } &.gamingDark:hover { - -webkit-text-fill-color: unset; - color: white; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:active { - -webkit-text-fill-color: unset !important; - color: white; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - border-color: white; + border-color: black; } &.gamingLight:hover { - -webkit-text-fill-color: unset !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - border-color: white; + border-color: black; } &.gamingLight:active { - -webkit-text-fill-color: unset !important; color: white; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - border-color: white; + border-color: black; } &.gamingDark { -webkit-text-fill-color: unset !important; - color: white; - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -320,7 +337,8 @@ onBeforeUnmount(() => { &.gamingLight { -webkit-text-fill-color: unset !important; color: white; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -339,13 +357,13 @@ onBeforeUnmount(() => { margin-right: 6px; &.gamingDark { - color: black; - -webkit-text-fill-color: unset !important; + color: white; + } &.gamingLight { color: white; - -webkit-text-fill-color: unset !important; + } } diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index caa095d757..cd104f6dce 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -62,7 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts"> -import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; +import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus.js'; import MkSwitchButton from '@/components/MkSwitch.button.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu'; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 481f79ed1f..a4f008f9e8 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -292,7 +292,7 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: white !important; + color: black !important; } &.gamingDark { @@ -300,7 +300,7 @@ function more(ev: MouseEvent) { } &.gamingLight:before { - color: white !important; + color: black !important; content: ""; display: block; width: calc(100% - 38px); @@ -320,8 +320,8 @@ function more(ev: MouseEvent) { } &.gamingLight:hover, &.gamingLight.active { - color: white !important; + color: black !important; &.gamingLight:before { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; @@ -502,7 +502,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover { - color: black; + color: white; background-size: 1800% 1800% !important; text-decoration: none; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -511,7 +511,7 @@ function more(ev: MouseEvent) { } &.gamingLight:active { - color: black; + color: white; background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -520,11 +520,10 @@ function more(ev: MouseEvent) { } &.gamingLight:hover, &.gamingLight.active { - color: black; + color: white; &.gamingLight:before { - color: white !important; content: ""; display: block; width: calc(100% - 34px); @@ -659,7 +658,7 @@ function more(ev: MouseEvent) { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - color: black; + color: white; } &.gamingLight:hover, &.gamingLight.active { @@ -670,7 +669,7 @@ function more(ev: MouseEvent) { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - color: black; + color: white; } } @@ -811,8 +810,7 @@ function more(ev: MouseEvent) { &.gamingLight:hover, &.gamingLight.active { text-decoration: none; - color: black; - + color: white; &.gamingLight:before { content: ""; display: block; @@ -857,7 +855,7 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: black; + color: white; } } From 504f71181cd7e053064d5566b75f9edcb86f25d4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 17:16:44 +0900 Subject: [PATCH 065/501] 2023.9.0-rc.1-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2022abc711..a66138916f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-rc.1", + "version": "2023.9.0-rc.1-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 473355a5d9563aa01c6d2ec7f05035b4b0858942 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 19:10:51 +0900 Subject: [PATCH 066/501] fix --- packages/frontend/src/components/MkButton.vue | 6 +- .../src/components/MkFollowButton.vue | 14 +++-- packages/frontend/src/components/MkMenu.vue | 12 ++-- .../frontend/src/components/MkPostForm.vue | 20 +++---- .../components/MkReactionsViewer.reaction.vue | 5 +- packages/frontend/src/components/MkSwitch.vue | 2 +- packages/frontend/src/components/MkTab.vue | 2 + packages/frontend/src/store.ts | 4 +- .../src/ui/_common_/navbar-for-mobile.vue | 2 +- packages/frontend/src/ui/_common_/navbar.vue | 55 +++++++++++++------ 10 files changed, 75 insertions(+), 47 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 6bd59b3906..94eab89ba9 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -319,7 +319,7 @@ function onMousedown(evt: MouseEvent): void { &.gamingDark { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - color: white !important; + color: black; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; @@ -327,7 +327,7 @@ function onMousedown(evt: MouseEvent): void { &:not(:disabled):hover { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% ; - color: white !important; + color: black; -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; @@ -336,7 +336,7 @@ function onMousedown(evt: MouseEvent): void { &:not(:disabled):active { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - color: white !important; + color: black; -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index f7cf3f2abb..c68bf96a0f 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -205,7 +205,7 @@ onBeforeUnmount(() => { -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - + border: solid 1px black; } &.gamingLight { @@ -215,6 +215,7 @@ onBeforeUnmount(() => { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + border: solid 1px white; } &.full { @@ -293,6 +294,7 @@ onBeforeUnmount(() => { -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + border: solid 1px white; } &.gamingDark:active { @@ -302,7 +304,7 @@ onBeforeUnmount(() => { -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - border-color: black; + border: solid 1px white; } &.gamingLight:hover { @@ -311,7 +313,7 @@ onBeforeUnmount(() => { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - border-color: black; + border: solid 1px white; } &.gamingLight:active { @@ -321,12 +323,13 @@ onBeforeUnmount(() => { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - border-color: black; + border: solid 1px white; } &.gamingDark { -webkit-text-fill-color: unset !important; color: black; + border: solid 1px white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -337,6 +340,7 @@ onBeforeUnmount(() => { &.gamingLight { -webkit-text-fill-color: unset !important; color: white; + border: solid 1px white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -357,7 +361,7 @@ onBeforeUnmount(() => { margin-right: 6px; &.gamingDark { - color: white; + color: black; } diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index cd104f6dce..d9341c5872 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -8,30 +8,30 @@ SPDX-License-Identifier: AGPL-3.0-only <div ref="itemsEl" v-hotkey="keymap" class="_popup _shadow" - :class="[$style.root, { [$style.center]: align === 'center', [$style.asDrawer]: asDrawer }]" + :class="[$style.root, { [$style.center]: align === 'center', [$style.asDrawer]: asDrawer },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :style="{ width: (width && !asDrawer) ? width + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '' }" @contextmenu.self="e => e.preventDefault()" > <template v-for="(item, i) in items2"> <div v-if="item === null" role="separator" :class="$style.divider"></div> - <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item]"> + <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> <span>{{ item.text }}</span> </span> - <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item]"> + <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> <span><MkEllipsis/></span> </span> - <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="$style.item" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="[$style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </MkA> - <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="$style.item" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="[$style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </a> - <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </button> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index a3c0596402..f8ef7715f6 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1064,23 +1064,21 @@ defineExpose({ color: var(--fgOnAccent); background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); - &.gamingLight { - + &.gamingLight{ + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + } + &.gamingDark{ + color: white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; } - &.gamingDark{ - color: white; - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; - - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; - } } .headerRightItem { diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index d9c8a8a86c..6da1404bb6 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -192,7 +192,7 @@ useTooltip(buttonEl, async (showing) => { box-shadow: 0 0 0px 1px var(--accent) inset; &.gamingDark{ - color:white; + color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -213,6 +213,9 @@ useTooltip(buttonEl, async (showing) => { color: var(--accent); &.gamingLight{ color: white; + } + &.gamingDark{ + color: black; } } diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 4a14121c70..1c50b27ae3 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.disabled]: disabled && gaming === '', [$style.checked]: checked && gaming === '' , [$style.gamingdarkDisabled]: disabled && gaming === 'dark', [$style.gamingLightDisabled]: disabled && gaming === 'light'}]"> +<div :class="[$style.root, { [$style.disabled]: disabled && gaming === '', [$style.checked]: checked && gaming === '' }]"> <input ref="input" type="checkbox" diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue index 1fef2d020f..4bfba7afe2 100644 --- a/packages/frontend/src/components/MkTab.vue +++ b/packages/frontend/src/components/MkTab.vue @@ -83,6 +83,7 @@ export default defineComponent({ background: var(--accentedBg); &.gamingDark{ + color:white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -90,6 +91,7 @@ export default defineComponent({ animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight{ + color: black; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 7400a6bb47..1030555b09 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -41,8 +41,8 @@ export const noteActions: NoteAction[] = []; export const noteViewInterruptors: NoteViewInterruptor[] = []; export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; -export const bannerDark='https://files.prismisskey.space/misskey/ac141052-7a16-4608-bc08-263566326a6d.jpg' -export const bannerLight ='https://files.prismisskey.space/misskey/e8d13afc-2348-4b13-a64a-f55a19e8d7aa.jpg' +export const bannerDark='https://files.prismisskey.space/misskey/5182a391-5b9f-4ba9-90a5-a692aa59b938.png' +export const bannerLight ='https://files.prismisskey.space/misskey/96e65f59-eab3-47d4-bb1e-d141100bd2fc.png' export const iconDark='https://files.prismisskey.space/misskey/f3b3c9f8-ff2a-474d-a858-64ffe9023e22.png' export const iconLight='https://files.prismisskey.space/misskey/c7e56b1d-4c4f-408f-bf73-3175f4eb26ca.png' diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 54fbf1606a..7b63285aa9 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -377,7 +377,7 @@ function more() { &.gamingDark:hover, &.gamingDark.active { text-decoration: none; - color: white; + color: black; &.gamingDark:before { content: ""; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index a4f008f9e8..8ba1649bec 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -292,15 +292,15 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: black !important; + color: white; } &.gamingDark { - color: black !important; + color: black; } &.gamingLight:before { - color: black !important; + color: white; content: ""; display: block; width: calc(100% - 38px); @@ -321,7 +321,8 @@ function more(ev: MouseEvent) { &.gamingLight:hover, &.gamingLight.active { - color: black !important; + color: white; + &.gamingLight:before { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; @@ -461,7 +462,7 @@ function more(ev: MouseEvent) { } &.gamingDark:hover { - color: white; + color: black; background-size: 1800% 1800%; text-decoration: none; @@ -471,8 +472,7 @@ function more(ev: MouseEvent) { } &.gamingDark.active { - color: white; - + color: black; background-size: 1800% 1800%; -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -480,7 +480,7 @@ function more(ev: MouseEvent) { } &.gamingDark:hover, &.gamingDark.active { - color: white; + color: black; &.gamingDark:before { content: ""; @@ -502,7 +502,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover { - color: white; + color: white; background-size: 1800% 1800% !important; text-decoration: none; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -512,7 +512,6 @@ function more(ev: MouseEvent) { &.gamingLight:active { color: white; - background-size: 1800% 1800% !important; -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; @@ -520,7 +519,7 @@ function more(ev: MouseEvent) { } &.gamingLight:hover, &.gamingLight.active { - color: white; + color: white; &.gamingLight:before { @@ -562,9 +561,17 @@ function more(ev: MouseEvent) { color: white; } + &.gamingDark.active { + color: black; + } + &.gamingLight { color: black; } + + &.gamingLight.active { + color: white; + } } .itemText { @@ -616,6 +623,7 @@ function more(ev: MouseEvent) { display: block; position: relative; width: 100%; + color: black; height: 52px; margin-bottom: 16px; text-align: center; @@ -669,12 +677,12 @@ function more(ev: MouseEvent) { -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - color: white; + color: white; } } &.gamingDark:before { - color: white; + color: black; content: ""; display: block; position: absolute; @@ -750,16 +758,20 @@ function more(ev: MouseEvent) { width: 100%; text-align: center; + &.gamingLight { + color: white; + } + &:hover, &.active { text-decoration: none; color: var(--accent); &.gamingDark { - color: white; + color: black; } &.gamingLight { - color: #DEE7E4; + color: white; } &:before { @@ -810,7 +822,8 @@ function more(ev: MouseEvent) { &.gamingLight:hover, &.gamingLight.active { text-decoration: none; - color: white; + color: white !important; + &.gamingLight:before { content: ""; display: block; @@ -854,8 +867,16 @@ function more(ev: MouseEvent) { color: white; } + &.gamingDark.active { + color: black; + } + &.gamingLight { - color: white; + color: black; + } + + &.gamingLight.active { + color: white; } } From 60aafe946f65add7c89ed7e8bc5e6d6a5b526e30 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 19:11:21 +0900 Subject: [PATCH 067/501] 2023.9.0-rc.1-prismisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a66138916f..8fc177e0cd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-rc.1-prismisskey.1", + "version": "2023.9.0-rc.1-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From 62fbe750f75e0bba924fa1a6917b49850fe5be09 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 21:00:32 +0900 Subject: [PATCH 068/501] fix --- packages/frontend/src/ui/_common_/navbar.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 8ba1649bec..5c87859cd4 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -426,11 +426,11 @@ function more(ev: MouseEvent) { color: var(--navFg); &.gamingDark { - color: white; + color: var(--navFg); } &.gamingLight { - color: black; + color: var(--navFg); } &:hover { From 7a82a2269c17f18f9ddcb15eefa55eb6d5f3ec5b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Sep 2023 21:00:57 +0900 Subject: [PATCH 069/501] 2023.9.0-rc.2-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e5540f68d..2423f11199 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-rc.2", + "version": "2023.9.0-rc.2-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 6c699f14bc10f4f8ebae2eb09a941a165fb33ec3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 00:33:52 +0900 Subject: [PATCH 070/501] fix --- packages/frontend/src/components/MkSignupDialog.form.vue | 3 ++- packages/frontend/src/components/MkSignupDialog.rules.vue | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 14cb91c85c..626804dcce 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -366,15 +366,16 @@ async function onSubmit(): Promise<void> { -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + color: var(--navFg); } &.gamingLight { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + color: var(--navFg); } } diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index b4633f419d..86a8bdc4e8 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -174,15 +174,16 @@ async function updateAgreeNote(v: boolean) { -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + color: var(--navFg); } &.gamingLight { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + color: var(--navFg); } } From a550ad12c55dc965c2d94b41eefb71de32fa5c24 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 07:41:26 +0900 Subject: [PATCH 071/501] fix --- packages/frontend/src/ui/_common_/navbar.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 5c87859cd4..868a07cf75 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -292,7 +292,7 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: white; + color: var(--fg); } &.gamingDark { @@ -759,7 +759,7 @@ function more(ev: MouseEvent) { text-align: center; &.gamingLight { - color: white; + color: var(--fg); } &:hover, &.active { @@ -771,7 +771,7 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: white; + color: var(--fg); } &:before { From 9474945d847986120e1712af3191ab708b36e8ae Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 07:57:11 +0900 Subject: [PATCH 072/501] fix --- packages/frontend/src/ui/_common_/navbar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 868a07cf75..86aea36808 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -292,7 +292,7 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: var(--fg); + color: white; } &.gamingDark { From 4b64870ee832f47cf2bd7c015992c1531c523fa9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 16:39:47 +0900 Subject: [PATCH 073/501] fix --- .../frontend/src/components/MkVisitorDashboard.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index ae54fa1428..bed62f7be9 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -191,13 +191,21 @@ function exploreOtherServers() { } .gamingDark{ + color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; } - +.gamingDark:hover{ + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd) !important; + background-size: 1800% 1800% !important; + -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; +} .stats { display: grid; grid-template-columns: 1fr 1fr; From e1cd062c16d4b7903b1952956a2f8303b6792f82 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 16:41:56 +0900 Subject: [PATCH 074/501] 2023.9.0-rc.4-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 166744853d..11484054ce 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-rc.4", + "version": "2023.9.0-rc.4-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 7f74060f8d35cb86f655e92cb2263db23c61fa04 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 18:15:35 +0900 Subject: [PATCH 075/501] 2023.9.0-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 67e41b6de6..e5fda1714a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0", + "version": "2023.9.0-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From ee54a5ab2afb8261a14d8723df205e1a1b9ab953 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 18:44:08 +0900 Subject: [PATCH 076/501] 2023.9.0-prismisskey.1 --- packages/frontend/src/components/MkMenu.vue | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index cda7bb377b..fc878d4733 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -87,8 +87,7 @@ if (darkMode.value && gamingMode.value == true) { watch(darkMode, () => { if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; - document.documentElement.style.setProperty('--accent', '#fff'); - document.documentElement.style.setProperty('--accentLighten', '#fff'); + } else if (!darkMode.value && gamingMode.value == true) { gaming.value = 'light'; } else { @@ -99,8 +98,7 @@ watch(darkMode, () => { watch(gamingMode, () => { if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; - document.documentElement.style.setProperty('--accent', '#fff'); - document.documentElement.style.setProperty('--accentLighten', '#fff'); + } else if (!darkMode.value && gamingMode.value == true) { gaming.value = 'light'; } else { From 0071cb09a9ba6a2d6b673bc10bcdededb5a01ae9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 18:49:11 +0900 Subject: [PATCH 077/501] 2023.9.0-prismisskey.1 --- packages/frontend/src/components/MkMenu.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index fc878d4733..f227987975 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -76,8 +76,7 @@ const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; - document.documentElement.style.setProperty('--accent', '#fff'); - document.documentElement.style.setProperty('--accentLighten', '#fff'); + } else if (!darkMode.value && gamingMode.value == true) { gaming.value = 'light'; } else { From da147417e98d36b878f8732d2d2911eba767970c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Sep 2023 21:12:25 +0900 Subject: [PATCH 078/501] fix emoji --- .../src/server/api/endpoints/admin/emoji/add.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index ed13ef5365..66b8bcbe9d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -71,20 +71,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const duplicationEmoji = await this.emojisRepository.find({ - where: { - name: ps.name, - }, - }); - - duplicationEmoji.forEach( - (emoji) => { - if (emoji.name === ps.name) { - throw new ApiError(meta.errors.duplicationEmojiAdd); - } - } - ) - const emoji = await this.customEmojiService.add({ driveFile, name: ps.name, From 9fe170a4883e53abe1d59a9925c2d4c16f34741d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 02:02:33 +0900 Subject: [PATCH 079/501] gaming lighting speed change --- packages/frontend/src/components/MkButton.vue | 36 +++---- .../src/components/MkChannelFollowButton.vue | 48 +++++----- .../src/components/MkFollowButton.vue | 60 ++++++------ .../frontend/src/components/MkLaunchPad.vue | 12 +-- .../frontend/src/components/MkMention.vue | 12 +-- packages/frontend/src/components/MkMenu.vue | 36 +++---- .../frontend/src/components/MkPostForm.vue | 12 +-- packages/frontend/src/components/MkRadio.vue | 12 +-- packages/frontend/src/components/MkRange.vue | 36 +++---- .../components/MkReactionsViewer.reaction.vue | 12 +-- .../src/components/MkSignupDialog.form.vue | 12 +-- .../src/components/MkSignupDialog.rules.vue | 12 +-- .../frontend/src/components/MkSuperMenu.vue | 12 +-- .../src/components/MkSwitch.button.vue | 12 +-- packages/frontend/src/components/MkSwitch.vue | 24 ++--- packages/frontend/src/components/MkTab.vue | 12 +-- .../src/components/MkUserSetupDialog.vue | 12 +-- .../src/components/MkVisitorDashboard.vue | 12 +-- .../components/global/MkPageHeader.tabs.vue | 12 +-- .../frontend/src/pages/settings/general.vue | 7 +- packages/frontend/src/store.ts | 5 + packages/frontend/src/style.scss | 2 +- .../src/ui/_common_/navbar-for-mobile.vue | 36 +++---- packages/frontend/src/ui/_common_/navbar.vue | 96 +++++++++---------- 24 files changed, 276 insertions(+), 266 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 94eab89ba9..379b928477 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -293,26 +293,26 @@ function onMousedown(evt: MouseEvent): void { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; color: white !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; &:not(:disabled):hover { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; color: white !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &:not(:disabled):active { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; color: white !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite ; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; } } @@ -320,26 +320,26 @@ function onMousedown(evt: MouseEvent): void { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; color: black; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; &:not(:disabled):hover { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% ; color: black; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; } &:not(:disabled):active { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; color: black; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite ; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; - animation: AnimationDark 45s cubic-bezier(0, 0.45, 0.30, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; } } diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue index c95fad0789..888866be56 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.vue +++ b/packages/frontend/src/components/MkChannelFollowButton.vue @@ -123,18 +123,18 @@ async function onClick() { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight { color: #fff; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } @@ -188,27 +188,27 @@ async function onClick() { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:active { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; border-color: white; } &.gamingLight:hover { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border-color: white; } @@ -216,9 +216,9 @@ async function onClick() { color: white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border-color: white; } @@ -226,18 +226,18 @@ async function onClick() { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight { color: white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index c68bf96a0f..97ea03c103 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -202,9 +202,9 @@ onBeforeUnmount(() => { color: black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; border: solid 1px black; } @@ -212,9 +212,9 @@ onBeforeUnmount(() => { color: white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border: solid 1px white; } @@ -225,9 +225,9 @@ onBeforeUnmount(() => { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } @@ -235,9 +235,9 @@ onBeforeUnmount(() => { color: white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -291,9 +291,9 @@ onBeforeUnmount(() => { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; border: solid 1px white; } @@ -301,18 +301,18 @@ onBeforeUnmount(() => { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; border: solid 1px white; } &.gamingLight:hover { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border: solid 1px white; } @@ -320,9 +320,9 @@ onBeforeUnmount(() => { color: white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; border: solid 1px white; } @@ -332,9 +332,9 @@ onBeforeUnmount(() => { border: solid 1px white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight { @@ -343,9 +343,9 @@ onBeforeUnmount(() => { border: solid 1px white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index c6684ccb87..75ccd06074 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -131,16 +131,16 @@ function close() { border-radius: 10px; &.gamingDark:hover{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; color: black; } &.gamingLight:hover{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; color: white; } &:hover { diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index d877565a33..ec8a30db69 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -82,17 +82,17 @@ const bgCss = (gaming.value === '') ? bg.toRgbString() : ""; color: white; opacity: 0.9; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingDark{ opacity: 0.9; color: white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.isMe { diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index f227987975..73a9b55fef 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -340,17 +340,17 @@ onBeforeUnmount(() => { &.gamingDark:before{ color:black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight:before{ color:white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } @@ -392,17 +392,17 @@ onBeforeUnmount(() => { &.gamingDark:before{ color:black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight:before{ color:white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -452,17 +452,17 @@ onBeforeUnmount(() => { &.gamingDark:before{ color:black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight:before{ color:white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index f8ef7715f6..ce910220da 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1067,17 +1067,17 @@ defineExpose({ &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; } &.gamingDark{ color: white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; } } diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index 18195a0067..b7344cffe4 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -111,17 +111,17 @@ function toggle(): void { border-color: black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; } &.gamingLight{ color:white; border-color: white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; } > .button { border-color: var(--accent); diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 4322eb2dfb..2cf4b6c994 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -244,16 +244,16 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } @@ -290,32 +290,32 @@ const onMousedown = (ev: MouseEvent | TouchEvent) => { &.gamingDark{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &:hover { background: var(--accentLighten); &.gamingDark{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } } } diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 6da1404bb6..558e34504b 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -194,17 +194,17 @@ useTooltip(buttonEl, async (showing) => { &.gamingDark{ color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; box-shadow: 0 0 0px 1px white inset; } &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; box-shadow: 0 0 0px 1px white inset; color: white !important; } diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 626804dcce..495b1a81ea 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -363,18 +363,18 @@ async function onSubmit(): Promise<void> { &.gamingDark { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; color: var(--navFg); } &.gamingLight { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; color: var(--navFg); } diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index 86a8bdc4e8..8b0db57773 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -171,18 +171,18 @@ async function updateAgreeNote(v: boolean) { &.gamingDark { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; color: var(--navFg); } &.gamingLight { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationLight cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; color: var(--navFg); } diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index c4903419fc..ea7488bd88 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -105,17 +105,17 @@ defineProps<{ &.gamingDark{ color: black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight{ color: white; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue index 1cf6bd86a9..3cda474da3 100644 --- a/packages/frontend/src/components/MkSwitch.button.vue +++ b/packages/frontend/src/components/MkSwitch.button.vue @@ -113,17 +113,17 @@ const toggle = () => { .gamingLight { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } .gamingDark { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } .buttonDisabled { diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 1c50b27ae3..25c20e67a5 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -104,9 +104,9 @@ const toggle = () => { cursor: not-allowed; background: linear-gradient(270deg, #a84f4f, #a88c4f, #9aa24b, #6da85c, #53a8a6, #7597b5, #8679b5, #b579b5, #b56d96); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; } &.gamingLightDisabled{ @@ -114,9 +114,9 @@ const toggle = () => { cursor: not-allowed; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; } //&.checked { @@ -139,17 +139,17 @@ const toggle = () => { &.gamingDark { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.25, 0.25, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; } &.gamingLight{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; } } diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue index 4bfba7afe2..47cf3eda04 100644 --- a/packages/frontend/src/components/MkTab.vue +++ b/packages/frontend/src/components/MkTab.vue @@ -86,17 +86,17 @@ export default defineComponent({ color:white; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight{ color: black; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index 45317f3bda..9dfaf9471d 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -238,16 +238,16 @@ async function later(later: boolean) { &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index bed62f7be9..66a4588d54 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -194,17 +194,17 @@ function exploreOtherServers() { color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } .gamingDark:hover{ color: black; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd) !important; background-size: 1800% 1800% !important; - -webkit-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation:AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation:AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } .stats { display: grid; diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index e40d26db76..9c6999bd2d 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -278,16 +278,16 @@ onUnmounted(() => { &.gamingLight{ background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingDark{ background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.animate { transition: width 0.15s ease, left 0.15s ease; diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 1fbc31b15c..2eec6b8dac 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -166,6 +166,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.numberOfPageCache }}</template> <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> </MkRange> + <MkRange v-model="numberOfGamingSpeed" :min="1" :max="60" :step="1" easing> + </MkRange> </div> </FormSection> @@ -250,6 +252,7 @@ const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); +const numberOfGamingSpeed = computed(defaultStore.makeGetterSetter('numberOfGamingSpeed')); const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); @@ -266,7 +269,9 @@ watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); }); - +watch(numberOfGamingSpeed, () =>{ + document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); +}) watch(fontSize, () => { if (fontSize.value == null) { miLocalStorage.removeItem('fontSize'); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 1030555b09..e74116e041 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -90,6 +90,7 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: 'yyyy-MM-dd HH-mm-ss [{{number}}]', }, + keepOriginalUploading: { where: 'account', default: false, @@ -346,6 +347,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 3, }, + numberOfGamingSpeed: { + where: 'device', + default: 44, + }, showNoteActionsOnlyHover: { where: 'device', default: false, diff --git a/packages/frontend/src/style.scss b/packages/frontend/src/style.scss index a2c860403a..9ee1fa2a36 100644 --- a/packages/frontend/src/style.scss +++ b/packages/frontend/src/style.scss @@ -12,7 +12,7 @@ --marginHalf: 10px; --margin: var(--marginFull); - + --gamingspeed: 43s; // switch dynamically --minBottomSpacingMobile: calc(72px + max(12px, env(safe-area-inset-bottom, 0px))); --minBottomSpacing: var(--minBottomSpacingMobile); diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 7b63285aa9..33e9522d4b 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -247,18 +247,18 @@ function more() { border-radius: 999px; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:hover, &.gamingLight.active { &.gamingLight:before { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -276,18 +276,18 @@ function more() { border-radius: 999px; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { &.gamingDark:before { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -393,9 +393,9 @@ function more() { border-radius: 999px; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -417,9 +417,9 @@ function more() { border-radius: 999px; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 86aea36808..f3ebf38ccc 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -314,9 +314,9 @@ function more(ev: MouseEvent) { border-radius: 999px; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:hover, &.gamingLight.active { @@ -326,9 +326,9 @@ function more(ev: MouseEvent) { &.gamingLight:before { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -347,9 +347,9 @@ function more(ev: MouseEvent) { border-radius: 999px; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { @@ -358,9 +358,9 @@ function more(ev: MouseEvent) { &.gamingDark:before { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -466,17 +466,17 @@ function more(ev: MouseEvent) { background-size: 1800% 1800%; text-decoration: none; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark.active { color: black; background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { @@ -495,9 +495,9 @@ function more(ev: MouseEvent) { bottom: 0; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -505,17 +505,17 @@ function more(ev: MouseEvent) { color: white; background-size: 1800% 1800% !important; text-decoration: none; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:active { color: white; background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } &.gamingLight:hover, &.gamingLight.active { @@ -536,9 +536,9 @@ function more(ev: MouseEvent) { border-radius: 999px; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } @@ -663,9 +663,9 @@ function more(ev: MouseEvent) { border-radius: 100%; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; color: white; } @@ -674,9 +674,9 @@ function more(ev: MouseEvent) { &.gamingLight:before { background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; color: white; } } @@ -696,9 +696,9 @@ function more(ev: MouseEvent) { border-radius: 100%; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800%; - -webkit-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - -moz-animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; - animation: AnimationDark 44s cubic-bezier(0, 0.2, 0.90, 1) infinite; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingDark:hover, &.gamingDark.active { @@ -707,9 +707,9 @@ function more(ev: MouseEvent) { &.gamingDark:before { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -814,9 +814,9 @@ function more(ev: MouseEvent) { border-radius: 999px; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800% !important; - -webkit-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationDark 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } @@ -838,9 +838,9 @@ function more(ev: MouseEvent) { border-radius: 100%; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - -moz-animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - animation: AnimationLight 45s cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; } } } From 235df532efdcefdc5fb41d0cee855e1ffc5ce4c2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 02:07:30 +0900 Subject: [PATCH 080/501] =?UTF-8?q?navbar=E3=81=AB=E3=82=AD=E3=83=A3?= =?UTF-8?q?=E3=83=83=E3=82=B7=E3=83=A5=E3=82=AF=E3=83=AA=E3=82=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/navbar.ts | 14 ++++++++++++++ packages/frontend/src/store.ts | 1 + 2 files changed, 15 insertions(+) diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 7f182a98f7..36d1a3187d 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -12,6 +12,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { ui } from '@/config.js'; import { unisonReload } from '@/scripts/unison-reload.js'; +import {fetchCustomEmojis} from "@/custom-emojis.js"; export const navbarItemDef = reactive({ notifications: { @@ -155,4 +156,17 @@ export const navbarItemDef = reactive({ show: computed(() => $i != null), to: `/@${$i?.username}`, }, + cacheclear: { + icon: 'ti ti-trash', + title: i18n.ts.clearCache, + action: async () => { + os.waiting(); + miLocalStorage.removeItem('locale'); + miLocalStorage.removeItem('theme'); + miLocalStorage.removeItem('emojis'); + miLocalStorage.removeItem('lastEmojisFetchedAt'); + await fetchCustomEmojis(true); + unisonReload(); + } + }, }); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index e74116e041..18aef358bd 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -133,6 +133,7 @@ export const defaultStore = markRaw(new Storage('base', { 'search', '-', 'ui', + 'cacheclear', ], }, visibility: { From 8154d86a9dc72932a585df74928a21e308a9f695 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 02:56:53 +0900 Subject: [PATCH 081/501] =?UTF-8?q?widget=E3=81=AB=E3=82=B2=E3=83=BC?= =?UTF-8?q?=E3=83=9F=E3=83=B3=E3=82=B0=E3=81=AE=E3=82=AA=E3=83=B3=E3=82=AA?= =?UTF-8?q?=E3=83=95=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 ++ .../frontend/src/widgets/WidgetGamingMode.vue | 43 +++++++++++++++++++ packages/frontend/src/widgets/index.ts | 2 + 3 files changed, 48 insertions(+) create mode 100644 packages/frontend/src/widgets/WidgetGamingMode.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index 1517bb1603..408420fa7c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -828,6 +828,8 @@ export interface Locale { "high": string; "middle": string; "low": string; + "GamingSpeedChange": string; + "GamingSpeedChangeInfo": string; "emailNotConfiguredWarning": string; "ratio": string; "previewNoteText": string; @@ -1946,6 +1948,7 @@ export interface Locale { "instanceInfo": string; "memo": string; "notifications": string; + "gamingMode": string; "timeline": string; "calendar": string; "trends": string; diff --git a/packages/frontend/src/widgets/WidgetGamingMode.vue b/packages/frontend/src/widgets/WidgetGamingMode.vue new file mode 100644 index 0000000000..966e223087 --- /dev/null +++ b/packages/frontend/src/widgets/WidgetGamingMode.vue @@ -0,0 +1,43 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div> + <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> +</div> +</template> + +<script lang="ts" setup> +import { Interpreter, Parser } from '@syuilo/aiscript'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { GetFormResultType } from '@/scripts/form.js'; +import MkButton from '@/components/MkButton.vue'; +import {i18n} from "@/i18n.js"; +import MkSwitch from "@/components/MkSwitch.vue"; +import {computed} from "vue"; +import {defaultStore} from "@/store.js"; +const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const name = 'gamingMode'; + +const widgetPropsDef = { +}; + +type WidgetProps = GetFormResultType<typeof widgetPropsDef>; + +const props = defineProps<WidgetComponentProps<WidgetProps>>(); +const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); + +const { widgetProps, configure } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index 405c49ab06..655ba609ed 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -10,6 +10,7 @@ export default function(app: App) { app.component('WidgetInstanceInfo', defineAsyncComponent(() => import('./WidgetInstanceInfo.vue'))); app.component('WidgetMemo', defineAsyncComponent(() => import('./WidgetMemo.vue'))); app.component('WidgetNotifications', defineAsyncComponent(() => import('./WidgetNotifications.vue'))); + app.component('WidgetGamingMode', defineAsyncComponent(() => import('./WidgetGamingMode.vue'))); app.component('WidgetTimeline', defineAsyncComponent(() => import('./WidgetTimeline.vue'))); app.component('WidgetCalendar', defineAsyncComponent(() => import('./WidgetCalendar.vue'))); app.component('WidgetRss', defineAsyncComponent(() => import('./WidgetRss.vue'))); @@ -40,6 +41,7 @@ export const widgets = [ 'instanceInfo', 'memo', 'notifications', + 'gamingMode', 'timeline', 'calendar', 'rss', From 1a9d3737b1948e8a63811be5f591e07cb749dc6b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 02:57:48 +0900 Subject: [PATCH 082/501] =?UTF-8?q?=E3=82=B2=E3=83=BC=E3=83=9F=E3=83=B3?= =?UTF-8?q?=E3=82=B0=E3=81=AE=E3=82=B9=E3=83=94=E3=83=BC=E3=83=89=E3=81=AE?= =?UTF-8?q?=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ja-JP.yml | 3 +++ packages/frontend/src/pages/settings/general.vue | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 2093613e98..ff31c96a77 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -825,6 +825,8 @@ priority: "優先度" high: "高" middle: "中" low: "低" +GamingSpeedChange: "ゲーミングの光るスピードの調整" +GamingSpeedChangeInfo: "左にすれば早くなる、右にすれば遅くなる。それだけ。" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" previewNoteText: "本文をプレビュー" @@ -1863,6 +1865,7 @@ _widgets: instanceInfo: "サーバー情報" memo: "付箋" notifications: "通知" + gamingMode: "ゲーミングモード" timeline: "タイムライン" calendar: "カレンダー" trends: "トレンド" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 2eec6b8dac..ba4a630ecf 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -167,6 +167,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> </MkRange> <MkRange v-model="numberOfGamingSpeed" :min="1" :max="60" :step="1" easing> + <template #label>{{ i18n.ts.GamingSpeedChange }}</template> + <template #caption>{{ i18n.ts.GamingSpeedChangeInfo }}</template> </MkRange> </div> </FormSection> @@ -269,6 +271,7 @@ watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); }); +document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); watch(numberOfGamingSpeed, () =>{ document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); }) @@ -303,7 +306,6 @@ watch([ highlightSensitiveMedia, keepScreenOn, showMediaTimeline, - enableGamingMode ], async () => { await reloadAsk(); }); From 5c7f434126eb4e0635dd588b295e0bfe6f4cb2f5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 02:57:53 +0900 Subject: [PATCH 083/501] 2023.9.0-prismisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e5fda1714a..da1ae511d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.0-prismisskey.1", + "version": "2023.9.0-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From 6fef96e6577117ecd72caf645e88048fd78ace4f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 18:10:01 +0900 Subject: [PATCH 084/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=80=81?= =?UTF-8?q?=E8=A6=8B=E3=82=84=E3=81=99=E3=81=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/global/MkCustomEmoji.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index e20cc12c84..2d496e5fb9 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -69,7 +69,8 @@ let errored = $ref(url.value == null); height: 2em; vertical-align: middle; transition: transform 0.2s ease; - + max-width: 100% !important; + object-fit: contain !important; &:hover { transform: scale(1.2); } From b8c4c09e59c175fd2b6ac7407cb8b3bc8f70d383 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 21:12:22 +0900 Subject: [PATCH 085/501] =?UTF-8?q?=E5=85=AC=E9=96=8B=E7=AF=84=E5=9B=B2?= =?UTF-8?q?=E3=80=81=E8=A6=8B=E3=82=84=E3=81=99=E3=81=8F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/components/MkNote.vue | 24 +++++++++--- .../frontend/src/pages/settings/general.vue | 38 ++++++++++++++++++- packages/frontend/src/store.ts | 16 ++++++++ .../src/ui/_common_/navbar-for-mobile.vue | 14 +++++++ packages/frontend/src/ui/_common_/navbar.vue | 15 ++++++++ 7 files changed, 102 insertions(+), 7 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 408420fa7c..c8562a2c36 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -832,6 +832,7 @@ export interface Locale { "GamingSpeedChangeInfo": string; "emailNotConfiguredWarning": string; "ratio": string; + "showVisibilityColor": string; "previewNoteText": string; "customCss": string; "customCssWarn": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ff31c96a77..e9a5576ca1 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -829,6 +829,7 @@ GamingSpeedChange: "ゲーミングの光るスピードの調整" GamingSpeedChangeInfo: "左にすれば早くなる、右にすれば遅くなる。それだけ。" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" +showVisibilityColor: "ノートの公開範囲を色付けする" previewNoteText: "本文をプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index b397f3eee9..223e8f818d 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -9,7 +9,8 @@ SPDX-License-Identifier: AGPL-3.0-only v-show="!isDeleted" ref="el" v-hotkey="keymap" - :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" + :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover } ,{[$style.home] : defaultStore.state.showVisibilityColor && note.visibility === 'home',[$style.followers] : defaultStore.state.showVisibilityColor && note.visibility === 'followers',[$style.specified] : defaultStore.state.showVisibilityColor && note.visibility === 'specified'}]" + :tabindex="!isDeleted ? '-1' : undefined" > <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> @@ -33,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTime :time="note.createdAt"/> </button> <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> - <i v-if="note.visibility === 'home'" class="ti ti-home"></i> + <i v-if="note.visibility === 'home'" class="ti ti-home" ></i> <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> @@ -176,8 +177,7 @@ const props = defineProps<{ }>(); const inChannel = inject('inChannel', null); -const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); - +const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);; let note = $ref(deepClone(props.note)); // plugin @@ -480,6 +480,8 @@ function focusAfter() { focusNext(el.value); } + + function readPromo() { os.api('promo/read', { noteId: appearNote.id, @@ -489,13 +491,22 @@ function readPromo() { </script> <style lang="scss" module> + .root { position: relative; transition: box-shadow 0.1s ease; font-size: 1.05em; overflow: clip; contain: content; - +&.home{ + background-color: rgba( var(--homeColor) , 0.20) !important; +} + &.followers{ + background-color: rgba(var(--followerColor), 0.20) !important; + } + &.specified{ + background-color: rgba(var(--specifiedColor), 0.20) !important; + } // これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、 // 下の方までスクロールすると上のノートの高さがここで決め打ちされたものに変化し、表示しているノートの位置が変わってしまう // ノートがマウントされたときに自身の高さを取得し contain-intrinsic-size を設定しなおせばほぼ解決できそうだが、 @@ -940,4 +951,7 @@ function readPromo() { padding: 0 6px; opacity: .8; } +.root:has(.ti-home){ + background-color: rgba(255, 255, 100, 0.10) !important; +} </style> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index ba4a630ecf..1c1207770a 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -48,6 +48,16 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch> + <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor}}</MkSwitch> + <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> + <template #label>{{ i18n.ts._visibility.home }}</template> + </MkColorInput> + <MkColorInput v-if="showVisibilityColor" v-model="followerColor"> + <template #label>{{ i18n.ts._visibility.followers }}</template> + </MkColorInput> + <MkColorInput v-if="showVisibilityColor" v-model="specifiedColor"> + <template #label>{{ i18n.ts._visibility.specified }}</template> + </MkColorInput> <MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch> <MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch> <MkSwitch v-model="showGapBetweenNotesInTimeline">{{ i18n.ts.showGapBetweenNotesInTimeline }}</MkSwitch> @@ -212,6 +222,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; import { globalEvents } from '@/events'; import { claimAchievement } from '@/scripts/achievements.js'; +import MkColorInput from "@/components/MkColorInput.vue"; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -255,6 +266,9 @@ const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostF const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); const numberOfGamingSpeed = computed(defaultStore.makeGetterSetter('numberOfGamingSpeed')); +const homeColor = computed(defaultStore.makeGetterSetter('homeColor')); +const followerColor = computed(defaultStore.makeGetterSetter('followerColor')); +const specifiedColor = computed(defaultStore.makeGetterSetter('specifiedColor')); const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); @@ -266,12 +280,32 @@ const showTimelineReplies = computed(defaultStore.makeGetterSetter('showTimeline const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); - +const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); }); + document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); +function hexToRgb(hex) { + // 16進数のカラーコードから "#" を除去 + hex = hex.replace(/^#/, ''); + + // 16進数をRGBに変換 + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + + return `${r},${g},${b}`; +} +document.documentElement.style.setProperty('--homeColor', hexToRgb(homeColor.value)); +document.documentElement.style.setProperty("--followerColor",hexToRgb(followerColor.value)); +document.documentElement.style.setProperty("--specifiedColor",hexToRgb(specifiedColor.value)) +watch([homeColor,specifiedColor,followerColor], ()=>{ + document.documentElement.style.setProperty('--homeColor', hexToRgb(homeColor.value)); + document.documentElement.style.setProperty("--followerColor",hexToRgb(followerColor.value)); + document.documentElement.style.setProperty("--specifiedColor",hexToRgb(specifiedColor.value)) +}) watch(numberOfGamingSpeed, () =>{ document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); }) @@ -290,7 +324,6 @@ watch(useSystemFont, () => { miLocalStorage.removeItem('useSystemFont'); } }); - watch([ lang, fontSize, @@ -306,6 +339,7 @@ watch([ highlightSensitiveMedia, keepScreenOn, showMediaTimeline, + showVisibilityColor, ], async () => { await reloadAsk(); }); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 18aef358bd..d48517aaf2 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -348,6 +348,18 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 3, }, + specifiedColor:{ + where: 'device', + default: '#FFFF64', + }, + followerColor:{ + where: 'device', + default: '#FF00FF', + }, + homeColor:{ + where: 'device', + default: '#00FFFF', + }, numberOfGamingSpeed: { where: 'device', default: 44, @@ -364,6 +376,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true, }, + showVisibilityColor:{ + where: 'device', + default: false, + }, reactionsDisplaySize: { where: 'device', default: 'medium' as 'small' | 'medium' | 'large', diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 33e9522d4b..47a3a7d6ad 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -84,9 +84,23 @@ import {instance} from '@/instance'; let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); +function hexToRgb(hex) { + // 16進数のカラーコードから "#" を除去 + hex = hex.replace(/^#/, ''); + // 16進数をRGBに変換 + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + + return `${r},${g},${b}`; +} const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +document.documentElement.style.setProperty('--homeColor', hexToRgb(defaultStore.state.homeColor)); +document.documentElement.style.setProperty("--followerColor",hexToRgb(defaultStore.state.followerColor)); +document.documentElement.style.setProperty("--specifiedColor",hexToRgb(defaultStore.state.specifiedColor)) +document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed+'s'); let gaming = ref() if (darkMode.value) { diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index f3ebf38ccc..93e986e638 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -94,6 +94,21 @@ import {$i, openAccountMenu as openAccountMenu_} from '@/account'; import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; import {i18n} from '@/i18n'; import {instance} from '@/instance'; +function hexToRgb(hex) { + // 16進数のカラーコードから "#" を除去 + hex = hex.replace(/^#/, ''); + + // 16進数をRGBに変換 + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); + + return `${r},${g},${b}`; +} +document.documentElement.style.setProperty('--homeColor', hexToRgb(defaultStore.state.homeColor)); +document.documentElement.style.setProperty("--followerColor",hexToRgb(defaultStore.state.followerColor)); +document.documentElement.style.setProperty("--specifiedColor",hexToRgb(defaultStore.state.specifiedColor)) +document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed+'s'); const iconOnly = ref(false); let bannerUrl = ref(defaultStore.state.bannerUrl); From 0b89548481d4615270da30532f4d4896c995db1c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 25 Sep 2023 21:14:12 +0900 Subject: [PATCH 086/501] 2023.9.1-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb82b503a2..6d4f38e7ec 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.1", + "version": "2023.9.1-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From bc12bba97d34bec0a834da85fdfd677aed715e1f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 26 Sep 2023 10:54:01 +0900 Subject: [PATCH 087/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E5=91=A8=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/core/CustomEmojiService.ts | 29 +++++++++++++ .../backend/src/server/api/EndpointsModule.ts | 8 ++++ packages/backend/src/server/api/endpoints.ts | 5 ++- .../admin/emoji/set-issensitive-bulk.ts | 41 +++++++++++++++++++ .../admin/emoji/set-localonly-bulk.ts | 41 +++++++++++++++++++ packages/frontend/src/components/MkDialog.vue | 11 +++-- packages/frontend/src/os.ts | 22 +++++++++- .../src/pages/custom-emojis-manager.vue | 28 ++++++++++++- 8 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/set-issensitive-bulk.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/set-localonly-bulk.ts diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 1b545a124e..2bdf89ffa1 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -231,7 +231,36 @@ export class CustomEmojiService implements OnApplicationShutdown { emojis: await this.emojiEntityService.packDetailedMany(ids), }); } + @bindThis + public async setLocalOnlyBulk(ids: MiEmoji['id'][], localOnly: boolean | false) { + await this.emojisRepository.update({ + id: In(ids), + }, { + updatedAt: new Date(), + localOnly: localOnly, + }); + this.localEmojisCache.refresh(); + + this.globalEventService.publishBroadcastStream('emojiUpdated', { + emojis: await this.emojiEntityService.packDetailedMany(ids), + }); + } + @bindThis + public async setisSensitiveBulk(ids: MiEmoji['id'][], isSensitive: boolean | false) { + await this.emojisRepository.update({ + id: In(ids), + }, { + updatedAt: new Date(), + isSensitive: isSensitive, + }); + + this.localEmojisCache.refresh(); + + this.globalEventService.publishBroadcastStream('emojiUpdated', { + emojis: await this.emojiEntityService.packDetailedMany(ids), + }); + } @bindThis public async setLicenseBulk(ids: MiEmoji['id'][], license: string | null) { await this.emojisRepository.update({ diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 7b127cc35e..7cac90b161 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -24,6 +24,8 @@ import * as ep___admin_drive_cleanup from './endpoints/admin/drive/cleanup.js'; import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; +import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js'; +import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; @@ -373,6 +375,8 @@ const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useC const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }; const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; +const $admin_emoji_setlocalOnlyBulk: Provider = { provide: 'ep:admin/emoji/set-localonly-bulk', useClass: ep___admin_emoji_setlocalOnlyBulk.default }; +const $admin_emoji_setisSensitiveBulk: Provider = { provide: 'ep:admin/emoji/set-issensitive-bulk', useClass: ep___admin_emoji_setisSensitiveBulk.default }; const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; @@ -726,6 +730,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_files, $admin_drive_showFile, $admin_emoji_addAliasesBulk, + $admin_emoji_setlocalOnlyBulk, + $admin_emoji_setisSensitiveBulk, $admin_emoji_add, $admin_emoji_copy, $admin_emoji_deleteBulk, @@ -1084,6 +1090,8 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, + $admin_emoji_setlocalOnlyBulk, + $admin_emoji_setisSensitiveBulk, $admin_emoji_update, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 29cfbd9430..82e8309f94 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -5,7 +5,8 @@ import type { Schema } from '@/misc/json-schema.js'; import { RolePolicies } from '@/core/RoleService.js'; - +import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js'; +import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; @@ -381,6 +382,8 @@ const eps = [ ['admin/emoji/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk], ['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk], ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], + ['admin/emoji/set-localonly-bulk', ep___admin_emoji_setlocalOnlyBulk], + ['admin/emoji/set-issensitive-bulk', ep___admin_emoji_setisSensitiveBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-issensitive-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-issensitive-bulk.ts new file mode 100644 index 0000000000..9206e00e21 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-issensitive-bulk.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', +} as const; + +export const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + isSensitive: { + type: 'boolean', + nullable: false, + description: 'Use `null` to reset the licence.', + }, + }, + required: ['ids'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private customEmojiService: CustomEmojiService, + ) { + super(meta, paramDef, async (ps, me) => { + await this.customEmojiService.setisSensitiveBulk(ps.ids, ps.isSensitive ?? false); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/set-localonly-bulk.ts b/packages/backend/src/server/api/endpoints/admin/emoji/set-localonly-bulk.ts new file mode 100644 index 0000000000..662bf24534 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/set-localonly-bulk.ts @@ -0,0 +1,41 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', +} as const; + +export const paramDef = { + type: 'object', + properties: { + ids: { type: 'array', items: { + type: 'string', format: 'misskey:id', + } }, + localOnly: { + type: 'boolean', + nullable: false, + description: 'Use `null` to reset the licence.', + }, + }, + required: ['ids'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + private customEmojiService: CustomEmojiService, + ) { + super(meta, paramDef, async (ps, me) => { + await this.customEmojiService.setLocalOnlyBulk(ps.ids, ps.localOnly ?? false); + }); + } +} diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index a83c18c0b3..19c98498f2 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -24,9 +24,11 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="type === 'info'" :class="$style.iconInner" class="ti ti-info-circle"></i> <i v-else-if="type === 'question'" :class="$style.iconInner" class="ti ti-help-circle"></i> <MkLoading v-else-if="type === 'waiting'" :class="$style.iconInner" :em="true"/> + <div v-if="type === 'mksw'" :class="$style.text"><MkSwitch :helpText="text" v-model="mkresult"/></div> </div> <header v-if="title" :class="$style.title"><Mfm :text="title"/></header> <div v-if="text" :class="$style.text"><Mfm :text="text"/></div> + <MkInput v-if="input" v-model="inputValue" autofocus :type="input.type || 'text'" :placeholder="input.placeholder || undefined" :autocomplete="input.autocomplete" @keydown="onInputKeydown"> <template v-if="input.type === 'password'" #prefix><i class="ti ti-lock"></i></template> <template #caption> @@ -62,9 +64,10 @@ import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkSelect from '@/components/MkSelect.vue'; import { i18n } from '@/i18n.js'; +import MkSwitch from "@/components/MkSwitch.vue"; type Input = { - type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local'; + type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local' | 'mksw'; placeholder?: string | null; autocomplete?: string; default: string | number | null; @@ -88,11 +91,12 @@ type Select = { }; const props = withDefaults(defineProps<{ - type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting'; + type?: 'success' | 'error' | 'warning' | 'info' | 'question' | 'waiting' | 'mksw'; title: string; text?: string; input?: Input; select?: Select; + mksw?: boolean; icon?: string; actions?: { text: string; @@ -121,7 +125,7 @@ const modal = shallowRef<InstanceType<typeof MkModal>>(); const inputValue = ref<string | number | null>(props.input?.default ?? null); const selectedValue = ref(props.select?.default ?? null); - +const mkresult= ref(false) let disabledReason = $ref<null | 'charactersExceeded' | 'charactersBelow'>(null); const okButtonDisabled = $computed<boolean>(() => { if (props.input) { @@ -153,6 +157,7 @@ async function ok() { const result = props.input ? inputValue.value : props.select ? selectedValue.value : + mkresult ? mkresult.value : true; done(false, result); } diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 8aed5797e1..a68fb47a3f 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -25,6 +25,7 @@ import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; +import MkSwitch from "@/components/MkSwitch.vue"; export const openingWindowsCount = ref(0); @@ -196,9 +197,8 @@ export function alert(props: { }, 'closed'); }); } - export function confirm(props: { - type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'; + type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question'|'mksw'; title?: string | null; text?: string | null; okText?: string; @@ -215,6 +215,24 @@ export function confirm(props: { }, 'closed'); }); } +export function switch1(props: { + type: 'mksw'; + title?: string | null; + text?: string | null; + okText?: string; + cancelText?: string; +}): Promise<{ canceled: boolean , result: boolean }> { + return new Promise((resolve, reject) => { + popup(MkDialog, { + ...props, + showCancelButton: true, + }, { + done: result => { + resolve(result ? result : { canceled: true }); + }, + }, 'closed'); + }); +} // TODO: const T extends ... にしたい // https://zenn.dev/general_link/articles/813e47b7a0eef7#const-type-parameters diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index bee73045b7..e64c31b8e5 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -21,6 +21,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton inline @click="selectAll">Select all</MkButton> <MkButton inline @click="setCategoryBulk">Set category</MkButton> <MkButton inline @click="setTagBulk">Set tag</MkButton> + <MkButton inline @click="setisSensitiveBulk">Set isSensitive</MkButton> + <MkButton inline @click="setlocalOnlyBulk">Set localOnly</MkButton> <MkButton inline @click="addTagBulk">Add tag</MkButton> <MkButton inline @click="removeTagBulk">Remove tag</MkButton> <MkButton inline @click="setLicenseBulk">Set License</MkButton> @@ -84,6 +86,7 @@ import { selectFile, selectFiles } from '@/scripts/select-file.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import {switch1, swtch} from "@/os.js"; const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); @@ -273,7 +276,30 @@ const setTagBulk = async () => { }); emojisPaginationComponent.value.reload(); }; - +const setisSensitiveBulk = async () => { + const { canceled, result } = await os.switch1({ + title: 'isSensitive', + type: "mksw" + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-issensitive-bulk', { + ids: selectedEmojis.value, + isSensitive: result + }); + emojisPaginationComponent.value.reload(); +}; +const setlocalOnlyBulk = async () => { + const { canceled, result } = await os.switch1({ + title: 'localOnly', + type: "mksw" + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-localonly-bulk', { + ids: selectedEmojis.value, + localOnly: result + }); + emojisPaginationComponent.value.reload(); +}; const delBulk = async () => { const { canceled } = await os.confirm({ type: 'warning', From dd7b6f92dfc74148d7ee7fdcffbef59c0df9dfb6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 26 Sep 2023 10:54:46 +0900 Subject: [PATCH 088/501] =?UTF-8?q?=E5=85=AC=E9=96=8B=E7=AF=84=E5=9B=B2?= =?UTF-8?q?=E3=81=AB=E8=89=B2=E4=BB=98=E3=81=91(localonly)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkNote.vue | 359 ++++++++++-------- .../frontend/src/pages/settings/general.vue | 4 + 2 files changed, 206 insertions(+), 157 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 223e8f818d..611796d035 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -4,140 +4,160 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div - v-if="!muted" - v-show="!isDeleted" - ref="el" - v-hotkey="keymap" - :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover } ,{[$style.home] : defaultStore.state.showVisibilityColor && note.visibility === 'home',[$style.followers] : defaultStore.state.showVisibilityColor && note.visibility === 'followers',[$style.specified] : defaultStore.state.showVisibilityColor && note.visibility === 'specified'}]" + <div + v-if="!muted" + v-show="!isDeleted" + ref="el" + v-hotkey="keymap" + :class="[$style.root, + { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover } , + {[$style.home] : defaultStore.state.showVisibilityColor && note.visibility === 'home' + ,[$style.followers] : defaultStore.state.showVisibilityColor && note.visibility === 'followers' + ,[$style.specified] : defaultStore.state.showVisibilityColor && note.visibility === 'specified' + },{[$style.localonly] : defaultStore.state.showVisibilityColor && note.localOnly } + ]" - :tabindex="!isDeleted ? '-1' : undefined" -> - <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> - <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> - <!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> - <!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>--> - <div v-if="isRenote" :class="$style.renote"> - <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> - <MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/> - <i class="ti ti-repeat" style="margin-right: 4px;"></i> - <I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText"> - <template #user> - <MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)"> - <MkUserName :user="note.user"/> - </MkA> - </template> - </I18n> - <div :class="$style.renoteInfo"> - <button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()"> - <i class="ti ti-dots" :class="$style.renoteMenu"></i> - <MkTime :time="note.createdAt"/> - </button> - <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> - <i v-if="note.visibility === 'home'" class="ti ti-home" ></i> + :tabindex="!isDeleted ? '-1' : undefined" + > + <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> + <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> + <!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> + <!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>--> + <div v-if="isRenote" :class="$style.renote"> + <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> + <MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/> + <i class="ti ti-repeat" style="margin-right: 4px;"></i> + <I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText"> + <template #user> + <MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)"> + <MkUserName :user="note.user"/> + </MkA> + </template> + </I18n> + <div :class="$style.renoteInfo"> + <button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()"> + <i class="ti ti-dots" :class="$style.renoteMenu"></i> + <MkTime :time="note.createdAt"/> + </button> + <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" + :title="i18n.ts._visibility[note.visibility]"> + <i v-if="note.visibility === 'home'" class="ti ti-home"></i> <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> - <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> - <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i + class="ti ti-rocket-off"></i></span> + <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span> + </div> </div> - </div> - <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> - <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> - <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> - </div> - <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> - <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> - <MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/> - <div :class="$style.main"> - <MkNoteHeader :note="appearNote" :mini="true"/> - <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> - <div style="container-type: inline-size;"> - <p v-if="appearNote.cw != null" :class="$style.cw"> - <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :i="$i"/> - <MkCwButton v-model="showContent" :note="appearNote"/> - </p> - <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> - <div :class="$style.text"> - <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> - <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :emojiUrls="appearNote.emojis"/> - <div v-if="translating || translation" :class="$style.translation"> - <MkLoading v-if="translating" mini/> - <div v-else> - <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :i="$i" :emojiUrls="appearNote.emojis"/> + <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> + <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> + <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" + :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> + </div> + <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> + <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> + <MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/> + <div :class="$style.main"> + <MkNoteHeader :note="appearNote" :mini="true"/> + <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> + <div style="container-type: inline-size;"> + <p v-if="appearNote.cw != null" :class="$style.cw"> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" + :i="$i"/> + <MkCwButton v-model="showContent" :note="appearNote"/> + </p> + <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> + <div :class="$style.text"> + <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> + <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i + class="ti ti-arrow-back-up"></i></MkA> + <Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" + :emojiUrls="appearNote.emojis"/> + <div v-if="translating || translation" :class="$style.translation"> + <MkLoading v-if="translating" mini/> + <div v-else> + <b>{{ i18n.t('translatedFrom', {x: translation.sourceLang}) }}: </b> + <Mfm :text="translation.text" :author="appearNote.user" :i="$i" :emojiUrls="appearNote.emojis"/> + </div> </div> </div> + <div v-if="appearNote.files.length > 0"> + <MkMediaList :mediaList="appearNote.files"/> + </div> + <MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll"/> + <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" + :class="$style.urlPreview"/> + <div v-if="appearNote.renote" :class="$style.quote"> + <MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/> + </div> + <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> + <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> + </button> + <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> + <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> + </button> </div> - <div v-if="appearNote.files.length > 0"> - <MkMediaList :mediaList="appearNote.files"/> - </div> - <MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll"/> - <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/> - <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> - <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> - <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> - </button> - <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> - <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> - </button> + <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" + :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }} + </MkA> </div> - <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> + <MkReactionsViewer :note="appearNote" :maxNumber="16"> + <template #more> + <div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div> + </template> + </MkReactionsViewer> + <footer :class="$style.footer"> + <button :class="$style.footerButton" class="_button" @click="reply()"> + <i class="ti ti-arrow-back-up"></i> + <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p> + </button> + <button + v-if="canRenote" + ref="renoteButton" + :class="$style.footerButton" + class="_button" + @mousedown="renote()" + > + <i class="ti ti-repeat"></i> + <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p> + </button> + <button v-else :class="$style.footerButton" class="_button" disabled> + <i class="ti ti-ban"></i> + </button> + <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" + @mousedown="react()"> + <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> + <i v-else class="ti ti-plus"></i> + </button> + <button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" + @click="undoReact(appearNote)"> + <i class="ti ti-minus"></i> + </button> + <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" + class="_button" @mousedown="clip()"> + <i class="ti ti-paperclip"></i> + </button> + <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> + <i class="ti ti-dots"></i> + </button> + </footer> </div> - <MkReactionsViewer :note="appearNote" :maxNumber="16"> - <template #more> - <div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div> - </template> - </MkReactionsViewer> - <footer :class="$style.footer"> - <button :class="$style.footerButton" class="_button" @click="reply()"> - <i class="ti ti-arrow-back-up"></i> - <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p> - </button> - <button - v-if="canRenote" - ref="renoteButton" - :class="$style.footerButton" - class="_button" - @mousedown="renote()" - > - <i class="ti ti-repeat"></i> - <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p> - </button> - <button v-else :class="$style.footerButton" class="_button" disabled> - <i class="ti ti-ban"></i> - </button> - <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> - <i v-else class="ti ti-plus"></i> - </button> - <button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)"> - <i class="ti ti-minus"></i> - </button> - <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()"> - <i class="ti ti-paperclip"></i> - </button> - <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> - <i class="ti ti-dots"></i> - </button> - </footer> - </div> - </article> -</div> -<div v-else :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> - <template #name> - <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> - <MkUserName :user="appearNote.user"/> - </MkA> - </template> - </I18n> -</div> + </article> + </div> + <div v-else :class="$style.muted" @click="muted = false"> + <I18n :src="i18n.ts.userSaysSomething" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> + </div> </template> <script lang="ts" setup> -import { computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent } from 'vue'; +import {computed, inject, onMounted, ref, shallowRef, Ref, defineAsyncComponent} from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; @@ -150,26 +170,26 @@ import MkPoll from '@/components/MkPoll.vue'; import MkUsersTooltip from '@/components/MkUsersTooltip.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkInstanceTicker from '@/components/MkInstanceTicker.vue'; -import { pleaseLogin } from '@/scripts/please-login.js'; -import { focusPrev, focusNext } from '@/scripts/focus.js'; -import { checkWordMute } from '@/scripts/check-word-mute.js'; -import { userPage } from '@/filters/user.js'; +import {pleaseLogin} from '@/scripts/please-login.js'; +import {focusPrev, focusNext} from '@/scripts/focus.js'; +import {checkWordMute} from '@/scripts/check-word-mute.js'; +import {userPage} from '@/filters/user.js'; import * as os from '@/os.js'; -import { defaultStore, noteViewInterruptors } from '@/store.js'; -import { reactionPicker } from '@/scripts/reaction-picker.js'; -import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; -import { $i } from '@/account.js'; -import { i18n } from '@/i18n.js'; -import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js'; -import { useNoteCapture } from '@/scripts/use-note-capture.js'; -import { deepClone } from '@/scripts/clone.js'; -import { useTooltip } from '@/scripts/use-tooltip.js'; -import { claimAchievement } from '@/scripts/achievements.js'; -import { getNoteSummary } from '@/scripts/get-note-summary.js'; -import { MenuItem } from '@/types/menu'; +import {defaultStore, noteViewInterruptors} from '@/store.js'; +import {reactionPicker} from '@/scripts/reaction-picker.js'; +import {extractUrlFromMfm} from '@/scripts/extract-url-from-mfm.js'; +import {$i} from '@/account.js'; +import {i18n} from '@/i18n.js'; +import {getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu} from '@/scripts/get-note-menu.js'; +import {useNoteCapture} from '@/scripts/use-note-capture.js'; +import {deepClone} from '@/scripts/clone.js'; +import {useTooltip} from '@/scripts/use-tooltip.js'; +import {claimAchievement} from '@/scripts/achievements.js'; +import {getNoteSummary} from '@/scripts/get-note-summary.js'; +import {MenuItem} from '@/types/menu'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; -import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; -import { shouldCollapsed } from '@/scripts/collapsed.js'; +import {showMovedDialog} from '@/scripts/show-moved-dialog.js'; +import {shouldCollapsed} from '@/scripts/collapsed.js'; const props = defineProps<{ note: Misskey.entities.Note; @@ -177,7 +197,8 @@ const props = defineProps<{ }>(); const inChannel = inject('inChannel', null); -const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null);; +const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); +; let note = $ref(deepClone(props.note)); // plugin @@ -280,7 +301,7 @@ function renote(viaKeyboard = false) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); + os.popup(MkRippleEffect, {x, y}, {}, 'end'); } os.api('notes/create', { @@ -311,7 +332,7 @@ function renote(viaKeyboard = false) { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); + os.popup(MkRippleEffect, {x, y}, {}, 'end'); } const configuredVisibility = defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility; @@ -370,7 +391,7 @@ function react(viaKeyboard = false): void { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); + os.popup(MkRippleEffect, {x, y}, {}, 'end'); } } else { blur(); @@ -412,20 +433,38 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const {menu, cleanup} = getNoteMenu({ + note: note, + translating, + translation, + menuButton, + isDeleted, + currentClip: currentClip?.value + }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } function menu(viaKeyboard = false): void { - const { menu, cleanup } = getNoteMenu({ note: note, translating, translation, menuButton, isDeleted, currentClip: currentClip?.value }); + const {menu, cleanup} = getNoteMenu({ + note: note, + translating, + translation, + menuButton, + isDeleted, + currentClip: currentClip?.value + }); os.popupMenu(menu, menuButton.value, { viaKeyboard, }).then(focus).finally(cleanup); } async function clip() { - os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); + os.popupMenu(await getNoteClipMenu({ + note: note, + isDeleted, + currentClip: currentClip?.value + }), clipButton.value).then(focus); } function showRenoteMenu(viaKeyboard = false): void { @@ -481,7 +520,6 @@ function focusAfter() { } - function readPromo() { os.api('promo/read', { noteId: appearNote.id, @@ -498,14 +536,20 @@ function readPromo() { font-size: 1.05em; overflow: clip; contain: content; -&.home{ - background-color: rgba( var(--homeColor) , 0.20) !important; -} - &.followers{ - background-color: rgba(var(--followerColor), 0.20) !important; - } - &.specified{ - background-color: rgba(var(--specifiedColor), 0.20) !important; + + &.home { + background-color: rgba(var(--homeColor), 0.20) !important; + } + + &.followers { + background-color: rgba(var(--followerColor), 0.20) !important; + } + + &.specified { + background-color: rgba(var(--specifiedColor), 0.20) !important; + } + &.localonly { + background-color: rgba(var(--localOnlyColor), 0.20) !important; } // これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、 // 下の方までスクロールすると上のノートの高さがここで決め打ちされたものに変化し、表示しているノートの位置が変わってしまう @@ -513,7 +557,7 @@ function readPromo() { // 今度はその処理自体がパフォーマンス低下の原因にならないか懸念される。また、被リアクションでも高さは変化するため、やはり多少のズレは生じる // 一度レンダリングされた要素はブラウザがよしなにサイズを覚えておいてくれるような実装になるまで待った方が良さそう(なるのか?) //content-visibility: auto; - //contain-intrinsic-size: 0 128px; + //contain-intrinsic-size: 0 128px; &:focus-visible { outline: none; @@ -951,7 +995,8 @@ function readPromo() { padding: 0 6px; opacity: .8; } -.root:has(.ti-home){ + +.root:has(.ti-home) { background-color: rgba(255, 255, 100, 0.10) !important; } </style> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 1c1207770a..b4c7b021e9 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -57,6 +57,9 @@ SPDX-License-Identifier: AGPL-3.0-only </MkColorInput> <MkColorInput v-if="showVisibilityColor" v-model="specifiedColor"> <template #label>{{ i18n.ts._visibility.specified }}</template> + </MkColorInput> + <MkColorInput v-if="showVisibilityColor" v-model="localOnlyColor"> + <template #label>{{ i18n.ts.localOnly }}</template> </MkColorInput> <MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch> <MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch> @@ -269,6 +272,7 @@ const numberOfGamingSpeed = computed(defaultStore.makeGetterSetter('numberOfGami const homeColor = computed(defaultStore.makeGetterSetter('homeColor')); const followerColor = computed(defaultStore.makeGetterSetter('followerColor')); const specifiedColor = computed(defaultStore.makeGetterSetter('specifiedColor')); +const localOnlyColor = computed(defaultStore.makeGetterSetter('localOnlyColor')); const instanceTicker = computed(defaultStore.makeGetterSetter('instanceTicker')); const enableInfiniteScroll = computed(defaultStore.makeGetterSetter('enableInfiniteScroll')); const useReactionPickerForContextMenu = computed(defaultStore.makeGetterSetter('useReactionPickerForContextMenu')); From 90028a96b62bf6da8b2f4534f4bd30c45daee045 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 26 Sep 2023 10:55:42 +0900 Subject: [PATCH 089/501] =?UTF-8?q?=E5=85=AC=E9=96=8B=E7=AF=84=E5=9B=B2?= =?UTF-8?q?=E3=81=AB=E8=89=B2=E4=BB=98=E3=81=91(localonly)=20=E3=82=B3?= =?UTF-8?q?=E3=83=9F=E3=83=83=E3=83=88=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/store.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index d48517aaf2..94323a7b17 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -360,6 +360,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: '#00FFFF', }, + localOnlyColor:{ + where:'device', + default: '#2b2c41' + }, numberOfGamingSpeed: { where: 'device', default: 44, From 39b66a2638ecd2c9f6689c643fd6509a7e2153a0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 26 Sep 2023 10:56:03 +0900 Subject: [PATCH 090/501] =?UTF-8?q?=E8=89=B2=E3=81=8C=E3=81=8A=E3=81=8B?= =?UTF-8?q?=E3=81=97=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkTab.vue | 5 +++-- packages/frontend/src/ui/_common_/navbar.vue | 13 +++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue index 47cf3eda04..2972df6a16 100644 --- a/packages/frontend/src/components/MkTab.vue +++ b/packages/frontend/src/components/MkTab.vue @@ -83,7 +83,7 @@ export default defineComponent({ background: var(--accentedBg); &.gamingDark{ - color:white; + color: black !important; background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); background-size: 1800% 1800%; -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; @@ -91,7 +91,8 @@ export default defineComponent({ animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; } &.gamingLight{ - color: black; + + color:white !important; background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); background-size: 1800% 1800% !important; -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 93e986e638..b007cb1b15 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -108,6 +108,7 @@ function hexToRgb(hex) { document.documentElement.style.setProperty('--homeColor', hexToRgb(defaultStore.state.homeColor)); document.documentElement.style.setProperty("--followerColor",hexToRgb(defaultStore.state.followerColor)); document.documentElement.style.setProperty("--specifiedColor",hexToRgb(defaultStore.state.specifiedColor)) +document.documentElement.style.setProperty("--localOnlyColor",hexToRgb(defaultStore.state.localOnlyColor)) document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed+'s'); const iconOnly = ref(false); @@ -307,11 +308,11 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: white; + color: black; } &.gamingDark { - color: black; + color: white; } &.gamingLight:before { @@ -681,7 +682,7 @@ function more(ev: MouseEvent) { -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - color: white; + color: white !important; } &.gamingLight:hover, &.gamingLight.active { @@ -692,12 +693,12 @@ function more(ev: MouseEvent) { -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; - color: white; + color: white !important; } } &.gamingDark:before { - color: black; + color: black !important; content: ""; display: block; position: absolute; @@ -717,7 +718,7 @@ function more(ev: MouseEvent) { } &.gamingDark:hover, &.gamingDark.active { - color: black; + color: black !important; &.gamingDark:before { background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); From 2b72034847345ad0abb280576096c157c1791d3e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 27 Sep 2023 16:40:32 +0900 Subject: [PATCH 091/501] =?UTF-8?q?mfm=E3=81=AE=E3=82=84=E3=81=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 ++- locales/ja-JP.yml | 3 ++- packages/frontend/src/components/MkPostForm.vue | 8 ++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index b68857481f..4467ec80bd 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -648,6 +648,7 @@ export interface Locale { "metrics": string; "overview": string; "logs": string; + "mfm": string; "delayed": string; "database": string; "channel": string; @@ -2267,9 +2268,9 @@ export interface Locale { "_moderationLogTypes": { "createRole": string; "deleteRole": string; - "updateRole": string; "assignRole": string; "unassignRole": string; + "updateRole": string; "suspend": string; "unsuspend": string; "addCustomEmoji": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 31720a906e..a9760b4d1b 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -645,6 +645,7 @@ copy: "コピー" metrics: "メトリクス" overview: "概要" logs: "ログ" +mfm: 'mfm 装飾' delayed: "遅延" database: "データベース" channel: "チャンネル" @@ -2180,9 +2181,9 @@ _webhookSettings: _moderationLogTypes: createRole: "ロールを作成" deleteRole: "ロールを削除" - updateRole: "ロールを更新" assignRole: "ロールへアサイン" unassignRole: "ロールのアサイン解除" + updateRole: "ロール設定更新" suspend: "凍結" unsuspend: "凍結解除" addCustomEmoji: "カスタム絵文字追加" diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index ce910220da..d77e6e0327 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -116,10 +116,12 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> - <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" + <button v-if="postFormActions.length" v-tooltip="i18n.ts.plugin" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> + <button v-tooltip="i18n.ts.mfm" :class="['_button', $style.footerButton]" @click="insertMfm"><i + class="ti ti-wand"></i></button> </div> <div :class="$style.footerRight"> <button v-tooltip="i18n.ts.previewNoteText" class="_button" @@ -879,7 +881,9 @@ function insertMention() { async function insertEmoji(ev: MouseEvent) { os.openEmojiPicker(ev.currentTarget ?? ev.target, {}, textareaEl); } - +async function insertMfm(){ + insertTextAtCursor(textareaEl, '$'); +} function showActions(ev) { os.popupMenu(postFormActions.map(action => ({ text: action.title, From 9f1a497206ce76fb2f666a26ba8e7d06dd69c9aa Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 28 Sep 2023 16:48:02 +0900 Subject: [PATCH 092/501] =?UTF-8?q?=E3=81=84=E3=81=84=E3=81=8B=E3=82=93?= =?UTF-8?q?=E3=81=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- packages/frontend/src/components/MkTimeline.vue | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6d4f38e7ec..1b37e5f6e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.1-prismisskey.1", + "version": "2023.9.1-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 5919c1c68e..6f594ff522 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -92,11 +92,13 @@ if (props.src === 'antenna') { endpoint = 'notes/hybrid-timeline'; query = { withFiles: true, - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('hybridTimeline', { withFiles: true, - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'social') { @@ -113,10 +115,12 @@ if (props.src === 'antenna') { } else if (props.src === 'all') { endpoint = 'notes/hybrid-all-timeline'; query = { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }; connection = stream.useChannel('hybridAllTimeline', { - withReplies: defaultStore.state.showTimelineReplies, + withRenotes: props.withRenotes, + withReplies: props.withReplies, }); connection.on('note', prepend); } else if (props.src === 'global') { From 582a6948e5b6642d1a878e560d6d9b732eb9caf3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 29 Sep 2023 23:18:18 +0900 Subject: [PATCH 093/501] =?UTF-8?q?=E3=81=84=E3=81=84=E3=81=8B=E3=82=93?= =?UTF-8?q?=E3=81=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 - locales/ja-JP.yml | 5 - package.json | 2 +- .../1695901659683-note-updated-at.js | 11 + ...1695944637565-notificationRecieveConfig.js | 18 + packages/backend/src/core/AntennaService.ts | 4 +- packages/backend/src/core/CacheService.ts | 4 +- .../backend/src/core/CustomEmojiService.ts | 2 +- .../backend/src/core/GlobalEventService.ts | 271 +++++++++++-- packages/backend/src/core/MetaService.ts | 4 +- .../backend/src/core/NoteCreateService.ts | 6 +- .../backend/src/core/NotificationService.ts | 47 ++- packages/backend/src/core/ReactionService.ts | 3 +- packages/backend/src/core/RoleService.ts | 4 +- .../backend/src/core/UserFollowingService.ts | 9 +- packages/backend/src/core/UserListService.ts | 78 +++- packages/backend/src/core/WebhookService.ts | 4 +- .../src/core/entities/NoteEntityService.ts | 1 + .../src/core/entities/UserEntityService.ts | 2 +- packages/backend/src/models/Note.ts | 5 + packages/backend/src/models/UserProfile.ts | 28 +- .../backend/src/models/json-schema/note.ts | 7 +- .../backend/src/models/json-schema/user.ts | 10 +- .../ImportUserListsProcessorService.ts | 2 +- .../server/api/endpoints/admin/ad/create.ts | 11 +- .../server/api/endpoints/admin/ad/delete.ts | 8 + .../src/server/api/endpoints/admin/ad/list.ts | 4 + .../server/api/endpoints/admin/ad/update.ts | 11 + .../server/api/endpoints/admin/show-user.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 6 +- .../src/server/api/endpoints/notes/update.ts | 1 + .../users/lists/create-from-public.ts | 2 +- .../server/api/endpoints/users/lists/pull.ts | 16 +- .../server/api/endpoints/users/lists/push.ts | 2 +- .../src/server/api/stream/Connection.ts | 6 +- .../src/server/api/stream/channels/antenna.ts | 4 +- .../api/stream/channels/role-timeline.ts | 4 +- .../backend/src/server/api/stream/types.ts | 259 ------------ packages/backend/src/types.ts | 27 ++ packages/backend/test/e2e/users.ts | 8 +- .../src/components/MkNoteDetailed.vue | 3 + .../frontend/src/components/MkNoteHeader.vue | 1 + .../components/MkNotificationSelectWindow.vue | 78 ++++ .../MkNotificationSettingWindow.vue | 95 ----- .../src/components/MkNotifications.vue | 9 +- .../src/components/MkNotifyButton.vue | 367 ++++++++++++++++++ packages/frontend/src/pages/admin/ads.vue | 32 +- .../src/pages/admin/modlog.ModLog.vue | 26 +- packages/frontend/src/pages/notifications.vue | 5 +- .../notifications.notification-config.vue | 50 +++ .../src/pages/settings/notifications.vue | 48 ++- packages/frontend/src/pages/timeline.vue | 2 +- packages/frontend/src/pages/user/home.vue | 2 + .../frontend/src/scripts/use-note-capture.ts | 1 + packages/frontend/src/ui/_common_/common.vue | 4 +- .../src/ui/deck/notifications-column.vue | 10 +- .../frontend/src/widgets/WidgetActivity.vue | 2 +- .../frontend/src/widgets/WidgetAichan.vue | 2 +- .../frontend/src/widgets/WidgetAiscript.vue | 2 +- .../src/widgets/WidgetAiscriptApp.vue | 2 +- .../frontend/src/widgets/WidgetButton.vue | 2 +- .../frontend/src/widgets/WidgetCalendar.vue | 2 +- .../frontend/src/widgets/WidgetClicker.vue | 2 +- packages/frontend/src/widgets/WidgetClock.vue | 2 +- .../src/widgets/WidgetDigitalClock.vue | 2 +- .../frontend/src/widgets/WidgetFederation.vue | 2 +- .../src/widgets/WidgetInstanceCloud.vue | 2 +- .../frontend/src/widgets/WidgetJobQueue.vue | 2 +- packages/frontend/src/widgets/WidgetMemo.vue | 2 +- .../src/widgets/WidgetNotifications.vue | 16 +- .../src/widgets/WidgetOnlineUsers.vue | 2 +- .../frontend/src/widgets/WidgetPhotos.vue | 2 +- .../frontend/src/widgets/WidgetPostForm.vue | 2 +- .../frontend/src/widgets/WidgetProfile.vue | 2 +- packages/frontend/src/widgets/WidgetRss.vue | 2 +- .../frontend/src/widgets/WidgetRssTicker.vue | 2 +- .../frontend/src/widgets/WidgetSlideshow.vue | 2 +- .../frontend/src/widgets/WidgetTimeline.vue | 2 +- .../frontend/src/widgets/WidgetTrends.vue | 2 +- .../frontend/src/widgets/WidgetUnixClock.vue | 2 +- .../frontend/src/widgets/WidgetUserList.vue | 2 +- packages/misskey-js/etc/misskey-js.api.md | 25 +- packages/misskey-js/src/api.types.ts | 2 +- packages/misskey-js/src/consts.ts | 16 + packages/misskey-js/src/entities.ts | 20 +- .../sw/src/scripts/create-notification.ts | 2 +- packages/sw/src/scripts/operations.ts | 2 +- packages/sw/src/sw.ts | 2 +- 88 files changed, 1215 insertions(+), 552 deletions(-) create mode 100644 packages/backend/migration/1695901659683-note-updated-at.js create mode 100644 packages/backend/migration/1695944637565-notificationRecieveConfig.js delete mode 100644 packages/backend/src/server/api/stream/types.ts create mode 100644 packages/frontend/src/components/MkNotificationSelectWindow.vue delete mode 100644 packages/frontend/src/components/MkNotificationSettingWindow.vue create mode 100644 packages/frontend/src/components/MkNotifyButton.vue create mode 100644 packages/frontend/src/pages/settings/notifications.notification-config.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index ff8b7298be..aa74bf297c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1136,9 +1136,6 @@ export interface Locale { "authenticationRequiredToContinue": string; "dateAndTime": string; "showRenotes": string; - "edited": string; - "notificationRecieveConfig": string; - "mutualFollow": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 7d088cfb14..0ba9f48101 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1133,10 +1133,6 @@ authentication: "認証" authenticationRequiredToContinue: "続けるには認証を行ってください" dateAndTime: "日時" showRenotes: "リノートを表示" -edited: "編集済み" -notificationRecieveConfig: "通知の受信設定" -mutualFollow: "相互フォロー" -fileAttachedOnly: "ファイル付きのみ" _announcement: forExistingUsers: "既存ユーザーのみ" @@ -2190,7 +2186,6 @@ _moderationLogTypes: updateRole: "ロールを更新" assignRole: "ロールへアサイン" unassignRole: "ロールのアサイン解除" - updateRole: "ロール設定更新" suspend: "凍結" unsuspend: "凍結解除" addCustomEmoji: "カスタム絵文字追加" diff --git a/package.json b/package.json index 02afa58332..77f74fd6b8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.2", + "version": "2023.9.2-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/migration/1695901659683-note-updated-at.js b/packages/backend/migration/1695901659683-note-updated-at.js new file mode 100644 index 0000000000..d8a151a1f7 --- /dev/null +++ b/packages/backend/migration/1695901659683-note-updated-at.js @@ -0,0 +1,11 @@ +export class NoteUpdatedAt1695901659683 { + name = 'NoteUpdatedAt1695901659683' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "updatedAt"`); + } +} diff --git a/packages/backend/migration/1695944637565-notificationRecieveConfig.js b/packages/backend/migration/1695944637565-notificationRecieveConfig.js new file mode 100644 index 0000000000..42d3dce5d6 --- /dev/null +++ b/packages/backend/migration/1695944637565-notificationRecieveConfig.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class NotificationRecieveConfig1695944637565 { + name = 'NotificationRecieveConfig1695944637565' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "mutingNotificationTypes"`); + await queryRunner.query(`ALTER TABLE "user_profile" ADD "notificationRecieveConfig" jsonb NOT NULL DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "notificationRecieveConfig"`); + await queryRunner.query(`ALTER TABLE "user_profile" ADD "mutingNotificationTypes" "public"."user_profile_notificationrecieveconfig_enum" array NOT NULL DEFAULT '{}'`); + } +} diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 841ce4b84a..d9f27b8c63 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -15,7 +15,7 @@ import { DI } from '@/di-symbols.js'; import type { AntennasRepository, UserListJoiningsRepository } from '@/models/_.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; -import { StreamMessages } from '@/server/api/stream/types.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -50,7 +50,7 @@ export class AntennaService implements OnApplicationShutdown { const obj = JSON.parse(data); if (obj.channel === 'internal') { - const { type, body } = obj.message as StreamMessages['internal']['payload']; + const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'antennaCreated': this.antennas.push({ diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index 6ca684d53c..561979c4bf 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -11,7 +11,7 @@ import type { MiLocalUser, MiUser } from '@/models/User.js'; import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { bindThis } from '@/decorators.js'; -import { StreamMessages } from '@/server/api/stream/types.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -160,7 +160,7 @@ export class CacheService implements OnApplicationShutdown { const obj = JSON.parse(data); if (obj.channel === 'internal') { - const { type, body } = obj.message as StreamMessages['internal']['payload']; + const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'userChangeSuspendedState': case 'remoteUserUpdated': { diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 2bdf89ffa1..83fe91fbe3 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -17,7 +17,7 @@ import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import { query } from '@/misc/prelude/url.js'; -import type { Serialized } from '@/server/api/stream/types.js'; +import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 4bc4f54c21..b74fbbe584 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -5,27 +5,254 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; +import type { MiChannel } from '@/models/Channel.js'; import type { MiUser } from '@/models/User.js'; +import type { MiUserProfile } from '@/models/UserProfile.js'; import type { MiNote } from '@/models/Note.js'; -import type { MiUserList } from '@/models/UserList.js'; import type { MiAntenna } from '@/models/Antenna.js'; -import type { - StreamChannels, - AdminStreamTypes, - AntennaStreamTypes, - BroadcastTypes, - DriveStreamTypes, - InternalStreamTypes, - MainStreamTypes, - NoteStreamTypes, - UserListStreamTypes, - RoleTimelineStreamTypes, -} from '@/server/api/stream/types.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiDriveFolder } from '@/models/DriveFolder.js'; +import type { MiUserList } from '@/models/UserList.js'; +import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; +import type { MiSignin } from '@/models/Signin.js'; +import type { MiPage } from '@/models/Page.js'; +import type { MiWebhook } from '@/models/Webhook.js'; +import type { MiMeta } from '@/models/Meta.js'; +import { MiRole, MiRoleAssignment } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { bindThis } from '@/decorators.js'; -import { MiRole } from '@/models/_.js'; +import { Serialized } from '@/types.js'; +import type Emitter from 'strict-event-emitter-types'; +import type { EventEmitter } from 'events'; + +//#region Stream type-body definitions +export interface BroadcastTypes { + emojiAdded: { + emoji: Packed<'EmojiDetailed'>; + }; + emojiUpdated: { + emojis: Packed<'EmojiDetailed'>[]; + }; + emojiDeleted: { + emojis: { + id?: string; + name: string; + [other: string]: any; + }[]; + }; + announcementCreated: { + announcement: Packed<'Announcement'>; + }; +} + +export interface MainEventTypes { + notification: Packed<'Notification'>; + mention: Packed<'Note'>; + reply: Packed<'Note'>; + renote: Packed<'Note'>; + follow: Packed<'UserDetailedNotMe'>; + followed: Packed<'User'>; + unfollow: Packed<'User'>; + meUpdated: Packed<'User'>; + pageEvent: { + pageId: MiPage['id']; + event: string; + var: any; + userId: MiUser['id']; + user: Packed<'User'>; + }; + urlUploadFinished: { + marker?: string | null; + file: Packed<'DriveFile'>; + }; + readAllNotifications: undefined; + unreadNotification: Packed<'Notification'>; + unreadMention: MiNote['id']; + readAllUnreadMentions: undefined; + unreadSpecifiedNote: MiNote['id']; + readAllUnreadSpecifiedNotes: undefined; + readAllAntennas: undefined; + unreadAntenna: MiAntenna; + readAllAnnouncements: undefined; + myTokenRegenerated: undefined; + signin: MiSignin; + registryUpdated: { + scope?: string[]; + key: string; + value: any | null; + }; + driveFileCreated: Packed<'DriveFile'>; + readAntenna: MiAntenna; + receiveFollowRequest: Packed<'User'>; + announcementCreated: { + announcement: Packed<'Announcement'>; + }; +} + +export interface DriveEventTypes { + fileCreated: Packed<'DriveFile'>; + fileDeleted: MiDriveFile['id']; + fileUpdated: Packed<'DriveFile'>; + folderCreated: Packed<'DriveFolder'>; + folderDeleted: MiDriveFolder['id']; + folderUpdated: Packed<'DriveFolder'>; +} + +export interface NoteEventTypes { + pollVoted: { + choice: number; + userId: MiUser['id']; + }; + deleted: { + deletedAt: Date; + }; + updated: { + cw: string | null; + text: string; + }; + reacted: { + reaction: string; + emoji?: { + name: string; + url: string; + } | null; + userId: MiUser['id']; + }; + unreacted: { + reaction: string; + userId: MiUser['id']; + }; +} +type NoteStreamEventTypes = { + [key in keyof NoteEventTypes]: { + id: MiNote['id']; + body: NoteEventTypes[key]; + }; +}; + +export interface UserListEventTypes { + userAdded: Packed<'User'>; + userRemoved: Packed<'User'>; +} + +export interface AntennaEventTypes { + note: MiNote; +} + +export interface RoleTimelineEventTypes { + note: Packed<'Note'>; +} + +export interface AdminEventTypes { + newAbuseUserReport: { + id: MiAbuseUserReport['id']; + targetUserId: MiUser['id'], + reporterId: MiUser['id'], + comment: string; + }; +} +//#endregion + +// 辞書(interface or type)から{ type, body }ユニオンを定義 +// https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type +// VS Codeの展開を防止するためにEvents型を定義 +type Events<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } }; +type EventUnionFromDictionary< + T extends object, + U = Events<T> +> = U[keyof U]; + +type SerializedAll<T> = { + [K in keyof T]: Serialized<T[K]>; +}; + +export interface InternalEventTypes { + userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; + userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; + remoteUserUpdated: { id: MiUser['id']; }; + follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; + unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; + blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; + blockingDeleted: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; + policiesUpdated: MiRole['policies']; + roleCreated: MiRole; + roleDeleted: MiRole; + roleUpdated: MiRole; + userRoleAssigned: MiRoleAssignment; + userRoleUnassigned: MiRoleAssignment; + webhookCreated: MiWebhook; + webhookDeleted: MiWebhook; + webhookUpdated: MiWebhook; + antennaCreated: MiAntenna; + antennaDeleted: MiAntenna; + antennaUpdated: MiAntenna; + metaUpdated: MiMeta; + followChannel: { userId: MiUser['id']; channelId: MiChannel['id']; }; + unfollowChannel: { userId: MiUser['id']; channelId: MiChannel['id']; }; + updateUserProfile: MiUserProfile; + mute: { muterId: MiUser['id']; muteeId: MiUser['id']; }; + unmute: { muterId: MiUser['id']; muteeId: MiUser['id']; }; + userListMemberAdded: { userListId: MiUserList['id']; memberId: MiUser['id']; }; + userListMemberRemoved: { userListId: MiUserList['id']; memberId: MiUser['id']; }; +} + +// name/messages(spec) pairs dictionary +export type GlobalEvents = { + internal: { + name: 'internal'; + payload: EventUnionFromDictionary<SerializedAll<InternalEventTypes>>; + }; + broadcast: { + name: 'broadcast'; + payload: EventUnionFromDictionary<SerializedAll<BroadcastTypes>>; + }; + main: { + name: `mainStream:${MiUser['id']}`; + payload: EventUnionFromDictionary<SerializedAll<MainEventTypes>>; + }; + drive: { + name: `driveStream:${MiUser['id']}`; + payload: EventUnionFromDictionary<SerializedAll<DriveEventTypes>>; + }; + note: { + name: `noteStream:${MiNote['id']}`; + payload: EventUnionFromDictionary<SerializedAll<NoteStreamEventTypes>>; + }; + userList: { + name: `userListStream:${MiUserList['id']}`; + payload: EventUnionFromDictionary<SerializedAll<UserListEventTypes>>; + }; + roleTimeline: { + name: `roleTimelineStream:${MiRole['id']}`; + payload: EventUnionFromDictionary<SerializedAll<RoleTimelineEventTypes>>; + }; + antenna: { + name: `antennaStream:${MiAntenna['id']}`; + payload: EventUnionFromDictionary<SerializedAll<AntennaEventTypes>>; + }; + admin: { + name: `adminStream:${MiUser['id']}`; + payload: EventUnionFromDictionary<SerializedAll<AdminEventTypes>>; + }; + notes: { + name: 'notesStream'; + payload: Serialized<Packed<'Note'>>; + }; +}; + +// API event definitions +// ストリームごとのEmitterの辞書を用意 +type EventEmitterDictionary = { [x in keyof GlobalEvents]: Emitter.default<EventEmitter, { [y in GlobalEvents[x]['name']]: (e: GlobalEvents[x]['payload']) => void }> }; +// 共用体型を交差型にする型 https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection +type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; +// Emitter辞書から共用体型を作り、UnionToIntersectionで交差型にする +export type StreamEventEmitter = UnionToIntersection<EventEmitterDictionary[keyof GlobalEvents]>; +// { [y in name]: (e: spec) => void }をまとめてその交差型をEmitterにかけるとts(2590)にひっかかる + +// provide stream channels union +export type StreamChannels = GlobalEvents[keyof GlobalEvents]['name']; @Injectable() export class GlobalEventService { @@ -51,7 +278,7 @@ export class GlobalEventService { } @bindThis - public publishInternalEvent<K extends keyof InternalStreamTypes>(type: K, value?: InternalStreamTypes[K]): void { + public publishInternalEvent<K extends keyof InternalEventTypes>(type: K, value?: InternalEventTypes[K]): void { this.publish('internal', type, typeof value === 'undefined' ? null : value); } @@ -61,17 +288,17 @@ export class GlobalEventService { } @bindThis - public publishMainStream<K extends keyof MainStreamTypes>(userId: MiUser['id'], type: K, value?: MainStreamTypes[K]): void { + public publishMainStream<K extends keyof MainEventTypes>(userId: MiUser['id'], type: K, value?: MainEventTypes[K]): void { this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishDriveStream<K extends keyof DriveStreamTypes>(userId: MiUser['id'], type: K, value?: DriveStreamTypes[K]): void { + public publishDriveStream<K extends keyof DriveEventTypes>(userId: MiUser['id'], type: K, value?: DriveEventTypes[K]): void { this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: MiNote['id'], type: K, value?: NoteStreamTypes[K]): void { + public publishNoteStream<K extends keyof NoteEventTypes>(noteId: MiNote['id'], type: K, value?: NoteEventTypes[K]): void { this.publish(`noteStream:${noteId}`, type, { id: noteId, body: value, @@ -79,17 +306,17 @@ export class GlobalEventService { } @bindThis - public publishUserListStream<K extends keyof UserListStreamTypes>(listId: MiUserList['id'], type: K, value?: UserListStreamTypes[K]): void { + public publishUserListStream<K extends keyof UserListEventTypes>(listId: MiUserList['id'], type: K, value?: UserListEventTypes[K]): void { this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: MiAntenna['id'], type: K, value?: AntennaStreamTypes[K]): void { + public publishAntennaStream<K extends keyof AntennaEventTypes>(antennaId: MiAntenna['id'], type: K, value?: AntennaEventTypes[K]): void { this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); } @bindThis - public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: MiRole['id'], type: K, value?: RoleTimelineStreamTypes[K]): void { + public publishRoleTimelineStream<K extends keyof RoleTimelineEventTypes>(roleId: MiRole['id'], type: K, value?: RoleTimelineEventTypes[K]): void { this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value); } @@ -99,7 +326,7 @@ export class GlobalEventService { } @bindThis - public publishAdminStream<K extends keyof AdminStreamTypes>(userId: MiUser['id'], type: K, value?: AdminStreamTypes[K]): void { + public publishAdminStream<K extends keyof AdminEventTypes>(userId: MiUser['id'], type: K, value?: AdminEventTypes[K]): void { this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); } } diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index 00e1e3c1fc..508544dc07 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -10,7 +10,7 @@ import { DI } from '@/di-symbols.js'; import { MiMeta } from '@/models/Meta.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { bindThis } from '@/decorators.js'; -import { StreamMessages } from '@/server/api/stream/types.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -46,7 +46,7 @@ export class MetaService implements OnApplicationShutdown { const obj = JSON.parse(data); if (obj.channel === 'internal') { - const { type, body } = obj.message as StreamMessages['internal']['payload']; + const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'metaUpdated': { this.cache = body; diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 972319ddcf..f20727ce41 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -110,9 +110,8 @@ class NotificationManager { // 通知される側のユーザーが通知する側のユーザーをミュートしていない限りは通知する if (!mentioneesMutedUserIds.includes(this.notifier.id)) { this.notificationService.createNotification(x.target, x.reason, { - notifierId: this.notifier.id, noteId: this.note.id, - }); + }, this.notifier.id); } } } @@ -515,9 +514,8 @@ export class NoteCreateService implements OnApplicationShutdown { }).then(followings => { for (const following of followings) { this.notificationService.createNotification(following.followerId, 'note', { - notifierId: user.id, noteId: note.id, - }); + }, user.id); } }); } diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 258ae44f7d..ba8798f181 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -18,6 +18,7 @@ import { NotificationEntityService } from '@/core/entities/NotificationEntitySer import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import type { Config } from '@/config.js'; +import { UserListService } from '@/core/UserListService.js'; @Injectable() export class NotificationService implements OnApplicationShutdown { @@ -38,6 +39,7 @@ export class NotificationService implements OnApplicationShutdown { private globalEventService: GlobalEventService, private pushNotificationService: PushNotificationService, private cacheService: CacheService, + private userListService: UserListService, ) { } @@ -74,27 +76,56 @@ export class NotificationService implements OnApplicationShutdown { public async createNotification( notifieeId: MiUser['id'], type: MiNotification['type'], - data: Partial<MiNotification>, + data: Omit<Partial<MiNotification>, 'notifierId'>, + notifierId?: MiUser['id'] | null, ): Promise<MiNotification | null> { const profile = await this.cacheService.userProfileCache.fetch(notifieeId); - const isMuted = profile.mutingNotificationTypes.includes(type); - if (isMuted) return null; + const recieveConfig = profile.notificationRecieveConfig[type]; + if (recieveConfig?.type === 'never') { + return null; + } - if (data.notifierId) { - if (notifieeId === data.notifierId) { + if (notifierId) { + if (notifieeId === notifierId) { return null; } const mutings = await this.cacheService.userMutingsCache.fetch(notifieeId); - if (mutings.has(data.notifierId)) { + if (mutings.has(notifierId)) { return null; } + + if (recieveConfig?.type === 'following') { + const isFollowing = await this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId)); + if (!isFollowing) { + return null; + } + } else if (recieveConfig?.type === 'follower') { + const isFollower = await this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId)); + if (!isFollower) { + return null; + } + } else if (recieveConfig?.type === 'mutualFollow') { + const [isFollowing, isFollower] = await Promise.all([ + this.cacheService.userFollowingsCache.fetch(notifieeId).then(followings => followings.has(notifierId)), + this.cacheService.userFollowingsCache.fetch(notifierId).then(followings => followings.has(notifieeId)), + ]); + if (!isFollowing && !isFollower) { + return null; + } + } else if (recieveConfig?.type === 'list') { + const isMember = await this.userListService.membersCache.fetch(recieveConfig.userListId).then(members => members.has(notifierId)); + if (!isMember) { + return null; + } + } } const notification = { id: this.idService.genId(), createdAt: new Date(), type: type, + notifierId: notifierId, ...data, } as MiNotification; @@ -117,8 +148,8 @@ export class NotificationService implements OnApplicationShutdown { this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed); this.pushNotificationService.pushNotification(notifieeId, 'notification', packed); - if (type === 'follow') this.emailNotificationFollow(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); - if (type === 'receiveFollowRequest') this.emailNotificationReceiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! })); + if (type === 'follow') this.emailNotificationFollow(notifieeId, await this.usersRepository.findOneByOrFail({ id: notifierId! })); + if (type === 'receiveFollowRequest') this.emailNotificationReceiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: notifierId! })); }, () => { /* aborted, ignore it */ }); return notification; diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index d9bde502c8..25464b19a8 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -219,10 +219,9 @@ export class ReactionService { // リアクションされたユーザーがローカルユーザーなら通知を作成 if (note.userHost === null) { this.notificationService.createNotification(note.userId, 'reaction', { - notifierId: user.id, noteId: note.id, reaction: reaction, - }); + }, user.id); } //#region 配信 diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index ec4d804219..c3445abc53 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -15,7 +15,7 @@ import { MetaService } from '@/core/MetaService.js'; import { CacheService } from '@/core/CacheService.js'; import type { RoleCondFormulaValue } from '@/models/Role.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { StreamMessages } from '@/server/api/stream/types.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import { IdService } from '@/core/IdService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -116,7 +116,7 @@ export class RoleService implements OnApplicationShutdown { const obj = JSON.parse(data); if (obj.channel === 'internal') { - const { type, body } = obj.message as StreamMessages['internal']['payload']; + const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'roleCreated': { const cached = this.rolesCache.get(); diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 5b2b0205d9..230f6ef261 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -230,8 +230,7 @@ export class UserFollowingService implements OnModuleInit { // 通知を作成 this.notificationService.createNotification(follower.id, 'followRequestAccepted', { - notifierId: followee.id, - }); + }, followee.id); } if (alreadyFollowed) return; @@ -304,8 +303,7 @@ export class UserFollowingService implements OnModuleInit { // 通知を作成 this.notificationService.createNotification(followee.id, 'follow', { - notifierId: follower.id, - }); + }, follower.id); } } @@ -488,9 +486,8 @@ export class UserFollowingService implements OnModuleInit { // 通知を作成 this.notificationService.createNotification(followee.id, 'receiveFollowRequest', { - notifierId: follower.id, followRequestId: followRequest.id, - }); + }, follower.id); } if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { diff --git a/packages/backend/src/core/UserListService.ts b/packages/backend/src/core/UserListService.ts index a71d50bba5..93dc5edbba 100644 --- a/packages/backend/src/core/UserListService.ts +++ b/packages/backend/src/core/UserListService.ts @@ -3,7 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { Inject, Injectable } from '@nestjs/common'; +import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; +import * as Redis from 'ioredis'; import type { UserListJoiningsRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; import type { MiUserList } from '@/models/UserList.js'; @@ -16,12 +17,22 @@ import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { QueueService } from '@/core/QueueService.js'; +import { RedisKVCache } from '@/misc/cache.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; @Injectable() -export class UserListService { +export class UserListService implements OnApplicationShutdown { public static TooManyUsersError = class extends Error {}; + public membersCache: RedisKVCache<Set<string>>; + constructor( + @Inject(DI.redis) + private redisClient: Redis.Redis, + + @Inject(DI.redisForSub) + private redisForSub: Redis.Redis, + @Inject(DI.userListJoiningsRepository) private userListJoiningsRepository: UserListJoiningsRepository, @@ -32,10 +43,48 @@ export class UserListService { private proxyAccountService: ProxyAccountService, private queueService: QueueService, ) { + this.membersCache = new RedisKVCache<Set<string>>(this.redisClient, 'userListMembers', { + lifetime: 1000 * 60 * 30, // 30m + memoryCacheLifetime: 1000 * 60, // 1m + fetcher: (key) => this.userListJoiningsRepository.find({ where: { userListId: key }, select: ['userId'] }).then(xs => new Set(xs.map(x => x.userId))), + toRedisConverter: (value) => JSON.stringify(Array.from(value)), + fromRedisConverter: (value) => new Set(JSON.parse(value)), + }); + + this.redisForSub.on('message', this.onMessage); } @bindThis - public async push(target: MiUser, list: MiUserList, me: MiUser) { + private async onMessage(_: string, data: string): Promise<void> { + const obj = JSON.parse(data); + + if (obj.channel === 'internal') { + const { type, body } = obj.message as GlobalEvents['internal']['payload']; + switch (type) { + case 'userListMemberAdded': { + const { userListId, memberId } = body; + const members = await this.membersCache.get(userListId); + if (members) { + members.add(memberId); + } + break; + } + case 'userListMemberRemoved': { + const { userListId, memberId } = body; + const members = await this.membersCache.get(userListId); + if (members) { + members.delete(memberId); + } + break; + } + default: + break; + } + } + } + + @bindThis + public async addMember(target: MiUser, list: MiUserList, me: MiUser) { const currentCount = await this.userListJoiningsRepository.countBy({ userListId: list.id, }); @@ -50,6 +99,7 @@ export class UserListService { userListId: list.id, } as MiUserListJoining); + this.globalEventService.publishInternalEvent('userListMemberAdded', { userListId: list.id, memberId: target.id }); this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target)); // このインスタンス内にこのリモートユーザーをフォローしているユーザーがいなくても投稿を受け取るためにダミーのユーザーがフォローしたということにする @@ -60,4 +110,26 @@ export class UserListService { } } } + + @bindThis + public async removeMember(target: MiUser, list: MiUserList) { + await this.userListJoiningsRepository.delete({ + userId: target.id, + userListId: list.id, + }); + + this.globalEventService.publishInternalEvent('userListMemberRemoved', { userListId: list.id, memberId: target.id }); + this.globalEventService.publishUserListStream(list.id, 'userRemoved', await this.userEntityService.pack(target)); + } + + @bindThis + public dispose(): void { + this.redisForSub.off('message', this.onMessage); + this.membersCache.dispose(); + } + + @bindThis + public onApplicationShutdown(signal?: string | undefined): void { + this.dispose(); + } } diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 1344f0ac97..ff70f7bc0c 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -9,7 +9,7 @@ import type { WebhooksRepository } from '@/models/_.js'; import type { MiWebhook } from '@/models/Webhook.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; -import { StreamMessages } from '@/server/api/stream/types.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import type { OnApplicationShutdown } from '@nestjs/common'; @Injectable() @@ -45,7 +45,7 @@ export class WebhookService implements OnApplicationShutdown { const obj = JSON.parse(data); if (obj.channel === 'internal') { - const { type, body } = obj.message as StreamMessages['internal']['payload']; + const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'webhookCreated': if (body.active) { diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index bf42e98ce0..a024286b48 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -308,6 +308,7 @@ export class NoteEntityService implements OnModuleInit { const packed: Packed<'Note'> = await awaitAll({ id: note.id, createdAt: note.createdAt.toISOString(), + updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined, userId: note.userId, user: this.userEntityService.pack(note.user ?? note.userId, me, { detail: false, diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 3dd64ce625..47fc98f479 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -452,7 +452,7 @@ export class UserEntityService implements OnModuleInit { hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), mutedWords: profile!.mutedWords, mutedInstances: profile!.mutedInstances, - mutingNotificationTypes: profile!.mutingNotificationTypes, + notificationRecieveConfig: profile!.notificationRecieveConfig, emailNotificationTypes: profile!.emailNotificationTypes, achievements: profile!.achievements, loggedInDays: profile!.loggedInDates.length, diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index ed86d4549e..f396a0cd7a 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -24,6 +24,11 @@ export class MiNote { }) public createdAt: Date; + @Column('timestamp with time zone', { + default: null, + }) + public updatedAt: Date | null; + @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index e4405c9da7..d6d85c5609 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -8,6 +8,7 @@ import { obsoleteNotificationTypes, ffVisibility, notificationTypes } from '@/ty import { id } from './util/id.js'; import { MiUser } from './User.js'; import { MiPage } from './Page.js'; +import { MiUserList } from './UserList.js'; // TODO: このテーブルで管理している情報すべてレジストリで管理するようにしても良いかも // ただ、「emailVerified が true なユーザーを find する」のようなクエリは書けなくなるからウーン @@ -222,16 +223,25 @@ export class MiUserProfile { }) public mutedInstances: string[]; - @Column('enum', { - enum: [ - ...notificationTypes, - // マイグレーションで削除が困難なので古いenumは残しておく - ...obsoleteNotificationTypes, - ], - array: true, - default: [], + @Column('jsonb', { + default: {}, }) - public mutingNotificationTypes: typeof notificationTypes[number][]; + public notificationRecieveConfig: { + [notificationType in typeof notificationTypes[number]]?: { + type: 'all'; + } | { + type: 'never'; + } | { + type: 'following'; + } | { + type: 'follower'; + } | { + type: 'mutualFollow'; + } | { + type: 'list'; + userListId: MiUserList['id']; + }; + }; @Column('varchar', { length: 32, array: true, default: '{}', diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index eb744aa109..ad0cb3c45d 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -17,6 +17,11 @@ export const packedNoteSchema = { optional: false, nullable: false, format: 'date-time', }, + updatedAt: { + type: 'string', + optional: true, nullable: true, + format: 'date-time', + }, deletedAt: { type: 'string', optional: true, nullable: true, @@ -142,7 +147,7 @@ export const packedNoteSchema = { isSensitive: { type: 'boolean', optional: true, nullable: false, - } + }, }, }, }, diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index f15b225a30..0181ea50e8 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -387,13 +387,9 @@ export const packedMeDetailedOnlySchema = { nullable: false, optional: false, }, }, - mutingNotificationTypes: { - type: 'array', - nullable: true, optional: false, - items: { - type: 'string', - nullable: false, optional: false, - }, + notificationRecieveConfig: { + type: 'object', + nullable: false, optional: false, }, emailNotificationTypes: { type: 'array', diff --git a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts index 54ca1a86df..60a0d1605f 100644 --- a/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportUserListsProcessorService.ts @@ -101,7 +101,7 @@ export class ImportUserListsProcessorService { if (await this.userListJoiningsRepository.findOneBy({ userListId: list!.id, userId: target.id }) != null) continue; - this.userListService.push(target, list!, user); + this.userListService.addMember(target, list!, user); } catch (e) { this.logger.warn(`Error in line:${linenum} ${e}`); } diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts index a13d08fd3a..e48dffecf4 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts @@ -8,6 +8,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AdsRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; export const meta = { tags: ['admin'], @@ -39,9 +40,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private adsRepository: AdsRepository, private idService: IdService, + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { - await this.adsRepository.insert({ + const ad = await this.adsRepository.insert({ id: this.idService.genId(), createdAt: new Date(), expiresAt: new Date(ps.expiresAt), @@ -53,7 +55,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ratio: ps.ratio, place: ps.place, memo: ps.memo, + }).then(r => this.adsRepository.findOneByOrFail({ id: r.identifiers[0].id })); + + this.moderationLogService.log(me, 'createAd', { + adId: ad.id, + ad: ad, }); + + return ad; }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts index d3c53d4f67..8097133a4c 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/delete.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AdsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -37,6 +38,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.adsRepository) private adsRepository: AdsRepository, + + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const ad = await this.adsRepository.findOneBy({ id: ps.id }); @@ -44,6 +47,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ad == null) throw new ApiError(meta.errors.noSuchAd); await this.adsRepository.delete(ad.id); + + this.moderationLogService.log(me, 'deleteAd', { + adId: ad.id, + ad: ad, + }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts index adff3ed0ae..29eff89523 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts @@ -22,6 +22,7 @@ export const paramDef = { limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, + publishing: { type: 'boolean', default: false }, }, required: [], } as const; @@ -36,6 +37,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId); + if (ps.publishing) { + query.andWhere('ad.expiresAt > :now', { now: new Date() }).andWhere('ad.startsAt <= :now', { now: new Date() }); + } const ads = await query.limit(ps.limit).getMany(); return ads; diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts index 5b77f67e10..d065f9ec50 100644 --- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { AdsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -46,6 +47,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.adsRepository) private adsRepository: AdsRepository, + + private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { const ad = await this.adsRepository.findOneBy({ id: ps.id }); @@ -63,6 +66,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- startsAt: new Date(ps.startsAt), dayOfWeek: ps.dayOfWeek, }); + + const updatedAd = await this.adsRepository.findOneByOrFail({ id: ad.id }); + + this.moderationLogService.log(me, 'updateAd', { + adId: ad.id, + before: ad, + after: updatedAd, + }); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/show-user.ts b/packages/backend/src/server/api/endpoints/admin/show-user.ts index e065b99e93..3454597532 100644 --- a/packages/backend/src/server/api/endpoints/admin/show-user.ts +++ b/packages/backend/src/server/api/endpoints/admin/show-user.ts @@ -81,7 +81,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- receiveAnnouncementEmail: profile.receiveAnnouncementEmail, mutedWords: profile.mutedWords, mutedInstances: profile.mutedInstances, - mutingNotificationTypes: profile.mutingNotificationTypes, + notificationRecieveConfig: profile.notificationRecieveConfig, isModerator: isModerator, isSilenced: isSilenced, isSuspended: user.isSuspended, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index b11e091957..431bb4c60a 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -165,9 +165,7 @@ export const paramDef = { mutedInstances: { type: 'array', items: { type: 'string', } }, - mutingNotificationTypes: { type: 'array', items: { - type: 'string', enum: notificationTypes, - } }, + notificationRecieveConfig: { type: 'object' }, emailNotificationTypes: { type: 'array', items: { type: 'string', } }, @@ -248,7 +246,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- profileUpdates.enableWordMute = ps.mutedWords.length > 0; } if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances; - if (ps.mutingNotificationTypes !== undefined) profileUpdates.mutingNotificationTypes = ps.mutingNotificationTypes as typeof notificationTypes[number][]; + if (ps.notificationRecieveConfig !== undefined) profileUpdates.notificationRecieveConfig = ps.notificationRecieveConfig; if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; if (typeof ps.isExplorable === 'boolean') updates.isExplorable = ps.isExplorable; if (typeof ps.hideOnlineStatus === 'boolean') updates.hideOnlineStatus = ps.hideOnlineStatus; diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index ccd2878d3c..cdf7f085e0 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -75,6 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } await this.notesRepository.update({ id: note.id }, { + updatedAt: new Date(), cw: ps.cw, text: ps.text, }); diff --git a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts index fd1bb48a4e..eae55905d3 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/create-from-public.ts @@ -144,7 +144,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } try { - await this.userListService.push(currentUser, userList, me); + await this.userListService.addMember(currentUser, userList, me); } catch (err) { if (err instanceof UserListService.TooManyUsersError) { throw new ApiError(meta.errors.tooManyUsers); diff --git a/packages/backend/src/server/api/endpoints/users/lists/pull.ts b/packages/backend/src/server/api/endpoints/users/lists/pull.ts index 0b01061740..e90122224c 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/pull.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/pull.ts @@ -4,12 +4,11 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import type { UserListsRepository, UserListJoiningsRepository } from '@/models/_.js'; +import type { UserListsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { GetterService } from '@/server/api/GetterService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { DI } from '@/di-symbols.js'; +import { UserListService } from '@/core/UserListService.js'; import { ApiError } from '../../../error.js'; export const meta = { @@ -53,12 +52,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.userListsRepository) private userListsRepository: UserListsRepository, - @Inject(DI.userListJoiningsRepository) - private userListJoiningsRepository: UserListJoiningsRepository, - - private userEntityService: UserEntityService, + private userListService: UserListService, private getterService: GetterService, - private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { // Fetch the list @@ -77,10 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - // Pull the user - await this.userListJoiningsRepository.delete({ userListId: userList.id, userId: user.id }); - - this.globalEventService.publishUserListStream(userList.id, 'userRemoved', await this.userEntityService.pack(user)); + await this.userListService.removeMember(user, userList); }); } } diff --git a/packages/backend/src/server/api/endpoints/users/lists/push.ts b/packages/backend/src/server/api/endpoints/users/lists/push.ts index 9bb1a71f58..72a6a7380d 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/push.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/push.ts @@ -127,7 +127,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } try { - await this.userListService.push(user, userList, me); + await this.userListService.addMember(user, userList, me); } catch (err) { if (err instanceof UserListService.TooManyUsersError) { throw new ApiError(meta.errors.tooManyUsers); diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index fd91681fc1..a73071ea99 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -12,10 +12,10 @@ import type { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; import { MiUserProfile } from '@/models/_.js'; +import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js'; import type { ChannelsService } from './ChannelsService.js'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; -import type { StreamEventEmitter, StreamMessages } from './types.js'; /** * Main stream connection @@ -122,7 +122,7 @@ export default class Connection { } @bindThis - private onBroadcastMessage(data: StreamMessages['broadcast']['payload']) { + private onBroadcastMessage(data: GlobalEvents['broadcast']['payload']) { this.sendMessageToWs(data.type, data.body); } @@ -196,7 +196,7 @@ export default class Connection { } @bindThis - private async onNoteStreamMessage(data: StreamMessages['note']['payload']) { + private async onNoteStreamMessage(data: GlobalEvents['note']['payload']) { this.sendMessageToWs('noteUpdated', { id: data.body.id, type: data.type, diff --git a/packages/backend/src/server/api/stream/channels/antenna.ts b/packages/backend/src/server/api/stream/channels/antenna.ts index 87648a3a77..a48e6ba5c6 100644 --- a/packages/backend/src/server/api/stream/channels/antenna.ts +++ b/packages/backend/src/server/api/stream/channels/antenna.ts @@ -7,8 +7,8 @@ import { Injectable } from '@nestjs/common'; import { isUserRelated } from '@/misc/is-user-related.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import Channel from '../channel.js'; -import type { StreamMessages } from '../types.js'; class AntennaChannel extends Channel { public readonly chName = 'antenna'; @@ -35,7 +35,7 @@ class AntennaChannel extends Channel { } @bindThis - private async onEvent(data: StreamMessages['antenna']['payload']) { + private async onEvent(data: GlobalEvents['antenna']['payload']) { if (data.type === 'note') { const note = await this.noteEntityService.pack(data.body.id, this.user, { detail: true }); diff --git a/packages/backend/src/server/api/stream/channels/role-timeline.ts b/packages/backend/src/server/api/stream/channels/role-timeline.ts index 76b5875343..38d3604cc5 100644 --- a/packages/backend/src/server/api/stream/channels/role-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/role-timeline.ts @@ -9,8 +9,8 @@ import type { Packed } from '@/misc/json-schema.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; +import type { GlobalEvents } from '@/core/GlobalEventService.js'; import Channel from '../channel.js'; -import { StreamMessages } from '../types.js'; class RoleTimelineChannel extends Channel { public readonly chName = 'roleTimeline'; @@ -37,7 +37,7 @@ class RoleTimelineChannel extends Channel { } @bindThis - private async onEvent(data: StreamMessages['roleTimeline']['payload']) { + private async onEvent(data: GlobalEvents['roleTimeline']['payload']) { if (data.type === 'note') { const note = data.body; diff --git a/packages/backend/src/server/api/stream/types.ts b/packages/backend/src/server/api/stream/types.ts deleted file mode 100644 index 2436750cd6..0000000000 --- a/packages/backend/src/server/api/stream/types.ts +++ /dev/null @@ -1,259 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import type { MiChannel } from '@/models/Channel.js'; -import type { MiUser } from '@/models/User.js'; -import type { MiUserProfile } from '@/models/UserProfile.js'; -import type { MiNote } from '@/models/Note.js'; -import type { MiAntenna } from '@/models/Antenna.js'; -import type { MiDriveFile } from '@/models/DriveFile.js'; -import type { MiDriveFolder } from '@/models/DriveFolder.js'; -import type { MiUserList } from '@/models/UserList.js'; -import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; -import type { MiSignin } from '@/models/Signin.js'; -import type { MiPage } from '@/models/Page.js'; -import type { Packed } from '@/misc/json-schema.js'; -import type { MiWebhook } from '@/models/Webhook.js'; -import type { MiMeta } from '@/models/Meta.js'; -import { MiRole, MiRoleAssignment } from '@/models/_.js'; -import type Emitter from 'strict-event-emitter-types'; -import type { EventEmitter } from 'events'; - -//#region Stream type-body definitions -export interface InternalStreamTypes { - userChangeSuspendedState: { id: MiUser['id']; isSuspended: MiUser['isSuspended']; }; - userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; - remoteUserUpdated: { id: MiUser['id']; }; - follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; - unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; - blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; - blockingDeleted: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; - policiesUpdated: MiRole['policies']; - roleCreated: MiRole; - roleDeleted: MiRole; - roleUpdated: MiRole; - userRoleAssigned: MiRoleAssignment; - userRoleUnassigned: MiRoleAssignment; - webhookCreated: MiWebhook; - webhookDeleted: MiWebhook; - webhookUpdated: MiWebhook; - antennaCreated: MiAntenna; - antennaDeleted: MiAntenna; - antennaUpdated: MiAntenna; - metaUpdated: MiMeta; - followChannel: { userId: MiUser['id']; channelId: MiChannel['id']; }; - unfollowChannel: { userId: MiUser['id']; channelId: MiChannel['id']; }; - updateUserProfile: MiUserProfile; - mute: { muterId: MiUser['id']; muteeId: MiUser['id']; }; - unmute: { muterId: MiUser['id']; muteeId: MiUser['id']; }; -} - -export interface BroadcastTypes { - emojiAdded: { - emoji: Packed<'EmojiDetailed'>; - }; - emojiUpdated: { - emojis: Packed<'EmojiDetailed'>[]; - }; - emojiDeleted: { - emojis: { - id?: string; - name: string; - [other: string]: any; - }[]; - }; - announcementCreated: { - announcement: Packed<'Announcement'>; - }; -} - -export interface MainStreamTypes { - notification: Packed<'Notification'>; - mention: Packed<'Note'>; - reply: Packed<'Note'>; - renote: Packed<'Note'>; - follow: Packed<'UserDetailedNotMe'>; - followed: Packed<'User'>; - unfollow: Packed<'User'>; - meUpdated: Packed<'User'>; - pageEvent: { - pageId: MiPage['id']; - event: string; - var: any; - userId: MiUser['id']; - user: Packed<'User'>; - }; - urlUploadFinished: { - marker?: string | null; - file: Packed<'DriveFile'>; - }; - readAllNotifications: undefined; - unreadNotification: Packed<'Notification'>; - unreadMention: MiNote['id']; - readAllUnreadMentions: undefined; - unreadSpecifiedNote: MiNote['id']; - readAllUnreadSpecifiedNotes: undefined; - readAllAntennas: undefined; - unreadAntenna: MiAntenna; - readAllAnnouncements: undefined; - myTokenRegenerated: undefined; - signin: MiSignin; - registryUpdated: { - scope?: string[]; - key: string; - value: any | null; - }; - driveFileCreated: Packed<'DriveFile'>; - readAntenna: MiAntenna; - receiveFollowRequest: Packed<'User'>; - announcementCreated: { - announcement: Packed<'Announcement'>; - }; -} - -export interface DriveStreamTypes { - fileCreated: Packed<'DriveFile'>; - fileDeleted: MiDriveFile['id']; - fileUpdated: Packed<'DriveFile'>; - folderCreated: Packed<'DriveFolder'>; - folderDeleted: MiDriveFolder['id']; - folderUpdated: Packed<'DriveFolder'>; -} - -export interface NoteStreamTypes { - pollVoted: { - choice: number; - userId: MiUser['id']; - }; - deleted: { - deletedAt: Date; - }; - updated: { - cw: string | null; - text: string; - }; - reacted: { - reaction: string; - emoji?: { - name: string; - url: string; - } | null; - userId: MiUser['id']; - }; - unreacted: { - reaction: string; - userId: MiUser['id']; - }; -} -type NoteStreamEventTypes = { - [key in keyof NoteStreamTypes]: { - id: MiNote['id']; - body: NoteStreamTypes[key]; - }; -}; - -export interface UserListStreamTypes { - userAdded: Packed<'User'>; - userRemoved: Packed<'User'>; -} - -export interface AntennaStreamTypes { - note: MiNote; -} - -export interface RoleTimelineStreamTypes { - note: Packed<'Note'>; -} - -export interface AdminStreamTypes { - newAbuseUserReport: { - id: MiAbuseUserReport['id']; - targetUserId: MiUser['id'], - reporterId: MiUser['id'], - comment: string; - }; -} -//#endregion - -// 辞書(interface or type)から{ type, body }ユニオンを定義 -// https://stackoverflow.com/questions/49311989/can-i-infer-the-type-of-a-value-using-extends-keyof-type -// VS Codeの展開を防止するためにEvents型を定義 -type Events<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } }; -type EventUnionFromDictionary< - T extends object, - U = Events<T> -> = U[keyof U]; - -// redis通すとDateのインスタンスはstringに変換されるので -export type Serialized<T> = { - [K in keyof T]: - T[K] extends Date - ? string - : T[K] extends (Date | null) - ? (string | null) - : T[K] extends Record<string, any> - ? Serialized<T[K]> - : T[K]; -}; - -type SerializedAll<T> = { - [K in keyof T]: Serialized<T[K]>; -}; - -// name/messages(spec) pairs dictionary -export type StreamMessages = { - internal: { - name: 'internal'; - payload: EventUnionFromDictionary<SerializedAll<InternalStreamTypes>>; - }; - broadcast: { - name: 'broadcast'; - payload: EventUnionFromDictionary<SerializedAll<BroadcastTypes>>; - }; - main: { - name: `mainStream:${MiUser['id']}`; - payload: EventUnionFromDictionary<SerializedAll<MainStreamTypes>>; - }; - drive: { - name: `driveStream:${MiUser['id']}`; - payload: EventUnionFromDictionary<SerializedAll<DriveStreamTypes>>; - }; - note: { - name: `noteStream:${MiNote['id']}`; - payload: EventUnionFromDictionary<SerializedAll<NoteStreamEventTypes>>; - }; - userList: { - name: `userListStream:${MiUserList['id']}`; - payload: EventUnionFromDictionary<SerializedAll<UserListStreamTypes>>; - }; - roleTimeline: { - name: `roleTimelineStream:${MiRole['id']}`; - payload: EventUnionFromDictionary<SerializedAll<RoleTimelineStreamTypes>>; - }; - antenna: { - name: `antennaStream:${MiAntenna['id']}`; - payload: EventUnionFromDictionary<SerializedAll<AntennaStreamTypes>>; - }; - admin: { - name: `adminStream:${MiUser['id']}`; - payload: EventUnionFromDictionary<SerializedAll<AdminStreamTypes>>; - }; - notes: { - name: 'notesStream'; - payload: Serialized<Packed<'Note'>>; - }; -}; - -// API event definitions -// ストリームごとのEmitterの辞書を用意 -type EventEmitterDictionary = { [x in keyof StreamMessages]: Emitter.default<EventEmitter, { [y in StreamMessages[x]['name']]: (e: StreamMessages[x]['payload']) => void }> }; -// 共用体型を交差型にする型 https://stackoverflow.com/questions/54938141/typescript-convert-union-to-intersection -type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; -// Emitter辞書から共用体型を作り、UnionToIntersectionで交差型にする -export type StreamEventEmitter = UnionToIntersection<EventEmitterDictionary[keyof StreamMessages]>; -// { [y in name]: (e: spec) => void }をまとめてその交差型をEmitterにかけるとts(2590)にひっかかる - -// provide stream channels union -export type StreamChannels = StreamMessages[keyof StreamMessages]['name']; diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 7b928263ab..a9b9a55bc0 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -57,6 +57,9 @@ export const moderationLogTypes = [ 'unmarkSensitiveDriveFile', 'resolveAbuseReport', 'createInvitation', + 'createAd', + 'updateAd', + 'deleteAd', ] as const; export type ModerationLogPayloads = { @@ -202,4 +205,28 @@ export type ModerationLogPayloads = { createInvitation: { invitations: any[]; }; + createAd: { + adId: string; + ad: any; + }; + updateAd: { + adId: string; + before: any; + after: any; + }; + deleteAd: { + adId: string; + ad: any; + }; +}; + +export type Serialized<T> = { + [K in keyof T]: + T[K] extends Date + ? string + : T[K] extends (Date | null) + ? (string | null) + : T[K] extends Record<string, any> + ? Serialized<T[K]> + : T[K]; }; diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index 0b3f200260..2a3a694cf5 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -166,7 +166,7 @@ describe('ユーザー', () => { unreadAnnouncements: user.unreadAnnouncements, mutedWords: user.mutedWords, mutedInstances: user.mutedInstances, - mutingNotificationTypes: user.mutingNotificationTypes, + notificationRecieveConfig: user.notificationRecieveConfig, emailNotificationTypes: user.emailNotificationTypes, achievements: user.achievements, loggedInDays: user.loggedInDays, @@ -414,7 +414,7 @@ describe('ユーザー', () => { assert.deepStrictEqual(response.unreadAnnouncements, []); assert.deepStrictEqual(response.mutedWords, []); assert.deepStrictEqual(response.mutedInstances, []); - assert.deepStrictEqual(response.mutingNotificationTypes, []); + assert.deepStrictEqual(response.notificationRecieveConfig, {}); assert.deepStrictEqual(response.emailNotificationTypes, ['follow', 'receiveFollowRequest']); assert.deepStrictEqual(response.achievements, []); assert.deepStrictEqual(response.loggedInDays, 0); @@ -495,8 +495,8 @@ describe('ユーザー', () => { { parameters: (): object => ({ mutedWords: [] }) }, { parameters: (): object => ({ mutedInstances: ['xxxx.xxxxx'] }) }, { parameters: (): object => ({ mutedInstances: [] }) }, - { parameters: (): object => ({ mutingNotificationTypes: ['note', 'follow', 'mention', 'reply', 'renote', 'quote', 'reaction', 'pollEnded', 'receiveFollowRequest', 'followRequestAccepted', 'achievementEarned', 'app'] }) }, - { parameters: (): object => ({ mutingNotificationTypes: [] }) }, + { parameters: (): object => ({ notificationRecieveConfig: { mention: { type: 'following' } } }) }, + { parameters: (): object => ({ notificationRecieveConfig: {} }) }, { parameters: (): object => ({ emailNotificationTypes: ['mention', 'reply', 'quote', 'follow', 'receiveFollowRequest'] }) }, { parameters: (): object => ({ emailNotificationTypes: [] }) }, ] as const)('を書き換えることができる($#)', async ({ parameters }) => { diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 8a7d9f73cf..de7dd869ee 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -93,6 +93,9 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <footer> <div :class="$style.noteFooterInfo"> + <div v-if="appearNote.updatedAt"> + {{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/> + </div> <MkA :to="notePage(appearNote)"> <MkTime :time="appearNote.createdAt" mode="detail"/> </MkA> diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index dda7238d27..05f98c638e 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> </div> <div :class="$style.info"> + <span v-if="note.updatedAt" style="margin-right: 0.5em;" :title="i18n.ts.edited"><i class="ti ti-pencil"></i></span> <MkA :to="notePage(note)"> <MkTime :time="note.createdAt"/> </MkA> diff --git a/packages/frontend/src/components/MkNotificationSelectWindow.vue b/packages/frontend/src/components/MkNotificationSelectWindow.vue new file mode 100644 index 0000000000..3d5a56975b --- /dev/null +++ b/packages/frontend/src/components/MkNotificationSelectWindow.vue @@ -0,0 +1,78 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkModalWindow + ref="dialog" + :width="400" + :height="450" + :withOkButton="true" + :okButtonDisabled="false" + @ok="ok()" + @close="dialog?.close()" + @closed="emit('closed')" +> + <template #header>{{ i18n.ts.notificationSetting }}</template> + + <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_gaps_m"> + <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo> + <div class="_buttons"> + <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> + <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> + </div> + <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> + </div> + </MkSpacer> +</MkModalWindow> +</template> + +<script lang="ts" setup> +import { ref, Ref } from 'vue'; +import MkSwitch from './MkSwitch.vue'; +import MkInfo from './MkInfo.vue'; +import MkButton from './MkButton.vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import { notificationTypes } from '@/const.js'; +import { i18n } from '@/i18n.js'; + +type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>> + +const emit = defineEmits<{ + (ev: 'done', v: { excludeTypes: string[] }): void, + (ev: 'closed'): void, +}>(); + +const props = withDefaults(defineProps<{ + excludeTypes?: typeof notificationTypes[number][]; +}>(), { + excludeTypes: () => [], +}); + +const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); + +const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(!props.excludeTypes.includes(t)) }), {} as any); + +function ok() { + emit('done', { + excludeTypes: (Object.keys(typesMap) as typeof notificationTypes[number][]) + .filter(type => !typesMap[type].value), + }); + + if (dialog) dialog.close(); +} + +function disableAll() { + for (const type of notificationTypes) { + typesMap[type].value = false; + } +} + +function enableAll() { + for (const type of notificationTypes) { + typesMap[type].value = true; + } +} +</script> diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue deleted file mode 100644 index a25914b214..0000000000 --- a/packages/frontend/src/components/MkNotificationSettingWindow.vue +++ /dev/null @@ -1,95 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<MkModalWindow - ref="dialog" - :width="400" - :height="450" - :withOkButton="true" - :okButtonDisabled="false" - @ok="ok()" - @close="dialog?.close()" - @closed="emit('closed')" -> - <template #header>{{ i18n.ts.notificationSetting }}</template> - - <MkSpacer :marginMin="20" :marginMax="28"> - <div class="_gaps_m"> - <template v-if="showGlobalToggle"> - <MkSwitch v-model="useGlobalSetting"> - {{ i18n.ts.useGlobalSetting }} - <template #caption>{{ i18n.ts.useGlobalSettingDesc }}</template> - </MkSwitch> - </template> - <template v-if="!useGlobalSetting"> - <MkInfo>{{ i18n.ts.notificationSettingDesc }}</MkInfo> - <div class="_buttons"> - <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton> - <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton> - </div> - <MkSwitch v-for="ntype in notificationTypes" :key="ntype" v-model="typesMap[ntype].value">{{ i18n.t(`_notification._types.${ntype}`) }}</MkSwitch> - </template> - </div> - </MkSpacer> -</MkModalWindow> -</template> - -<script lang="ts" setup> -import { ref, Ref } from 'vue'; -import MkSwitch from './MkSwitch.vue'; -import MkInfo from './MkInfo.vue'; -import MkButton from './MkButton.vue'; -import MkModalWindow from '@/components/MkModalWindow.vue'; -import { notificationTypes } from '@/const'; -import { i18n } from '@/i18n.js'; - -type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>> - -const emit = defineEmits<{ - (ev: 'done', v: { includingTypes: string[] | null }): void, - (ev: 'closed'): void, -}>(); - -const props = withDefaults(defineProps<{ - includingTypes?: typeof notificationTypes[number][] | null; - showGlobalToggle?: boolean; -}>(), { - includingTypes: () => [], - showGlobalToggle: true, -}); - -let includingTypes = $computed(() => props.includingTypes?.filter(x => notificationTypes.includes(x)) ?? []); - -const dialog = $shallowRef<InstanceType<typeof MkModalWindow>>(); - -const typesMap: TypesMap = notificationTypes.reduce((p, t) => ({ ...p, [t]: ref<boolean>(includingTypes.includes(t)) }), {} as any); -let useGlobalSetting = $ref((includingTypes === null || includingTypes.length === 0) && props.showGlobalToggle); - -function ok() { - if (useGlobalSetting) { - emit('done', { includingTypes: null }); - } else { - emit('done', { - includingTypes: (Object.keys(typesMap) as typeof notificationTypes[number][]) - .filter(type => typesMap[type].value), - }); - } - - if (dialog) dialog.close(); -} - -function disableAll() { - for (const type of notificationTypes) { - typesMap[type].value = false; - } -} - -function enableAll() { - for (const type of notificationTypes) { - typesMap[type].value = true; - } -} -</script> diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index ad1cb92ce1..6ba2e513c5 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -30,11 +30,11 @@ import MkNote from '@/components/MkNote.vue'; import { useStream } from '@/stream.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; -import { notificationTypes } from '@/const'; +import { notificationTypes } from '@/const.js'; import { infoImageUrl } from '@/instance.js'; const props = defineProps<{ - includeTypes?: typeof notificationTypes[number][]; + excludeTypes?: typeof notificationTypes[number][]; }>(); const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); @@ -43,13 +43,12 @@ const pagination: Paging = { endpoint: 'i/notifications' as const, limit: 10, params: computed(() => ({ - includeTypes: props.includeTypes ?? undefined, - excludeTypes: props.includeTypes ? undefined : $i.mutingNotificationTypes, + excludeTypes: props.excludeTypes ?? undefined, })), }; const onNotification = (notification) => { - const isMuted = props.includeTypes ? !props.includeTypes.includes(notification.type) : $i.mutingNotificationTypes.includes(notification.type); + const isMuted = props.excludeTypes ? props.excludeTypes.includes(notification.type) : false; if (isMuted || document.visibilityState === 'visible') { useStream().send('readNotification'); } diff --git a/packages/frontend/src/components/MkNotifyButton.vue b/packages/frontend/src/components/MkNotifyButton.vue new file mode 100644 index 0000000000..b25d79ee6b --- /dev/null +++ b/packages/frontend/src/components/MkNotifyButton.vue @@ -0,0 +1,367 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> +<template> + <button + class="_button" + :class="[$style.root,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' +,}]" v-if="isFollowing" + @click="onClick" + > + <span v-if="props.user.notify === 'none'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] "><i class="ti ti-bell"></i></span> + <span v-else-if="props.user.notify === 'normal'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"><i class="ti ti-bell-off"></i></span> + </button> +</template> + +<script lang="ts" setup> +import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue'; +import * as Misskey from 'misskey-js'; +import * as os from '@/os.js'; +import {useStream} from '@/stream.js'; +import {defaultStore} from "@/store.js"; + +let gaming = ref(''); + +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; +} else { + gaming.value = ''; +} + +watch(darkMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}) + +const props = withDefaults(defineProps<{ + user: Misskey.entities.UserDetailed, + full?: boolean, + large?: boolean, +}>(), { + full: false, + large: false, +}); + +let isFollowing = $ref(props.user.isFollowing); +let notify = $ref(props.user.notify); +const connection = useStream().useChannel('main'); + +if (props.user.isFollowing == null) { + os.api('users/show', { + userId: props.user.id, + }).then(onFollowChange); +} + +if (props.user.notify == null) { + os.api('users/show', { + userId: props.user.id, + }).then(onNotifyChange); +} + +function onFollowChange(user: Misskey.entities.UserDetailed) { + if (user.id === props.user.id) { + isFollowing = user.isFollowing; + } +} +function onNotifyChange(user: Misskey.entities.UserDetailed) { + if (user.id === props.user.id) { + notify = user.notify; + console.log(props.user.notify) + } +} +async function onClick() { + try { + await os.apiWithDialog('following/update', { + userId: props.user.id, + notify: props.user.notify === 'normal' ? 'none' : 'normal', + }).then(() => { + props.user.notify = props.user.notify === 'normal' ? 'none' : 'normal'; + }); + }finally { + + } +} + +onMounted(() => { + connection.on('follow', onFollowChange); + connection.on('unfollow', onFollowChange); +}); +onBeforeUnmount(() => { + connection.dispose(); +}); +</script> + +<style lang="scss" module> +.root { + position: relative; + display: inline-block; + font-weight: bold; + color: var(--fgOnWhite); + border: solid 1px var(--accent); + padding: 0; + height: 31px; + font-size: 16px; + border-radius: 32px; + background: #fff; + vertical-align: bottom; + + &.gamingDark { + color: black !important; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + border: solid 1px black; + } + + &.gamingLight { + color: white !important; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + border: solid 1px white; + } + + &.full { + padding: 0 8px 0 12px; + font-size: 14px; + &.gamingDark { + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + + } + + &.gamingLight { + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + } + } + + &.large { + font-size: 16px; + height: 38px; + padding: 0 12px 0 16px; + } + + &:not(.full) { + width: 31px; + } + + &:focus-visible { + &:after { + content: ""; + pointer-events: none; + position: absolute; + top: -5px; + right: -5px; + bottom: -5px; + left: -5px; + border: 2px solid var(--focus); + border-radius: 32px; + } + } + + &:hover { + //background: mix($primary, #fff, 20); + } + + &:active { + //background: mix($primary, #fff, 40); + } + + &.active { + color: var(--fgOnAccent); + background: var(--accent); + + &:hover { + background: var(--accentLighten); + border-color: var(--accentLighten); + } + + &:active { + background: var(--accentDarken); + border-color: var(--accentDarken); + } + + &.gamingDark:hover { + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + border: solid 1px white; + } + + &.gamingDark:active { + color: black; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + border: solid 1px white; + } + + &.gamingLight:hover { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + border: solid 1px white; + } + + &.gamingLight:active { + color: white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + border: solid 1px white; + } + + &.gamingDark { + -webkit-text-fill-color: unset !important; + color: black; + border: solid 1px white; + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &.gamingLight { + -webkit-text-fill-color: unset !important; + color: white; + border: solid 1px white; + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite !important; + + } + + } + + +} + + + .gamingDark { + color: black; + + } + + .gamingLight { + color: white; + + } + + +@-webkit-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationLight { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-webkit-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@-moz-keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} + +@keyframes AnimationDark { + 0% { + background-position: 0% 50% + } + 50% { + background-position: 100% 50% + } + 100% { + background-position: 0% 50% + } +} +</style> diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue index cd9d86ca45..6aa0cf0427 100644 --- a/packages/frontend/src/pages/admin/ads.vue +++ b/packages/frontend/src/pages/admin/ads.vue @@ -5,11 +5,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> + <template #header> + <XHeader :actions="headerActions" :tabs="headerTabs" /> + </template> <MkSpacer :contentMax="900"> + <MkSwitch :modelValue="publishing" @update:modelValue="onChangePublishing"> + {{ i18n.ts.publishing }} + </MkSwitch> <div> <div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad"> - <MkAd v-if="ad.url" :specify="ad"/> + <MkAd v-if="ad.url" :specify="ad" /> <MkInput v-model="ad.url" type="url"> <template #label>URL</template> </MkInput> @@ -46,7 +51,8 @@ SPDX-License-Identifier: AGPL-3.0-only <span> {{ i18n.ts._ad.timezoneinfo }} <div v-for="(day, index) in daysOfWeek" :key="index"> - <input :id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0" @change="toggleDayOfWeek(ad, index)"> + <input :id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0" + @change="toggleDayOfWeek(ad, index)"> <label :for="`ad${ad.id}-${index}`">{{ day }}</label> </div> </span> @@ -55,8 +61,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.memo }}</template> </MkTextarea> <div class="buttons"> - <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + <MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)"><i + class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + <MkButton class="button" inline danger @click="remove(ad)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }} + </MkButton> </div> </div> <MkButton class="button" @click="more()"> @@ -75,6 +83,7 @@ import MkInput from '@/components/MkInput.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkRadios from '@/components/MkRadios.vue'; import MkFolder from '@/components/MkFolder.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; import FormSplit from '@/components/form/split.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; @@ -86,8 +95,9 @@ let ads: any[] = $ref([]); const localTime = new Date(); const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000; const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday]; +let publishing = false; -os.api('admin/ad/list').then(adsResponse => { +os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => { ads = adsResponse.map(r => { const exdate = new Date(r.expiresAt); const stdate = new Date(r.startsAt); @@ -101,6 +111,10 @@ os.api('admin/ad/list').then(adsResponse => { }); }); +const onChangePublishing = (v) => { + publishing = v; + refresh(); +}; // 選択された曜日(index)のビットフラグを操作する function toggleDayOfWeek(ad, index) { ad.dayOfWeek ^= 1 << index; @@ -131,6 +145,8 @@ function remove(ad) { if (ad.id == null) return; os.apiWithDialog('admin/ad/delete', { id: ad.id, + }).then(() => { + refresh(); }); }); } @@ -172,7 +188,7 @@ function save(ad) { } } function more() { - os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id }).then(adsResponse => { + os.api('admin/ad/list', { untilId: ads.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => { ads = ads.concat(adsResponse.map(r => { const exdate = new Date(r.expiresAt); const stdate = new Date(r.startsAt); @@ -188,7 +204,7 @@ function more() { } function refresh() { - os.api('admin/ad/list').then(adsResponse => { + os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => { ads = adsResponse.map(r => { const exdate = new Date(r.expiresAt); const stdate = new Date(r.startsAt); diff --git a/packages/frontend/src/pages/admin/modlog.ModLog.vue b/packages/frontend/src/pages/admin/modlog.ModLog.vue index 8d83b32fa1..99b8544f33 100644 --- a/packages/frontend/src/pages/admin/modlog.ModLog.vue +++ b/packages/frontend/src/pages/admin/modlog.ModLog.vue @@ -6,7 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkFolder> <template #label> - <b>{{ i18n.ts._moderationLogTypes[log.type] }}</b> + <b + :class="{ + [$style.logGreen]: ['createRole', 'addCustomEmoji', 'createGlobalAnnouncement', 'createUserAnnouncement', 'createAd', 'createInvitation'].includes(log.type), + [$style.logYellow]: ['markSensitiveDriveFile', 'resetPassword'].includes(log.type), + [$style.logRed]: ['suspend', 'deleteRole', 'suspendRemoteInstance', 'deleteGlobalAnnouncement', 'deleteUserAnnouncement', 'deleteCustomEmoji', 'deleteNote', 'deleteDriveFile', 'deleteAd'].includes(log.type) + }" + >{{ i18n.ts._moderationLogTypes[log.type] }}</b> <span v-if="log.type === 'updateUserNote'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'suspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> <span v-else-if="log.type === 'unsuspend'">: @{{ log.info.userUsername }}{{ log.info.userHost ? '@' + log.info.userHost : '' }}</span> @@ -18,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else-if="log.type === 'deleteRole'">: {{ log.info.role.name }}</span> <span v-else-if="log.type === 'addCustomEmoji'">: {{ log.info.emoji.name }}</span> <span v-else-if="log.type === 'updateCustomEmoji'">: {{ log.info.before.name }}</span> + <span v-else-if="log.type === 'deleteCustomEmoji'">: {{ log.info.emoji.name }}</span> <span v-else-if="log.type === 'markSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span> <span v-else-if="log.type === 'unmarkSensitiveDriveFile'">: @{{ log.info.fileUserUsername }}{{ log.info.fileUserHost ? '@' + log.info.fileUserHost : '' }}</span> <span v-else-if="log.type === 'suspendRemoteInstance'">: {{ log.info.host }}</span> @@ -76,6 +83,11 @@ SPDX-License-Identifier: AGPL-3.0-only <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/> </div> </template> + <template v-else-if="log.type === 'updateAd'"> + <div :class="$style.diff"> + <CodeDiff :context="5" :hideHeader="true" :oldString="JSON5.stringify(log.info.before, null, '\t')" :newString="JSON5.stringify(log.info.after, null, '\t')" language="javascript" maxHeight="300px"/> + </div> + </template> <details> <summary>raw</summary> @@ -114,4 +126,16 @@ const props = defineProps<{ border-radius: 6px; overflow: clip; } + +.logYellow { + color: var(--warning); +} + +.logRed { + color: var(--error); +} + +.logGreen { + color: var(--success); +} </style> diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index 3ae6319c7e..8d2475b085 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="800"> <div v-if="tab === 'all'"> - <XNotifications class="notifications" :includeTypes="includeTypes"/> + <XNotifications class="notifications" :excludeTypes="excludeTypes"/> </div> <div v-else-if="tab === 'mentions'"> <MkNotes :pagination="mentionsPagination"/> @@ -27,10 +27,11 @@ import MkNotes from '@/components/MkNotes.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { notificationTypes } from '@/const'; +import { notificationTypes } from '@/const.js'; let tab = $ref('all'); let includeTypes = $ref<string[] | null>(null); +const excludeTypes = $computed(() => includeTypes ? notificationTypes.filter(t => !includeTypes.includes(t)) : null); const mentionsPagination = { endpoint: 'notes/mentions' as const, diff --git a/packages/frontend/src/pages/settings/notifications.notification-config.vue b/packages/frontend/src/pages/settings/notifications.notification-config.vue new file mode 100644 index 0000000000..c1f107c2b8 --- /dev/null +++ b/packages/frontend/src/pages/settings/notifications.notification-config.vue @@ -0,0 +1,50 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="_gaps_m"> + <MkSelect v-model="type"> + <option value="all">{{ i18n.ts.all }}</option> + <option value="following">{{ i18n.ts.following }}</option> + <option value="follower">{{ i18n.ts.followers }}</option> + <option value="mutualFollow">{{ i18n.ts.mutualFollow }}</option> + <option value="list">{{ i18n.ts.userList }}</option> + <option value="never">{{ i18n.ts.none }}</option> + </MkSelect> + + <MkSelect v-if="type === 'list'" v-model="userListId"> + <template #label>{{ i18n.ts.userList }}</template> + <option v-for="list in props.userLists" :key="list.id" :value="list.id">{{ list.name }}</option> + </MkSelect> + + <div class="_buttons"> + <MkButton inline primary @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton> + </div> +</div> +</template> + +<script lang="ts" setup> +import { } from 'vue'; +import * as Misskey from 'misskey-js'; +import MkSelect from '@/components/MkSelect.vue'; +import MkButton from '@/components/MkButton.vue'; +import { i18n } from '@/i18n.js'; + +const props = defineProps<{ + value: any; + userLists: Misskey.entities.UserList[]; +}>(); + +const emit = defineEmits<{ + (ev: 'update', result: any): void; +}>(); + +let type = $ref(props.value.type); +let userListId = $ref(props.value.userListId); + +function save() { + emit('update', { type, userListId }); +} +</script> diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 5b3c29e7c8..88e02af1f4 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -5,7 +5,26 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps_m"> - <FormLink @click="configure"><template #icon><i class="ti ti-settings"></i></template>{{ i18n.ts.notificationSetting }}</FormLink> + <FormSection first> + <template #label>{{ i18n.ts.notificationRecieveConfig }}</template> + <div class="_gaps_s"> + <MkFolder v-for="type in notificationTypes" :key="type"> + <template #label>{{ i18n.t('_notification._types.' + type) }}</template> + <template #suffix> + {{ + $i.notificationRecieveConfig[type]?.type === 'never' ? i18n.ts.none : + $i.notificationRecieveConfig[type]?.type === 'following' ? i18n.ts.following : + $i.notificationRecieveConfig[type]?.type === 'follower' ? i18n.ts.followers : + $i.notificationRecieveConfig[type]?.type === 'mutualFollow' ? i18n.ts.mutualFollow : + $i.notificationRecieveConfig[type]?.type === 'list' ? i18n.ts.userList : + i18n.ts.all + }} + </template> + + <XNotificationConfig :userLists="userLists" :value="$i.notificationRecieveConfig[type] ?? { type: 'all' }" @update="(res) => updateReceiveConfig(type, res)"/> + </MkFolder> + </div> + </FormSection> <FormSection> <div class="_gaps_m"> <FormLink @click="readAllNotifications">{{ i18n.ts.markAsReadAllNotifications }}</FormLink> @@ -37,19 +56,22 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; +import XNotificationConfig from './notifications.notification-config.vue'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; +import MkFolder from '@/components/MkFolder.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import * as os from '@/os.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; -import { notificationTypes } from '@/const'; +import { notificationTypes } from '@/const.js'; let allowButton = $shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>(); let pushRegistrationInServer = $computed(() => allowButton?.pushRegistrationInServer); let sendReadMessage = $computed(() => pushRegistrationInServer?.sendReadMessage || false); +const userLists = await os.api('users/lists/list'); async function readAllUnreadNotes() { await os.api('i/read-all-unread-notes'); @@ -59,21 +81,15 @@ async function readAllNotifications() { await os.api('notifications/mark-all-as-read'); } -function configure() { - const includingTypes = notificationTypes.filter(x => !$i!.mutingNotificationTypes.includes(x)); - os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), { - includingTypes, - showGlobalToggle: false, - }, { - done: async (res) => { - const { includingTypes: value } = res; - await os.apiWithDialog('i/update', { - mutingNotificationTypes: notificationTypes.filter(x => !value.includes(x)), - }).then(i => { - $i!.mutingNotificationTypes = i.mutingNotificationTypes; - }); +async function updateReceiveConfig(type, value) { + await os.apiWithDialog('i/update', { + notificationRecieveConfig: { + ...$i!.notificationRecieveConfig, + [type]: value, }, - }, 'closed'); + }).then(i => { + $i!.notificationRecieveConfig = i.notificationRecieveConfig; + }); } function onChangeSendReadMessage(v: boolean) { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index ef61014e3e..3523603b3d 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -183,7 +183,7 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis }] : []), { key: 'social', title: i18n.ts._timelines.social, - icon: 'ti ti-universe', + icon: 'ti ti-rocket', iconOnly: true, }] : []), ...(isGlobalTimelineAvailable ? [{ key: 'global', diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 385c81a97f..db4eba4e72 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -34,6 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> <div v-if="$i" class="actions"> <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> + <MkNotifyButton v-if="$i.id != user.id " :user="user"></MkNotifyButton> <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> </div> </div> @@ -166,6 +167,7 @@ import { confetti } from '@/scripts/confetti.js'; import MkNotes from '@/components/MkNotes.vue'; import { api } from '@/os.js'; import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; +import MkNotifyButton from "@/components/MkNotifyButton.vue"; function calcAge(birthdate: string): number { const date = new Date(birthdate); diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts index e815e74444..a4c913749e 100644 --- a/packages/frontend/src/scripts/use-note-capture.ts +++ b/packages/frontend/src/scripts/use-note-capture.ts @@ -72,6 +72,7 @@ export function useNoteCapture(props: { } case 'updated': { + note.value.updatedAt = new Date().toISOString(); note.value.cw = body.cw; note.value.text = body.text; break; diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index 10972f3bc1..732a4cfeaa 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -65,9 +65,7 @@ const dev = _DEV_; let notifications = $ref<Misskey.entities.Notification[]>([]); -function onNotification(notification: Misskey.entities.Notification, isClient: boolean = false) { - if ($i.mutingNotificationTypes.includes(notification.type)) return; - +function onNotification(notification: Misskey.entities.Notification, isClient = false) { if (document.visibilityState === 'visible') { if (!isClient) { useStream().send('readNotification'); diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue index 0455d43deb..b6bbf1fb55 100644 --- a/packages/frontend/src/ui/deck/notifications-column.vue +++ b/packages/frontend/src/ui/deck/notifications-column.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XColumn :column="column" :isStacked="isStacked" :menu="menu"> <template #header><i class="ti ti-bell" style="margin-right: 8px;"></i>{{ column.name }}</template> - <XNotifications :includeTypes="column.includingTypes"/> + <XNotifications :excludeTypes="props.column.excludeTypes"/> </XColumn> </template> @@ -25,13 +25,13 @@ const props = defineProps<{ }>(); function func() { - os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), { - includingTypes: props.column.includingTypes, + os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), { + excludeTypes: props.column.excludeTypes, }, { done: async (res) => { - const { includingTypes } = res; + const { excludeTypes } = res; updateColumn(props.column.id, { - includingTypes: includingTypes, + excludeTypes: excludeTypes, }); }, }, 'closed'); diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue index ed969abcfc..6b890d41a8 100644 --- a/packages/frontend/src/widgets/WidgetActivity.vue +++ b/packages/frontend/src/widgets/WidgetActivity.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import XCalendar from './WidgetActivity.calendar.vue'; import XChart from './WidgetActivity.chart.vue'; import { GetFormResultType } from '@/scripts/form.js'; diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue index ce71413328..76b35f6fed 100644 --- a/packages/frontend/src/widgets/WidgetAichan.vue +++ b/packages/frontend/src/widgets/WidgetAichan.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, onUnmounted, shallowRef } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; const name = 'ai'; diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue index 4b927563f8..1b8c8ad9bc 100644 --- a/packages/frontend/src/widgets/WidgetAiscript.vue +++ b/packages/frontend/src/widgets/WidgetAiscript.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import { Interpreter, Parser, utils } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import MkContainer from '@/components/MkContainer.vue'; diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue index a41a8bdbd4..53b6020ffc 100644 --- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue +++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, Ref, ref, watch } from 'vue'; import { Interpreter, Parser } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue index b80117a9c5..a7bdd4c49c 100644 --- a/packages/frontend/src/widgets/WidgetButton.vue +++ b/packages/frontend/src/widgets/WidgetButton.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { Interpreter, Parser } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { createAiScriptEnv } from '@/scripts/aiscript/api.js'; diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue index 4b592203ec..7fabd09a24 100644 --- a/packages/frontend/src/widgets/WidgetCalendar.vue +++ b/packages/frontend/src/widgets/WidgetCalendar.vue @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { i18n } from '@/i18n.js'; import { useInterval } from '@/scripts/use-interval.js'; diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index 9b19c007de..5e7464f3a4 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkClickerGame from '@/components/MkClickerGame.vue'; diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue index 6e6e1fba96..e4ea2c97dd 100644 --- a/packages/frontend/src/widgets/WidgetClock.vue +++ b/packages/frontend/src/widgets/WidgetClock.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkAnalogClock from '@/components/MkAnalogClock.vue'; diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue index a250d28230..9ff5f8dcef 100644 --- a/packages/frontend/src/widgets/WidgetDigitalClock.vue +++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { timezones } from '@/scripts/timezones.js'; import MkDigitalClock from '@/components/MkDigitalClock.vue'; diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue index 1d067aac69..47f94402fb 100644 --- a/packages/frontend/src/widgets/WidgetFederation.vue +++ b/packages/frontend/src/widgets/WidgetFederation.vue @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue index ffcf059a42..4ae77e86fc 100644 --- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue +++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkTagCloud from '@/components/MkTagCloud.vue'; diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue index e0025a8074..89770b2216 100644 --- a/packages/frontend/src/widgets/WidgetJobQueue.vue +++ b/packages/frontend/src/widgets/WidgetJobQueue.vue @@ -52,7 +52,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onUnmounted, reactive } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; import number from '@/filters/number.js'; diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue index c12afd50b0..1f5666b3ef 100644 --- a/packages/frontend/src/widgets/WidgetMemo.vue +++ b/packages/frontend/src/widgets/WidgetMemo.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import { defaultStore } from '@/store.js'; diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue index b9999d8011..796578395f 100644 --- a/packages/frontend/src/widgets/WidgetNotifications.vue +++ b/packages/frontend/src/widgets/WidgetNotifications.vue @@ -10,14 +10,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template #func="{ buttonStyleClass }"><button class="_button" :class="buttonStyleClass" @click="configureNotification()"><i class="ti ti-settings"></i></button></template> <div> - <XNotifications :includeTypes="widgetProps.includingTypes"/> + <XNotifications :excludeTypes="widgetProps.excludeTypes"/> </div> </MkContainer> </template> <script lang="ts" setup> import { defineAsyncComponent } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import XNotifications from '@/components/MkNotifications.vue'; @@ -35,10 +35,10 @@ const widgetPropsDef = { type: 'number' as const, default: 300, }, - includingTypes: { + excludeTypes: { type: 'array' as const, hidden: true, - default: null, + default: [], }, }; @@ -54,12 +54,12 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name, ); const configureNotification = () => { - os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSettingWindow.vue')), { - includingTypes: widgetProps.includingTypes, + os.popup(defineAsyncComponent(() => import('@/components/MkNotificationSelectWindow.vue')), { + excludeTypes: widgetProps.excludeTypes, }, { done: async (res) => { - const { includingTypes } = res; - widgetProps.includingTypes = includingTypes; + const { excludeTypes } = res; + widgetProps.excludeTypes = excludeTypes; save(); }, }, 'closed'); diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue index a77468009e..46fe991f37 100644 --- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue +++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue index 8c01d3cce9..9af4f80873 100644 --- a/packages/frontend/src/widgets/WidgetPhotos.vue +++ b/packages/frontend/src/widgets/WidgetPhotos.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onUnmounted, ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js'; diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue index d20ea3f8f4..320b47a4ff 100644 --- a/packages/frontend/src/widgets/WidgetPostForm.vue +++ b/packages/frontend/src/widgets/WidgetPostForm.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkPostForm from '@/components/MkPostForm.vue'; diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue index 8cd5ffa9e1..fc54af2d71 100644 --- a/packages/frontend/src/widgets/WidgetProfile.vue +++ b/packages/frontend/src/widgets/WidgetProfile.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { $i } from '@/account.js'; import { userPage } from '@/filters/user.js'; diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index 22833415dd..5540a09c71 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, watch, computed } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import { url as base } from '@/config.js'; diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue index ce8142f1ad..2b2a5233be 100644 --- a/packages/frontend/src/widgets/WidgetRssTicker.vue +++ b/packages/frontend/src/widgets/WidgetRssTicker.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref, watch, computed } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import MarqueeText from '@/components/MkMarquee.vue'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue index fe7ac8303d..82b6246add 100644 --- a/packages/frontend/src/widgets/WidgetSlideshow.vue +++ b/packages/frontend/src/widgets/WidgetSlideshow.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onMounted, ref, shallowRef } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index 61402dd6aa..2bc4e79e32 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; import MkContainer from '@/components/MkContainer.vue'; diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue index 0a57305526..0d4df28a95 100644 --- a/packages/frontend/src/widgets/WidgetTrends.vue +++ b/packages/frontend/src/widgets/WidgetTrends.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue index 69bceb7572..33585cd721 100644 --- a/packages/frontend/src/widgets/WidgetUnixClock.vue +++ b/packages/frontend/src/widgets/WidgetUnixClock.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { onUnmounted, ref, watch } from 'vue'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; const name = 'unixClock'; diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue index 343e9a4292..a0d460c704 100644 --- a/packages/frontend/src/widgets/WidgetUserList.vue +++ b/packages/frontend/src/widgets/WidgetUserList.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import * as os from '@/os.js'; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 3a4f477800..f0fc47c207 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1545,7 +1545,7 @@ export type Endpoints = { receiveAnnouncementEmail?: boolean; alwaysMarkNsfw?: boolean; mutedWords?: string[][]; - mutingNotificationTypes?: Notification_2['type'][]; + notificationRecieveConfig?: any; emailNotificationTypes?: string[]; alsoKnownAs?: string[]; }; @@ -2475,7 +2475,22 @@ type MeDetailed = UserDetailed & { isDeleted: boolean; isExplorable: boolean; mutedWords: string[][]; - mutingNotificationTypes: string[]; + notificationRecieveConfig: { + [notificationType in typeof notificationTypes_2[number]]?: { + type: 'all'; + } | { + type: 'never'; + } | { + type: 'following'; + } | { + type: 'follower'; + } | { + type: 'mutualFollow'; + } | { + type: 'list'; + userListId: string; + }; + }; noCrawle: boolean; receiveAnnouncementEmail: boolean; usePasswordLessLogin: boolean; @@ -2619,7 +2634,7 @@ type ModerationLog = { }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; @@ -2628,6 +2643,7 @@ export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; type Note = { id: ID; createdAt: DateString; + updatedAt?: DateString | null; text: string | null; cw: string | null; user: User; @@ -2966,7 +2982,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:631:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts -// src/entities.ts:579:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts +// src/entities.ts:595:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index 46d790fe31..e69d8324a1 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -430,7 +430,7 @@ export type Endpoints = { receiveAnnouncementEmail?: boolean; alwaysMarkNsfw?: boolean; mutedWords?: string[][]; - mutingNotificationTypes?: Notification['type'][]; + notificationRecieveConfig?: any; emailNotificationTypes?: string[]; alsoKnownAs?: string[]; }; res: MeDetailed; }; diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts index 14a5b5643c..271a64274f 100644 --- a/packages/misskey-js/src/consts.ts +++ b/packages/misskey-js/src/consts.ts @@ -75,6 +75,9 @@ export const moderationLogTypes = [ 'unmarkSensitiveDriveFile', 'resolveAbuseReport', 'createInvitation', + 'createAd', + 'updateAd', + 'deleteAd', ] as const; export type ModerationLogPayloads = { @@ -220,4 +223,17 @@ export type ModerationLogPayloads = { createInvitation: { invitations: any[]; }; + createAd: { + adId: string; + ad: any; + }; + updateAd: { + adId: string; + before: any; + after: any; + }; + deleteAd: { + adId: string; + ad: any; + }; }; diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index de573b00cd..e6bac2a5f4 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -1,4 +1,4 @@ -import { ModerationLogPayloads } from './consts.js'; +import { ModerationLogPayloads, notificationTypes } from './consts.js'; export type ID = string; export type DateString = string; @@ -104,7 +104,22 @@ export type MeDetailed = UserDetailed & { isDeleted: boolean; isExplorable: boolean; mutedWords: string[][]; - mutingNotificationTypes: string[]; + notificationRecieveConfig: { + [notificationType in typeof notificationTypes[number]]?: { + type: 'all'; + } | { + type: 'never'; + } | { + type: 'following'; + } | { + type: 'follower'; + } | { + type: 'mutualFollow'; + } | { + type: 'list'; + userListId: string; + }; + }; noCrawle: boolean; receiveAnnouncementEmail: boolean; usePasswordLessLogin: boolean; @@ -162,6 +177,7 @@ export type GalleryPost = { export type Note = { id: ID; createdAt: DateString; + updatedAt?: DateString | null; text: string | null; cw: string | null; user: User; diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts index f33ab1c33c..e96f8585c7 100644 --- a/packages/sw/src/scripts/create-notification.ts +++ b/packages/sw/src/scripts/create-notification.ts @@ -6,7 +6,7 @@ /* * Notification manager for SW */ -import type { BadgeNames, PushNotificationDataMap } from '@/types'; +import type { BadgeNames, PushNotificationDataMap } from '@/types.js'; import { char2fileName } from '@/scripts/twemoji-base.js'; import { cli } from '@/scripts/operations.js'; import { getAccountFromId } from '@/scripts/get-account-from-id.js'; diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts index ca75e395c0..be4f066b5f 100644 --- a/packages/sw/src/scripts/operations.ts +++ b/packages/sw/src/scripts/operations.ts @@ -8,7 +8,7 @@ * 各種操作 */ import * as Misskey from 'misskey-js'; -import type { SwMessage, SwMessageOrderType } from '@/types'; +import type { SwMessage, SwMessageOrderType } from '@/types.js'; import { getAccountFromId } from '@/scripts/get-account-from-id.js'; import { getUrlWithLoginId } from '@/scripts/login-id.js'; diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts index 080161cf14..b79fd8ce7a 100644 --- a/packages/sw/src/sw.ts +++ b/packages/sw/src/sw.ts @@ -5,7 +5,7 @@ import { get } from 'idb-keyval'; import * as Misskey from 'misskey-js'; -import type { PushNotificationDataMap } from '@/types'; +import type { PushNotificationDataMap } from '@/types.js'; import { createEmptyNotification, createNotification } from '@/scripts/create-notification.js'; import { swLang } from '@/scripts/lang.js'; import * as swos from '@/scripts/operations.js'; From 3398341a63dddbcde9ab3bac0aff8ae2c7aa5b05 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 29 Sep 2023 23:27:02 +0900 Subject: [PATCH 094/501] =?UTF-8?q?=E3=81=84=E3=81=84=E3=81=8B=E3=82=93?= =?UTF-8?q?=E3=81=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/scripts/emojiKitchen/emojiData.ts | 76695 ++++++++-------- .../src/scripts/emojiKitchen/emojiMixer.ts | 31 +- 2 files changed, 39994 insertions(+), 36732 deletions(-) diff --git a/packages/frontend/src/scripts/emojiKitchen/emojiData.ts b/packages/frontend/src/scripts/emojiKitchen/emojiData.ts index e11ead82ff..7b218a5478 100644 --- a/packages/frontend/src/scripts/emojiKitchen/emojiData.ts +++ b/packages/frontend/src/scripts/emojiKitchen/emojiData.ts @@ -1,37031 +1,40276 @@ // original Source: https://github.com/alcor/emoji-supply/blob/main/docs/kitchen/emojidata.js +// Last commit data: 2a08dea export const points = -['2764-fe0f','1f9e1','1f49b','1f49a','1fa75','1f499','1f49c','1fa77','1f90e','1fa76','1f5a4','1f90d','1fa84','1f600','1f603','1f604','1f601','1f606','1f605','1f923','1f602','1f642','1f643','1fae0','1f609','1f60a','1f607','1f970','1f60d','1f929','1f618','1f617','263a-fe0f','1f61a','1f619','1f972','1f60b','1f61b','1f61c','1f92a','1f61d','1f911','1f917','1f92d','1fae2','1fae3','1f92b','1f914','1fae1','1f910','1f928','1f610','1f611','1f636','1fae5','1f636-200d-1f32b-fe0f','1f60f','1f612','1f644','1f62c','1f62e-200d-1f4a8','1f925','1f60c','1f614','1f62a','1f924','1f634','1f637','1f912','1f915','1f922','1f92e','1f927','1f975','1f976','1f974','1f635','1f92f','1f920','1f973','1f978','1f60e','1f913','1f9d0','1f615','1fae4','1f61f','1f641','2639-fe0f','1f62e','1f62f','1f632','1f633','1f97a','1f979','1f626','1f627','1f628','1f630','1f625','1f622','1f62d','1f631','1f616','1f623','1f61e','1f613','1f629','1f62b','1f971','1f624','1f621','1f620','1f92c','1f608','1f47f','1f480','1f4a9','1f921','1f47b','1f47d','1f916','1f648','1f48c','1f498','1f49d','1f496','1f497','1f493','1f49e','1f495','1f49f','2763-fe0f','1f494','2764-fe0f-200d-1fa79','1f48b','1f4af','1f4a5','1f4ab','1f573-fe0f','1f4ac','1f5ef-fe0f','1f44d','1f9e0','1fac0','1fac1','1f9b7','1f9b4','1f440','1f441-fe0f','1fae6','1f937','1fac2','1f435','1f436','1f429','1f43a','1f98a','1f99d','1f431','1f981','1f42f','1f984','1f98c','1f437','1f410','1f999','1f42d','1f430','1f994','1f987','1f43b','1f428','1f43c','1f9a5','1f43e','1f414','1f426','1f427','1f54a-fe0f','1f989','1f9a9','1fabf','1f422','1f433','1f41f','1f988','1f419','1f41a','1fab8','1f40c','1f98b','1f41d','1fab2','1f41e','1f997','1fab3','1f577-fe0f','1f982','1f9a0','1f490','1f338','1f4ae','1fab7','1f3f5-fe0f','1f339','1f940','1f33a','1f33b','1f33c','1f337','1fabb','1f331','1fab4','1f332','1f333','1f334','1f335','1f33f','1f340','1f341','1f343','1fab9','1f344','1f347','1f348','1f349','1f34a','1f34b','1f34c','1f34d','1f96d','1f350','1f352','1f353','1fad0','1f95d','1f345','1fad2','1f965','1f951','1f954','1f955','1f33d','1f336-fe0f','1fad1','1f952','1f96c','1f966','1f9c4','1f9c5','1f95c','1fad8','1f330','1fada','1fadb','1f35e','1f950','1fad3','1f968','1f96f','1f95e','1f9c7','1f9c0','1f356','1f969','1f953','1f354','1f35f','1f32d','1f96a','1f32e','1f32f','1fad4','1f959','1f9c6','1f373','1f958','1f372','1fad5','1f963','1f957','1f37f','1f9c2','1f96b','1f371','1f358','1f359','1f35a','1f35b','1f35c','1f35d','1f360','1f363','1f364','1f365','1f960','1f961','1f980','1f99e','1f9aa','1f366','1f367','1f368','1f369','1f36a','1f382','1f370','1f9c1','1f967','1f36b','1f36c','1f37c','2615','1f375','1f376','1f379','1f964','1f9cb','1f9c9','1f962','1f37d-fe0f','1f30d','1f30b','1f3d5-fe0f','1f3d6-fe0f','1f3dd-fe0f','1f3de-fe0f','1f3df-fe0f','1f3db-fe0f','1faa8','1fab5','1f3e0','1f307','1f3a1','1f3a2','1f3aa','1f682','1f68c','1f695','1f697','1f3ce-fe0f','1f6f9','1f6fc','1f6a8','1f6d1','2693','1f6df','1f6f6','2708-fe0f','1f680','1f6f8','1f9f3','231b','23f3','23f0','1f31a','1f31b','1f31c','1f321-fe0f','1f31d','1f31e','1fa90','2b50','1f31f','1f30c','2601-fe0f','26c5','1f327-fe0f','1f329-fe0f','1f32a-fe0f','1f32c-fe0f','1f300','1f308','2602-fe0f','26a1','26c4','2604-fe0f','1f525','1f4a7','1f30a','1f383','1f386','1f388','1f38a','1f380','1f381','1f397-fe0f','1f39f-fe0f','1f396-fe0f','1f3c6','1f3c5','1f947','1f948','1f949','26bd','26be','1f94e','1f3c0','1f3d0','1f3c8','1f3c9','1f3be','1f94f','1f3b3','1f3cf','1f3d1','1f3d2','1f94d','1f3d3','1f3f8','1f94a','1f94b','1f945','26f3','26f8-fe0f','1f93f','1f3bd','1f3bf','1f6f7','1f94c','1f3af','1fa80','1fa81','1f3b1','1f52e','1f3ae','1f3b0','1f3b2','1f9e9','1faa9','2660-fe0f','2665-fe0f','265f-fe0f','1f0cf','1f004','1f3b4','1f3ad','1f3a8','1f9f5','1faa1','1f9f6','1f9e6','1f460','1fa70','1faae','1f451','1f393','1f48d','1f48e','1f508','1f4e3','1f514','1f399-fe0f','1f39a-fe0f','1f39b-fe0f','1f3a4','1f3a7','1f4fb','1f3b7','1fa97','1f3b8','1f3b9','1f3ba','1f3bb','1fa95','1f941','1fa98','1f4f1','260e-fe0f','1f4df','1f4e0','1f50b','1faab','1f50c','1f4bb','1f5a8-fe0f','1f4be','1f4bf','1f39e-fe0f','1f3ac','1f4fa','1f4f7','1f4fc','1f50e','1f4a1','1f4da','1f4f0','1f4b8','270f-fe0f','2712-fe0f','1f58a-fe0f','1f58c-fe0f','1f58d-fe0f','1f4c8','1f4c9','1f4ca','1f587-fe0f','1f4cf','2702-fe0f','1f5c3-fe0f','1f5d1-fe0f','1f512','1f5dd-fe0f','26cf-fe0f','1f6e0-fe0f','1fa83','1f3f9','2696-fe0f','26d3-fe0f','1f9f2','1f9ea','1f9ec','1f52c','1f52d','1fa78','1fa7a','1f6cb-fe0f','1faa4','1fa92','1f9f9','1f9fc','1fae7','1f9ff','1f6ae','26a0-fe0f','262f-fe0f','262e-fe0f','2648','2649','264a','264b','264c','264d','264e','264f','2650','2651','2652','2653','26ce','1f4f4','2716-fe0f','2795','2796','2797','267e-fe0f','2049-fe0f','2753','2757','3030-fe0f','267b-fe0f','2705','27b0','27bf','a9-fe0f','ae-fe0f','2122-fe0f','1f192','1f193','1f195','1f197','1f198','1f199'] + ['2764-fe0f','1f9e1','1f49b','1f49a','1fa75','1f499','1f49c','1fa77','1f90e','1fa76','1f5a4','1f90d','1fa84','1f600','1f603','1f604','1f601','1f606','1f605','1f923','1f602','1f642','1f643','1fae0','1f609','1f60a','1f607','1f970','1f60d','1f929','1f618','1f617','263a-fe0f','1f61a','1f619','1f972','1f60b','1f61b','1f61c','1f92a','1f61d','1f911','1f917','1f92d','1fae2','1fae3','1f92b','1f914','1fae1','1f910','1f928','1f610','1f611','1f636','1fae5','1f636-200d-1f32b-fe0f','1f60f','1f612','1f644','1f62c','1f62e-200d-1f4a8','1f925','1f60c','1f614','1f62a','1f924','1f634','1f637','1f912','1f915','1f922','1f92e','1f927','1f975','1f976','1f974','1f635','1f92f','1f920','1f973','1f978','1f60e','1f913','1f9d0','1f615','1fae4','1f61f','1f641','2639-fe0f','1f62e','1f62f','1f632','1f633','1f97a','1f979','1f626','1f627','1f628','1f630','1f625','1f622','1f62d','1f631','1f616','1f623','1f61e','1f613','1f629','1f62b','1f971','1f624','1f621','1f620','1f92c','1f608','1f47f','1f480','1f4a9','1f921','1f479','1f47a','1f47b','1f47d','1f916','1f648','1f48c','1f498','1f49d','1f496','1f497','1f493','1f49e','1f495','1f49f','2763-fe0f','1f494','2764-fe0f-200d-1fa79','1f48b','1f4af','1f4a5','1f4ab','1f573-fe0f','1f4ac','1f5ef-fe0f','1f44d','1f9e0','1fac0','1fac1','1f9b7','1f9b4','1f440','1f441-fe0f','1f444','1fae6','1f937','1f5e3-fe0f','1f464','1f465','1fac2','1f463','1f435','1f436','1f429','1f43a','1f98a','1f99d','1f431','1f981','1f42f','1f984','1f98c','1f42e','1f437','1f410','1f999','1f42d','1f430','1f994','1f987','1f43b','1f428','1f43c','1f9a5','1f43e','1f414','1f426','1f427','1f54a-fe0f','1f989','1f9a9','1fabf','1f438','1f422','1f433','1f41f','1f988','1f419','1f41a','1fab8','1f40c','1f98b','1f41d','1fab2','1f41e','1f997','1fab3','1f577-fe0f','1f982','1f9a0','1f490','1f338','1f4ae','1fab7','1f3f5-fe0f','1f339','1f940','1f33a','1f33b','1f33c','1f337','1fabb','1f331','1fab4','1f332','1f333','1f334','1f335','1f33e','1f33f','1f340','1f341','1f342','1f343','1fab9','1f344','1f347','1f348','1f349','1f34a','1f34b','1f34c','1f34d','1f96d','1f34e','1f350','1f352','1f353','1fad0','1f95d','1f345','1fad2','1f965','1f951','1f954','1f955','1f33d','1f336-fe0f','1fad1','1f952','1f96c','1f966','1f9c4','1f9c5','1f95c','1fad8','1f330','1fada','1fadb','1f35e','1f950','1fad3','1f968','1f96f','1f95e','1f9c7','1f9c0','1f356','1f357','1f969','1f953','1f354','1f35f','1f32d','1f96a','1f32e','1f32f','1fad4','1f959','1f9c6','1f373','1f958','1f372','1fad5','1f963','1f957','1f37f','1f9c8','1f9c2','1f96b','1f371','1f358','1f359','1f35a','1f35b','1f35c','1f35d','1f360','1f362','1f363','1f364','1f365','1f361','1f95f','1f960','1f961','1f980','1f99e','1f9aa','1f366','1f367','1f368','1f369','1f36a','1f382','1f370','1f9c1','1f967','1f36b','1f36c','1f36d','1f36e','1f36f','1f37c','1f95b','2615','1fad6','1f375','1f376','1f37e','1f377','1f379','1f942','1fad7','1f964','1f9cb','1f9c3','1f9c9','1f962','1f37d-fe0f','1f3fa','1f30d','1f3d4-fe0f','26f0-fe0f','1f30b','1f3d5-fe0f','1f3d6-fe0f','1f3dd-fe0f','1f3de-fe0f','1f3df-fe0f','1f3db-fe0f','1f9f1','1faa8','1fab5','1f3da-fe0f','1f3e0','1f3f0','1f5fc','1f307','2668-fe0f','1f3a0','1f3a1','1f3a2','1f3aa','1f682','1f687','1f68c','1f695','1f697','1f69a','1f69b','1f69c','1f3ce-fe0f','1f3cd-fe0f','1f6b2','1f6f4','1f6f9','1f6fc','1f6e3-fe0f','26fd','1f6a8','1f6a6','1f6d1','1f6a7','2693','1f6df','1f6f6','2708-fe0f','1f6a0','1f6a1','1f680','1f6f8','1f9f3','231b','23f3','231a','23f0','1f570-fe0f','1f31a','1f31b','1f31c','1f321-fe0f','1f31d','1f31e','1fa90','2b50','1f31f','1f30c','2601-fe0f','26c5','1f327-fe0f','1f329-fe0f','1f32a-fe0f','1f32c-fe0f','1f300','1f308','2602-fe0f','26a1','26c4','2604-fe0f','1f525','1f4a7','1f30a','1f383','1f386','1f388','1f38a','1f380','1f381','1f397-fe0f','1f39f-fe0f','1f396-fe0f','1f3c6','1f3c5','1f947','1f948','1f949','26bd','26be','1f94e','1f3c0','1f3d0','1f3c8','1f3c9','1f3be','1f94f','1f3b3','1f3cf','1f3d1','1f3d2','1f94d','1f3d3','1f3f8','1f94a','1f94b','1f945','26f3','26f8-fe0f','1f3a3','1f93f','1f3bd','1f3bf','1f6f7','1f94c','1f3af','1fa80','1fa81','1f3b1','1f52e','1f3ae','1f3b0','1f3b2','1f9e9','1faa9','2660-fe0f','2665-fe0f','265f-fe0f','1f0cf','1f004','1f3b4','1f3ad','1f3a8','1f9f5','1faa1','1f9f6','1f455','1f9e6','1f457','1faad','1f45c','1f6cd-fe0f','1f45f','1f97f','1f460','1fa70','1faae','1f451','1f452','1f393','1f48d','1f48e','1f508','1f4e3','1f514','1f399-fe0f','1f39a-fe0f','1f39b-fe0f','1f3a4','1f3a7','1f4fb','1f3b7','1fa97','1f3b8','1f3b9','1f3ba','1f3bb','1fa95','1f941','1fa98','1fa87','1fa88','1f4f1','260e-fe0f','1f4df','1f4e0','1f50b','1faab','1f50c','1f4bb','1f5a8-fe0f','1f4be','1f4bf','1f39e-fe0f','1f3ac','1f4fa','1f4f7','1f4fc','1f50e','1f4a1','1fa94','1f4da','1f4f0','1f4b8','1f4e6','1f5f3-fe0f','270f-fe0f','2712-fe0f','1f58a-fe0f','1f58c-fe0f','1f58d-fe0f','1f4c8','1f4c9','1f4ca','1f587-fe0f','1f4cf','2702-fe0f','1f5c3-fe0f','1f5d1-fe0f','1f512','1f5dd-fe0f','26cf-fe0f','1f6e0-fe0f','1fa83','1f3f9','2699-fe0f','2696-fe0f','26d3-fe0f','1f9f2','1f9ea','1f9ec','1f52c','1f52d','1fa78','1fa7a','1f6cb-fe0f','1faa4','1fa92','1f9f9','1f9fa','1f9fc','1fae7','1f6d2','1f9ff','1f5ff','1f6ae','26a0-fe0f','2622-fe0f','2623-fe0f','262f-fe0f','262e-fe0f','2648','2649','264a','264b','264c','264d','264e','264f','2650','2651','2652','2653','26ce','1f4f4','2716-fe0f','2795','2796','2797','267e-fe0f','2049-fe0f','2753','2757','3030-fe0f','267b-fe0f','2705','27b0','27bf','a9-fe0f','ae-fe0f','2122-fe0f','1f522','1f192','1f193','1f195','1f197','1f198','1f199','1f47e'] export const revisions = -[20201001,20230418,20211115,20230301,20220815,20230127,20220203,20221101,20220506,20210831,20220406,20210218,20230126,20230216,20220110,20221107,20210521,20230426,20230405,20220823,20230421,20230221,20230118,20230613] + [20201001,20230418,20230803,20211115,20230301,20220815,20230127,20220203,20221101,20220506,20210831,20220406,20210218,20230126,20230821,20230216,20220110,20221107,20210521,20230818,20230426,20220823,20230421,20230221,20230613] export const pairs = ` -0.0.0. -0.0.1. -0.0.2. -0.0.3. -7.4.0. -0.0.5. -0.0.6. -0.0.6. -7.7.0. -0.0.8. -7.9.0. -0.0.a. -0.0.b. -g.c.0. -0.r.0. -0.s.0. -0.t.0. -0.u.0. -0.1f.0. -0.1h.0. -0.1i.0. -0.1C.0. -0.1S.0. -0.1T.0. -0.0.1X. -0.0.1Y. -0.0.1Z. -0.0.1+. -0.0.1/. -0.0.20. -0.0.21. -0.0.22. -0.0.23. -0.0.24. -0.0.25. -b.26.0. -0.0.27. -0.0.28. -0.0.2a. -0.0.2b. -3.0.2k. -0.0.2l. -6.2m.0. -4.2n.0. -2.2r.0. -9.2y.0. -9.2N.0. -b.36.0. -0.0.38. -5.39.0. -0.0.3d. -0.0.3h. -0.0.3i. -3.0.4O. -0.0.4Q. -5.4T.0. -0.0.5B. -0.0.5C. -0.0.5H. -0.0.5I. -3.0.5R. -0.0.5W. -1.5Y.0. -h.5+.0. -0.0.5/. -0.0.60. -2.62.0. -0.0.63. -0.0.6M. -0.0.6+. -0.0.71. -h.86.0. -0.1.1. -0.1.2. -0.1.3. -7.4.1. -0.1.5. -0.1.6. -7.7.1. -0.1.8. -7.9.1. -0.1.a. -0.1.b. -g.c.1. -0.r.1. -0.s.1. -0.t.1. -0.u.1. -0.1f.1. -0.1h.1. -0.1i.1. -0.1C.1. -0.1S.1. -0.1T.1. -0.1.1X. -0.1.1Y. -0.1.1Z. -0.1.1+. -0.1.1/. -0.1.20. -0.1.21. -0.1.22. -0.1.23. -0.1.24. -0.1.25. -b.26.1. -0.1.27. -0.1.28. -0.1.2a. -0.1.2b. -3.1.2k. -0.1.2l. -6.2m.1. -4.2n.1. -2.2r.1. -9.2y.1. -9.2N.1. -b.36.1. -0.1.38. -5.39.1. -0.1.3d. -0.1.3h. -0.1.3i. -3.1.4O. -0.1.4Q. -5.4T.1. -0.1.5B. -0.1.5C. -0.1.5H. -0.1.5I. -3.1.5R. -0.1.5W. -1.5Y.1. -0.1.5/. -0.1.60. -2.62.1. -0.1.63. -0.1.6M. -0.1.6+. -0.1.71. -0.2.2. -0.2.3. -7.4.2. -0.2.5. -0.2.6. -7.7.2. -0.2.8. -7.9.2. -0.2.a. -0.2.b. -g.c.2. -0.r.2. -0.s.2. -0.t.2. -0.u.2. -3.1f.2. -0.1h.2. -3.1i.2. -3.1C.2. -3.1S.2. -0.1T.2. -0.2.1X. -0.2.1Y. -0.2.1Z. -0.2.1+. -0.2.1/. -0.2.20. -0.2.21. -0.2.22. -0.2.23. -0.2.24. -0.2.25. -b.26.2. -0.2.27. -0.2.28. -0.2.2a. -0.2.2b. -d.2.2k. -0.2.2l. -6.2m.2. -4.2n.2. -2.2r.2. -9.2y.2. -9.2N.2. -b.36.2. -0.2.38. -5.39.2. -0.2.3d. -0.2.3h. -0.2.3i. -a.3y.2. -a.3F.2. -0.2.4O. -0.2.4Q. -5.4T.2. -0.2.5B. -0.2.5C. -0.2.5H. -0.2.5I. -d.2.5R. -0.2.5W. -1.5Y.2. -0.2.5/. -d.2.60. -2.62.2. -0.2.63. -0.2.6M. -0.2.6+. -0.2.71. -0.3.3. -7.4.3. -0.3.5. -0.3.6. -7.7.3. -0.3.8. -7.9.3. -0.3.a. -0.3.b. -g.c.3. -0.r.3. -0.s.3. -0.t.3. -0.u.3. -0.1f.3. -0.1h.3. -0.1i.3. -0.1C.3. -0.1S.3. -0.1T.3. -0.3.1X. -0.3.1Y. -0.3.1Z. -0.3.1+. -0.3.1/. -0.3.20. -0.3.21. -0.3.22. -0.3.23. -0.3.24. -0.3.25. -b.26.3. -0.3.27. -0.3.28. -0.3.2a. -0.3.2b. -d.3.2k. -0.3.2l. -6.2m.3. -4.2n.3. -2.2r.3. -9.2y.3. -9.2N.3. -b.36.3. -0.3.38. -5.39.3. -0.3.3d. -0.3.3h. -0.3.3i. -5.3A.3. -0.3.4O. -0.3.4Q. -5.4T.3. -0.3.4V. -0.3.5B. -0.3.5C. -0.3.5H. -0.3.5I. -d.3.5R. -0.3.5W. -1.5Y.3. -0.3.5/. -0.3.60. -2.62.3. -0.3.63. -0.3.6M. -0.3.6+. -0.3.71. -7.4.4. -7.4.5. -7.4.6. -7.7.4. -7.4.8. -7.9.4. -3.4.a. -7.4.b. -7.4.c. -7.4.r. -7.4.s. -7.4.t. -7.4.u. -7.4.1e. -7.4.1f. -7.4.1h. -7.4.1i. -7.4.1S. -7.4.1X. -3.4.1Y. -3.4.1Z. -3.4.1+. -3.4.1/. -3.4.20. -3.4.21. -7.4.22. -7.4.23. -7.4.24. -7.4.25. -3.4.26. -3.4.27. -7.4.28. -7.4.2a. -7.4.2b. -3.4.2k. -7.4.2l. -7.4.2m. -7.4.2r. -7.4.2y. -3.4.2N. -7.4.38. -3.4.39. -7.4.3d. -7.4.3h. -7.4.3i. -7.4.4O. -7.4.4Q. -7.4.4T. -7.4.4V. -7.4.5B. -7.4.5C. -7.4.5H. -7.4.5I. -7.4.5R. -7.4.5W. -1.5Y.4. -7.4.5/. -7.4.60. -3.4.62. -7.4.63. -3.4.6M. -7.4.6+. -7.4.71. -7.4.7J. -0.5.5. -0.5.6. -7.7.5. -0.5.8. -7.9.5. -0.5.a. -0.5.b. -g.c.5. -0.r.5. -0.s.5. -0.t.5. -0.u.5. -0.1f.5. -0.1h.5. -0.1i.5. -0.1C.5. -0.1S.5. -0.1T.5. -0.5.1X. -0.5.1Y. -0.5.1Z. -0.5.1+. -0.5.1/. -0.5.20. -0.5.21. -0.5.22. -0.5.23. -0.5.24. -0.5.25. -b.26.5. -0.5.27. -0.5.28. -0.5.2a. -0.5.2b. -d.5.2k. -0.5.2l. -6.2m.5. -4.2n.5. -2.2r.5. -9.2y.5. -9.2N.5. -b.36.5. -0.5.38. -5.39.5. -0.5.3d. -0.5.3h. -0.5.3i. -d.5.4O. -0.5.4Q. -5.4T.5. -0.5.5B. -0.5.5C. -0.5.5H. -0.5.5I. -d.5.5R. -0.5.5W. -1.5Y.5. -0.5.5/. -0.5.60. -2.62.5. -0.5.63. -0.5.6M. -0.5.6+. -0.5.71. -0.6.6. -7.7.6. -0.6.8. -7.9.6. -0.6.a. -0.6.b. -g.c.6. -0.r.6. -0.s.6. -0.t.6. -0.u.6. -0.1f.6. -0.1h.6. -0.1i.6. -0.1C.6. -0.1S.6. -0.1T.6. -0.6.1X. -0.6.1Y. -0.6.1Z. -0.6.1+. -0.6.1/. -0.6.20. -0.6.21. -0.6.22. -0.6.23. -0.6.24. -0.6.25. -b.26.6. -0.6.27. -0.6.28. -0.6.2a. -0.6.2b. -d.6.2k. -0.6.2l. -6.2m.6. -4.2n.6. -2.2r.6. -9.2y.6. -9.2N.6. -b.36.6. -0.6.38. -5.39.6. -0.6.3d. -0.6.3h. -0.6.3i. -0.6.4O. -0.6.4Q. -5.4T.6. -0.6.5B. -0.6.5C. -0.6.5H. -0.6.5I. -d.6.5R. -0.6.5W. -1.5Y.6. -0.6.5/. -0.6.60. -2.62.6. -0.6.63. -0.6.6M. -0.6.6+. -0.6.71. -7.7.7. -7.7.8. -7.9.7. -m.7.a. -7.7.b. -7.7.c. -7.7.r. -7.7.s. -7.7.t. -7.7.u. -7.7.1e. -7.7.1f. -7.7.1h. -7.7.1i. -7.7.1S. -7.7.1X. -3.7.1Y. -7.7.1Z. -3.7.1+. -7.7.1/. -7.7.20. -3.7.21. -3.7.22. -7.7.23. -3.7.24. -7.7.25. -7.7.26. -7.7.27. -7.7.28. -3.7.2a. -7.7.2b. -7.7.2k. -3.7.2l. -7.7.2m. -7.7.2r. -3.7.2y. -3.7.2N. -3.7.38. -7.7.39. -7.7.3d. -7.7.3h. -7.7.3i. -7.7.4O. -3.7.4Q. -7.7.4T. -7.7.4V. -7.7.5B. -7.7.5C. -7.7.5H. -3.7.5I. -7.7.5R. -3.7.5W. -1.5Y.7. -7.7.5/. -3.7.60. -7.7.62. -7.7.63. -7.7.6M. -7.7.6+. -7.7.71. -3.7.7J. -0.8.8. -7.9.8. -0.8.a. -0.8.b. -g.c.8. -0.r.8. -0.s.8. -0.t.8. -0.u.8. -0.1f.8. -0.1h.8. -0.1i.8. -0.1C.8. -0.1S.8. -0.1T.8. -0.8.1X. -0.8.1Y. -0.8.1Z. -0.8.1+. -0.8.1/. -0.8.20. -0.8.21. -0.8.22. -0.8.23. -0.8.24. -0.8.25. -b.26.8. -0.8.27. -0.8.28. -0.8.2a. -0.8.2b. -3.8.2k. -0.8.2l. -6.2m.8. -4.2n.8. -2.2r.8. -9.2y.8. -9.2N.8. -b.36.8. -0.8.38. -5.39.8. -0.8.3d. -0.8.3h. -0.8.3i. -a.3v.8. -0.8.4O. -0.8.4Q. -5.4T.8. -3.8.4V. -0.8.5B. -0.8.5C. -0.8.5H. -0.8.5I. -3.8.5R. -0.8.5W. -1.5Y.8. -0.8.5/. -0.8.60. -2.62.8. -0.8.63. -2.66.8. -0.8.6M. -0.8.6+. -0.8.71. -7.9.9. -3.9.a. -7.9.b. -7.9.c. -7.9.r. -7.9.s. -7.9.t. -7.9.u. -7.9.1e. -7.9.1f. -7.9.1h. -7.9.1i. -7.9.1S. -7.9.1X. -3.9.1Y. -7.9.1Z. -3.9.1+. -7.9.1/. -7.9.20. -3.9.21. -3.9.22. -7.9.23. -3.9.24. -7.9.25. -7.9.26. -7.9.27. -7.9.28. -3.9.2a. -7.9.2b. -7.9.2k. -3.9.2l. -7.9.2m. -7.9.2r. -3.9.2y. -3.9.2N. -3.9.38. -7.9.39. -7.9.3d. -7.9.3h. -7.9.3i. -7.9.4O. -3.9.4Q. -7.9.4T. -7.9.4V. -7.9.5B. -7.9.5C. -7.9.5H. -3.9.5I. -7.9.5R. -3.9.5W. -1.5Y.9. -7.9.5/. -3.9.60. -7.9.62. -7.9.63. -7.9.6M. -7.9.6+. -7.9.71. -3.9.7J. -0.a.a. -0.b.a. -g.c.a. -0.r.a. -0.s.a. -0.t.a. -0.u.a. -0.1f.a. -0.1h.a. -3.1i.a. -0.1C.a. -3.1S.a. -0.1T.a. -0.a.1X. -0.a.1Y. -0.a.1Z. -0.a.1+. -0.a.1/. -0.a.20. -0.a.21. -0.a.22. -0.a.23. -0.a.24. -0.a.25. -b.26.a. -0.a.27. -0.a.28. -0.a.2a. -0.a.2b. -3.a.2k. -0.a.2l. -6.2m.a. -4.2n.a. -2.2r.a. -9.2y.a. -9.2N.a. -b.36.a. -0.a.38. -5.39.a. -0.a.3d. -0.a.3h. -0.a.3i. -3.a.4O. -3.a.4Q. -5.4T.a. -3.a.4V. -0.a.5B. -0.a.5C. -0.a.5H. -0.a.5I. -3.a.5R. -0.a.5W. -1.5Y.a. -0.a.5/. -0.a.60. -2.62.a. -0.a.63. -2.66.a. -0.a.6M. -0.a.6+. -0.a.71. -0.b.b. -g.c.b. -0.r.b. -0.s.b. -0.t.b. -0.u.b. -0.1f.b. -0.1h.b. -0.1i.b. -0.1C.b. -0.1S.b. -d.1T.b. -0.b.1X. -0.b.1Y. -0.b.1Z. -0.b.1+. -0.b.1/. -3.b.20. -0.b.21. -0.b.22. -0.b.23. -0.b.24. -0.b.25. -b.26.b. -3.b.27. -0.b.28. -0.b.2a. -0.b.2b. -3.b.2k. -0.b.2l. -6.2m.b. -4.2n.b. -2.2r.b. -e.2x.b. -9.2y.b. -9.2N.b. -b.36.b. -0.b.38. -5.39.b. -0.b.3d. -3.b.3h. -0.b.3i. -0.b.4O. -0.b.4Q. -5.4T.b. -0.b.5B. -0.b.5C. -3.b.5H. -0.b.5I. -3.b.5R. -0.b.5W. -1.5Y.b. -0.b.5/. -3.b.60. -2.62.b. -0.b.63. -2.66.b. -0.b.6M. -0.b.6+. -0.b.71. -g.c.c. -g.c.d. -g.c.e. -g.c.f. -g.c.g. -g.c.h. -g.c.i. -g.c.j. -g.c.k. -g.c.l. -g.c.m. -2.n.c. -g.c.o. -g.c.p. -g.c.q. -g.c.r. -g.c.s. -g.c.t. -g.c.u. -g.c.v. -g.c.w. -g.c.x. -g.c.y. -g.c.z. -g.c.A. -g.c.B. -g.c.C. -g.c.D. -g.c.E. -g.c.F. -g.c.G. -3.c.H. -2.I.c. -2.J.c. -g.c.K. -g.c.L. -2.M.c. -g.c.N. -g.c.O. -g.c.P. -g.c.Q. -g.c.R. -3.S.c. -g.c.T. -g.c.U. -g.c.V. -g.c.W. -g.c.X. -g.c.Y. -g.c.Z. -g.c.+. -g.c./. -g.c.10. -g.c.11. -g.c.12. -g.c.13. -g.c.14. -g.c.15. -g.c.16. -g.c.17. -g.c.18. -g.c.19. -g.c.1a. -g.c.1b. -g.c.1c. -g.c.1d. -g.c.1e. -g.c.1f. -g.c.1g. -g.c.1h. -g.c.1i. -g.c.1j. -g.c.1k. -2.1l.c. -g.c.1m. -g.c.1n. -g.c.1o. -g.c.1p. -g.c.1q. -g.c.1r. -g.c.1s. -g.c.1t. -2.1u.c. -g.c.1v. -g.c.1w. -g.c.1x. -g.c.1y. -g.c.1z. -g.c.1A. -g.c.1B. -g.c.1C. -g.c.1D. -g.c.1E. -g.c.1F. -g.c.1G. -g.c.1H. -g.c.1I. -g.c.1J. -g.c.1K. -g.c.1L. -g.c.1M. -g.c.1N. -g.c.1O. -g.c.1P. -g.c.1Q. -g.c.1R. -g.c.1S. -g.c.1T. -g.c.1U. -g.c.1V. -g.c.1W. -g.c.1X. -g.c.1Y. -g.c.1Z. -g.c.1+. -g.c.1/. -g.c.20. -g.c.21. -g.c.22. -g.c.23. -g.c.24. -g.c.25. -g.c.26. -g.c.27. -g.c.28. -4.c.29. -g.c.2a. -g.c.2b. -4.c.2c. -4.c.2d. -4.c.2f. -4.c.2g. -4.c.2h. -4.c.2i. -4.c.2j. -g.c.2l. -6.2m.c. -4.2n.c. -1.c.2o. -g.c.2p. -2.2q.c. -2.2r.c. -7.2s.c. -7.2t.c. -2.2u.c. -g.c.2v. -g.c.2w. -d.2x.c. -9.2y.c. -g.c.2z. -g.c.2A. -9.2B.c. -g.c.2C. -g.c.2D. -g.c.2E. -g.c.2F. -g.c.2G. -9.2H.c. -g.c.2I. -g.c.2J. -g.c.2K. -4.c.2L. -c.2M.c. -9.2N.c. -2.2O.c. -4.c.2P. -9.2Q.c. -8.c.2R. -7.2S.c. -g.c.2T. -1.2U.c. -9.2V.c. -1.2W.c. -g.c.2X. -8.c.2Y. -8.c.2Z. -g.c.2+. -8.c.2/. -g.c.30. -8.c.31. -3.c.32. -8.c.33. -8.c.34. -g.c.35. -g.c.36. -g.c.37. -g.c.38. -g.c.39. -4.c.3a. -4.c.3c. -g.c.3d. -1.c.3e. -4.c.3f. -8.c.3g. -g.c.3h. -g.c.3i. -1.c.3j. -8.c.3k. -4.c.3l. -g.c.3m. -8.c.3n. -3.c.3o. -g.c.3p. -8.c.3q. -4.c.3r. -4.c.3s. -8.c.3t. -8.c.3u. -a.3v.c. -4.c.3w. -4.c.3x. -a.3y.c. -2.3z.c. -5.3A.c. -2.3B.c. -g.c.3C. -4.c.3D. -4.c.3E. -a.3F.c. -5.3G.c. -4.c.3H. -4.c.3I. -4.c.3J. -4.c.3K. -4.c.3L. -g.c.3M. -4.c.3N. -4.c.3O. -4.c.3P. -g.c.3Q. -4.c.3R. -4.c.3S. -1.c.3T. -4.c.3U. -4.c.3V. -4.c.3W. -4.c.3X. -4.c.3Y. -4.c.3Z. -1.c.3+. -1.c.3/. -5.40.c. -4.c.41. -4.c.42. -4.c.43. -4.c.44. -4.c.45. -g.c.47. -4.c.48. -4.c.49. -1.c.4a. -4.c.4b. -4.c.4c. -g.c.4d. -4.c.4e. -4.c.4f. -4.c.4g. -4.c.4h. -4.c.4i. -4.c.4j. -8.c.4k. -4.c.4l. -4.c.4m. -4.c.4n. -4.c.4o. -4.c.4p. -4.c.4q. -4.c.4r. -4.c.4s. -4.c.4t. -4.c.4u. -4.c.4v. -4.c.4w. -4.c.4x. -4.c.4y. -4.c.4z. -4.c.4A. -4.c.4B. -4.c.4C. -4.c.4D. -8.c.4E. -1.c.4F. -8.c.4G. -8.c.4H. -1.c.4I. -3.c.4J. -3.c.4K. -3.c.4L. -4.c.4M. -4.c.4N. -g.c.4O. -4.c.4P. -g.c.4Q. -1.c.4R. -4.c.4S. -4.c.4T. -8.c.4U. -g.c.4V. -4.c.4W. -8.c.4Y. -1.c.4Z. -4.c.4+. -4.c.4/. -1.c.50. -g.c.51. -g.c.52. -8.c.53. -8.c.54. -8.c.55. -8.c.56. -1.c.57. -8.c.58. -8.c.59. -a.5a.c. -2.5b.c. -4.c.5c. -5.5d.c. -8.c.5e. -8.c.5f. -4.c.5g. -8.c.5h. -4.c.5i. -4.c.5j. -4.c.5k. -4.c.5l. -4.c.5m. -4.c.5n. -4.c.5o. -4.c.5p. -4.c.5q. -4.c.5r. -1.c.5s. -4.c.5t. -4.c.5u. -4.c.5v. -1.c.5w. -1.c.5y. -1.c.5z. -8.c.5A. -g.c.5B. -g.c.5C. -1.c.5D. -8.c.5E. -g.c.5F. -8.c.5G. -g.c.5H. -g.c.5I. -f.c.5K. -3.c.5L. -8.c.5M. -8.c.5N. -g.c.5O. -8.c.5P. -4.c.5Q. -g.c.5R. -4.c.5S. -4.c.5T. -g.c.5U. -8.c.5V. -g.c.5W. -8.c.5X. -8.c.5Y. -g.c.5Z. -h.5+.c. -g.c.5/. -g.c.60. -4.c.61. -2.62.c. -4.c.64. -4.c.65. -2.66.c. -4.c.67. -4.c.68. -4.c.69. -4.c.6a. -a.6b.c. -4.c.6c. -4.c.6d. -4.c.6e. -4.c.6f. -4.c.6g. -4.c.6h. -4.c.6i. -4.c.6j. -4.c.6k. -4.c.6l. -4.c.6m. -4.c.6n. -4.c.6o. -4.c.6p. -4.c.6q. -4.c.6r. -4.c.6s. -4.c.6t. -4.c.6u. -4.c.6v. -4.c.6w. -4.c.6x. -4.c.6y. -4.c.6z. -4.c.6A. -4.c.6B. -4.c.6C. -4.c.6D. -4.c.6E. -g.c.6F. -4.c.6G. -4.c.6H. -4.c.6I. -4.c.6J. -4.c.6K. -8.c.6L. -g.c.6M. -4.c.6N. -4.c.6O. -4.c.6P. -4.c.6Q. -4.c.6R. -4.c.6S. -4.c.6T. -4.c.6U. -4.c.6V. -4.c.6W. -1.c.6X. -4.c.6Y. -1.c.6Z. -g.c.6+. -1.c.6/. -1.c.70. -g.c.71. -4.c.72. -4.c.73. -1.c.74. -4.c.75. -1.c.76. -1.c.77. -4.c.78. -5.79.c. -1.c.7a. -4.c.7b. -4.c.7c. -4.c.7d. -4.c.7e. -4.c.7f. -4.c.7g. -4.c.7h. -4.c.7i. -4.c.7j. -4.c.7k. -4.c.7l. -1.c.7m. -4.c.7o. -4.c.7p. -1.c.7r. -1.c.7s. -8.c.7t. -4.c.7u. -4.c.7v. -8.c.7w. -4.c.7x. -4.c.7y. -1.c.7z. -4.c.7A. -8.c.7B. -4.c.7C. -g.c.7D. -4.c.7E. -4.c.7F. -4.c.7G. -4.c.7H. -4.c.7I. -4.c.7J. -1.c.7K. -1.c.7L. -1.c.7M. -1.c.7N. -1.c.7O. -1.c.7P. -4.c.7Q. -8.c.7R. -4.c.7S. -4.c.7T. -1.c.7U. -8.c.7V. -4.c.7W. -4.c.7X. -4.c.7Y. -4.c.7Z. -1.c.7+. -1.c.7/. -1.c.80. -1.c.81. -1.c.82. -4.c.83. -1.c.84. -1.c.85. -h.86.c. -1.c.87. -1.c.88. -8.c.89. -4.c.8a. -3.c.8b. -4.c.8c. -8.c.8d. -4.c.8e. -4.c.8f. -4.c.8g. -4.c.8h. -4.c.8i. -4.c.8j. -4.c.8k. -4.c.8l. -4.c.8m. -4.c.8n. -4.c.8o. -4.c.8p. -4.c.8q. -4.c.8r. -4.c.8s. -4.c.8t. -8.c.8u. -8.c.8v. -8.c.8w. -8.c.8x. -4.c.8y. -4.c.8z. -3.c.8A. -3.c.8B. -8.c.8C. -4.c.8D. -4.c.8E. -4.c.8F. -4.c.8G. -3.c.8H. -3.c.8I. -4.c.8J. -4.c.8K. -4.c.8L. -4.c.8M. -4.c.8N. -4.c.8O. -4.c.8P. -g.c.2p. -g.c.2p. -8.c.3b. -1.c.46. -1.c.4X. -1.c.5x. -1.c.5J. -1.c.7n. -1.c.7q. +e.0.0. +e.0.1. +e.0.2. +e.1.1. +e.0.3. +e.1.2. +e.1.3. +e.2.2. +e.4.0. +e.0.5. +e.2.3. +e.4.1. +e.0.6. +e.1.5. +e.3.3. +e.4.2. +e.1.6. +e.2.5. +e.4.3. +e.7.0. +e.0.8. +e.2.6. +e.3.5. +e.4.4. +e.7.1. +e.1.8. +e.3.6. +e.4.5. +e.7.2. +e.9.0. +e.0.a. +e.2.8. +e.4.6. +e.5.5. +e.7.3. +e.9.1. +e.0.b. +e.1.a. +e.3.8. +e.5.6. +e.9.2. +j.4.7. +e.1.b. +e.2.a. +e.4.8. +e.6.6. +e.7.5. +e.9.3. +i.c.0. +e.2.b. +e.3.a. +e.5.8. +e.7.6. +e.9.4. +i.c.1. +e.3.b. +e.4.a. +e.6.8. +e.7.7. +e.9.5. +i.c.2. +e.4.b. +e.5.a. +e.7.8. +e.9.6. +i.c.3. +e.4.c. +e.5.b. +e.6.a. +e.8.8. +e.9.7. +e.6.b. +e.7.a. +e.9.8. +i.c.5. +e.7.b. +e.8.a. +e.9.9. +i.c.6. +e.7.c. +e.8.b. +e.9.a. +e.9.b. +e.a.a. +i.c.8. +e.9.c. +e.b.a. +e.b.b. +i.c.a. +i.c.b. +i.c.c. +j.n.0. +j.n.1. +i.c.d. +j.n.2. +i.c.e. +j.n.3. +i.c.f. +j.n.4. +i.c.g. +j.n.5. 0.d.d. +0.r.0. +i.c.h. +j.n.6. 0.d.e. +0.r.1. +0.s.0. +i.c.i. +j.n.7. 0.d.f. -0.d.g. -0.d.h. -0.d.i. -0.d.j. -0.d.k. -0.d.l. -0.d.m. -2.n.d. -0.d.o. -0.d.p. -0.d.q. -0.d.r. -0.d.s. -3.d.t. -0.d.u. -0.d.v. -0.d.w. -3.d.x. -0.d.y. -0.d.z. -0.d.A. -0.d.B. -0.d.C. -0.d.D. -0.d.E. -3.d.F. -0.d.G. -0.d.H. -2.I.d. -2.J.d. -0.d.K. -0.d.L. -2.M.d. -0.d.N. -0.d.O. -0.d.P. -0.d.Q. -0.d.R. -3.S.d. -b.T.d. -0.d.U. -0.d.V. -0.d.W. -0.d.X. -b.Y.d. -3.d.Z. -0.d.+. -0.d./. -0.d.10. -0.d.11. -0.d.12. -0.d.13. -0.d.14. -0.d.15. -0.d.16. -0.d.17. -3.d.18. -0.d.19. -3.d.1a. -0.d.1b. -0.d.1c. -3.d.1d. -0.d.1e. -3.d.1f. -0.d.1g. -3.d.1h. -3.d.1i. -3.d.1j. -0.d.1k. -2.1l.d. -0.d.1m. -0.d.1n. -0.d.1o. -0.d.1p. -3.d.1q. -3.d.1r. -3.d.1s. -0.d.1t. -2.1u.d. -0.d.1v. -3.d.1w. -3.d.1x. -3.d.1y. -0.d.1z. -0.d.1A. -0.d.1B. -0.d.1C. -0.d.1D. -0.d.1E. -0.d.1F. -0.d.1G. -0.d.1H. -0.d.1I. -0.d.1J. -0.d.1K. -0.d.1L. -0.d.1M. -0.d.1N. -0.d.1O. -0.d.1P. -3.d.1Q. -0.d.1R. -0.d.1S. -0.d.1T. -3.d.1U. -0.d.1V. -0.d.1W. -0.d.1X. -0.d.1Y. -0.d.1Z. -0.d.1+. -0.d.1/. -0.d.20. -0.d.21. -0.d.22. -0.d.23. -0.d.24. -0.d.25. -b.26.d. -0.d.27. -4.d.28. -0.d.2a. -0.d.2b. -0.d.2l. -6.2m.d. -0.d.2p. -2.2q.d. -2.2r.d. -7.2s.d. -7.2t.d. -2.2u.d. -3.d.2v. -0.d.2w. -e.2x.d. -9.2y.d. -0.d.2z. -3.d.2A. -9.2B.d. -0.d.2C. -0.d.2D. -3.d.2E. -0.d.2F. -0.d.2G. -9.2H.d. -0.d.2I. -3.d.2J. -0.d.2K. -c.2M.d. -9.2N.d. -2.2O.d. -9.2Q.d. -7.2S.d. -0.d.2T. -1.2U.d. -9.2V.d. -1.2W.d. -0.d.2X. -b.2+.d. -0.d.30. -0.d.35. -b.36.d. -0.d.37. -3.d.38. -5.39.d. -0.d.3d. -3.d.3h. -3.d.3i. -0.d.3m. -0.d.3p. -a.3v.d. -a.3y.d. -2.3z.d. -5.3A.d. -2.3B.d. -0.d.3C. -a.3F.d. -5.3G.d. -0.d.3M. -0.d.3Q. -5.40.d. -0.d.47. -0.d.4d. -0.d.4O. -3.d.4Q. -5.4T.d. -3.d.4V. -0.d.51. -0.d.52. -a.5a.d. -2.5b.d. -5.5d.d. -0.d.5B. -0.d.5C. -0.d.5F. -3.d.5H. -0.d.5I. -f.d.5K. -7.5M.d. -0.d.5O. -3.d.5R. -0.d.5U. -0.d.5W. -1.5Y.d. -0.d.5Z. -0.d.5/. -0.d.60. -2.62.d. -2.66.d. -a.6b.d. -c.6e.d. -0.d.6F. -0.d.6M. -0.d.6+. -3.d.71. -5.79.d. -3.d.7D. -0.d.2p. -0.d.2p. 0.e.e. +0.r.2. +0.s.1. +0.t.0. +i.c.j. +j.n.8. +0.d.g. 0.e.f. +0.r.3. +0.s.2. +0.t.1. +0.u.0. +i.c.k. +j.n.9. +0.d.h. 0.e.g. -0.e.h. -0.e.i. -0.e.j. -0.e.k. -0.e.l. -3.e.m. -2.n.e. -0.e.o. -0.e.p. -0.e.q. -0.e.r. -0.e.s. -3.e.t. -0.e.u. -0.e.v. -0.e.w. -3.e.x. -0.e.y. -0.e.z. -0.e.A. -0.e.B. -0.e.C. -0.e.D. -0.e.E. -0.e.F. -0.e.G. -0.e.H. -2.I.e. -2.J.e. -0.e.K. -0.e.L. -2.M.e. -0.e.N. -0.e.O. -0.e.P. -0.e.Q. -0.e.R. -3.S.e. -b.T.e. -0.e.U. -0.e.V. -0.e.W. -0.e.X. -b.Y.e. -3.e.Z. -0.e.+. -0.e./. -0.e.10. -0.e.11. -0.e.12. -0.e.13. -0.e.14. -0.e.15. -0.e.16. -0.e.17. -3.e.18. -0.e.19. -0.e.1a. -0.e.1b. -0.e.1c. -3.e.1d. -0.e.1e. -3.e.1f. -0.e.1g. -3.e.1h. -3.e.1i. -3.e.1j. -0.e.1k. -2.1l.e. -0.e.1m. -0.e.1n. -0.e.1o. -0.e.1p. -0.e.1q. -0.e.1r. -0.e.1s. -3.e.1t. -2.1u.e. -0.e.1v. -3.e.1w. -0.e.1x. -0.e.1y. -0.e.1z. -0.e.1A. -0.e.1B. -0.e.1C. -0.e.1D. -0.e.1E. -0.e.1F. -0.e.1G. -0.e.1H. -0.e.1I. -0.e.1J. -0.e.1K. -0.e.1L. -0.e.1M. -0.e.1N. -0.e.1O. -0.e.1P. -3.e.1Q. -0.e.1R. -0.e.1S. -0.e.1T. -3.e.1U. -0.e.1V. -0.e.1W. -0.e.1X. -0.e.1Y. -0.e.1Z. -0.e.1+. -0.e.1/. -0.e.20. -0.e.21. -0.e.22. -0.e.23. -0.e.24. -0.e.25. -b.26.e. -0.e.27. -4.e.28. -0.e.2a. -0.e.2b. -0.e.2l. -6.2m.e. -0.e.2p. -2.2q.e. -2.2r.e. -7.2s.e. -7.2t.e. -2.2u.e. -3.e.2v. -0.e.2w. -e.2x.e. -9.2y.e. -0.e.2z. -3.e.2A. -9.2B.e. -0.e.2C. -0.e.2D. -3.e.2E. -0.e.2F. -0.e.2G. -9.2H.e. -0.e.2I. -3.e.2J. -0.e.2K. -c.2M.e. -9.2N.e. -2.2O.e. -9.2Q.e. -7.2S.e. -0.e.2T. -1.2U.e. -9.2V.e. -1.2W.e. -0.e.2X. -b.2+.e. -0.e.30. -0.e.35. -b.36.e. -0.e.37. -3.e.38. -5.39.e. -0.e.3d. -3.e.3h. -3.e.3i. -0.e.3m. -0.e.3p. -a.3v.e. -a.3y.e. -2.3z.e. -5.3A.e. -2.3B.e. -0.e.3C. -a.3F.e. -5.3G.e. -0.e.3M. -0.e.3Q. -5.40.e. -0.e.47. -0.e.4d. -0.e.4O. -3.e.4Q. -5.4T.e. -3.e.4V. -0.e.51. -0.e.52. -a.5a.e. -2.5b.e. -5.5d.e. -0.e.5B. -0.e.5C. -0.e.5F. -3.e.5H. -0.e.5I. -f.e.5K. -7.5M.e. -0.e.5O. -3.e.5R. -0.e.5U. -0.e.5W. -1.5Y.e. -0.e.5Z. -0.e.5/. -0.e.60. -2.62.e. -2.66.e. -a.6b.e. -c.6e.e. -0.e.6F. -0.e.6M. -0.e.6+. -3.e.71. -5.79.e. -3.e.7D. -0.e.2p. -0.e.2p. 0.f.f. +0.s.3. +0.t.2. +0.u.1. +e.4.r. +i.c.l. +j.n.a. +0.d.i. +0.e.h. 0.f.g. +0.r.5. +0.t.3. +0.u.2. +e.4.s. +i.c.m. +j.n.b. +0.d.j. +0.e.i. 0.f.h. -0.f.i. -0.f.j. -0.f.k. -0.f.l. -0.f.m. -2.n.f. -0.f.o. -0.f.p. -0.f.q. -0.f.r. -0.f.s. -3.f.t. -0.f.u. -0.f.v. -0.f.w. -3.f.x. -0.f.y. -0.f.z. -0.f.A. -0.f.B. -0.f.C. -0.f.D. -0.f.E. -0.f.F. -0.f.G. -0.f.H. -2.I.f. -2.J.f. -0.f.K. -0.f.L. -2.M.f. -0.f.N. -0.f.O. -0.f.P. -0.f.Q. -0.f.R. -3.S.f. -b.T.f. -0.f.U. -0.f.V. -0.f.W. -0.f.X. -b.Y.f. -0.f.Z. -0.f.+. -0.f./. -0.f.10. -0.f.11. -0.f.12. -0.f.13. -0.f.14. -0.f.15. -0.f.16. -0.f.17. -3.f.18. -0.f.19. -3.f.1a. -0.f.1b. -0.f.1c. -3.f.1d. -0.f.1e. -3.f.1f. -0.f.1g. -3.f.1h. -3.f.1i. -3.f.1j. -0.f.1k. -2.1l.f. -0.f.1m. -0.f.1n. -0.f.1o. -0.f.1p. -0.f.1q. -0.f.1r. -0.f.1s. -0.f.1t. -2.1u.f. -0.f.1v. -3.f.1w. -0.f.1x. -0.f.1y. -0.f.1z. -0.f.1A. -0.f.1B. -0.f.1C. -0.f.1D. -0.f.1E. -0.f.1F. -0.f.1G. -0.f.1H. -0.f.1I. -0.f.1J. -0.f.1K. -0.f.1L. -0.f.1M. -0.f.1N. -0.f.1O. -0.f.1P. -3.f.1Q. -0.f.1R. -0.f.1S. -0.f.1T. -0.f.1U. -0.f.1V. -0.f.1W. -0.f.1X. -0.f.1Y. -0.f.1Z. -0.f.1+. -0.f.1/. -0.f.20. -0.f.21. -0.f.22. -0.f.23. -0.f.24. -0.f.25. -b.26.f. -0.f.27. -4.f.28. -0.f.2a. -0.f.2b. -0.f.2l. -6.2m.f. -0.f.2p. -2.2q.f. -2.2r.f. -7.2s.f. -7.2t.f. -2.2u.f. -3.f.2v. -0.f.2w. -e.2x.f. -9.2y.f. -0.f.2z. -3.f.2A. -9.2B.f. -0.f.2C. -0.f.2D. -3.f.2E. -0.f.2F. -0.f.2G. -9.2H.f. -0.f.2I. -3.f.2J. -0.f.2K. -c.2M.f. -9.2N.f. -2.2O.f. -9.2Q.f. -7.2S.f. -0.f.2T. -1.2U.f. -9.2V.f. -1.2W.f. -0.f.2X. -d.2+.f. -0.f.30. -0.f.35. -b.36.f. -0.f.37. -3.f.38. -5.39.f. -0.f.3d. -3.f.3h. -0.f.3i. -0.f.3m. -0.f.3p. -a.3v.f. -a.3y.f. -2.3z.f. -5.3A.f. -2.3B.f. -0.f.3C. -a.3F.f. -5.3G.f. -0.f.3M. -0.f.3Q. -5.40.f. -0.f.47. -0.f.4d. -3.f.4O. -3.f.4Q. -5.4T.f. -3.f.4V. -0.f.51. -0.f.52. -a.5a.f. -2.5b.f. -5.5d.f. -0.f.5B. -0.f.5C. -0.f.5F. -3.f.5H. -0.f.5I. -f.f.5K. -7.5M.f. -0.f.5O. -3.f.5R. -0.f.5U. -0.f.5W. -1.5Y.f. -0.f.5Z. -0.f.5/. -0.f.60. -2.62.f. -2.66.f. -a.6b.f. -c.6e.f. -0.f.6F. -0.f.6M. -0.f.6+. -3.f.71. -5.79.f. -3.f.7D. -0.f.2p. -0.f.2p. 0.g.g. +0.r.6. +0.s.5. +0.u.3. +3.n.c. +e.4.t. +0.d.k. +0.e.j. +0.f.i. 0.g.h. +0.s.6. +0.t.5. +e.4.u. +e.7.r. +i.c.o. +0.d.l. +0.e.k. +0.f.j. 0.g.i. +0.r.8. +0.t.6. +0.u.5. +4.h.h. +e.7.s. +i.c.p. +0.d.m. +0.e.l. +0.f.k. 0.g.j. +0.s.8. +0.u.6. +4.h.i. +e.7.t. +e.9.r. +i.c.q. +0.f.l. 0.g.k. -0.g.l. -0.g.m. -2.n.g. -0.g.o. -0.g.p. -0.g.q. -0.g.r. -0.g.s. -0.g.t. -0.g.u. -0.g.v. -0.g.w. -3.g.x. -0.g.y. -0.g.z. -0.g.A. -0.g.B. -0.g.C. -0.g.D. -0.g.E. -0.g.F. -0.g.G. -0.g.H. -2.I.g. -2.J.g. -0.g.K. -0.g.L. -2.M.g. -0.g.N. -0.g.O. -0.g.P. -0.g.Q. -0.g.R. -3.S.g. -b.T.g. -0.g.U. -0.g.V. -0.g.W. -0.g.X. -b.Y.g. -0.g.Z. -0.g.+. -0.g./. -0.g.10. -0.g.11. -0.g.12. -0.g.13. -0.g.14. -0.g.15. -0.g.16. -0.g.17. -0.g.18. -0.g.19. -0.g.1a. -0.g.1b. -0.g.1c. -3.g.1d. -0.g.1e. -0.g.1f. -0.g.1g. -3.g.1h. -3.g.1i. -3.g.1j. -0.g.1k. -2.1l.g. -0.g.1m. -0.g.1n. -0.g.1o. -0.g.1p. -0.g.1q. -0.g.1r. -0.g.1s. -0.g.1t. -2.1u.g. -0.g.1v. -3.g.1w. -0.g.1x. -0.g.1y. -0.g.1z. -0.g.1A. -0.g.1B. -0.g.1C. -0.g.1D. -0.g.1E. -0.g.1F. -0.g.1G. -0.g.1H. -0.g.1I. -0.g.1J. -0.g.1K. -0.g.1L. -0.g.1M. -0.g.1N. -0.g.1O. -0.g.1P. -3.g.1Q. -0.g.1R. -0.g.1S. -0.g.1T. -3.g.1U. -0.g.1V. -0.g.1W. -0.g.1X. -0.g.1Y. -0.g.1Z. -0.g.1+. -0.g.1/. -0.g.20. -0.g.21. -0.g.22. -0.g.23. -0.g.24. -0.g.25. -b.26.g. -0.g.27. -4.g.28. -0.g.2a. -0.g.2b. -0.g.2l. -6.2m.g. -0.g.2p. -2.2q.g. -2.2r.g. -7.2s.g. -7.2t.g. -2.2u.g. -3.g.2v. -0.g.2w. -e.2x.g. -9.2y.g. -0.g.2z. -3.g.2A. -9.2B.g. -0.g.2C. -0.g.2D. -3.g.2E. -0.g.2F. -0.g.2G. -9.2H.g. -0.g.2I. -3.g.2J. -0.g.2K. -c.2M.g. -9.2N.g. -2.2O.g. -9.2Q.g. -7.2S.g. -0.g.2T. -1.2U.g. -9.2V.g. -1.2W.g. -0.g.2X. -d.2+.g. -0.g.30. -0.g.35. -b.36.g. -0.g.37. -0.g.38. -5.39.g. -0.g.3d. -3.g.3h. -0.g.3i. -0.g.3m. -0.g.3p. -a.3v.g. -a.3y.g. -2.3z.g. -5.3A.g. -2.3B.g. -0.g.3C. -a.3F.g. -5.3G.g. -0.g.3M. -0.g.3Q. -5.40.g. -0.g.47. -0.g.4d. -0.g.4O. -3.g.4Q. -5.4T.g. -3.g.4V. -0.g.51. -0.g.52. -a.5a.g. -2.5b.g. -5.5d.g. -0.g.5B. -0.g.5C. -0.g.5F. -3.g.5H. -0.g.5I. -f.g.5K. -7.5M.g. -0.g.5O. -0.g.5R. -0.g.5U. -0.g.5W. -1.5Y.g. -0.g.5Z. -0.g.5/. -0.g.60. -2.62.g. -2.66.g. -a.6b.g. -c.6e.g. -0.g.6F. -0.g.6M. -0.g.6+. -3.g.71. -5.79.g. -3.g.7D. -0.g.2p. -0.g.2p. -3.h.h. -3.h.i. -3.h.j. -3.h.k. -0.h.l. -0.h.m. -2.n.h. -0.h.o. -0.h.p. -3.h.q. -0.h.r. -0.h.s. -0.h.t. -0.h.u. -0.h.v. -0.h.w. -0.h.x. -0.h.y. -3.h.z. -0.h.A. -0.h.B. -0.h.C. -0.h.D. -0.h.E. -0.h.F. -0.h.G. -0.h.H. -2.I.h. -2.J.h. -0.h.K. -3.h.L. -2.M.h. -0.h.N. -3.h.O. -0.h.P. -0.h.Q. -0.h.R. -3.S.h. -b.T.h. -3.h.U. -3.h.V. -3.h.W. -0.h.X. -b.Y.h. -0.h.Z. -3.h.+. -3.h./. -0.h.10. -0.h.11. -0.h.12. -0.h.13. -0.h.14. -0.h.15. -0.h.16. -0.h.17. -0.h.18. -0.h.19. -0.h.1a. -0.h.1b. -0.h.1c. -0.h.1d. -3.h.1e. -0.h.1f. -3.h.1g. -0.h.1h. -0.h.1i. -3.h.1j. -0.h.1k. -2.1l.h. -0.h.1m. -0.h.1n. -0.h.1o. -0.h.1p. -0.h.1q. -0.h.1r. -0.h.1s. -0.h.1t. -2.1u.h. -0.h.1v. -0.h.1w. -0.h.1x. -0.h.1y. -0.h.1z. -0.h.1A. -0.h.1B. -0.h.1C. -0.h.1D. -3.h.1E. -0.h.1F. -0.h.1G. -0.h.1H. -0.h.1I. -0.h.1J. -0.h.1K. -0.h.1L. -0.h.1M. -0.h.1N. -0.h.1O. -0.h.1P. -0.h.1Q. -0.h.1R. -3.h.1S. -3.h.1T. -0.h.1U. -0.h.1V. -0.h.1W. -0.h.1X. -3.h.1Y. -0.h.1Z. -3.h.1+. -0.h.1/. -0.h.20. -3.h.21. -0.h.22. -0.h.23. -0.h.24. -0.h.25. -b.26.h. -3.h.27. -4.h.28. -0.h.2a. -0.h.2b. -0.h.2l. -6.2m.h. -0.h.2p. -2.2q.h. -2.2r.h. -7.2s.h. -7.2t.h. -2.2u.h. -3.h.2v. -0.h.2w. -e.2x.h. -9.2y.h. -0.h.2z. -3.h.2A. -9.2B.h. -0.h.2C. -0.h.2D. -0.h.2E. -0.h.2F. -0.h.2G. -9.2H.h. -0.h.2I. -3.h.2J. -3.h.2K. -c.2M.h. -9.2N.h. -2.2O.h. -9.2Q.h. -7.2S.h. -0.h.2T. -1.2U.h. -9.2V.h. -1.2W.h. -0.h.2X. -b.2+.h. -0.h.30. -3.h.35. -b.36.h. -0.h.37. -3.h.38. -5.39.h. -0.h.3d. -3.h.3h. -0.h.3i. -0.h.3m. -0.h.3p. -a.3v.h. -a.3y.h. -2.3z.h. -5.3A.h. -2.3B.h. -0.h.3C. -a.3F.h. -5.3G.h. -0.h.3M. -0.h.3Q. -5.40.h. -0.h.47. -0.h.4d. -0.h.4O. -3.h.4Q. -5.4T.h. -3.h.4V. -3.h.51. -0.h.52. -a.5a.h. -2.5b.h. -5.5d.h. -0.h.5B. -0.h.5C. -0.h.5F. -3.h.5H. -0.h.5I. -f.h.5K. -7.5M.h. -3.h.5O. -0.h.5R. -0.h.5U. -0.h.5W. -1.5Y.h. -0.h.5Z. -0.h.5/. -0.h.60. -2.62.h. -2.66.h. -a.6b.h. -c.6e.h. -0.h.6F. -0.h.6M. -0.h.6+. -3.h.71. -5.79.h. -3.h.7D. -0.h.2p. -0.h.2p. 0.i.i. +0.r.a. +0.t.8. +3.n.d. +4.e.m. +4.h.j. +e.7.u. +e.9.s. +0.d.o. +0.f.m. +0.g.l. 0.i.j. +0.r.b. +0.s.a. +0.u.8. +3.n.e. +4.h.k. +e.9.t. +0.d.p. +0.e.o. +0.g.m. +0.h.l. 0.i.k. -0.i.l. -3.i.m. -2.n.i. -0.i.o. -0.i.p. -0.i.q. -0.i.r. -0.i.s. -3.i.t. -0.i.u. -0.i.v. -0.i.w. -3.i.x. -0.i.y. -0.i.z. -0.i.A. -0.i.B. -0.i.C. -0.i.D. -0.i.E. -0.i.F. -0.i.G. -0.i.H. -2.I.i. -2.J.i. -0.i.K. -0.i.L. -2.M.i. -0.i.N. -0.i.O. -0.i.P. -0.i.Q. -0.i.R. -3.S.i. -b.T.i. -0.i.U. -0.i.V. -0.i.W. -0.i.X. -b.Y.i. -3.i.Z. -0.i.+. -0.i./. -0.i.10. -0.i.11. -0.i.12. -0.i.13. -0.i.14. -0.i.15. -0.i.16. -0.i.17. -3.i.18. -0.i.19. -3.i.1a. -0.i.1b. -0.i.1c. -3.i.1d. -0.i.1e. -0.i.1f. -0.i.1g. -3.i.1h. -3.i.1i. -3.i.1j. -0.i.1k. -2.1l.i. -0.i.1m. -0.i.1n. -0.i.1o. -0.i.1p. -0.i.1q. -0.i.1r. -3.i.1s. -0.i.1t. -2.1u.i. -0.i.1v. -3.i.1w. -0.i.1x. -0.i.1y. -0.i.1z. -0.i.1A. -0.i.1B. -0.i.1C. -0.i.1D. -0.i.1E. -0.i.1F. -0.i.1G. -0.i.1H. -0.i.1I. -0.i.1J. -0.i.1K. -0.i.1L. -0.i.1M. -0.i.1N. -0.i.1O. -0.i.1P. -0.i.1Q. -0.i.1R. -0.i.1S. -3.i.1T. -0.i.1U. -0.i.1V. -0.i.1W. -0.i.1X. -0.i.1Y. -0.i.1Z. -0.i.1+. -0.i.1/. -0.i.20. -0.i.21. -0.i.22. -0.i.23. -0.i.24. -0.i.25. -b.26.i. -0.i.27. -4.i.28. -0.i.2a. -0.i.2b. -0.i.2l. -6.2m.i. -0.i.2p. -2.2q.i. -2.2r.i. -7.2s.i. -7.2t.i. -2.2u.i. -3.i.2v. -0.i.2w. -e.2x.i. -9.2y.i. -0.i.2z. -3.i.2A. -9.2B.i. -0.i.2C. -0.i.2D. -3.i.2E. -0.i.2F. -0.i.2G. -9.2H.i. -0.i.2I. -3.i.2J. -0.i.2K. -c.2M.i. -9.2N.i. -2.2O.i. -9.2Q.i. -7.2S.i. -0.i.2T. -1.2U.i. -9.2V.i. -1.2W.i. -0.i.2X. -d.2+.i. -0.i.30. -0.i.35. -b.36.i. -0.i.37. -3.i.38. -5.39.i. -0.i.3d. -3.i.3h. -3.i.3i. -0.i.3m. -0.i.3p. -a.3v.i. -a.3y.i. -2.3z.i. -5.3A.i. -2.3B.i. -0.i.3C. -a.3F.i. -5.3G.i. -0.i.3M. -0.i.3Q. -5.40.i. -0.i.47. -0.i.4d. -3.i.4O. -3.i.4Q. -5.4T.i. -3.i.4V. -0.i.51. -0.i.52. -a.5a.i. -2.5b.i. -5.5d.i. -0.i.5B. -0.i.5C. -0.i.5F. -3.i.5H. -0.i.5I. -f.i.5K. -7.5M.i. -0.i.5O. -3.i.5R. -0.i.5U. -0.i.5W. -1.5Y.i. -0.i.5Z. -0.i.5/. -0.i.60. -2.62.i. -2.66.i. -a.6b.i. -c.6e.i. -0.i.6F. -0.i.6M. -0.i.6+. -3.i.71. -5.79.i. -3.i.7D. -0.i.2p. -0.i.2p. 0.j.j. +0.s.b. +0.t.a. +3.n.f. +e.9.u. +i.c.r. +0.d.q. +0.e.p. +0.f.o. +0.h.m. +0.i.l. 0.k.j. +0.t.b. +0.u.a. +3.n.g. +i.c.s. +0.e.q. +0.f.p. +0.g.o. 0.j.l. -3.j.m. -2.n.j. -0.j.o. -0.j.p. -0.j.q. -3.j.r. -0.j.s. -3.j.t. -0.j.u. -0.j.v. -3.j.w. -3.j.x. -0.j.y. -0.j.z. -0.j.A. -0.j.B. -0.j.C. -0.j.D. -0.j.E. -0.j.F. -3.j.G. -0.j.H. -2.I.j. -2.J.j. -0.j.K. -0.j.L. -2.M.j. -0.j.N. -0.j.O. -0.j.P. -3.j.Q. -0.j.R. -3.S.j. -b.T.j. -3.j.U. -0.j.V. -0.j.W. -0.j.X. -b.Y.j. -3.j.Z. -0.j.+. -0.j./. -0.j.10. -0.j.11. -0.j.12. -0.j.13. -0.j.14. -0.j.15. -0.j.16. -0.j.17. -3.j.18. -0.j.19. -3.j.1a. -3.j.1b. -0.j.1c. -3.j.1d. -0.j.1e. -3.j.1f. -0.j.1g. -3.j.1h. -3.j.1i. -3.j.1j. -3.j.1k. -2.1l.j. -0.j.1m. -0.j.1n. -0.j.1o. -3.j.1p. -0.j.1q. -0.j.1r. -3.j.1s. -0.j.1t. -2.1u.j. -0.j.1v. -3.j.1w. -3.j.1x. -0.j.1y. -0.j.1z. -0.j.1A. -0.j.1B. -0.j.1C. -0.j.1D. -0.j.1E. -0.j.1F. -0.j.1G. -0.j.1H. -0.j.1I. -0.j.1J. -0.j.1K. -0.j.1L. -0.j.1M. -0.j.1N. -0.j.1O. -0.j.1P. -3.j.1Q. -0.j.1R. -3.j.1S. -3.j.1T. -3.j.1U. -0.j.1V. -0.j.1W. -0.j.1X. -0.j.1Y. -0.j.1Z. -0.j.1+. -0.j.1/. -0.j.20. -0.j.21. -0.j.22. -0.j.23. -0.j.24. -0.j.25. -b.26.j. -3.j.27. -4.j.28. -0.j.2a. -0.j.2b. -0.j.2l. -6.2m.j. -0.j.2p. -2.2q.j. -2.2r.j. -7.2s.j. -7.2t.j. -2.2u.j. -0.j.2v. -0.j.2w. -e.2x.j. -9.2y.j. -0.j.2z. -3.j.2A. -9.2B.j. -0.j.2C. -0.j.2D. -3.j.2E. -3.j.2F. -0.j.2G. -9.2H.j. -3.j.2I. -3.j.2J. -0.j.2K. -c.2M.j. -9.2N.j. -2.2O.j. -9.2Q.j. -7.2S.j. -0.j.2T. -1.2U.j. -9.2V.j. -1.2W.j. -0.j.2X. -d.2+.j. -0.j.30. -0.j.35. -3.36.j. -0.j.37. -3.j.38. -5.39.j. -0.j.3d. -3.j.3h. -3.j.3i. -0.j.3m. -0.j.3p. -a.3v.j. -a.3y.j. -2.3z.j. -5.3A.j. -2.3B.j. -0.j.3C. -a.3F.j. -5.3G.j. -0.j.3M. -0.j.3Q. -5.40.j. -0.j.47. -0.j.4d. -3.j.4O. -3.j.4Q. -5.4T.j. -3.j.4V. -0.j.51. -0.j.52. -a.5a.j. -2.5b.j. -5.5d.j. -0.j.5B. -0.j.5C. -0.j.5F. -3.j.5H. -0.j.5I. -f.j.5K. -7.5M.j. -0.j.5O. -3.j.5R. -0.j.5U. -3.j.5W. -1.5Y.j. -0.j.5Z. -0.j.5/. -0.j.60. -2.62.j. -2.66.j. -a.6b.j. -c.6e.j. -0.j.6F. -0.j.6M. -0.j.6+. -3.j.71. -5.79.j. -3.j.7D. -0.j.2p. -0.j.2p. 0.k.k. +0.u.b. +3.n.h. +4.i.m. +i.c.t. +0.f.q. +0.g.p. +0.h.o. 0.k.l. -3.k.m. -2.n.k. -0.k.o. -0.k.p. -0.k.q. -3.k.r. -0.k.s. -3.k.t. -0.k.u. -0.k.v. -0.k.w. -3.k.x. -0.k.y. -0.k.z. -0.k.A. -0.k.B. -0.k.C. -0.k.D. -0.k.E. -0.k.F. -0.k.G. -0.k.H. -2.I.k. -2.J.k. -0.k.K. -0.k.L. -2.M.k. -0.k.N. -0.k.O. -0.k.P. -0.k.Q. -0.k.R. -3.S.k. -b.T.k. -0.k.U. -0.k.V. -0.k.W. -0.k.X. -b.Y.k. -0.k.Z. -0.k.+. -0.k./. -0.k.10. -0.k.11. -0.k.12. -0.k.13. -0.k.14. -0.k.15. -0.k.16. -0.k.17. -3.k.18. -0.k.19. -3.k.1a. -0.k.1b. -0.k.1c. -3.k.1d. -0.k.1e. -3.k.1f. -0.k.1g. -3.k.1h. -3.k.1i. -3.k.1j. -0.k.1k. -2.1l.k. -0.k.1m. -0.k.1n. -0.k.1o. -0.k.1p. -0.k.1q. -0.k.1r. -3.k.1s. -0.k.1t. -2.1u.k. -0.k.1v. -3.k.1w. -0.k.1x. -0.k.1y. -0.k.1z. -0.k.1A. -0.k.1B. -0.k.1C. -0.k.1D. -0.k.1E. -0.k.1F. -0.k.1G. -0.k.1H. -0.k.1I. -0.k.1J. -0.k.1K. -0.k.1L. -0.k.1M. -3.k.1N. -0.k.1O. -0.k.1P. -3.k.1Q. -0.k.1R. -0.k.1S. -3.k.1T. -3.k.1U. -0.k.1V. -0.k.1W. -0.k.1X. -0.k.1Y. -0.k.1Z. -0.k.1+. -0.k.1/. -0.k.20. -0.k.21. -0.k.22. -0.k.23. -0.k.24. -0.k.25. -b.26.k. -0.k.27. -0.k.28. -4.k.29. -0.k.2a. -0.k.2b. -4.k.2c. -4.k.2d. -4.k.2f. -4.k.2g. -4.k.2h. -4.k.2i. -4.k.2j. -0.k.2l. -6.2m.k. -1.k.2o. -0.k.2p. -2.2q.k. -2.2r.k. -7.2s.k. -7.2t.k. -2.2u.k. -3.k.2v. -0.k.2w. -e.2x.k. -9.2y.k. -0.k.2z. -3.k.2A. -9.2B.k. -0.k.2C. -0.k.2D. -3.k.2E. -3.k.2F. -0.k.2G. -9.2H.k. -3.k.2I. -3.k.2J. -0.k.2K. -4.k.2L. -c.2M.k. -9.2N.k. -2.2O.k. -4.k.2P. -9.2Q.k. -8.k.2R. -7.2S.k. -0.k.2T. -1.2U.k. -9.2V.k. -1.2W.k. -0.k.2X. -8.k.2Y. -8.k.2Z. -d.2+.k. -8.k.2/. -0.k.30. -3.k.31. -8.k.32. -8.k.33. -8.k.34. -0.k.35. -b.36.k. -0.k.37. -3.k.38. -5.39.k. -4.k.3a. -4.k.3c. -0.k.3d. -1.k.3e. -4.k.3f. -3.k.3g. -3.k.3h. -3.k.3i. -1.k.3j. -3.k.3k. -4.k.3l. -0.k.3m. -3.k.3n. -3.k.3o. -0.k.3p. -3.k.3q. -4.k.3r. -4.k.3s. -8.k.3t. -8.k.3u. -a.3v.k. -4.k.3w. -4.k.3x. -a.3y.k. -2.3z.k. -5.3A.k. -2.3B.k. -0.k.3C. -4.k.3D. -4.k.3E. -a.3F.k. -5.3G.k. -4.k.3H. -4.k.3I. -4.k.3J. -4.k.3K. -4.k.3L. -0.k.3M. -4.k.3N. -4.k.3O. -4.k.3P. -0.k.3Q. -4.k.3R. -4.k.3S. -1.k.3T. -4.k.3U. -4.k.3V. -4.k.3W. -4.k.3X. -4.k.3Y. -4.k.3Z. -1.k.3+. -1.k.3/. -5.40.k. -4.k.41. -4.k.42. -4.k.43. -4.k.44. -4.k.45. -0.k.47. -4.k.48. -4.k.49. -1.k.4a. -4.k.4b. -4.k.4c. -0.k.4d. -4.k.4e. -4.k.4f. -4.k.4g. -4.k.4h. -4.k.4i. -4.k.4j. -8.k.4k. -4.k.4l. -4.k.4m. -4.k.4n. -4.k.4o. -4.k.4p. -4.k.4q. -4.k.4r. -4.k.4s. -4.k.4t. -4.k.4u. -4.k.4v. -4.k.4w. -4.k.4x. -4.k.4y. -4.k.4z. -4.k.4A. -4.k.4B. -4.k.4C. -4.k.4D. -4.k.4E. -1.k.4F. -8.k.4G. -8.k.4H. -1.k.4I. -8.k.4J. -8.k.4K. -8.k.4L. -4.k.4M. -4.k.4N. -3.k.4O. -4.k.4P. -3.k.4Q. -1.k.4R. -4.k.4S. -4.k.4T. -8.k.4U. -3.k.4V. -4.k.4W. -3.k.4Y. -1.k.4Z. -4.k.4+. -4.k.4/. -1.k.50. -0.k.51. -0.k.52. -8.k.53. -3.k.54. -8.k.55. -8.k.56. -1.k.57. -3.k.58. -8.k.59. -a.5a.k. -2.5b.k. -4.k.5c. -5.5d.k. -3.k.5e. -8.k.5f. -4.k.5g. -3.k.5h. -4.k.5i. -4.k.5j. -4.k.5k. -4.k.5l. -4.k.5m. -4.k.5n. -4.k.5o. -4.k.5p. -4.k.5q. -4.k.5r. -1.k.5s. -4.k.5t. -4.k.5u. -4.k.5v. -1.k.5w. -1.k.5y. -1.k.5z. -8.k.5A. -0.k.5B. -0.k.5C. -1.k.5D. -8.k.5E. -0.k.5F. -8.k.5G. -3.k.5H. -0.k.5I. -f.k.5K. -8.k.5L. -8.k.5M. -8.k.5N. -0.k.5O. -8.k.5P. -4.k.5Q. -3.k.5R. -4.k.5S. -4.k.5T. -0.k.5U. -8.k.5V. -0.k.5W. -8.k.5X. -8.k.5Y. -0.k.5Z. -0.k.5/. -0.k.60. -4.k.61. -2.62.k. -4.k.64. -4.k.65. -2.66.k. -4.k.67. -4.k.68. -4.k.69. -4.k.6a. -a.6b.k. -4.k.6c. -4.k.6d. -4.k.6e. -4.k.6f. -4.k.6g. -4.k.6h. -4.k.6i. -4.k.6j. -4.k.6k. -4.k.6l. -4.k.6m. -4.k.6n. -4.k.6o. -4.k.6p. -4.k.6q. -4.k.6r. -4.k.6s. -4.k.6t. -4.k.6u. -4.k.6v. -4.k.6w. -4.k.6x. -4.k.6y. -4.k.6z. -4.k.6A. -4.k.6B. -4.k.6C. -4.k.6D. -4.k.6E. -0.k.6F. -4.k.6G. -4.k.6H. -4.k.6I. -4.k.6J. -4.k.6K. -8.k.6L. -0.k.6M. -4.k.6N. -4.k.6O. -4.k.6P. -4.k.6Q. -4.k.6R. -4.k.6S. -4.k.6T. -4.k.6U. -4.k.6V. -4.k.6W. -1.k.6X. -4.k.6Y. -1.k.6Z. -0.k.6+. -1.k.6/. -1.k.70. -3.k.71. -4.k.72. -4.k.73. -1.k.74. -4.k.75. -1.k.76. -1.k.77. -4.k.78. -5.79.k. -1.k.7a. -4.k.7b. -4.k.7c. -4.k.7d. -4.k.7e. -4.k.7f. -4.k.7g. -4.k.7h. -4.k.7i. -4.k.7j. -4.k.7k. -4.k.7l. -1.k.7m. -4.k.7o. -4.k.7p. -1.k.7r. -1.k.7s. -3.k.7t. -4.k.7u. -4.k.7v. -4.k.7w. -4.k.7x. -4.k.7y. -1.k.7z. -4.k.7A. -8.k.7B. -4.k.7C. -3.k.7D. -4.k.7E. -4.k.7F. -4.k.7G. -4.k.7H. -4.k.7I. -4.k.7J. -1.k.7K. -1.k.7L. -1.k.7M. -1.k.7N. -1.k.7O. -1.k.7P. -4.k.7Q. -3.k.7R. -4.k.7S. -4.k.7T. -1.k.7U. -3.k.7V. -4.k.7W. -4.k.7X. -4.k.7Y. -4.k.7Z. -1.k.7+. -1.k.7/. -1.k.80. -1.k.81. -1.k.82. -4.k.83. -1.k.84. -1.k.85. -1.k.87. -1.k.88. -8.k.89. -4.k.8a. -3.k.8b. -4.k.8c. -8.k.8d. -4.k.8e. -4.k.8f. -4.k.8g. -4.k.8h. -4.k.8i. -4.k.8j. -4.k.8k. -4.k.8l. -4.k.8m. -4.k.8n. -4.k.8o. -4.k.8p. -4.k.8q. -4.k.8r. -4.k.8s. -4.k.8t. -4.k.8u. -4.k.8v. -4.k.8w. -4.k.8x. -4.k.8y. -4.k.8z. -4.k.8A. -4.k.8B. -4.k.8C. -4.k.8D. -4.k.8E. -4.k.8F. -4.k.8G. -4.k.8H. -4.k.8I. -4.k.8J. -4.k.8K. -4.k.8L. -4.k.8M. -4.k.8N. -4.k.8O. -4.k.8P. -0.k.2p. -0.k.2p. -8.k.3b. -1.k.46. -1.k.4X. -1.k.5x. -1.k.5J. -1.k.7n. -1.k.7q. +3.n.i. +4.j.m. +i.c.u. +0.d.r. +0.g.q. +0.h.p. +0.i.o. 0.l.l. -3.l.m. -2.n.l. +4.k.m. +e.n.j. +i.c.v. +0.d.s. +0.e.r. +0.i.p. +0.j.o. +3.n.k. +4.h.q. +4.l.m. +i.c.w. +0.e.s. +0.f.r. +0.i.q. +0.j.p. +0.k.o. +3.n.l. +4.d.t. +4.m.m. +i.c.x. +0.d.u. +0.f.s. +0.g.r. +0.j.q. +0.k.p. 0.l.o. -0.p.l. -0.l.q. -0.r.l. -0.s.l. -0.t.l. -0.u.l. -0.v.l. -0.w.l. -0.x.l. -0.y.l. -0.l.z. -0.l.A. -0.l.B. -0.l.C. -0.l.D. -0.l.E. -0.l.F. -0.G.l. -0.l.H. -2.I.l. -2.J.l. -0.l.K. -0.l.L. -2.M.l. -0.l.N. -0.l.O. -0.l.P. -0.l.Q. -0.l.R. -3.S.l. -b.T.l. -0.l.U. -0.l.V. -0.l.W. -0.l.X. -b.Y.l. -0.l.Z. -0.l.+. -0.l./. -0.l.10. -0.l.11. -0.l.12. -0.l.13. -0.l.14. -0.l.15. -0.l.16. -0.l.17. -0.l.18. -0.l.19. -0.l.1a. -0.l.1b. -0.l.1c. -3.l.1d. -0.l.1e. -0.l.1f. -0.l.1g. -3.l.1h. -3.l.1i. -3.l.1j. -0.l.1k. -2.1l.l. -0.l.1m. -0.l.1n. -0.l.1o. -0.l.1p. -0.l.1q. -0.l.1r. -0.l.1s. -0.l.1t. -2.1u.l. -0.l.1v. -3.l.1w. -0.l.1x. -0.l.1y. -0.l.1z. -0.l.1A. -0.1B.l. -0.l.1C. -0.l.1D. -0.l.1E. -0.l.1F. -0.l.1G. -0.l.1H. -0.l.1I. -0.l.1J. -0.l.1K. -0.l.1L. -0.l.1M. -0.l.1N. -0.l.1O. -0.l.1P. -0.l.1Q. -0.l.1R. -0.l.1S. -0.l.1T. -3.l.1U. -0.l.1V. -0.l.1W. -0.l.1X. -0.l.1Y. -0.l.1Z. -0.l.1+. -0.l.1/. -0.l.20. -0.l.21. -0.l.22. -0.l.23. -0.l.24. -0.l.25. -b.26.l. -0.l.27. -4.l.28. -0.l.2a. -0.l.2b. -0.l.2l. -6.2m.l. -0.l.2p. -2.2q.l. -2.2r.l. -7.2s.l. -7.2t.l. -2.2u.l. -0.l.2v. -0.l.2w. -e.2x.l. -9.2y.l. -0.l.2z. -3.l.2A. -9.2B.l. -0.l.2C. -0.l.2D. -3.l.2E. -0.l.2F. -0.l.2G. -9.2H.l. -0.l.2I. -3.l.2J. -0.l.2K. -c.2M.l. -9.2N.l. -2.2O.l. -9.2Q.l. -7.2S.l. -0.l.2T. -1.2U.l. -9.2V.l. -1.2W.l. -0.l.2X. -d.2+.l. -0.l.30. -0.l.35. -b.36.l. -0.l.37. -0.l.38. -5.39.l. -0.l.3d. -3.l.3h. -0.l.3i. -0.l.3m. -0.l.3p. -a.3v.l. -a.3y.l. -2.3z.l. -5.3A.l. -2.3B.l. -0.l.3C. -a.3F.l. -5.3G.l. -0.l.3M. -0.l.3Q. -5.40.l. -0.l.47. -0.l.4d. -0.l.4O. -3.l.4Q. -5.4T.l. -3.l.4V. -0.l.51. -0.l.52. -a.5a.l. -2.5b.l. -5.5d.l. -0.l.5B. -0.l.5C. -0.l.5F. -3.l.5H. -0.l.5I. -f.l.5K. -7.5M.l. -0.l.5O. -3.l.5R. -0.l.5U. -0.l.5W. -1.5Y.l. -0.l.5Z. -0.l.5/. -0.l.60. -2.62.l. -2.66.l. -a.6b.l. -c.6e.l. -0.l.6F. -0.l.6M. -0.l.6+. -3.l.71. -5.79.l. -3.l.7D. -0.l.2p. -0.l.2p. -3.m.m. -2.n.m. +3.n.m. +4.e.t. +i.c.y. +0.d.v. +0.e.u. +0.g.s. +0.h.r. +0.k.q. 0.m.o. +0.p.l. +3.n.n. +4.f.t. +i.c.z. +0.d.w. +0.e.v. +0.f.u. +0.g.t. +0.h.s. +0.i.r. +0.l.q. 0.p.m. +3.n.o. +0.e.w. +0.f.v. +0.g.u. +0.h.t. +0.i.s. 0.m.q. -0.r.m. -0.s.m. -0.t.m. -0.u.m. -0.v.m. -0.w.m. -0.x.m. -0.y.m. -0.m.z. -0.m.A. -0.m.B. -0.m.C. -0.m.D. -0.m.E. -3.m.F. -0.G.m. -0.m.H. -2.I.m. -2.J.m. -0.m.K. -0.m.L. -2.M.m. -0.m.N. -0.m.O. -0.m.P. -0.m.Q. -0.m.R. -3.S.m. -b.T.m. -0.m.U. -0.m.V. -0.m.W. -0.m.X. -b.Y.m. -0.m.Z. -0.m.+. -0.m./. -0.m.10. -0.m.11. -0.m.12. -0.m.13. -0.m.14. -0.m.15. -0.m.16. -0.m.17. -3.m.18. -0.m.19. -0.m.1a. -0.m.1b. -0.m.1c. -3.m.1d. -0.m.1e. -0.m.1f. -0.m.1g. -3.m.1h. -3.m.1i. -3.m.1j. -0.m.1k. -2.1l.m. -0.m.1m. -0.m.1n. -0.m.1o. -0.m.1p. -0.m.1q. -0.m.1r. -0.m.1s. -0.m.1t. -2.1u.m. -0.m.1v. -3.m.1w. -0.m.1x. -0.m.1y. -0.m.1z. -0.m.1A. -0.1B.m. -0.m.1C. -0.m.1D. -0.m.1E. -0.m.1F. -0.m.1G. -0.m.1H. -0.m.1I. -0.m.1J. -0.m.1K. -0.m.1L. -0.m.1M. -0.m.1N. -0.m.1O. -0.m.1P. -0.m.1Q. -0.m.1R. -0.m.1S. -0.m.1T. -0.m.1U. -0.m.1V. -0.m.1W. -0.m.1X. -0.m.1Y. -0.m.1Z. -0.m.1+. -0.m.1/. -0.m.20. -0.m.21. -0.m.22. -0.m.23. -0.m.24. -0.m.25. -b.26.m. -0.m.27. -0.m.28. -0.m.2a. -0.m.2b. -0.m.2l. -6.2m.m. -0.m.2p. -2.2q.m. -2.2r.m. -7.2s.m. -7.2t.m. -2.2u.m. -0.m.2v. -0.m.2w. -e.2x.m. -9.2y.m. -0.m.2z. -3.m.2A. -9.2B.m. -0.m.2C. -0.m.2D. -0.m.2E. -0.m.2F. -0.m.2G. -9.2H.m. -0.m.2I. -3.m.2J. -0.m.2K. -c.2M.m. -9.2N.m. -2.2O.m. -9.2Q.m. -7.2S.m. -0.m.2T. -1.2U.m. -9.2V.m. -1.2W.m. -0.m.2X. -d.2+.m. -0.m.30. -0.m.35. -b.36.m. -0.m.37. -0.m.38. -5.39.m. -0.m.3d. -3.m.3h. -0.m.3i. -0.m.3m. -0.m.3p. -a.3v.m. -a.3y.m. -2.3z.m. -5.3A.m. -2.3B.m. -0.m.3C. -a.3F.m. -5.3G.m. -0.m.3M. -0.m.3Q. -5.40.m. -0.m.47. -0.m.4d. -0.m.4O. -3.m.4Q. -5.4T.m. -3.m.4V. -0.m.51. -0.m.52. -a.5a.m. -2.5b.m. -5.5d.m. -0.m.5B. -0.m.5C. -0.m.5F. -3.m.5H. -0.m.5I. -f.m.5K. -7.5M.m. -0.m.5O. -3.m.5R. -0.m.5U. -0.m.5W. -1.5Y.m. -0.m.5Z. -0.m.5/. -0.m.60. -2.62.m. -2.66.m. -a.6b.m. -c.6e.m. -0.m.6F. -0.m.6M. -0.m.6+. -3.m.71. -5.79.m. -3.m.7D. -0.m.2p. -0.m.2p. -2.n.n. -2.n.o. -2.n.p. -2.n.q. -2.n.r. -2.n.s. -2.n.t. -2.n.u. -2.n.v. -2.n.w. -2.n.x. -2.n.y. -2.n.z. -2.n.A. -2.n.B. -2.n.C. -2.n.D. -2.n.E. -2.n.F. -2.n.G. -2.n.H. -2.I.n. -2.J.n. -2.n.K. -2.n.L. -2.n.M. -2.n.N. -2.n.O. -2.n.P. -2.n.Q. -2.n.R. -3.n.S. -2.n.T. -2.n.U. -2.n.V. -2.n.W. -2.n.X. -2.n.Y. -2.n.Z. -2.n.+. -2.n./. -2.n.10. -2.n.11. -2.n.12. -2.n.13. -2.n.14. -2.n.15. -2.n.16. -2.n.17. -2.n.18. -2.n.19. -2.n.1a. -2.n.1b. -2.n.1c. -2.n.1d. -2.n.1e. -2.n.1f. -2.n.1g. -2.n.1h. -2.n.1i. -2.n.1j. -2.n.1k. -2.1l.n. -2.n.1m. -2.n.1n. -2.n.1o. -2.n.1p. -2.n.1q. -2.n.1r. -2.n.1s. -2.n.1t. -2.1u.n. -2.n.1v. -2.n.1w. -2.n.1x. -2.n.1y. -2.n.1z. -2.n.1A. -2.n.1B. -2.n.1C. -2.n.1D. -2.n.1E. -2.n.1F. -2.n.1G. -2.n.1H. -2.n.1I. -2.n.1J. -2.n.1K. -2.n.1L. -2.n.1M. -2.n.1N. -2.n.1O. -2.n.1P. -2.n.1Q. -2.n.1R. -2.n.1S. -2.n.1T. -2.n.1U. -2.n.1V. -2.n.1W. -2.n.1X. -2.n.1Y. -2.n.1Z. -2.n.1+. -2.n.1/. -2.n.20. -2.n.21. -2.n.22. -2.n.23. -2.n.24. -2.n.25. -2.n.26. -2.n.27. -2.n.28. -2.n.2a. -2.n.2b. -2.n.2l. -6.2m.n. -2.n.2p. -2.n.2q. -2.n.2r. -7.2s.n. -7.2t.n. -2.2u.n. -2.n.2v. -2.n.2w. -e.2x.n. -2.n.2y. -2.n.2z. -2.n.2A. -2.n.2B. -2.n.2C. -2.n.2D. -2.n.2E. -2.n.2F. -2.n.2G. -2.n.2H. -2.n.2I. -2.n.2J. -2.n.2K. -c.2M.n. -2.n.2N. -2.n.2O. -2.n.2Q. -7.2S.n. -2.n.2T. -1.2U.n. -2.n.2V. -1.2W.n. -2.n.2X. -2.n.2+. -2.n.30. -2.n.35. -2.n.36. -2.n.37. -2.n.38. -2.n.39. -2.n.3d. -2.n.3h. -2.n.3i. -2.n.3m. -2.n.3p. -a.3v.n. -a.3y.n. -2.3z.n. -2.n.3A. -2.3B.n. -2.n.3C. -a.3F.n. -2.n.3G. -2.n.3M. -2.n.3Q. -2.n.40. -2.n.47. -2.n.4d. -2.n.4O. -2.n.4Q. -5.4T.n. -2.n.4V. -2.n.51. -2.n.52. -a.5a.n. -2.5b.n. -2.n.5d. -2.n.5B. -2.n.5C. -2.n.5F. -2.n.5H. -2.n.5I. -f.n.5K. -7.5M.n. -2.n.5O. -2.n.5R. -2.n.5U. -2.n.5W. -1.5Y.n. -2.n.5Z. -2.n.5/. -2.n.60. -2.n.62. -2.n.66. -a.6b.n. -c.6e.n. -2.n.6F. -2.n.6M. -2.n.6+. -2.n.71. -2.n.79. -2.n.7D. -2.n.2p. -2.n.2p. -3.o.o. +3.n.p. +4.d.x. +4.j.r. +4.o.o. +0.d.y. +0.f.w. +0.g.v. +0.h.u. +0.j.s. 0.p.o. +3.n.q. +4.e.x. +4.i.t. +4.k.r. +i.c.A. +0.d.z. +0.e.y. +0.g.w. +0.h.v. +0.i.u. +0.k.s. 0.o.q. -0.r.o. -0.s.o. -0.t.o. -0.u.o. -0.v.o. -0.w.o. -0.x.o. -0.y.o. -0.o.z. -0.o.A. -0.o.B. -0.o.C. -0.o.D. -0.o.E. -0.o.F. -0.G.o. -0.o.H. -2.I.o. -2.J.o. -0.o.K. -0.o.L. -2.M.o. -0.o.N. -0.o.O. -0.o.P. -0.o.Q. -0.o.R. -3.S.o. -b.T.o. -0.o.U. -0.o.V. -0.o.W. -0.o.X. -b.Y.o. -0.o.Z. -0.o.+. -0.o./. -0.o.10. -0.o.11. -0.o.12. -0.o.13. -0.o.14. -0.o.15. -0.o.16. -0.o.17. -3.o.18. -0.o.19. -0.o.1a. -0.o.1b. -0.o.1c. -3.o.1d. -0.o.1e. -0.o.1f. -0.o.1g. -3.o.1h. -3.o.1i. -3.o.1j. -0.o.1k. -2.1l.o. -0.o.1m. -0.o.1n. -0.o.1o. -0.o.1p. -0.o.1q. -0.o.1r. -3.o.1s. -0.o.1t. -2.1u.o. -0.o.1v. -3.o.1w. -0.o.1x. -0.o.1y. -0.o.1z. -0.o.1A. -0.1B.o. -0.o.1C. -0.o.1D. -0.o.1E. -0.o.1F. -0.o.1G. -0.o.1H. -0.o.1I. -0.o.1J. -0.o.1K. -0.o.1L. -0.o.1M. -0.o.1N. -0.o.1O. -0.o.1P. -3.o.1Q. -0.o.1R. -0.o.1S. -0.o.1T. -3.o.1U. -0.o.1V. -0.o.1W. -0.o.1X. -0.o.1Y. -0.o.1Z. -0.o.1+. -0.o.1/. -0.o.20. -0.o.21. -0.o.22. -0.o.23. -0.o.24. -0.o.25. -b.26.o. -0.o.27. -4.o.28. -0.o.2a. -0.o.2b. -0.o.2l. -6.2m.o. -0.o.2p. -2.2q.o. -2.2r.o. -7.2s.o. -7.2t.o. -2.2u.o. -3.o.2v. -0.o.2w. -e.2x.o. -9.2y.o. -0.o.2z. -3.o.2A. -9.2B.o. -0.o.2C. -0.o.2D. -3.o.2E. -3.o.2F. -0.o.2G. -9.2H.o. -0.o.2I. -3.o.2J. -0.o.2K. -c.2M.o. -9.2N.o. -2.2O.o. -9.2Q.o. -7.2S.o. -0.o.2T. -1.2U.o. -9.2V.o. -1.2W.o. -0.o.2X. -b.2+.o. -0.o.30. -0.o.35. -b.36.o. -0.o.37. -0.o.38. -5.39.o. -0.o.3d. -3.o.3h. -3.o.3i. -0.o.3m. -0.o.3p. -a.3v.o. -a.3y.o. -2.3z.o. -5.3A.o. -2.3B.o. -0.o.3C. -a.3F.o. -5.3G.o. -0.o.3M. -0.o.3Q. -5.40.o. -0.o.47. -0.o.4d. -3.o.4O. -3.o.4Q. -5.4T.o. -3.o.4V. -0.o.51. -0.o.52. -a.5a.o. -2.5b.o. -5.5d.o. -0.o.5B. -0.o.5C. -0.o.5F. -3.o.5H. -0.o.5I. -f.o.5K. -7.5M.o. -0.o.5O. -3.o.5R. -0.o.5U. -0.o.5W. -1.5Y.o. -0.o.5Z. -0.o.5/. -0.o.60. -2.62.o. -2.66.o. -a.6b.o. -c.6e.o. -0.o.6F. -0.o.6M. -0.o.6+. -3.o.71. -5.79.o. -3.o.7D. -0.o.2p. -0.o.2p. 0.p.p. +0.r.l. +4.f.x. +4.j.t. +i.c.B. +0.e.z. +0.f.y. +0.h.w. +0.i.v. +0.j.u. 0.p.q. -0.p.r. -0.p.s. -0.p.t. -0.u.p. -0.v.p. -0.w.p. -0.x.p. -0.y.p. -0.p.z. -0.p.A. -0.p.B. -0.p.C. -0.p.D. -0.p.E. -0.p.F. -0.p.G. -0.p.H. -2.I.p. -2.J.p. -0.p.K. -0.p.L. -2.M.p. -0.p.N. -0.p.O. -0.p.P. -0.p.Q. -0.p.R. -3.S.p. -b.T.p. -0.p.U. -0.p.V. -0.p.W. -0.p.X. -b.Y.p. -0.p.Z. -0.p.+. -0.p./. -0.p.10. -0.p.11. -0.p.12. -0.p.13. -0.p.14. -0.p.15. -0.p.16. -0.p.17. -0.p.18. -0.p.19. -0.p.1a. -0.p.1b. -0.p.1c. -3.p.1d. -0.p.1e. -0.p.1f. -0.p.1g. -3.p.1h. -3.p.1i. -3.p.1j. -0.p.1k. -2.1l.p. -0.p.1m. -0.p.1n. -0.p.1o. -0.p.1p. -0.p.1q. -0.p.1r. -0.p.1s. -0.p.1t. -2.1u.p. -0.p.1v. -0.p.1w. -0.p.1x. -0.p.1y. -0.p.1z. -0.p.1A. -0.1B.p. -0.p.1C. -0.p.1D. -0.p.1E. -0.p.1F. -0.p.1G. -0.p.1H. -0.p.1I. -0.p.1J. -0.p.1K. -0.p.1L. -0.p.1M. -0.p.1N. -0.p.1O. -0.p.1P. -0.p.1Q. -0.p.1R. -0.p.1S. -0.p.1T. -0.p.1U. -0.p.1V. -0.p.1W. -0.p.1X. -0.p.1Y. -0.p.1Z. -0.p.1+. -0.p.1/. -0.p.20. -0.p.21. -0.p.22. -0.p.23. -0.p.24. -0.p.25. -b.26.p. -0.p.27. -4.p.28. -0.p.2a. -0.p.2b. -0.p.2l. -6.2m.p. -0.p.2p. -2.2q.p. -2.2r.p. -7.2s.p. -7.2t.p. -2.2u.p. -0.p.2v. -0.p.2w. -e.2x.p. -9.2y.p. -0.p.2z. -3.p.2A. -9.2B.p. -0.p.2C. -0.p.2D. -3.p.2E. -0.p.2F. -0.p.2G. -9.2H.p. -0.p.2I. -3.p.2J. -0.p.2K. -c.2M.p. -9.2N.p. -2.2O.p. -9.2Q.p. -7.2S.p. -0.p.2T. -1.2U.p. -9.2V.p. -1.2W.p. -0.p.2X. -b.2+.p. -0.p.30. -0.p.35. -b.36.p. -0.p.37. -0.p.38. -5.39.p. -0.p.3d. -3.p.3h. -0.p.3i. -0.p.3m. -0.p.3p. -a.3v.p. -a.3y.p. -2.3z.p. -5.3A.p. -2.3B.p. -0.p.3C. -a.3F.p. -5.3G.p. -0.p.3M. -0.p.3Q. -5.40.p. -0.p.47. -0.p.4d. -0.p.4O. -3.p.4Q. -5.4T.p. -3.p.4V. -0.p.51. -0.p.52. -a.5a.p. -2.5b.p. -5.5d.p. -0.p.5B. -0.p.5C. -0.p.5F. -3.p.5H. -0.p.5I. -f.p.5K. -7.5M.p. -0.p.5O. -0.p.5R. -0.p.5U. -0.p.5W. -1.5Y.p. -0.p.5Z. -0.p.5/. -0.p.60. -2.62.p. -2.66.p. -a.6b.p. -c.6e.p. -0.p.6F. -0.p.6M. -0.p.6+. -3.p.71. -5.79.p. -3.p.7D. -0.p.2p. -0.p.2p. +0.r.m. +0.s.l. +4.g.x. +4.k.t. +i.c.C. +0.f.z. +0.g.y. +0.h.x. +0.i.w. +0.j.v. +0.k.u. 0.q.q. +0.s.m. +0.t.l. +3.n.r. +i.c.D. +0.d.A. +0.g.z. +0.h.y. +0.k.v. +0.r.o. +0.t.m. +0.u.l. +3.n.s. +4.i.x. +4.j.w. +i.c.E. +0.d.B. +0.e.A. +0.i.y. +0.k.w. +0.p.r. +0.s.o. +0.u.m. +0.v.l. +3.n.t. +4.h.z. +4.j.x. +i.c.F. +0.d.C. +0.e.B. +0.f.A. +0.i.z. +0.j.y. +0.p.s. 0.r.q. +0.t.o. +0.v.m. +0.w.l. +3.n.u. +4.k.x. +0.d.D. +0.e.C. +0.f.B. +0.g.A. +0.j.z. +0.k.y. +0.p.t. 0.s.q. +0.u.o. +0.w.m. +0.x.l. +3.n.v. +0.d.E. +0.e.D. +0.f.C. +0.g.B. +0.h.A. +0.k.z. 0.t.q. -0.u.q. -0.v.q. -0.w.q. -0.x.q. -0.y.q. -0.q.z. -0.A.q. -0.B.q. -0.C.q. -0.D.q. -0.E.q. -3.q.F. -0.G.q. -0.H.q. -2.I.q. -2.J.q. -0.K.q. -0.L.q. -2.M.q. -0.N.q. -0.O.q. -0.P.q. -0.Q.q. -0.R.q. -3.S.q. -b.T.q. -0.U.q. -0.V.q. -0.W.q. -0.X.q. -b.Y.q. -0.q.Z. -0.+.q. -0./.q. -0.10.q. -0.11.q. -3.12.q. -0.13.q. -0.14.q. -0.15.q. -0.16.q. -0.17.q. -0.18.q. -0.19.q. -0.1a.q. -0.1b.q. -0.1c.q. -0.1d.q. -0.q.1e. -0.q.1f. -0.q.1g. -3.q.1h. -3.q.1i. -0.1j.q. -0.1k.q. -2.1l.q. -0.1m.q. -0.1n.q. -0.1o.q. -0.1p.q. -0.1q.q. -0.1r.q. -0.1s.q. -0.1t.q. -2.1u.q. -0.1v.q. -0.1w.q. -0.1x.q. -0.1y.q. -0.1z.q. -0.1A.q. -0.1B.q. -0.1C.q. -0.1D.q. -0.1E.q. -0.1F.q. -0.1G.q. -0.1H.q. -0.1I.q. -0.1J.q. -0.1K.q. -0.1L.q. -0.1M.q. -0.1N.q. -0.1O.q. -0.1P.q. -3.q.1Q. -d.1R.q. -0.q.1S. -0.q.1T. -3.q.1U. -0.q.1V. -0.q.1W. -0.q.1X. -0.q.1Y. -0.q.1Z. -0.q.1+. -0.q.1/. -0.q.20. -0.q.21. -0.q.22. -0.q.23. -0.q.24. -0.q.25. -b.26.q. -0.q.27. -4.q.28. -0.q.2a. -0.q.2b. -0.q.2l. -6.2m.q. -0.q.2p. -2.2q.q. -2.2r.q. -7.2s.q. -7.2t.q. -2.2u.q. -3.q.2v. -0.q.2w. -e.2x.q. -9.2y.q. -0.q.2z. -3.q.2A. -9.2B.q. -0.q.2C. -0.q.2D. -3.q.2E. -3.q.2F. -0.q.2G. -9.2H.q. -0.q.2I. -3.q.2J. -0.q.2K. -c.2M.q. -9.2N.q. -2.2O.q. -9.2Q.q. -7.2S.q. -0.q.2T. -1.2U.q. -9.2V.q. -1.2W.q. -0.q.2X. -d.2+.q. -0.q.30. -0.q.35. -b.36.q. -0.q.37. -0.q.38. -5.39.q. -0.q.3d. -3.q.3h. -0.q.3i. -0.q.3m. -0.q.3p. -a.3v.q. -a.3y.q. -2.3z.q. -5.3A.q. -2.3B.q. -0.q.3C. -a.3F.q. -5.3G.q. -0.q.3M. -0.q.3Q. -5.40.q. -0.q.47. -0.q.4d. -3.q.4O. -3.q.4Q. -5.4T.q. -3.q.4V. -0.q.51. -0.q.52. -a.5a.q. -2.5b.q. -5.5d.q. -0.q.5B. -0.q.5C. -5.5F.q. -3.q.5H. -0.q.5I. -f.q.5K. -7.5M.q. -0.q.5O. -3.q.5R. -0.q.5U. -3.q.5W. -1.5Y.q. -0.q.5Z. -0.q.5/. -0.q.60. -2.62.q. -2.66.q. -a.6b.q. -c.6e.q. -0.q.6F. -0.q.6M. -0.q.6+. -3.q.71. -5.79.q. -3.q.7D. -0.q.2p. -0.q.2p. +0.u.p. +0.v.o. +0.x.m. +0.y.l. +3.n.w. +i.c.G. +0.e.E. +0.f.D. +0.g.C. +0.h.B. +0.i.A. +0.l.z. 0.r.r. +0.u.q. +0.v.p. +0.w.o. +0.y.m. +3.n.x. +4.c.H. +4.d.F. +0.e.F. +0.f.E. +0.g.D. +0.h.C. +0.i.B. +0.j.A. +0.m.z. 0.r.s. +0.v.q. +0.w.p. +0.x.o. +3.I.c. +3.n.y. +j.S.0. +0.f.F. +0.g.E. +0.h.D. +0.i.C. +0.j.B. +0.k.A. 0.r.t. +0.w.q. +0.x.p. +0.y.o. +3.J.c. +3.n.z. +e.s.s. +j.S.1. +0.d.G. +0.g.F. +0.h.E. +0.i.D. +0.j.C. +0.k.B. +0.l.A. +0.o.z. 0.u.r. -0.v.r. -0.w.r. -0.x.r. -0.y.r. -0.r.z. -0.r.A. -0.r.B. -0.r.C. -0.r.D. -0.r.E. -0.r.F. -0.r.G. -0.r.H. -2.I.r. -2.J.r. -0.r.K. -0.r.L. -2.M.r. -0.r.N. -0.r.O. -0.r.P. -0.r.Q. -0.r.R. -3.S.r. -b.T.r. -0.r.U. -0.r.V. -0.r.W. -0.r.X. -b.Y.r. -0.r.Z. -0.r.+. -0.r./. -0.r.10. -0.r.11. -0.r.12. -0.r.13. -0.r.14. -0.r.15. -0.r.16. -0.r.17. -0.r.18. -0.r.19. -0.r.1a. -0.r.1b. -0.r.1c. -0.r.1d. -0.r.1e. -0.r.1f. -0.r.1g. -0.r.1h. -0.r.1i. -0.r.1j. -0.r.1k. -2.1l.r. -0.r.1m. -0.r.1n. -0.r.1o. -0.r.1p. -0.r.1q. -0.r.1r. -0.r.1s. -0.r.1t. -2.1u.r. -0.r.1v. -0.r.1w. -0.r.1x. -0.r.1y. -0.r.1z. -0.r.1A. -0.1B.r. -0.r.1C. -0.r.1D. -0.r.1E. -0.r.1F. -0.r.1G. -0.r.1H. -0.r.1I. -0.r.1J. -0.r.1K. -0.r.1L. -0.r.1M. -0.r.1N. -0.r.1O. -0.r.1P. -0.r.1Q. -0.r.1R. -3.r.1S. -0.r.1T. -0.r.1U. -0.r.1V. -0.r.1W. -0.r.1X. -0.r.1Y. -0.r.1Z. -0.r.1+. -0.r.1/. -0.r.20. -0.r.21. -0.r.22. -0.r.23. -0.r.24. -0.r.25. -b.26.r. -0.r.27. -0.r.28. -6.r.29. -0.r.2a. -0.r.2b. -3.r.2c. -3.r.2d. -6.r.2f. -6.r.2g. -3.r.2h. -6.r.2i. -3.r.2j. -0.r.2l. -6.2m.r. -1.r.2o. -0.r.2p. -2.2q.r. -2.2r.r. -7.2s.r. -7.2t.r. -2.2u.r. -0.r.2v. -0.r.2w. -e.2x.r. -9.2y.r. -0.r.2z. -0.r.2A. -9.2B.r. -0.r.2C. -0.r.2D. -0.r.2E. -0.r.2F. -0.r.2G. -9.2H.r. -0.r.2I. -0.r.2J. -0.r.2K. -3.r.2L. -c.2M.r. -9.2N.r. -2.2O.r. -3.r.2P. -9.2Q.r. -8.r.2R. -7.2S.r. -0.r.2T. -1.2U.r. -9.2V.r. -1.2W.r. -0.r.2X. -3.r.2Y. -8.r.2Z. -b.2+.r. -8.r.2/. -0.r.30. -3.r.31. -3.r.32. -8.r.33. -3.r.34. -0.r.35. -b.36.r. -0.r.37. -0.r.38. -5.39.r. -6.r.3a. -6.r.3c. -0.r.3d. -1.r.3e. -6.r.3f. -3.r.3g. -0.r.3h. -0.r.3i. -1.r.3j. -3.r.3k. -6.r.3l. -0.r.3m. -8.r.3n. -8.r.3o. -0.r.3p. -3.r.3q. -6.r.3r. -4.r.3s. -8.r.3t. -8.r.3u. -6.r.3v. -6.r.3w. -6.r.3x. -a.3y.r. -2.3z.r. -5.3A.r. -2.3B.r. -0.r.3C. -6.r.3D. -6.r.3E. -a.3F.r. -5.3G.r. -6.r.3H. -6.r.3I. -3.r.3J. -3.r.3K. -6.r.3L. -0.r.3M. -6.r.3N. -3.r.3O. -6.r.3P. -0.r.3Q. -6.r.3R. -6.r.3S. -1.r.3T. -6.r.3U. -6.r.3V. -6.r.3W. -6.r.3X. -3.r.3Y. -6.r.3Z. -1.r.3+. -1.r.3/. -5.40.r. -6.r.41. -6.r.42. -6.r.43. -6.r.44. -3.r.45. -0.r.47. -6.r.48. -6.r.49. -1.r.4a. -6.r.4b. -6.r.4c. -0.r.4d. -6.r.4e. -6.r.4f. -6.r.4g. -6.r.4h. -6.r.4i. -6.r.4j. -8.r.4k. -6.r.4l. -6.r.4m. -6.r.4n. -6.r.4o. -6.r.4p. -6.r.4q. -6.r.4r. -6.r.4s. -6.r.4t. -6.r.4u. -6.r.4v. -6.r.4w. -6.r.4x. -3.r.4y. -6.r.4z. -6.r.4A. -6.r.4B. -3.r.4C. -6.r.4D. -6.r.4E. -1.r.4F. -8.r.4G. -8.r.4H. -1.r.4I. -8.r.4J. -8.r.4K. -8.r.4L. -6.r.4M. -6.r.4N. -0.r.4O. -6.r.4P. -0.r.4Q. -1.r.4R. -6.r.4S. -5.4T.r. -8.r.4U. -0.r.4V. -6.r.4W. -3.r.4Y. -1.r.4Z. -6.r.4+. -6.r.4/. -1.r.50. -0.r.51. -0.r.52. -8.r.53. -3.r.54. -8.r.55. -8.r.56. -1.r.57. -3.r.58. -8.r.59. -a.5a.r. -2.5b.r. -6.r.5c. -5.5d.r. -3.r.5e. -8.r.5f. -6.r.5g. -8.r.5h. -6.r.5i. -3.r.5j. -6.r.5k. -3.r.5l. -6.r.5m. -6.r.5n. -6.r.5o. -3.r.5p. -6.r.5q. -6.r.5r. -1.r.5s. -6.r.5t. -3.r.5u. -6.r.5v. -1.r.5w. -1.r.5y. -1.r.5z. -8.r.5A. -0.r.5B. -0.r.5C. -1.r.5D. -3.r.5E. -0.r.5F. -8.r.5G. -0.r.5H. -0.r.5I. -f.r.5K. -3.r.5L. -8.r.5M. -3.r.5N. -0.r.5O. -8.r.5P. -6.r.5Q. -0.r.5R. -6.r.5S. -3.r.5T. -0.r.5U. -8.r.5V. -0.r.5W. -8.r.5X. -8.r.5Y. -0.r.5Z. -h.5+.r. -0.r.5/. -0.r.60. -6.r.61. -2.62.r. -6.r.64. -6.r.65. -2.66.r. -6.r.67. -3.r.68. -6.r.69. -6.r.6a. -6.r.6b. -6.r.6c. -6.r.6d. -6.r.6e. -6.r.6f. -6.r.6g. -6.r.6h. -6.r.6i. -6.r.6j. -6.r.6k. -3.r.6l. -3.r.6m. -3.r.6n. -3.r.6o. -3.r.6p. -3.r.6q. -6.r.6r. -6.r.6s. -6.r.6t. -6.r.6u. -6.r.6v. -6.r.6w. -6.r.6x. -3.r.6y. -6.r.6z. -6.r.6A. -6.r.6B. -6.r.6C. -6.r.6D. -6.r.6E. -0.r.6F. -6.r.6G. -3.r.6H. -6.r.6I. -6.r.6J. -6.r.6K. -8.r.6L. -0.r.6M. -6.r.6N. -3.r.6O. -6.r.6P. -6.r.6Q. -3.r.6R. -3.r.6S. -6.r.6T. -6.r.6U. -3.r.6V. -6.r.6W. -1.r.6X. -3.r.6Y. -1.r.6Z. -0.r.6+. -1.r.6/. -1.r.70. -0.r.71. -6.r.72. -6.r.73. -1.r.74. -6.r.75. -1.r.76. -1.r.77. -6.r.78. -5.79.r. -1.r.7a. -3.r.7b. -6.r.7c. -6.r.7d. -6.r.7e. -3.r.7f. -6.r.7g. -6.r.7h. -6.r.7i. -6.r.7j. -6.r.7k. -6.r.7l. -1.r.7m. -6.r.7o. -6.r.7p. -1.r.7r. -1.r.7s. -3.r.7t. -6.r.7u. -6.r.7v. -6.r.7w. -3.r.7x. -6.r.7y. -1.r.7z. -6.r.7A. -3.r.7B. -6.r.7C. -0.r.7D. -6.r.7E. -6.r.7F. -3.r.7G. -6.r.7H. -3.r.7I. -3.r.7J. -1.r.7K. -1.r.7L. -1.r.7M. -1.r.7N. -1.r.7O. -1.r.7P. -6.r.7Q. -3.r.7R. -6.r.7S. -6.r.7T. -1.r.7U. -8.r.7V. -6.r.7W. -6.r.7X. -6.r.7Y. -3.r.7Z. -1.r.7+. -1.r.7/. -1.r.80. -1.r.81. -1.r.82. -3.r.83. -1.r.84. -1.r.85. -h.86.r. -1.r.87. -1.r.88. -8.r.89. -3.r.8a. -8.r.8b. -6.r.8c. -8.r.8d. -6.r.8e. -6.r.8f. -6.r.8g. -6.r.8h. -6.r.8i. -6.r.8j. -6.r.8k. -6.r.8l. -6.r.8m. -6.r.8n. -6.r.8o. -6.r.8p. -6.r.8q. -6.r.8r. -6.r.8s. -6.r.8t. -6.r.8u. -6.r.8v. -6.r.8w. -6.r.8x. -6.r.8y. -6.r.8z. -6.r.8A. -6.r.8B. -6.r.8C. -6.r.8D. -6.r.8E. -6.r.8F. -6.r.8G. -4.r.8H. -4.r.8I. -6.r.8J. -6.r.8K. -6.r.8L. -6.r.8M. -6.r.8N. -6.r.8O. -6.r.8P. -0.r.2p. -0.r.2p. -8.r.3b. -1.r.46. -1.r.4X. -1.r.5x. -1.r.5J. -1.r.7n. -1.r.7q. -0.s.s. -3.s.t. -0.u.s. -0.v.s. -0.w.s. -0.x.s. -0.y.s. -0.s.z. -0.s.A. -0.s.B. -0.s.C. -0.s.D. -0.s.E. -0.s.F. -0.s.G. -0.s.H. -2.I.s. -2.J.s. -0.s.K. -0.s.L. -2.M.s. -0.s.N. -0.s.O. -0.s.P. -0.s.Q. -0.s.R. -3.S.s. -b.T.s. -0.s.U. -0.s.V. -0.s.W. -0.s.X. -b.Y.s. -0.s.Z. -0.s.+. -0.s./. -0.s.10. -0.s.11. -0.s.12. -0.s.13. -0.s.14. -0.s.15. -0.s.16. -0.s.17. -3.s.18. -0.s.19. -0.s.1a. -0.s.1b. -0.s.1c. -0.s.1d. -0.s.1e. -0.s.1f. -0.s.1g. -3.s.1h. -3.s.1i. -3.s.1j. -0.s.1k. -2.1l.s. -0.s.1m. -0.s.1n. -0.s.1o. -0.s.1p. -0.s.1q. -0.s.1r. -0.s.1s. -0.s.1t. -2.1u.s. -0.s.1v. -3.s.1w. -0.s.1x. -0.s.1y. -0.s.1z. -0.s.1A. -0.1B.s. -0.s.1C. -0.s.1D. -0.s.1E. -0.s.1F. -0.s.1G. -0.s.1H. -0.s.1I. -0.s.1J. -0.s.1K. -0.s.1L. -0.s.1M. -0.s.1N. -0.s.1O. -0.s.1P. -0.s.1Q. -0.s.1R. -0.s.1S. -0.s.1T. -0.s.1U. -0.s.1V. -0.s.1W. -0.s.1X. -0.s.1Y. -0.s.1Z. -0.s.1+. -0.s.1/. -0.s.20. -0.s.21. -0.s.22. -0.s.23. -0.s.24. -0.s.25. -b.26.s. -0.s.27. -0.s.28. -6.s.29. -0.s.2a. -0.s.2b. -3.s.2c. -3.s.2d. -6.s.2f. -3.s.2g. -3.s.2h. -6.s.2i. -3.s.2j. -0.s.2l. -6.2m.s. -1.s.2o. -0.s.2p. -2.2q.s. -2.2r.s. -7.2s.s. -7.2t.s. -2.2u.s. -0.s.2v. -0.s.2w. -e.2x.s. -9.2y.s. -0.s.2z. -3.s.2A. -9.2B.s. -0.s.2C. -0.s.2D. -3.s.2E. -0.s.2F. -0.s.2G. -9.2H.s. -0.s.2I. -3.s.2J. -0.s.2K. -6.s.2L. -c.2M.s. -9.2N.s. -2.2O.s. -6.s.2P. -9.2Q.s. -8.s.2R. -7.2S.s. -0.s.2T. -1.2U.s. -9.2V.s. -1.2W.s. -0.s.2X. -8.s.2Y. -8.s.2Z. -d.2+.s. -8.s.2/. -0.s.30. -3.s.31. -8.s.32. -8.s.33. -3.s.34. -0.s.35. -b.36.s. -0.s.37. -0.s.38. -5.39.s. -6.s.3a. -6.s.3c. -0.s.3d. -1.s.3e. -3.s.3f. -8.s.3g. -3.s.3h. -0.s.3i. -1.s.3j. -3.s.3k. -6.s.3l. -0.s.3m. -3.s.3n. -3.s.3o. -0.s.3p. -8.s.3q. -6.s.3r. -4.s.3s. -8.s.3t. -8.s.3u. -6.s.3v. -3.s.3w. -6.s.3x. -a.3y.s. -2.3z.s. -5.3A.s. -2.3B.s. -0.s.3C. -3.s.3D. -6.s.3E. -5.3F.s. -5.3G.s. -3.s.3H. -3.s.3I. -3.s.3J. -6.s.3K. -3.s.3L. -0.s.3M. -6.s.3N. -6.s.3O. -6.s.3P. -0.s.3Q. -6.s.3R. -3.s.3S. -1.s.3T. -3.s.3U. -3.s.3V. -6.s.3W. -6.s.3X. -3.s.3Y. -6.s.3Z. -1.s.3+. -1.s.3/. -5.40.s. -6.s.41. -6.s.42. -6.s.43. -6.s.44. -6.s.45. -0.s.47. -6.s.48. -3.s.49. -1.s.4a. -6.s.4b. -6.s.4c. -0.s.4d. -6.s.4e. -6.s.4f. -3.s.4g. -3.s.4h. -6.s.4i. -6.s.4j. -8.s.4k. -6.s.4l. -3.s.4m. -6.s.4n. -6.s.4o. -6.s.4p. -6.s.4q. -6.s.4r. -6.s.4s. -6.s.4t. -6.s.4u. -6.s.4v. -6.s.4w. -6.s.4x. -3.s.4y. -6.s.4z. -3.s.4A. -6.s.4B. -3.s.4C. -6.s.4D. -6.s.4E. -1.s.4F. -8.s.4G. -8.s.4H. -1.s.4I. -8.s.4J. -8.s.4K. -8.s.4L. -6.s.4M. -6.s.4N. -3.s.4O. -6.s.4P. -3.s.4Q. -1.s.4R. -6.s.4S. -5.4T.s. -8.s.4U. -3.s.4V. -3.s.4W. -3.s.4Y. -1.s.4Z. -6.s.4+. -6.s.4/. -1.s.50. -0.s.51. -0.s.52. -8.s.53. -3.s.54. -8.s.55. -8.s.56. -1.s.57. -3.s.58. -8.s.59. -a.5a.s. -2.5b.s. -6.s.5c. -5.5d.s. -8.s.5e. -8.s.5f. -6.s.5g. -8.s.5h. -3.s.5i. -3.s.5j. -3.s.5k. -3.s.5l. -3.s.5m. -6.s.5n. -6.s.5o. -3.s.5p. -6.s.5q. -6.s.5r. -1.s.5s. -3.s.5t. -3.s.5u. -6.s.5v. -1.s.5w. -1.s.5y. -1.s.5z. -8.s.5A. -0.s.5B. -0.s.5C. -1.s.5D. -8.s.5E. -0.s.5F. -8.s.5G. -3.s.5H. -0.s.5I. -f.s.5K. -8.s.5L. -8.s.5M. -8.s.5N. -0.s.5O. -8.s.5P. -6.s.5Q. -0.s.5R. -6.s.5S. -3.s.5T. -0.s.5U. -8.s.5V. -0.s.5W. -8.s.5X. -8.s.5Y. -0.s.5Z. -h.5+.s. -0.s.5/. -0.s.60. -6.s.61. -2.62.s. -6.s.64. -3.s.65. -2.66.s. -6.s.67. -6.s.68. -3.s.69. -6.s.6a. -6.s.6b. -6.s.6c. -3.s.6d. -6.s.6e. -6.s.6f. -6.s.6g. -3.s.6h. -6.s.6i. -3.s.6j. -3.s.6k. -6.s.6l. -3.s.6m. -3.s.6n. -3.s.6o. -3.s.6p. -3.s.6q. -3.s.6r. -6.s.6s. -6.s.6t. -3.s.6u. -6.s.6v. -6.s.6w. -6.s.6x. -3.s.6y. -6.s.6z. -6.s.6A. -3.s.6B. -6.s.6C. -6.s.6D. -6.s.6E. -0.s.6F. -6.s.6G. -3.s.6H. -6.s.6I. -6.s.6J. -6.s.6K. -3.s.6L. -0.s.6M. -6.s.6N. -3.s.6O. -6.s.6P. -3.s.6Q. -6.s.6R. -3.s.6S. -6.s.6T. -3.s.6U. -3.s.6V. -6.s.6W. -1.s.6X. -3.s.6Y. -1.s.6Z. -0.s.6+. -1.s.6/. -1.s.70. -3.s.71. -3.s.72. -6.s.73. -1.s.74. -6.s.75. -1.s.76. -1.s.77. -3.s.78. -5.79.s. -1.s.7a. -3.s.7b. -6.s.7c. -6.s.7d. -6.s.7e. -3.s.7f. -3.s.7g. -3.s.7h. -6.s.7i. -3.s.7j. -6.s.7k. -6.s.7l. -1.s.7m. -6.s.7o. -6.s.7p. -1.s.7r. -1.s.7s. -8.s.7t. -6.s.7u. -6.s.7v. -6.s.7w. -3.s.7x. -6.s.7y. -1.s.7z. -6.s.7A. -8.s.7B. -3.s.7C. -3.s.7D. -6.s.7E. -3.s.7F. -3.s.7G. -3.s.7H. -3.s.7I. -3.s.7J. -1.s.7K. -1.s.7L. -1.s.7M. -1.s.7N. -1.s.7O. -1.s.7P. -6.s.7Q. -3.s.7R. -6.s.7S. -3.s.7T. -1.s.7U. -8.s.7V. -3.s.7W. -3.s.7X. -3.s.7Y. -3.s.7Z. -1.s.7+. -1.s.7/. -1.s.80. -1.s.81. -1.s.82. -6.s.83. -1.s.84. -1.s.85. -h.86.s. -1.s.87. -1.s.88. -3.s.89. -6.s.8a. -8.s.8b. -6.s.8c. -8.s.8d. -6.s.8e. -6.s.8f. -6.s.8g. -6.s.8h. -6.s.8i. -6.s.8j. -6.s.8k. -6.s.8l. -6.s.8m. -6.s.8n. -6.s.8o. -6.s.8p. -6.s.8q. -6.s.8r. -6.s.8s. -6.s.8t. -6.s.8u. -6.s.8v. -6.s.8w. -6.s.8x. -6.s.8y. -6.s.8z. -6.s.8A. -6.s.8B. -3.s.8C. -6.s.8D. -6.s.8E. -6.s.8F. -6.s.8G. -4.s.8H. -4.s.8I. -6.s.8J. -6.s.8K. -6.s.8L. -6.s.8M. -6.s.8N. -6.s.8O. -6.s.8P. -0.s.2p. -0.s.2p. -8.s.3b. -1.s.46. -1.s.4X. -1.s.5x. -1.s.5J. -1.s.7n. -1.s.7q. +0.x.q. +0.y.p. +4.s.t. +i.c.K. +j.S.2. +0.d.H. +0.e.G. +0.h.F. +0.i.E. +0.j.D. +0.k.C. +0.l.B. +0.m.A. +0.p.z. 0.t.t. +0.u.s. +0.v.r. +0.y.q. +i.c.L. +j.S.3. +0.e.H. +0.f.G. +0.i.F. +0.j.E. +0.k.D. +0.l.C. +0.m.B. +0.q.z. 0.u.t. -0.v.t. -0.w.t. -0.x.t. -0.y.t. -3.t.z. -0.t.A. -0.t.B. -0.t.C. -0.t.D. -0.t.E. -0.t.F. -0.t.G. -0.t.H. -2.I.t. -2.J.t. -0.t.K. -0.t.L. -2.M.t. -0.t.N. -0.t.O. -0.t.P. -0.t.Q. -0.t.R. -3.S.t. -b.T.t. -3.t.U. -0.t.V. -3.t.W. -0.t.X. -b.Y.t. -0.t.Z. -0.t.+. -3.t./. -0.t.10. -0.t.11. -0.t.12. -0.t.13. -0.t.14. -0.t.15. -0.t.16. -0.t.17. -0.t.18. -0.t.19. -0.t.1a. -0.t.1b. -3.t.1c. -0.t.1d. -0.t.1e. -0.t.1f. -3.t.1g. -0.t.1h. -0.t.1i. -3.t.1j. -0.t.1k. -2.1l.t. -0.t.1m. -0.t.1n. -0.t.1o. -0.t.1p. -0.t.1q. -0.t.1r. -3.t.1s. -0.t.1t. -2.1u.t. -0.t.1v. -0.t.1w. -0.t.1x. -0.t.1y. -0.t.1z. -0.t.1A. -0.1B.t. -0.t.1C. -0.t.1D. -0.t.1E. -0.t.1F. -0.t.1G. -0.t.1H. -0.t.1I. -0.t.1J. -0.t.1K. -0.t.1L. -0.t.1M. -0.t.1N. -0.t.1O. -0.t.1P. -0.t.1Q. -0.t.1R. -0.t.1S. -3.t.1T. -0.t.1U. -0.t.1V. -0.t.1W. -0.t.1X. -3.t.1Y. -3.t.1Z. -3.t.1+. -0.t.1/. -3.t.20. -3.t.21. -0.t.22. -0.t.23. -0.t.24. -0.t.25. -b.26.t. -3.t.27. -0.t.28. -0.t.2a. -0.t.2b. -0.t.2l. -6.2m.t. -0.t.2p. -2.2q.t. -2.2r.t. -7.2s.t. -7.2t.t. -2.2u.t. -0.t.2v. -0.t.2w. -e.2x.t. -9.2y.t. -0.t.2z. -3.t.2A. -9.2B.t. -0.t.2C. -0.t.2D. -0.t.2E. -0.t.2F. -0.t.2G. -9.2H.t. -0.t.2I. -3.t.2J. -0.t.2K. -c.2M.t. -9.2N.t. -2.2O.t. -9.2Q.t. -7.2S.t. -0.t.2T. -1.2U.t. -9.2V.t. -1.2W.t. -0.t.2X. -d.2+.t. -0.t.30. -3.t.35. -b.36.t. -0.t.37. -3.t.38. -5.39.t. -0.t.3d. -3.t.3h. -0.t.3i. -0.t.3m. -0.t.3p. -a.3v.t. -a.3y.t. -2.3z.t. -5.3A.t. -2.3B.t. -0.t.3C. -5.3F.t. -5.3G.t. -0.t.3M. -0.t.3Q. -5.40.t. -0.t.47. -0.t.4d. -0.t.4O. -0.t.4Q. -5.4T.t. -3.t.4V. -3.t.51. -0.t.52. -a.5a.t. -2.5b.t. -5.5d.t. -0.t.5B. -0.t.5C. -0.t.5F. -0.t.5H. -0.t.5I. -f.t.5K. -7.5M.t. -3.t.5O. -0.t.5R. -0.t.5U. -0.t.5W. -1.5Y.t. -0.t.5Z. -0.t.5/. -0.t.60. -2.62.t. -2.66.t. -a.6b.t. -c.6e.t. -0.t.6F. -0.t.6M. -0.t.6+. -3.t.71. -5.79.t. -0.t.7D. -0.t.2p. -0.t.2p. +0.v.s. +0.w.r. +3.I.d. +3.M.c. +3.n.A. +j.S.4. +0.f.H. +0.g.G. +0.j.F. +0.k.E. +0.l.D. +0.m.C. +0.o.A. 0.u.u. +0.v.t. +0.w.s. +0.x.r. +3.I.e. +3.J.d. +3.n.B. +j.S.5. +0.d.K. +0.g.H. +0.h.G. +0.k.F. +0.l.E. +0.m.D. +0.o.B. +0.p.A. 0.v.u. +0.w.t. +0.x.s. +0.y.r. +3.I.f. +3.J.e. +3.n.C. +j.S.6. +0.A.q. +0.d.L. +0.e.K. +0.h.H. +0.i.G. +0.l.F. +0.m.E. +0.o.C. +0.p.B. +0.r.z. 0.u.w. -0.x.u. -0.y.u. -3.u.z. -0.u.A. -0.u.B. -0.u.C. -0.u.D. -0.u.E. -0.u.F. -0.u.G. -0.u.H. -2.I.u. -2.J.u. -0.u.K. -3.u.L. -2.M.u. -0.u.N. -3.u.O. -0.u.P. -0.u.Q. -0.u.R. -3.S.u. -b.T.u. -3.u.U. -3.u.V. -0.u.W. -0.u.X. -b.Y.u. -0.u.Z. -3.u.+. -3.u./. -0.u.10. -0.u.11. -0.u.12. -0.u.13. -0.u.14. -0.u.15. -0.u.16. -0.u.17. -0.u.18. -0.u.19. -0.u.1a. -0.u.1b. -3.u.1c. -0.u.1d. -3.u.1e. -0.u.1f. -3.u.1g. -0.u.1h. -0.u.1i. -3.u.1j. -0.u.1k. -2.1l.u. -0.u.1m. -0.u.1n. -0.u.1o. -0.u.1p. -0.u.1q. -0.u.1r. -0.u.1s. -0.u.1t. -2.1u.u. -0.u.1v. -0.u.1w. -0.u.1x. -0.u.1y. -0.u.1z. -0.u.1A. -0.1B.u. -0.u.1C. -0.u.1D. -3.u.1E. -0.u.1F. -0.u.1G. -0.u.1H. -0.u.1I. -0.u.1J. -0.u.1K. -0.u.1L. -0.u.1M. -0.u.1N. -0.u.1O. -0.u.1P. -0.u.1Q. -0.u.1R. -0.u.1S. -3.u.1T. -0.u.1U. -0.u.1V. -0.u.1W. -0.u.1X. -3.u.1Y. -0.u.1Z. -0.u.1+. -0.u.1/. -0.u.20. -0.u.21. -0.u.22. -0.u.23. -0.u.24. -0.u.25. -b.26.u. -3.u.27. -0.u.28. -3.u.29. -0.u.2a. -0.u.2b. -3.u.2c. -3.u.2d. -6.u.2f. -6.u.2g. -6.u.2h. -6.u.2i. -6.u.2j. -0.u.2l. -6.2m.u. -1.u.2o. -0.u.2p. -2.2q.u. -2.2r.u. -7.2s.u. -7.2t.u. -2.2u.u. -0.u.2v. -0.u.2w. -e.2x.u. -9.2y.u. -0.u.2z. -3.u.2A. -9.2B.u. -0.u.2C. -0.u.2D. -0.u.2E. -0.u.2F. -0.u.2G. -9.2H.u. -0.u.2I. -3.u.2J. -0.u.2K. -3.u.2L. -c.2M.u. -9.2N.u. -2.2O.u. -3.u.2P. -9.2Q.u. -8.u.2R. -7.2S.u. -0.u.2T. -1.2U.u. -9.2V.u. -1.2W.u. -0.u.2X. -3.u.2Y. -3.u.2Z. -d.2+.u. -8.u.2/. -0.u.30. -3.u.31. -3.u.32. -8.u.33. -3.u.34. -3.u.35. -b.36.u. -0.u.37. -3.u.38. -5.39.u. -6.u.3a. -6.u.3c. -0.u.3d. -1.u.3e. -3.u.3f. -8.u.3g. -3.u.3h. -0.u.3i. -1.u.3j. -3.u.3k. -3.u.3l. -0.u.3m. -3.u.3n. -3.u.3o. -0.u.3p. -8.u.3q. -6.u.3r. -4.u.3s. -8.u.3t. -8.u.3u. -6.u.3v. -6.u.3w. -6.u.3x. -a.3y.u. -2.3z.u. -5.3A.u. -2.3B.u. -0.u.3C. -6.u.3D. -6.u.3E. -a.3F.u. -5.3G.u. -3.u.3H. -6.u.3I. -3.u.3J. -3.u.3K. -6.u.3L. -0.u.3M. -6.u.3N. -3.u.3O. -3.u.3P. -0.u.3Q. -6.u.3R. -3.u.3S. -1.u.3T. -6.u.3U. -3.u.3V. -6.u.3W. -6.u.3X. -3.u.3Y. -6.u.3Z. -1.u.3+. -1.u.3/. -5.40.u. -6.u.41. -6.u.42. -6.u.43. -6.u.44. -3.u.45. -0.u.47. -6.u.48. -6.u.49. -1.u.4a. -6.u.4b. -6.u.4c. -0.u.4d. -6.u.4e. -6.u.4f. -6.u.4g. -3.u.4h. -6.u.4i. -6.u.4j. -8.u.4k. -6.u.4l. -3.u.4m. -6.u.4n. -6.u.4o. -6.u.4p. -6.u.4q. -6.u.4r. -6.u.4s. -6.u.4t. -6.u.4u. -6.u.4v. -6.u.4w. -6.u.4x. -3.u.4y. -6.u.4z. -3.u.4A. -6.u.4B. -6.u.4C. -6.u.4D. -6.u.4E. -1.u.4F. -8.u.4G. -3.u.4H. -1.u.4I. -8.u.4J. -8.u.4K. -8.u.4L. -6.u.4M. -6.u.4N. -0.u.4O. -6.u.4P. -3.u.4Q. -1.u.4R. -6.u.4S. -5.4T.u. -8.u.4U. -3.u.4V. -3.u.4W. -3.u.4Y. -1.u.4Z. -3.u.4+. -3.u.4/. -1.u.50. -3.u.51. -0.u.52. -8.u.53. -3.u.54. -3.u.55. -3.u.56. -1.u.57. -3.u.58. -8.u.59. -a.5a.u. -2.5b.u. -6.u.5c. -5.5d.u. -3.u.5e. -8.u.5f. -6.u.5g. -8.u.5h. -3.u.5i. -3.u.5j. -3.u.5k. -3.u.5l. -3.u.5m. -3.u.5n. -6.u.5o. -3.u.5p. -6.u.5q. -6.u.5r. -1.u.5s. -3.u.5t. -3.u.5u. -3.u.5v. -1.u.5w. -1.u.5y. -1.u.5z. -8.u.5A. -0.u.5B. -0.u.5C. -1.u.5D. -8.u.5E. -0.u.5F. -3.u.5G. -0.u.5H. -0.u.5I. -f.u.5K. -8.u.5L. -8.u.5M. -8.u.5N. -3.u.5O. -8.u.5P. -6.u.5Q. -0.u.5R. -3.u.5S. -6.u.5T. -0.u.5U. -3.u.5V. -0.u.5W. -8.u.5X. -8.u.5Y. -0.u.5Z. -h.5+.u. -0.u.5/. -0.u.60. -6.u.61. -2.62.u. -6.u.64. -6.u.65. -2.66.u. -3.u.67. -3.u.68. -3.u.69. -3.u.6a. -6.u.6b. -6.u.6c. -6.u.6d. -6.u.6e. -6.u.6f. -6.u.6g. -6.u.6h. -3.u.6i. -6.u.6j. -3.u.6k. -6.u.6l. -3.u.6m. -3.u.6n. -3.u.6o. -3.u.6p. -3.u.6q. -6.u.6r. -6.u.6s. -6.u.6t. -6.u.6u. -3.u.6v. -3.u.6w. -6.u.6x. -3.u.6y. -3.u.6z. -6.u.6A. -6.u.6B. -6.u.6C. -6.u.6D. -6.u.6E. -0.u.6F. -6.u.6G. -3.u.6H. -6.u.6I. -6.u.6J. -6.u.6K. -8.u.6L. -0.u.6M. -6.u.6N. -6.u.6O. -6.u.6P. -6.u.6Q. -3.u.6R. -3.u.6S. -6.u.6T. -6.u.6U. -3.u.6V. -6.u.6W. -1.u.6X. -6.u.6Y. -1.u.6Z. -0.u.6+. -1.u.6/. -1.u.70. -3.u.71. -3.u.72. -6.u.73. -1.u.74. -6.u.75. -1.u.76. -1.u.77. -3.u.78. -5.79.u. -1.u.7a. -3.u.7b. -6.u.7c. -3.u.7d. -6.u.7e. -3.u.7f. -3.u.7g. -3.u.7h. -6.u.7i. -3.u.7j. -6.u.7k. -6.u.7l. -1.u.7m. -6.u.7o. -6.u.7p. -1.u.7r. -1.u.7s. -8.u.7t. -6.u.7u. -6.u.7v. -6.u.7w. -3.u.7x. -6.u.7y. -1.u.7z. -6.u.7A. -3.u.7B. -3.u.7C. -0.u.7D. -6.u.7E. -3.u.7F. -3.u.7G. -3.u.7H. -3.u.7I. -3.u.7J. -1.u.7K. -1.u.7L. -1.u.7M. -1.u.7N. -1.u.7O. -1.u.7P. -6.u.7Q. -3.u.7R. -6.u.7S. -6.u.7T. -1.u.7U. -3.u.7V. -3.u.7W. -6.u.7X. -3.u.7Y. -3.u.7Z. -1.u.7+. -1.u.7/. -1.u.80. -1.u.81. -1.u.82. -3.u.83. -1.u.84. -1.u.85. -h.86.u. -1.u.87. -1.u.88. -8.u.89. -3.u.8a. -3.u.8b. -6.u.8c. -8.u.8d. -6.u.8e. -6.u.8f. -6.u.8g. -6.u.8h. -6.u.8i. -6.u.8j. -6.u.8k. -6.u.8l. -6.u.8m. -6.u.8n. -6.u.8o. -6.u.8p. -6.u.8q. -6.u.8r. -6.u.8s. -6.u.8t. -6.u.8u. -6.u.8v. -6.u.8w. -6.u.8x. -6.u.8y. -6.u.8z. -6.u.8A. -6.u.8B. -6.u.8C. -6.u.8D. -6.u.8E. -6.u.8F. -6.u.8G. -4.u.8H. -4.u.8I. -6.u.8J. -6.u.8K. -6.u.8L. -6.u.8M. -6.u.8N. -6.u.8O. -6.u.8P. -0.u.2p. -0.u.2p. -3.u.3b. -1.u.46. -1.u.4X. -1.u.5x. -1.u.5J. -1.u.7n. -1.u.7q. 0.v.v. +0.x.t. +0.y.s. +3.I.g. +3.J.f. +3.n.D. +i.c.N. +j.S.7. +0.B.q. +0.e.L. +0.f.K. +0.i.H. +0.o.D. +0.p.C. +0.s.z. 0.v.w. +0.x.u. +0.y.t. +3.I.h. +3.J.g. +3.M.d. +3.n.E. +4.j.G. +4.m.F. +i.c.O. +j.S.8. +0.C.q. +0.f.L. +0.g.K. +0.j.H. +0.k.G. +0.o.E. +0.p.D. 0.v.x. +0.y.u. +3.I.i. +3.J.h. +3.M.e. +3.n.F. +4.t.z. +4.w.w. +i.c.P. +j.S.9. +0.D.q. +0.g.L. +0.G.l. +0.h.K. +0.k.H. +0.o.F. +0.p.E. +0.r.A. 0.v.y. -3.v.z. -0.v.A. -0.v.B. -0.v.C. -0.v.D. -0.v.E. -0.v.F. -0.v.G. -0.v.H. -2.I.v. -2.J.v. -0.v.K. -0.v.L. -2.M.v. -0.v.N. -3.v.O. -0.v.P. -0.v.Q. -0.v.R. -3.S.v. -b.T.v. -3.v.U. -3.v.V. -0.v.W. -0.v.X. -b.Y.v. -0.v.Z. -0.v.+. -3.v./. -0.v.10. -0.v.11. -0.v.12. -0.v.13. -0.v.14. -0.v.15. -0.v.16. -0.v.17. -0.v.18. -0.v.19. -0.v.1a. -0.v.1b. -0.v.1c. -0.v.1d. -0.v.1e. -0.v.1f. -3.v.1g. -0.v.1h. -0.v.1i. -3.v.1j. -0.v.1k. -2.1l.v. -0.v.1m. -0.v.1n. -0.v.1o. -0.v.1p. -0.v.1q. -0.v.1r. -0.v.1s. -0.v.1t. -2.1u.v. -0.v.1v. -0.v.1w. -0.v.1x. -0.v.1y. -0.v.1z. -0.v.1A. -0.1B.v. -0.v.1C. -0.v.1D. -3.v.1E. -0.v.1F. -0.v.1G. -0.v.1H. -0.v.1I. -0.v.1J. -0.v.1K. -0.v.1L. -0.v.1M. -0.v.1N. -0.v.1O. -0.v.1P. -0.v.1Q. -0.v.1R. -0.v.1S. -3.v.1T. -0.v.1U. -0.v.1V. -0.v.1W. -0.v.1X. -0.v.1Y. -0.v.1Z. -0.v.1+. -0.v.1/. -0.v.20. -0.v.21. -0.v.22. -0.v.23. -0.v.24. -0.v.25. -b.26.v. -0.v.27. -4.v.28. -0.v.2a. -0.v.2b. -0.v.2l. -6.2m.v. -0.v.2p. -2.2q.v. -2.2r.v. -7.2s.v. -7.2t.v. -2.2u.v. -0.v.2v. -3.v.2w. -e.2x.v. -9.2y.v. -0.v.2z. -3.v.2A. -9.2B.v. -0.v.2C. -0.v.2D. -0.v.2E. -0.v.2F. -0.v.2G. -9.2H.v. -0.v.2I. -3.v.2J. -0.v.2K. -c.2M.v. -9.2N.v. -2.2O.v. -9.2Q.v. -7.2S.v. -0.v.2T. -1.2U.v. -9.2V.v. -1.2W.v. -0.v.2X. -d.2+.v. -0.v.30. -3.v.35. -b.36.v. -0.v.37. -0.v.38. -5.39.v. -0.v.3d. -3.v.3h. -0.v.3i. -0.v.3m. -0.v.3p. -a.3v.v. -a.3y.v. -2.3z.v. -5.3A.v. -2.3B.v. -0.v.3C. -a.3F.v. -5.3G.v. -0.v.3M. -0.v.3Q. -5.40.v. -0.v.47. -0.v.4d. -0.v.4O. -0.v.4Q. -5.4T.v. -3.v.4V. -3.v.51. -0.v.52. -a.5a.v. -2.5b.v. -5.5d.v. -0.v.5B. -0.v.5C. -0.v.5F. -0.v.5H. -0.v.5I. -f.v.5K. -7.5M.v. -3.v.5O. -0.v.5R. -0.v.5U. -0.v.5W. -1.5Y.v. -0.v.5Z. -0.v.5/. -0.v.60. -2.62.v. -2.66.v. -a.6b.v. -c.6e.v. -0.v.6F. -0.v.6M. -0.v.6+. -0.v.71. -5.79.v. -0.v.7D. -0.v.2p. -0.v.2p. -3.w.w. 0.x.w. -0.y.w. -0.w.z. -0.w.A. -0.w.B. -0.w.C. -0.w.D. -0.w.E. -0.w.F. -0.w.G. -0.w.H. -2.I.w. -2.J.w. -0.w.K. -3.w.L. -2.M.w. -0.w.N. -3.w.O. -0.w.P. -0.w.Q. -0.w.R. -3.S.w. -b.T.w. -3.w.U. -3.w.V. -0.w.W. -0.w.X. -b.Y.w. -0.w.Z. -3.w.+. -3.w./. -0.w.10. -0.w.11. -0.w.12. -0.w.13. -0.w.14. -0.w.15. -0.w.16. -0.w.17. -0.w.18. -0.w.19. -0.w.1a. -0.w.1b. -0.w.1c. -0.w.1d. -0.w.1e. -0.w.1f. -0.w.1g. -3.w.1h. -0.w.1i. -3.w.1j. -0.w.1k. -2.1l.w. -0.w.1m. -0.w.1n. -0.w.1o. -0.w.1p. -0.w.1q. -0.w.1r. -0.w.1s. -0.w.1t. -2.1u.w. -0.w.1v. -0.w.1w. -0.w.1x. -0.w.1y. -0.w.1z. -0.w.1A. -0.1B.w. -0.w.1C. -0.w.1D. -3.w.1E. -0.w.1F. -0.w.1G. -0.w.1H. -0.w.1I. -0.w.1J. -0.w.1K. -0.w.1L. -0.w.1M. -0.w.1N. -0.w.1O. -0.w.1P. -0.w.1Q. -0.w.1R. -3.w.1S. -0.w.1T. -0.w.1U. -0.w.1V. -0.w.1W. -0.w.1X. -3.w.1Y. -0.w.1Z. -0.w.1+. -0.w.1/. -0.w.20. -0.w.21. -0.w.22. -0.w.23. -0.w.24. -0.w.25. -b.26.w. -3.w.27. -4.w.28. -0.w.2a. -0.w.2b. -0.w.2l. -6.2m.w. -0.w.2p. -2.2q.w. -2.2r.w. -7.2s.w. -7.2t.w. -2.2u.w. -0.w.2v. -0.w.2w. -e.2x.w. -9.2y.w. -0.w.2z. -3.w.2A. -9.2B.w. -0.w.2C. -0.w.2D. -0.w.2E. -0.w.2F. -0.w.2G. -9.2H.w. -0.w.2I. -3.w.2J. -0.w.2K. -c.2M.w. -9.2N.w. -2.2O.w. -9.2Q.w. -7.2S.w. -0.w.2T. -1.2U.w. -9.2V.w. -1.2W.w. -0.w.2X. -b.2+.w. -0.w.30. -0.w.35. -b.36.w. -0.w.37. -0.w.38. -5.39.w. -0.w.3d. -3.w.3h. -0.w.3i. -0.w.3m. -0.w.3p. -a.3v.w. -a.3y.w. -2.3z.w. -5.3A.w. -2.3B.w. -0.w.3C. -a.3F.w. -5.3G.w. -0.w.3M. -0.w.3Q. -5.40.w. -0.w.47. -0.w.4d. -0.w.4O. -3.w.4Q. -5.4T.w. -3.w.4V. -3.w.51. -0.w.52. -a.5a.w. -2.5b.w. -5.5d.w. -0.w.5B. -0.w.5C. -0.w.5F. -3.w.5H. -0.w.5I. -f.w.5K. -7.5M.w. -0.w.5O. -0.w.5R. -0.w.5U. -0.w.5W. -1.5Y.w. -0.w.5Z. -0.w.5/. -0.w.60. -2.62.w. -2.66.w. -a.6b.w. -c.6e.w. -0.w.6F. -0.w.6M. -0.w.6+. -3.w.71. -5.79.w. -0.w.7D. -0.w.2p. -0.w.2p. +3.I.j. +3.J.i. +3.M.f. +4.u.z. +i.c.Q. +j.S.a. +0.d.N. +0.E.q. +0.G.m. +0.i.K. +0.l.H. +0.p.F. +0.r.B. +0.s.A. 0.x.x. +0.y.w. +3.I.k. +3.J.j. +3.M.g. +4.h.L. +4.v.z. +i.c.R. +j.S.b. +0.d.O. +0.e.N. +0.i.L. +0.j.K. +0.m.H. +0.r.C. +0.s.B. +0.t.A. +0.w.z. 0.y.x. -3.x.z. -0.x.A. -0.x.B. -0.x.C. -0.x.D. -0.x.E. -0.x.F. -0.x.G. -0.x.H. -2.I.x. -2.J.x. -0.x.K. -3.x.L. -2.M.x. -0.x.N. -3.x.O. -0.x.P. -0.x.Q. -0.x.R. -3.S.x. -b.T.x. -3.x.U. -0.x.V. -0.x.W. -0.x.X. -b.Y.x. -0.x.Z. -0.x.+. -3.x./. -0.x.10. -0.x.11. -0.x.12. -0.x.13. -0.x.14. -0.x.15. -0.x.16. -0.x.17. -0.x.18. -0.x.19. -0.x.1a. -0.x.1b. -3.x.1c. -0.x.1d. -0.x.1e. -0.x.1f. -3.x.1g. -0.x.1h. -0.x.1i. -3.x.1j. -0.x.1k. -2.1l.x. -0.x.1m. -0.x.1n. -0.x.1o. -0.x.1p. -0.x.1q. -0.x.1r. -0.x.1s. -0.x.1t. -2.1u.x. -0.x.1v. -0.x.1w. -0.x.1x. -0.x.1y. -0.x.1z. -0.x.1A. -0.1B.x. -0.x.1C. -0.x.1D. -3.x.1E. -0.x.1F. -0.x.1G. -0.x.1H. -0.x.1I. -0.x.1J. -0.x.1K. -0.x.1L. -0.x.1M. -0.x.1N. -0.x.1O. -0.x.1P. -0.x.1Q. -0.x.1R. -3.x.1S. -3.x.1T. -0.x.1U. -0.x.1V. -0.x.1W. -0.x.1X. -3.x.1Y. -0.x.1Z. -0.x.1+. -0.x.1/. -0.x.20. -0.x.21. -0.x.22. -0.x.23. -0.x.24. -0.x.25. -b.26.x. -3.x.27. -4.x.28. -0.x.2a. -0.x.2b. -0.x.2l. -6.2m.x. -0.x.2p. -2.2q.x. -2.2r.x. -7.2s.x. -7.2t.x. -2.2u.x. -0.x.2v. -0.x.2w. -e.2x.x. -9.2y.x. -0.x.2z. -3.x.2A. -9.2B.x. -0.x.2C. -0.x.2D. -0.x.2E. -0.x.2F. -0.x.2G. -9.2H.x. -0.x.2I. -3.x.2J. -0.x.2K. -c.2M.x. -9.2N.x. -2.2O.x. -9.2Q.x. -7.2S.x. -0.x.2T. -1.2U.x. -9.2V.x. -1.2W.x. -0.x.2X. -d.2+.x. -0.x.30. -3.x.35. -b.36.x. -0.x.37. -3.x.38. -5.39.x. -0.x.3d. -3.x.3h. -0.x.3i. -0.x.3m. -0.x.3p. -a.3v.x. -a.3y.x. -2.3z.x. -5.3A.x. -2.3B.x. -0.x.3C. -a.3F.x. -5.3G.x. -0.x.3M. -0.x.3Q. -5.40.x. -0.x.47. -0.x.4d. -0.x.4O. -3.x.4Q. -5.4T.x. -3.x.4V. -3.x.51. -0.x.52. -a.5a.x. -2.5b.x. -5.5d.x. -0.x.5B. -0.x.5C. -0.x.5F. -0.x.5H. -0.x.5I. -0.x.5K. -7.5M.x. -3.x.5O. -0.x.5R. -0.x.5U. -0.x.5W. -1.5Y.x. -0.x.5Z. -0.x.5/. -0.x.60. -2.62.x. -2.66.x. -a.6b.x. -c.6e.x. -0.x.6F. -0.x.6M. -0.x.6+. -3.x.71. -5.79.x. -0.x.7D. -0.x.2p. -0.x.2p. +3.I.l. +3.J.k. +3.M.h. +3.n.G. +4.q.F. +4.S.c. +0.d.P. +0.e.O. +0.f.N. +0.G.o. +0.j.L. +0.k.K. +0.r.D. +0.s.C. +0.t.B. +0.u.A. 0.y.y. -3.y.z. -0.y.A. -0.y.B. -0.y.C. -0.y.D. -0.y.E. -0.y.F. -0.y.G. -0.y.H. -2.I.y. -2.J.y. -0.y.K. -3.y.L. -2.M.y. -0.y.N. -3.y.O. -0.y.P. -0.y.Q. -0.y.R. -3.S.y. -b.T.y. -0.y.U. -0.y.V. -0.y.W. -0.y.X. -b.Y.y. -0.y.Z. -0.y.+. -3.y./. -0.y.10. -0.y.11. -0.y.12. -0.y.13. -0.y.14. -0.y.15. -0.y.16. -0.y.17. -0.y.18. -0.y.19. -0.y.1a. -0.y.1b. -0.y.1c. -0.y.1d. -0.y.1e. -0.y.1f. -3.y.1g. -0.y.1h. -0.y.1i. -3.y.1j. -0.y.1k. -2.1l.y. -0.y.1m. -0.y.1n. -0.y.1o. -0.y.1p. -0.y.1q. -0.y.1r. -0.y.1s. -0.y.1t. -2.1u.y. -0.y.1v. -0.y.1w. -0.y.1x. -0.y.1y. -0.y.1z. -0.y.1A. -0.1B.y. -0.y.1C. -0.y.1D. -0.y.1E. -0.y.1F. -0.y.1G. -0.y.1H. -0.y.1I. -0.y.1J. -0.y.1K. -0.y.1L. -0.y.1M. -0.y.1N. -0.y.1O. -0.y.1P. -0.y.1Q. -0.y.1R. -0.y.1S. -3.y.1T. -0.y.1U. -0.y.1V. -0.y.1W. -0.y.1X. -0.y.1Y. -0.y.1Z. -0.y.1+. -0.y.1/. -0.y.20. -0.y.21. -0.y.22. -0.y.23. -0.y.24. -0.y.25. -b.26.y. -0.y.27. -4.y.28. -0.y.2a. -0.y.2b. -0.y.2l. -6.2m.y. -0.y.2p. -2.2q.y. -2.2r.y. -7.2s.y. -7.2t.y. -2.2u.y. -0.y.2v. -0.y.2w. -e.2x.y. -9.2y.y. -0.y.2z. -3.y.2A. -9.2B.y. -0.y.2C. -0.y.2D. -0.y.2E. -0.y.2F. -0.y.2G. -9.2H.y. -0.y.2I. -3.y.2J. -0.y.2K. -c.2M.y. -9.2N.y. -2.2O.y. -9.2Q.y. -7.2S.y. -0.y.2T. -1.2U.y. -9.2V.y. -1.2W.y. -0.y.2X. -d.2+.y. -0.y.30. -3.y.35. -b.36.y. -0.y.37. -0.y.38. -5.39.y. -0.y.3d. -3.y.3h. -0.y.3i. -0.y.3m. -0.y.3p. -a.3v.y. -a.3y.y. -2.3z.y. -5.3A.y. -2.3B.y. -0.y.3C. -a.3F.y. -5.3G.y. -0.y.3M. -0.y.3Q. -5.40.y. -0.y.47. -0.y.4d. -0.y.4O. -0.y.4Q. -5.4T.y. -3.y.4V. -3.y.51. -0.y.52. -a.5a.y. -2.5b.y. -5.5d.y. -0.y.5B. -0.y.5C. -0.y.5F. -0.y.5H. -0.y.5I. -f.y.5K. -7.5M.y. -3.y.5O. -0.y.5R. -0.y.5U. -0.y.5W. -1.5Y.y. -0.y.5Z. -0.y.5/. -0.y.60. -2.62.y. -2.66.y. -a.6b.y. -c.6e.y. -0.y.6F. -0.y.6M. -0.y.6+. -0.y.71. -5.79.y. -0.y.7D. -0.y.2p. -0.y.2p. +3.I.m. +3.J.l. +3.M.i. +3.n.H. +4.x.z. +i.c.T. +0.d.Q. +0.e.P. +0.f.O. +0.g.N. +0.k.L. +0.l.K. +0.o.H. +0.p.G. +0.r.E. +0.s.D. +0.t.C. +0.u.B. +0.v.A. +3.I.n. +3.J.m. +3.M.j. +4.y.z. +i.c.U. +0.d.R. +0.e.Q. +0.f.P. +0.g.O. +0.G.q. +0.h.N. +0.l.L. +0.m.K. +0.p.H. +0.r.F. +0.s.E. +0.t.D. +0.u.C. +0.v.B. +0.w.A. 0.z.z. +3.I.o. +3.J.n. +3.M.k. +i.c.V. +0.e.R. +0.f.Q. +0.g.P. +0.H.q. +0.i.N. +0.m.L. +0.s.F. +0.t.E. +0.u.D. +0.v.C. +0.w.B. +0.x.A. +3.I.p. +3.J.o. +3.M.l. +3.n.K. +4.h.O. +4.S.d. +i.c.W. +0.f.R. +0.g.Q. +0.h.P. +0.i.O. +0.j.N. +0.o.K. +0.t.F. +0.u.E. +0.v.D. +0.w.C. +0.x.B. +0.y.A. +3.I.q. +3.J.p. +3.M.m. +3.n.L. +4.S.e. +c.T.d. +i.c.X. 0.A.z. +0.d.U. +0.g.R. +0.h.Q. +0.i.P. +0.j.O. +0.k.N. +0.o.L. +0.p.K. +0.r.G. +0.u.F. +0.v.E. +0.w.D. +0.x.C. +0.y.B. +3.J.q. +3.n.M. +4.S.f. +c.T.e. +i.c.Y. 0.B.z. +0.d.V. +0.e.U. +0.h.R. +0.i.Q. +0.j.P. +0.k.O. +0.K.q. +0.l.N. +0.p.L. +0.r.H. +0.s.G. +0.v.F. +0.w.E. +0.x.D. +0.y.C. +3.M.o. +4.S.g. +c.T.f. +i.c.Z. 0.C.z. -0.D.z. -0.E.z. -0.F.z. -3.G.z. -0.H.z. -2.I.z. -2.J.z. -0.K.z. -0.L.z. -2.M.z. -0.N.z. -0.O.z. -0.P.z. -0.Q.z. -0.R.z. -3.S.z. -b.T.z. -0.U.z. -0.V.z. -0.W.z. -0.X.z. -b.Y.z. -3.Z.z. -0.+.z. -0./.z. -0.10.z. -0.11.z. -0.12.z. -0.13.z. -0.14.z. -0.15.z. -0.16.z. -0.17.z. -0.18.z. -0.19.z. -0.1a.z. -0.1b.z. -0.1c.z. -0.1d.z. -0.1e.z. -0.1f.z. -f.1g.z. -0.1h.z. -0.1i.z. -0.1j.z. -0.1k.z. -2.1l.z. -0.1m.z. -0.1n.z. -0.1o.z. -0.1p.z. -0.1q.z. -0.1r.z. -0.1s.z. -0.1t.z. -2.1u.z. -0.1v.z. -0.1w.z. -0.1x.z. -0.1y.z. -0.1z.z. -0.1A.z. -3.1B.z. -0.1C.z. -0.1D.z. -0.1E.z. -0.1F.z. -0.1G.z. -0.1H.z. -0.1I.z. -0.1J.z. -0.1K.z. -0.1L.z. -0.1M.z. -0.1N.z. -0.1O.z. -0.1P.z. -0.1Q.z. -0.1R.z. -0.1S.z. -d.1T.z. -0.1U.z. -0.1V.z. -0.z.1W. -0.1X.z. -0.1Y.z. -0.1Z.z. -d.1+.z. -0.1/.z. -0.20.z. -d.21.z. -0.22.z. -0.23.z. -0.24.z. -0.25.z. -b.26.z. -0.27.z. -4.28.z. -0.2a.z. -3.2b.z. -0.2l.z. -6.2m.z. -d.2p.z. -2.2q.z. -2.2r.z. -7.2s.z. -7.2t.z. -2.2u.z. -d.2v.z. -0.2w.z. -e.2x.z. -9.2y.z. -3.2z.z. -d.2A.z. -9.2B.z. -3.2C.z. -d.2D.z. -d.2E.z. -0.2F.z. -0.2G.z. -9.2H.z. -d.2I.z. -d.2J.z. -0.2K.z. -c.2M.z. -9.2N.z. -2.2O.z. -9.2Q.z. -7.2S.z. -d.2T.z. -1.2U.z. -9.2V.z. -1.2W.z. -0.2X.z. -b.2+.z. -d.30.z. -0.35.z. -b.36.z. -0.z.37. -d.38.z. -5.39.z. -5.3d.z. -0.3h.z. -5.3i.z. -0.z.3m. -5.3p.z. -a.3v.z. -a.3y.z. -2.3z.z. -5.3A.z. -2.3B.z. -5.3C.z. -a.3F.z. -5.3G.z. -0.z.3M. -0.z.3Q. -5.40.z. -0.z.47. -5.4d.z. -0.z.4O. -3.4Q.z. -5.4T.z. -3.z.4V. -5.51.z. -0.52.z. -a.5a.z. -2.5b.z. -5.5d.z. -5.5B.z. -5.5C.z. -5.5F.z. -0.5H.z. -0.5I.z. -f.z.5K. -7.5M.z. -5.5O.z. -5.5R.z. -0.z.5U. -3.5W.z. -1.5Y.z. -5.5Z.z. -5.5/.z. -0.60.z. -2.62.z. -2.66.z. -a.6b.z. -c.6e.z. -0.z.6F. -0.6M.z. -d.6+.z. -d.71.z. -5.79.z. -3.z.7D. -d.2p.z. -d.2p.z. +0.d.W. +0.e.V. +0.f.U. +0.i.R. +0.k.P. +0.l.O. +0.L.q. +0.m.N. +0.s.H. +0.t.G. +0.w.F. +0.x.E. +0.y.D. +3.I.r. +3.M.p. +4.j.Q. +4.S.h. +c.T.g. 0.A.A. +0.d.X. +0.D.z. +0.e.W. +0.f.V. +0.g.U. +0.j.R. +0.k.Q. +0.l.P. +0.m.O. +0.t.H. +0.u.G. +0.x.F. +0.y.E. +3.I.s. +3.J.r. +3.M.q. +3.n.N. +4.S.i. +c.T.h. 0.A.B. +0.e.X. +0.E.z. +0.f.W. +0.g.V. +0.k.R. +0.l.Q. +0.m.P. +0.o.N. +0.r.K. +0.u.H. +0.v.G. +0.y.F. +3.I.t. +3.J.s. +3.n.O. +4.h.U. +4.S.j. +c.T.i. +c.Y.d. 0.A.C. -0.A.D. -0.A.E. -0.A.F. -0.G.A. -0.A.H. -2.I.A. -2.J.A. -0.A.K. -0.A.L. -2.M.A. -0.A.N. -0.A.O. -0.A.P. -0.A.Q. -0.A.R. -3.S.A. -b.T.A. -0.A.U. -0.A.V. -0.A.W. -0.A.X. -b.Y.A. -0.A.Z. -0.A.+. -0.A./. -0.A.10. -0.A.11. -0.A.12. -0.A.13. -0.A.14. -0.A.15. -0.A.16. -0.A.17. -3.A.18. -0.A.19. -0.A.1a. -0.A.1b. -0.A.1c. -3.A.1d. -0.A.1e. -0.A.1f. -0.A.1g. -3.A.1h. -3.A.1i. -3.A.1j. -0.A.1k. -2.1l.A. -0.A.1m. -0.A.1n. -0.A.1o. -0.A.1p. -0.A.1q. -0.A.1r. -0.A.1s. -0.A.1t. -2.1u.A. -0.A.1v. -3.A.1w. -0.A.1x. -0.A.1y. -0.A.1z. -0.A.1A. -0.1B.A. -0.A.1C. -0.A.1D. -0.A.1E. -0.A.1F. -0.A.1G. -0.A.1H. -0.A.1I. -0.A.1J. -0.A.1K. -0.A.1L. -0.A.1M. -0.A.1N. -0.A.1O. -0.A.1P. -3.A.1Q. -0.A.1R. -0.A.1S. -0.A.1T. -0.A.1U. -0.A.1V. -0.A.1W. -0.A.1X. -0.A.1Y. -0.A.1Z. -0.A.1+. -0.A.1/. -0.A.20. -0.A.21. -0.A.22. -0.A.23. -0.A.24. -0.A.25. -b.26.A. -0.A.27. -4.A.28. -0.A.2a. -0.A.2b. -0.A.2l. -6.2m.A. -0.A.2p. -2.2q.A. -2.2r.A. -7.2s.A. -7.2t.A. -2.2u.A. -3.A.2v. -0.A.2w. -e.2x.A. -9.2y.A. -0.A.2z. -3.A.2A. -9.2B.A. -0.A.2C. -0.A.2D. -3.A.2E. -0.A.2F. -0.A.2G. -9.2H.A. -0.A.2I. -3.A.2J. -0.A.2K. -c.2M.A. -9.2N.A. -2.2O.A. -9.2Q.A. -7.2S.A. -0.A.2T. -1.2U.A. -9.2V.A. -1.2W.A. -0.A.2X. -d.2+.A. -0.A.30. -0.A.35. -b.36.A. -0.A.37. -3.A.38. -5.39.A. -0.A.3d. -3.A.3h. -0.A.3i. -0.A.3m. -0.A.3p. -a.3v.A. -a.3y.A. -2.3z.A. -5.3A.A. -2.3B.A. -0.A.3C. -a.3F.A. -5.3G.A. -0.A.3M. -0.A.3Q. -5.40.A. -0.A.47. -0.A.4d. -3.A.4O. -3.A.4Q. -5.4T.A. -3.A.4V. -0.A.51. -0.A.52. -a.5a.A. -2.5b.A. -5.5d.A. -0.A.5B. -0.A.5C. -0.A.5F. -3.A.5H. -0.A.5I. -f.A.5K. -7.5M.A. -0.A.5O. -3.A.5R. -0.A.5U. -0.A.5W. -1.5Y.A. -0.A.5Z. -0.A.5/. -0.A.60. -2.62.A. -2.66.A. -a.6b.A. -c.6e.A. -0.A.6F. -0.A.6M. -0.A.6+. -3.A.71. -5.79.A. -3.A.7D. -0.A.2p. -0.A.2p. 0.B.B. +0.f.X. +0.F.z. +0.g.W. +0.i.U. +0.l.R. +0.m.Q. +0.o.O. +0.p.N. +0.r.L. +0.s.K. +0.v.H. +0.w.G. +3.I.u. +3.J.t. +3.n.P. +4.d.Z. +4.h.V. +4.S.k. +c.T.j. +c.Y.e. +i.c.+. +0.A.D. 0.B.C. +0.g.X. +0.i.V. +0.m.R. +0.N.q. +0.o.P. +0.p.O. +0.s.L. +0.t.K. +0.w.H. +0.x.G. +3.I.v. +3.J.u. +3.M.r. +3.n.Q. +4.e.Z. +4.h.W. +4.j.U. +4.S.l. +c.T.k. +c.Y.f. +i.c./. +0.A.E. 0.B.D. -0.B.E. -0.B.F. -0.G.B. -0.B.H. -2.I.B. -2.J.B. -0.B.K. -0.B.L. -2.M.B. -0.B.N. -0.B.O. -0.B.P. -0.B.Q. -0.B.R. -3.S.B. -b.T.B. -0.B.U. -0.B.V. -0.B.W. -0.B.X. -b.Y.B. -0.B.Z. -0.B.+. -0.B./. -0.B.10. -0.B.11. -0.B.12. -0.B.13. -0.B.14. -0.B.15. -0.B.16. -0.B.17. -0.B.18. -0.B.19. -0.B.1a. -0.B.1b. -0.B.1c. -3.B.1d. -0.B.1e. -0.B.1f. -0.B.1g. -3.B.1h. -3.B.1i. -3.B.1j. -0.B.1k. -2.1l.B. -0.B.1m. -0.B.1n. -0.B.1o. -0.B.1p. -0.B.1q. -0.B.1r. -0.B.1s. -0.B.1t. -2.1u.B. -0.B.1v. -3.B.1w. -0.B.1x. -0.B.1y. -0.B.1z. -0.B.1A. -0.1B.B. -0.B.1C. -0.B.1D. -0.B.1E. -0.B.1F. -0.B.1G. -0.B.1H. -0.B.1I. -0.B.1J. -0.B.1K. -0.B.1L. -0.B.1M. -0.B.1N. -0.B.1O. -0.B.1P. -0.B.1Q. -0.B.1R. -0.B.1S. -0.B.1T. -0.B.1U. -0.B.1V. -0.B.1W. -0.B.1X. -0.B.1Y. -0.B.1Z. -0.B.1+. -0.B.1/. -0.B.20. -0.B.21. -0.B.22. -0.B.23. -0.B.24. -0.B.25. -b.26.B. -0.B.27. -4.B.28. -0.B.2a. -0.B.2b. -0.B.2l. -6.2m.B. -0.B.2p. -2.2q.B. -2.2r.B. -7.2s.B. -7.2t.B. -2.2u.B. -0.B.2v. -0.B.2w. -e.2x.B. -9.2y.B. -0.B.2z. -3.B.2A. -9.2B.B. -0.B.2C. -0.B.2D. -3.B.2E. -0.B.2F. -0.B.2G. -9.2H.B. -0.B.2I. -3.B.2J. -0.B.2K. -c.2M.B. -9.2N.B. -2.2O.B. -9.2Q.B. -7.2S.B. -0.B.2T. -1.2U.B. -9.2V.B. -1.2W.B. -0.B.2X. -b.2+.B. -0.B.30. -0.B.35. -b.36.B. -0.B.37. -0.B.38. -5.39.B. -0.B.3d. -3.B.3h. -0.B.3i. -0.B.3m. -0.B.3p. -a.3v.B. -a.3y.B. -2.3z.B. -5.3A.B. -2.3B.B. -0.B.3C. -a.3F.B. -5.3G.B. -0.B.3M. -0.B.3Q. -5.40.B. -0.B.47. -0.B.4d. -0.B.4O. -3.B.4Q. -5.4T.B. -3.B.4V. -0.B.51. -0.B.52. -a.5a.B. -2.5b.B. -5.5d.B. -0.B.5B. -0.B.5C. -0.B.5F. -3.B.5H. -0.B.5I. -f.B.5K. -7.5M.B. -0.B.5O. -3.B.5R. -0.B.5U. -0.B.5W. -1.5Y.B. -0.B.5Z. -0.B.5/. -0.B.60. -2.62.B. -2.66.B. -a.6b.B. -c.6e.B. -0.B.6F. -0.B.6M. -0.B.6+. -3.B.71. -5.79.B. -3.B.7D. -0.B.2p. -0.B.2p. 0.C.C. +0.f.Z. +0.h.X. +0.i.W. +0.j.V. +0.k.U. +0.o.Q. +0.O.q. +0.p.P. +0.t.L. +0.u.K. +0.x.H. +0.y.G. +3.I.w. +3.J.v. +3.M.s. +3.n.R. +4.S.m. +c.T.l. +c.Y.g. +i.c.10. +0.A.F. +0.B.E. 0.C.D. -0.E.C. -0.C.F. -3.G.C. -0.C.H. -2.I.C. -2.J.C. -0.C.K. -0.C.L. -2.M.C. -0.C.N. -0.C.O. -0.C.P. -0.C.Q. -0.C.R. -3.S.C. -b.T.C. -0.C.U. -0.C.V. -0.C.W. -0.C.X. -b.Y.C. -0.C.Z. -0.C.+. -0.C./. -0.C.10. -0.C.11. -0.C.12. -0.C.13. -0.C.14. -0.C.15. -0.C.16. -0.C.17. -3.C.18. -0.C.19. -3.C.1a. -0.C.1b. -0.C.1c. -3.C.1d. -0.C.1e. -0.C.1f. -0.C.1g. -3.C.1h. -3.C.1i. -3.C.1j. -0.C.1k. -2.1l.C. -0.C.1m. -0.C.1n. -0.C.1o. -0.C.1p. -0.C.1q. -0.C.1r. -3.C.1s. -0.C.1t. -2.1u.C. -0.C.1v. -3.C.1w. -0.C.1x. -0.C.1y. -0.C.1z. -0.C.1A. -0.1B.C. -0.C.1C. -0.C.1D. -0.C.1E. -0.C.1F. -0.C.1G. -0.C.1H. -0.C.1I. -0.C.1J. -0.C.1K. -0.C.1L. -0.C.1M. -0.C.1N. -0.C.1O. -0.C.1P. -0.C.1Q. -0.C.1R. -0.C.1S. -0.C.1T. -3.C.1U. -0.C.1V. -0.C.1W. -0.C.1X. -0.C.1Y. -0.C.1Z. -0.C.1+. -0.C.1/. -0.C.20. -0.C.21. -0.C.22. -0.C.23. -0.C.24. -0.C.25. -b.26.C. -0.C.27. -4.C.28. -0.C.2a. -0.C.2b. -0.C.2l. -6.2m.C. -0.C.2p. -2.2q.C. -2.2r.C. -7.2s.C. -7.2t.C. -2.2u.C. -3.C.2v. -0.C.2w. -e.2x.C. -9.2y.C. -0.C.2z. -3.C.2A. -9.2B.C. -0.C.2C. -0.C.2D. -3.C.2E. -3.C.2F. -0.C.2G. -9.2H.C. -0.C.2I. -3.C.2J. -0.C.2K. -c.2M.C. -9.2N.C. -2.2O.C. -9.2Q.C. -7.2S.C. -0.C.2T. -1.2U.C. -9.2V.C. -1.2W.C. -0.C.2X. -b.2+.C. -0.C.30. -0.C.35. -b.36.C. -0.C.37. -3.C.38. -5.39.C. -0.C.3d. -3.C.3h. -3.C.3i. -0.C.3m. -0.C.3p. -a.3v.C. -a.3y.C. -2.3z.C. -5.3A.C. -2.3B.C. -0.C.3C. -a.3F.C. -5.3G.C. -0.C.3M. -0.C.3Q. -5.40.C. -0.C.47. -0.C.4d. -0.C.4O. -3.C.4Q. -5.4T.C. -3.C.4V. -0.C.51. -0.C.52. -a.5a.C. -2.5b.C. -5.5d.C. -0.C.5B. -0.C.5C. -0.C.5F. -3.C.5H. -0.C.5I. -f.C.5K. -7.5M.C. -0.C.5O. -3.C.5R. -0.C.5U. -0.C.5W. -1.5Y.C. -0.C.5Z. -0.C.5/. -0.C.60. -2.62.C. -2.66.C. -a.6b.C. -c.6e.C. -0.C.6F. -0.C.6M. -0.C.6+. -3.C.71. -5.79.C. -3.C.7D. -0.C.2p. -0.C.2p. +0.g.Z. +0.i.X. +0.j.W. +0.k.V. +0.l.U. +0.o.R. +0.P.q. +0.p.Q. +0.v.K. +0.y.H. +3.I.x. +3.J.w. +3.M.t. +4.G.z. +4.n.S. +4.u.L. +c.T.m. +c.Y.h. +i.c.11. +0.B.F. +0.d.+. 0.D.D. -3.E.D. +0.E.C. +0.h.Z. +0.H.z. +0.j.X. +0.k.W. +0.l.V. +0.m.U. +0.p.R. +0.Q.q. +0.r.N. +0.v.L. +0.w.K. +3.I.y. +3.J.x. +3.M.u. +3.n.T. +4.S.o. +c.Y.i. +i.c.12. +0.C.F. +0.d./. +0.e.+. +0.k.X. +0.l.W. +0.m.V. +0.r.O. +0.R.q. +0.s.N. +0.x.K. +3.I.z. +3.J.y. +3.M.v. +3.n.U. +4.E.D. +4.i.Z. +4.S.p. +4.w.L. +c.T.o. +c.Y.j. +0.d.10. 0.D.F. -0.G.D. -0.D.H. -2.I.D. -2.J.D. -0.D.K. -0.D.L. -2.M.D. -0.D.N. -0.D.O. -0.D.P. -0.D.Q. -0.D.R. -3.S.D. -b.T.D. -0.D.U. -0.D.V. -0.D.W. -0.D.X. -b.Y.D. -3.D.Z. -0.D.+. -0.D./. -0.D.10. -0.D.11. -0.D.12. -0.D.13. -0.D.14. -0.D.15. -0.D.16. -0.D.17. -3.D.18. -0.D.19. -3.D.1a. -0.D.1b. -0.D.1c. -3.D.1d. -0.D.1e. -0.D.1f. -0.D.1g. -3.D.1h. -3.D.1i. -3.D.1j. -0.D.1k. -2.1l.D. -0.D.1m. -0.D.1n. -0.D.1o. -0.D.1p. -0.D.1q. -0.D.1r. -0.D.1s. -0.D.1t. -2.1u.D. -0.D.1v. -3.D.1w. -0.D.1x. -0.D.1y. -0.D.1z. -0.D.1A. -0.1B.D. -0.D.1C. -0.D.1D. -0.D.1E. -0.D.1F. -0.D.1G. -0.D.1H. -0.D.1I. -0.D.1J. -0.D.1K. -0.D.1L. -0.D.1M. -0.D.1N. -0.D.1O. -0.D.1P. -0.D.1Q. -0.D.1R. -0.D.1S. -0.D.1T. -3.D.1U. -0.D.1V. -0.D.1W. -0.D.1X. -0.D.1Y. -0.D.1Z. -0.D.1+. -0.D.1/. -0.D.20. -0.D.21. -0.D.22. -0.D.23. -0.D.24. -0.D.25. -b.26.D. -0.D.27. -4.D.28. -0.D.2a. -0.D.2b. -0.D.2l. -6.2m.D. -0.D.2p. -2.2q.D. -2.2r.D. -7.2s.D. -7.2t.D. -2.2u.D. -0.D.2v. -0.D.2w. -e.2x.D. -9.2y.D. -0.D.2z. -3.D.2A. -9.2B.D. -0.D.2C. -0.D.2D. -3.D.2E. -3.D.2F. -0.D.2G. -9.2H.D. -0.D.2I. -3.D.2J. -0.D.2K. -c.2M.D. -9.2N.D. -2.2O.D. -9.2Q.D. -7.2S.D. -0.D.2T. -1.2U.D. -9.2V.D. -1.2W.D. -0.D.2X. -b.2+.D. -0.D.30. -0.D.35. -b.36.D. -0.D.37. -0.D.38. -5.39.D. -0.D.3d. -3.D.3h. -3.D.3i. -0.D.3m. -0.D.3p. -a.3v.D. -a.3y.D. -2.3z.D. -5.3A.D. -2.3B.D. -0.D.3C. -a.3F.D. -5.3G.D. -0.D.3M. -0.D.3Q. -5.40.D. -0.D.47. -0.D.4d. -0.D.4O. -3.D.4Q. -5.4T.D. -3.D.4V. -0.D.51. -0.D.52. -a.5a.D. -2.5b.D. -5.5d.D. -0.D.5B. -0.D.5C. -0.D.5F. -3.D.5H. -0.D.5I. -f.D.5K. -7.5M.D. -0.D.5O. -3.D.5R. -0.D.5U. -0.D.5W. -1.5Y.D. -0.D.5Z. -0.D.5/. -0.D.60. -2.62.D. -2.66.D. -a.6b.D. -c.6e.D. -0.D.6F. -0.D.6M. -0.D.6+. -3.D.71. -5.79.D. -3.D.7D. -0.D.2p. -0.D.2p. +0.e./. 0.E.E. +0.f.+. +0.G.A. +0.l.X. +0.m.W. +0.o.U. +0.r.P. +0.s.O. +0.t.N. +0.y.K. +3.J.z. +3.M.w. +3.n.V. +4.j.Z. +4.S.q. +4.x.L. +c.T.p. +c.Y.k. +0.A.H. +0.d.11. +0.e.10. 0.E.F. +0.f./. +0.g.+. +0.G.B. +0.k.Z. +0.K.z. +0.m.X. +0.o.V. +0.p.U. +0.r.Q. +0.s.P. +0.t.O. +0.u.N. +3.M.x. +3.n.W. +4.y.L. +c.T.q. +c.Y.l. +i.c.13. +0.B.H. +0.d.12. +0.e.11. +0.f.10. +0.g./. +0.l.Z. +0.L.z. +0.o.W. +0.p.V. +0.r.R. +0.s.Q. +0.t.P. +0.U.q. +0.v.N. +3.I.A. +3.M.y. +3.n.X. +4.F.F. +4.G.C. +4.h.+. +4.u.O. +c.Y.m. +i.c.14. +0.C.H. +0.e.12. +0.f.11. +0.g.10. +0.G.D. +0.i.+. +0.m.Z. +0.o.X. +0.p.W. +0.s.R. +0.t.Q. +0.u.P. +0.V.q. +0.w.N. +3.I.B. +3.J.A. +3.M.z. +3.n.Y. +4.h./. +4.S.r. +4.v.O. +i.c.15. +j.1e.0. +0.1f.0. +0.A.K. +0.D.H. +0.f.12. +0.g.11. 0.G.E. +0.h.10. +0.i./. +0.j.+. +0.p.X. +0.t.R. +0.u.Q. +0.v.P. +0.W.q. +0.x.N. +3.I.C. +3.J.B. +3.n.Z. +4.S.s. +4.w.O. +c.T.r. +c.Y.o. +i.c.16. +j.1e.1. +0.1f.1. +0.A.L. +0.B.K. +0.d.13. 0.E.H. -2.I.E. -2.J.E. -0.E.K. -0.E.L. -2.M.E. -0.E.N. -0.E.O. -0.E.P. -0.E.Q. -0.E.R. -3.S.E. -b.T.E. -0.E.U. -0.E.V. -0.E.W. -0.E.X. -b.Y.E. -3.E.Z. -0.E.+. -0.E./. -0.E.10. -0.E.11. -0.E.12. -0.E.13. -0.E.14. -0.E.15. -0.E.16. -0.E.17. -3.E.18. -0.E.19. -0.E.1a. -0.E.1b. -0.E.1c. -3.E.1d. -0.E.1e. -0.E.1f. -0.E.1g. -3.E.1h. -3.E.1i. -3.E.1j. -0.E.1k. -2.1l.E. -0.E.1m. -0.E.1n. -0.E.1o. -0.E.1p. -0.E.1q. -3.E.1r. -3.E.1s. -0.E.1t. -2.1u.E. -0.E.1v. -3.E.1w. -0.E.1x. -0.E.1y. -0.E.1z. -0.E.1A. -0.1B.E. -0.E.1C. -0.E.1D. -0.E.1E. -0.E.1F. -0.E.1G. -0.E.1H. -0.E.1I. -0.E.1J. -0.E.1K. -0.E.1L. -0.E.1M. -0.E.1N. -0.E.1O. -0.E.1P. -0.E.1Q. -0.E.1R. -0.E.1S. -3.E.1T. -3.E.1U. -0.E.1V. -0.E.1W. -0.E.1X. -0.E.1Y. -0.E.1Z. -0.E.1+. -0.E.1/. -0.E.20. -0.E.21. -0.E.22. -0.E.23. -0.E.24. -0.E.25. -b.26.E. -0.E.27. -4.E.28. -0.E.2a. -0.E.2b. -0.E.2l. -6.2m.E. -0.E.2p. -2.2q.E. -2.2r.E. -7.2s.E. -7.2t.E. -2.2u.E. -3.E.2v. -0.E.2w. -e.2x.E. -9.2y.E. -0.E.2z. -3.E.2A. -9.2B.E. -0.E.2C. -0.E.2D. -3.E.2E. -3.E.2F. -0.E.2G. -9.2H.E. -0.E.2I. -3.E.2J. -0.E.2K. -c.2M.E. -9.2N.E. -2.2O.E. -9.2Q.E. -7.2S.E. -0.E.2T. -1.2U.E. -9.2V.E. -1.2W.E. -0.E.2X. -b.2+.E. -0.E.30. -0.E.35. -b.36.E. -0.E.37. -3.E.38. -5.39.E. -0.E.3d. -3.E.3h. -3.E.3i. -0.E.3m. -0.E.3p. -a.3v.E. -a.3y.E. -2.3z.E. -5.3A.E. -2.3B.E. -0.E.3C. -a.3F.E. -5.3G.E. -0.E.3M. -0.E.3Q. -5.40.E. -0.E.47. -0.E.4d. -3.E.4O. -3.E.4Q. -5.4T.E. -3.E.4V. -0.E.51. -0.E.52. -a.5a.E. -2.5b.E. -5.5d.E. -0.E.5B. -0.E.5C. -0.E.5F. -3.E.5H. -0.E.5I. -f.E.5K. -7.5M.E. -0.E.5O. -3.E.5R. -0.E.5U. -0.E.5W. -1.5Y.E. -0.E.5Z. -0.E.5/. -0.E.60. -2.62.E. -2.66.E. -a.6b.E. -c.6e.E. -0.E.6F. -0.E.6M. -0.E.6+. -3.E.71. -5.79.E. -3.E.7D. -0.E.2p. -0.E.2p. -3.F.F. -3.G.F. +0.g.12. +0.h.11. +0.i.10. +0.j./. +0.k.+. +0.o.Z. +0.r.U. +0.u.R. +0.v.Q. +0.w.P. +0.X.q. +0.y.N. +3.I.D. +3.J.C. +4.G.F. +4.S.t. +4.x.O. +c.T.s. +c.Y.p. +i.c.17. +j.1e.2. +0.B.L. +0.C.K. +0.d.14. +0.e.13. +0.h.12. 0.H.F. -2.I.F. -2.J.F. -0.K.F. -0.L.F. -2.M.F. -0.N.F. -0.O.F. -0.P.F. -0.Q.F. -0.R.F. -3.S.F. -b.T.F. -0.U.F. -0.V.F. -0.W.F. -0.X.F. -b.Y.F. -3.F.Z. -0.+.F. -0./.F. -0.10.F. -0.11.F. -0.12.F. -0.13.F. -0.14.F. -3.15.F. -0.16.F. -0.17.F. -0.18.F. -0.19.F. -0.1a.F. -0.1b.F. -3.1c.F. -0.1d.F. -0.1e.F. -0.F.1f. -3.F.1g. -3.F.1h. -3.F.1i. -0.1j.F. -0.1k.F. -2.1l.F. -0.1m.F. -0.1n.F. -0.1o.F. -0.1p.F. -0.1q.F. -0.1r.F. -0.1s.F. -3.1t.F. -2.1u.F. -0.1v.F. -0.1w.F. -0.1x.F. -0.1y.F. -0.1z.F. -0.1A.F. -0.1B.F. -0.1C.F. -0.1D.F. -3.1E.F. -0.1F.F. -0.1G.F. -0.1H.F. -0.1I.F. -0.1J.F. -3.1K.F. -0.1L.F. -0.1M.F. -3.1N.F. -0.1O.F. -0.1P.F. -3.F.1Q. -0.1R.F. -3.1S.F. -3.F.1T. -3.F.1U. -0.F.1V. -0.F.1W. -0.F.1X. -0.F.1Y. -0.F.1Z. -0.F.1+. -0.F.1/. -0.F.20. -0.F.21. -0.F.22. -0.F.23. -0.F.24. -0.F.25. -b.26.F. -0.F.27. -4.F.28. -0.F.2a. -0.F.2b. -0.F.2l. -6.2m.F. -3.F.2p. -2.2q.F. -2.2r.F. -7.2s.F. -7.2t.F. -2.2u.F. -3.F.2v. -3.F.2w. -e.2x.F. -9.2y.F. -0.F.2z. -3.F.2A. -9.2B.F. -0.F.2C. -0.F.2D. -3.F.2E. -3.F.2F. -0.F.2G. -9.2H.F. -0.F.2I. -3.F.2J. -0.F.2K. -c.2M.F. -9.2N.F. -2.2O.F. -9.2Q.F. -7.2S.F. -0.F.2T. -1.2U.F. -9.2V.F. -1.2W.F. -0.F.2X. -d.2+.F. -0.F.30. -0.F.35. -b.36.F. -0.F.37. -3.F.38. -5.39.F. -0.F.3d. -3.F.3h. -3.F.3i. -0.F.3m. -0.F.3p. -a.3v.F. -a.3y.F. -2.3z.F. -5.3A.F. -2.3B.F. -0.F.3C. -a.3F.F. -5.3G.F. -0.F.3M. -0.F.3Q. -5.40.F. -0.F.47. -0.F.4d. -3.F.4O. -3.F.4Q. -5.4T.F. -3.F.4V. -0.F.51. -0.F.52. -a.5a.F. -2.5b.F. -5.5d.F. -0.F.5B. -0.F.5C. -5.5F.F. -3.F.5H. -0.F.5I. -f.F.5K. -7.5M.F. -0.F.5O. -3.F.5R. -0.F.5U. -0.F.5W. -1.5Y.F. -0.F.5Z. -0.F.5/. -0.F.60. -2.62.F. -2.66.F. -a.6b.F. -c.6e.F. -0.F.6F. -0.F.6M. -0.F.6+. -3.F.71. -5.79.F. -3.F.7D. -3.F.2p. -3.F.2p. +0.i.11. +0.j.10. +0.k./. +0.l.+. +0.N.z. +0.p.Z. +0.r.V. +0.s.U. +0.v.R. +0.w.Q. +0.x.P. +3.I.E. +3.J.D. +3.M.A. +4.1f.2. +4.S.u. +4.y.O. +c.T.t. +c.Y.q. +i.c.18. +j.1e.3. +0.1f.3. +0.C.L. +0.d.15. +0.D.K. +0.e.14. +0.f.13. +0.i.12. +0.j.11. +0.k.10. +0.l./. +0.m.+. +0.O.z. +0.q.Z. +0.r.W. +0.s.V. +0.w.R. +0.x.Q. +0.y.P. +3.I.F. +3.J.E. +3.M.B. +4.S.v. +4.t.U. +c.T.u. +e.4.1e. +i.c.19. +0.1h.0. +0.d.16. +0.D.L. +0.e.15. +0.E.K. +0.f.14. +0.g.13. 0.G.G. +0.j.12. +0.k.11. +0.l.10. +0.m./. +0.P.z. +0.r.X. +0.s.W. +0.t.V. +0.x.R. +0.y.Q. +3.J.F. +3.M.C. +3.n.+. +4.S.w. +4.u.U. +c.T.v. +e.4.1f. +i.c.1a. +j.1e.5. +0.1f.5. +0.1h.1. +0.1i.0. +0.A.N. +0.d.17. +0.e.16. +0.E.L. +0.f.15. +0.g.14. 0.G.H. -2.I.G. -2.J.G. -0.G.K. -3.G.L. -2.M.G. -0.G.N. -3.G.O. -0.G.P. -0.G.Q. -0.G.R. -3.S.G. -b.T.G. -3.G.U. -3.G.V. -3.G.W. -0.G.X. -b.Y.G. -0.G.Z. -3.G.+. -3.G./. -0.G.10. -0.G.11. -0.G.12. -0.G.13. -0.G.14. -0.G.15. -0.G.16. -0.G.17. -0.G.18. -0.G.19. -0.G.1a. -0.G.1b. -3.G.1c. -0.G.1d. -3.G.1e. -0.G.1f. -3.G.1g. -0.G.1h. -0.G.1i. -3.G.1j. -0.G.1k. -2.1l.G. -0.G.1m. -0.G.1n. -0.G.1o. -0.G.1p. -0.G.1q. -0.G.1r. -3.G.1s. -0.G.1t. -2.1u.G. -0.G.1v. -0.G.1w. -0.G.1x. -0.G.1y. -0.G.1z. -0.G.1A. -0.1B.G. -0.G.1C. -0.G.1D. -3.G.1E. -0.G.1F. -0.G.1G. -0.G.1H. -0.G.1I. -0.G.1J. -0.G.1K. -0.G.1L. -0.G.1M. -0.G.1N. -0.G.1O. -0.G.1P. -3.G.1Q. -0.G.1R. -3.G.1S. -3.G.1T. -0.G.1U. -0.G.1V. -0.G.1W. -0.G.1X. -3.G.1Y. -3.G.1Z. -0.G.1+. -0.G.1/. -0.G.20. -0.G.21. -0.G.22. -0.G.23. -0.G.24. -0.G.25. -b.26.G. -3.G.27. -4.G.28. -0.G.2a. -0.G.2b. -0.G.2l. -6.2m.G. -0.G.2p. -2.2q.G. -2.2r.G. -7.2s.G. -7.2t.G. -2.2u.G. -0.G.2v. -3.G.2w. -e.2x.G. -9.2y.G. -0.G.2z. -3.G.2A. -9.2B.G. -0.G.2C. -0.G.2D. -3.G.2E. -0.G.2F. -0.G.2G. -9.2H.G. -0.G.2I. -3.G.2J. -0.G.2K. -c.2M.G. -9.2N.G. -2.2O.G. -9.2Q.G. -7.2S.G. -0.G.2T. -1.2U.G. -9.2V.G. -1.2W.G. -3.G.2X. -b.2+.G. -0.G.30. -3.G.35. -b.36.G. -0.G.37. -3.G.38. -5.39.G. -0.G.3d. -3.G.3h. -0.G.3i. -0.G.3m. -0.G.3p. -a.3v.G. -a.3y.G. -2.3z.G. -5.3A.G. -2.3B.G. -0.G.3C. -a.3F.G. -5.3G.G. -0.G.3M. -0.G.3Q. -5.40.G. -0.G.47. -0.G.4d. -0.G.4O. -3.G.4Q. -5.4T.G. -3.G.4V. -0.G.51. -0.G.52. -a.5a.G. -2.5b.G. -5.5d.G. -0.G.5B. -0.G.5C. -0.G.5F. -0.G.5H. -0.G.5I. -f.G.5K. -7.5M.G. -3.G.5O. -0.G.5R. -0.G.5U. -0.G.5W. -1.5Y.G. -0.G.5Z. -0.G.5/. -0.G.60. -2.62.G. -2.66.G. -a.6b.G. -c.6e.G. -0.G.6F. -0.G.6M. -0.G.6+. -3.G.71. -5.79.G. -3.G.7D. -0.G.2p. -0.G.2p. +0.h.13. +0.k.12. +0.K.F. +0.l.11. +0.m.10. +0.o.+. +0.Q.z. +0.s.X. +0.y.R. +3.M.D. +3.n./. +4.S.x. +4.t.W. +4.u.V. +4.v.U. +c.T.w. +c.Y.r. +i.c.1b. +j.1e.6. +0.1f.6. +0.1h.2. +0.1i.1. +0.A.O. +0.B.N. +0.e.17. +0.f.16. +0.g.15. +0.h.14. 0.H.H. -2.I.H. -2.J.H. +0.i.13. +0.l.12. +0.L.F. +0.m.11. +0.o./. +0.p.+. +0.R.z. +0.r.Z. +0.t.X. +0.u.W. +3.I.G. +3.M.E. +3.n.10. +4.d.18. +4.S.y. +4.v.V. +4.w.U. +c.T.x. +c.Y.s. +e.7.1e. +i.c.1c. +j.1j.0. +0.+.q. +0.1h.3. +0.A.P. +0.B.O. +0.C.N. +0.d.19. +0.f.17. +0.g.16. +0.h.15. +0.i.14. +0.j.13. +0.m.12. +0.o.10. +0.p./. +0.s.Z. +0.u.X. +0.v.W. +3.I.H. +3.J.G. +3.M.F. +3.n.11. +4.1i.2. +4.e.18. +4.S.z. +4.w.V. +4.x.U. +c.T.y. +c.Y.t. +e.7.1f. +j.1e.8. +j.1j.1. +0./.q. +0.1f.8. +0.1i.3. +0.A.Q. +0.B.P. +0.C.O. +0.D.N. +0.e.19. +0.g.17. +0.G.K. +0.h.16. +0.i.15. +0.j.14. +0.k.13. +0.o.11. +0.p.10. +0.t.Z. +0.v.X. +0.w.W. +0.x.V. +0.y.U. +3.I.I. +3.J.H. +3.n.12. +4.d.1a. +4.f.18. +c.T.z. +c.Y.u. +e.4.1h. +e.9.1e. +i.c.1d. +j.1j.2. +0.10.q. +0.1h.5. +0.A.R. +0.B.Q. +0.C.P. +0.d.1b. +0.D.O. +0.e.1a. +0.E.N. +0.f.19. +0.g.18. +0.h.17. +0.i.16. +0.j.15. +0.k.14. 0.K.H. +0.l.13. +0.o.12. +0.p.11. +0.u.Z. +0.U.z. +0.w.X. +0.x.W. +0.y.V. +3.J.I. +4.G.L. +c.Y.v. +e.4.1i. +e.9.1f. +j.1e.a. +j.1j.3. +0.11.q. +0.1f.a. +0.1h.6. +0.1i.5. +0.B.R. +0.C.Q. +0.d.1c. +0.D.P. +0.e.1b. +0.E.O. +0.g.19. +0.h.18. +0.i.17. +0.j.16. +0.k.15. +0.l.14. 0.L.H. -2.M.H. -0.N.H. -0.O.H. -0.H.P. -0.H.Q. -0.H.R. -3.S.H. -b.T.H. -0.U.H. -0.V.H. -0.W.H. -0.X.H. -b.Y.H. -0.H.Z. -0.+.H. -0./.H. -0.H.10. -0.H.11. -0.H.12. -0.H.13. -0.H.14. -0.H.15. -0.H.16. -0.H.17. -3.H.18. -0.H.19. -0.H.1a. -0.H.1b. -0.H.1c. -3.H.1d. -0.H.1e. -0.H.1f. -0.H.1g. -3.H.1h. -3.H.1i. -0.1j.H. -0.1k.H. -2.1l.H. -0.1m.H. -0.1n.H. -0.1o.H. -0.H.1p. -3.H.1q. -0.H.1r. -0.H.1s. -0.1t.H. -2.1u.H. -0.H.1v. -3.H.1w. -0.H.1x. -0.H.1y. -0.H.1z. -0.H.1A. -0.1B.H. -0.H.1C. -0.H.1D. -0.1E.H. -0.H.1F. -0.H.1G. -0.H.1H. -0.H.1I. -0.H.1J. -0.H.1K. -0.H.1L. -0.H.1M. -0.H.1N. -0.H.1O. -0.H.1P. -3.H.1Q. -0.H.1R. -0.H.1S. -0.H.1T. -3.H.1U. -0.H.1V. -0.H.1W. -0.H.1X. -0.H.1Y. -0.H.1Z. -0.H.1+. -0.H.1/. -0.H.20. -0.H.21. -0.H.22. -0.H.23. -0.H.24. -0.H.25. -b.26.H. -0.H.27. -4.H.28. -0.H.2a. -0.H.2b. -0.H.2l. -6.2m.H. -0.H.2p. -2.2q.H. -2.2r.H. -7.2s.H. -7.2t.H. -2.2u.H. -0.H.2v. -0.H.2w. -e.2x.H. -9.2y.H. -0.H.2z. -3.H.2A. -9.2B.H. -0.H.2C. -0.H.2D. -3.H.2E. -0.H.2F. -0.H.2G. -9.2H.H. -0.H.2I. -3.H.2J. -0.H.2K. -c.2M.H. -9.2N.H. -2.2O.H. -9.2Q.H. -7.2S.H. -0.H.2T. -1.2U.H. -9.2V.H. -1.2W.H. -0.H.2X. -d.2+.H. -0.H.30. -0.H.35. -3.36.H. -0.H.37. -0.H.38. -5.39.H. -0.H.3d. -3.H.3h. -0.H.3i. -0.H.3m. -0.H.3p. -a.3v.H. -a.3y.H. -2.3z.H. -5.3A.H. -2.3B.H. -0.H.3C. -a.3F.H. -5.3G.H. -0.H.3M. -0.H.3Q. -5.40.H. -0.H.47. -0.H.4d. -3.H.4O. -3.H.4Q. -5.4T.H. -3.H.4V. -0.H.51. -0.H.52. -a.5a.H. -2.5b.H. -5.5d.H. -0.H.5B. -0.H.5C. -0.H.5F. -3.H.5H. -0.H.5I. -f.H.5K. -7.5M.H. -0.H.5O. -0.H.5R. -0.H.5U. -0.H.5W. -1.5Y.H. -0.H.5Z. -0.H.5/. -0.H.60. -2.62.H. -2.66.H. -a.6b.H. -c.6e.H. -0.H.6F. -0.H.6M. -0.H.6+. -3.H.71. -5.79.H. -3.H.7D. -0.H.2p. -0.H.2p. -2.I.I. -2.J.I. -2.I.K. -2.I.L. -2.I.M. -2.I.N. -2.I.O. -2.I.P. -2.I.Q. -2.I.R. -3.I.S. -2.I.T. -2.I.U. -2.I.V. -2.I.W. -2.I.X. -2.I.Y. -2.I.Z. -2.I.+. -2.I./. -2.I.10. -2.I.11. -2.I.12. -2.I.13. -2.I.14. -2.I.15. -2.I.16. -2.I.17. -2.I.18. -2.I.19. -2.I.1a. -2.I.1b. -2.I.1c. -2.I.1d. -2.I.1e. -2.I.1f. -2.I.1g. -2.I.1h. -2.I.1i. -2.I.1j. -2.I.1k. -2.1l.I. -2.I.1m. -2.I.1n. -2.I.1o. -2.I.1p. -2.I.1q. -2.I.1r. -2.I.1s. -2.I.1t. -2.1u.I. -2.I.1v. -2.I.1w. -2.I.1x. -2.I.1y. -2.I.1z. -2.I.1A. -2.I.1B. -2.I.1C. -2.I.1D. -2.I.1E. -2.I.1F. -2.I.1G. -2.I.1H. -2.I.1I. -2.I.1J. -2.I.1K. -2.I.1L. -2.I.1M. -2.I.1N. -2.I.1O. -2.I.1P. -2.I.1Q. -2.I.1R. -2.I.1S. -2.I.1T. -2.I.1U. -2.I.1V. -2.I.1W. -2.I.1X. -2.I.1Y. -2.I.1Z. -2.I.1+. -2.I.1/. -2.I.20. -2.I.21. -2.I.22. -2.I.23. -2.I.24. -2.I.25. -2.I.26. -2.I.27. -2.I.28. -2.I.2a. -2.I.2b. -2.I.2l. -6.2m.I. -2.I.2p. -2.I.2q. -2.I.2r. -7.2s.I. -7.2t.I. -2.2u.I. -2.I.2v. -2.I.2w. -e.2x.I. -2.I.2y. -2.I.2z. -2.I.2A. -2.I.2B. -2.I.2C. -2.I.2D. -2.I.2E. -2.I.2F. -2.I.2G. -2.I.2H. -2.I.2I. -2.I.2J. -2.I.2K. -c.2M.I. -2.I.2N. -2.I.2O. -2.I.2Q. -7.2S.I. -2.I.2T. -1.2U.I. -2.I.2V. -1.2W.I. -2.I.2X. -2.I.2+. -2.I.30. -2.I.35. -2.I.36. -2.I.37. -2.I.38. -2.I.39. -2.I.3d. -2.I.3h. -2.I.3i. -2.I.3m. -2.I.3p. -a.3v.I. -a.3y.I. -2.3z.I. -2.I.3A. -2.3B.I. -2.I.3C. -a.3F.I. -2.I.3G. -2.I.3M. -2.I.3Q. -2.I.40. -2.I.47. -2.I.4d. -2.I.4O. -2.I.4Q. -5.4T.I. -2.I.4V. -2.I.51. -2.I.52. -a.5a.I. -2.5b.I. -2.I.5d. -2.I.5B. -2.I.5C. -2.I.5F. -2.I.5H. -2.I.5I. -f.I.5K. -7.5M.I. -2.I.5O. -2.I.5R. -2.I.5U. -2.I.5W. -1.5Y.I. -2.I.5Z. -2.I.5/. -2.I.60. -2.I.62. -2.I.66. -a.6b.I. -c.6e.I. -2.I.6F. -2.I.6M. -2.I.6+. -2.I.71. -2.I.79. -2.I.7D. -2.I.2p. -2.I.2p. -2.J.J. -2.J.K. -2.J.L. -2.J.M. -2.J.N. -2.J.O. -2.J.P. -2.J.Q. -2.J.R. -3.J.S. -2.J.T. -2.J.U. -2.J.V. -2.J.W. -2.J.X. -2.J.Y. -2.J.Z. -2.J.+. -2.J./. -2.J.10. -2.J.11. -2.J.12. -2.J.13. -2.J.14. -2.J.15. -2.J.16. -2.J.17. -2.J.18. -2.J.19. -2.J.1a. -2.J.1b. -2.J.1c. -2.J.1d. -2.J.1e. -2.J.1f. -2.J.1g. -2.J.1h. -2.J.1i. -2.J.1j. -2.J.1k. -2.1l.J. -2.J.1m. -2.J.1n. -2.J.1o. -2.J.1p. -2.J.1q. -2.J.1r. -2.J.1s. -2.J.1t. -2.1u.J. -2.J.1v. -2.J.1w. -2.J.1x. -2.J.1y. -2.J.1z. -2.J.1A. -2.J.1B. -2.J.1C. -2.J.1D. -2.J.1E. -2.J.1F. -2.J.1G. -2.J.1H. -2.J.1I. -2.J.1J. -2.J.1K. -2.J.1L. -2.J.1M. -2.J.1N. -2.J.1O. -2.J.1P. -2.J.1Q. -2.J.1R. -2.J.1S. -2.J.1T. -2.J.1U. -2.J.1V. -2.J.1W. -2.J.1X. -2.J.1Y. -2.J.1Z. -2.J.1+. -2.J.1/. -2.J.20. -2.J.21. -2.J.22. -2.J.23. -2.J.24. -2.J.25. -2.J.26. -2.J.27. -4.J.28. -2.J.2a. -2.J.2b. -2.J.2l. -6.2m.J. -2.J.2p. -2.J.2q. -2.J.2r. -7.2s.J. -7.2t.J. -2.2u.J. -2.J.2v. -2.J.2w. -e.2x.J. -2.J.2y. -2.J.2z. -2.J.2A. -2.J.2B. -2.J.2C. -2.J.2D. -2.J.2E. -2.J.2F. -2.J.2G. -2.J.2H. -2.J.2I. -2.J.2J. -2.J.2K. -c.2M.J. -2.J.2N. -2.J.2O. -2.J.2Q. -7.2S.J. -2.J.2T. -1.2U.J. -2.J.2V. -1.2W.J. -2.J.2X. -2.J.2+. -2.J.30. -2.J.35. -2.J.36. -2.J.37. -2.J.38. -2.J.39. -2.J.3d. -2.J.3h. -2.J.3i. -2.J.3m. -2.J.3p. -a.3v.J. -a.3y.J. -2.3z.J. -2.J.3A. -2.3B.J. -2.J.3C. -a.3F.J. -2.J.3G. -2.J.3M. -2.J.3Q. -2.J.40. -2.J.47. -2.J.4d. -2.J.4O. -2.J.4Q. -5.4T.J. -2.J.4V. -2.J.51. -2.J.52. -a.5a.J. -2.5b.J. -2.J.5d. -2.J.5B. -2.J.5C. -2.J.5F. -2.J.5H. -2.J.5I. -f.J.5K. -7.5M.J. -2.J.5O. -2.J.5R. -2.J.5U. -2.J.5W. -1.5Y.J. -2.J.5Z. -2.J.5/. -2.J.60. -2.J.62. -2.J.66. -a.6b.J. -c.6e.J. -2.J.6F. -2.J.6M. -2.J.6+. -2.J.71. -2.J.79. -2.J.7D. -2.J.2p. -2.J.2p. +0.m.13. +0.N.F. +0.p.12. +0.r.+. +0.v.Z. +0.V.z. +0.x.X. +0.y.W. +3.I.K. +3.J.J. +3.M.G. +4.f.1a. +4.S.A. +c.Y.w. +j.1e.b. +j.1j.4. +0.1f.b. +0.1i.6. +0.C.R. +0.D.Q. +0.e.1c. +0.E.P. +0.f.1b. +0.g.1a. +0.h.19. +0.j.17. +0.k.16. +0.l.15. +0.m.14. +0.O.F. +0.r./. +0.s.+. +0.W.z. +0.w.Z. +0.y.X. +3.I.L. +3.J.K. +3.M.H. +3.n.13. +4.12.q. +4.i.18. +4.S.B. +c.T.A. +c.Y.x. +e.7.1h. +i.c.1e. +j.1j.5. +0.1h.8. +0.A.U. +0.D.R. +0.E.Q. +0.f.1c. +0.g.1b. +0.h.1a. +0.i.19. +0.k.17. 0.K.K. +0.l.16. +0.m.15. +0.o.13. +0.P.F. +0.r.10. +0.s./. +0.t.+. +0.x.Z. +0.X.z. +3.I.M. +3.J.L. +3.n.14. +4.d.1d. +4.j.18. +4.S.C. +c.T.B. +c.Y.y. +e.7.1i. +i.c.1f. +j.1j.6. +0.1i.8. +0.A.V. +0.B.U. +0.E.R. +0.g.1c. +0.G.N. +0.h.1b. +0.j.19. +0.l.17. 0.L.K. -2.M.K. -0.N.K. -0.O.K. -0.K.P. -0.K.Q. -0.K.R. -3.S.K. -b.T.K. -0.U.K. -0.V.K. -0.W.K. -0.X.K. -b.Y.K. -0.K.Z. -0.+.K. -0./.K. -0.K.10. -0.K.11. -0.K.12. -0.K.13. -0.K.14. -0.K.15. -0.K.16. -0.K.17. -3.K.18. -0.K.19. -0.K.1a. -0.K.1b. -0.K.1c. -3.K.1d. -0.K.1e. -0.K.1f. -0.K.1g. -3.K.1h. -3.K.1i. -0.1j.K. -0.1k.K. -2.1l.K. -0.1m.K. -0.1n.K. -0.1o.K. -0.K.1p. -0.K.1q. -0.K.1r. -0.K.1s. -0.1t.K. -2.1u.K. -0.K.1v. -3.K.1w. -0.K.1x. -0.K.1y. -0.K.1z. -0.K.1A. -0.1B.K. -0.K.1C. -0.K.1D. -0.1E.K. -0.K.1F. -0.K.1G. -0.K.1H. -0.K.1I. -0.K.1J. -0.K.1K. -0.K.1L. -0.K.1M. -0.K.1N. -0.K.1O. -0.K.1P. -3.K.1Q. -0.K.1R. -0.K.1S. -0.K.1T. -0.K.1U. -0.K.1V. -0.K.1W. -0.K.1X. -0.K.1Y. -0.K.1Z. -0.K.1+. -0.K.1/. -0.K.20. -0.K.21. -0.K.22. -0.K.23. -0.K.24. -0.K.25. -b.26.K. -0.K.27. -4.K.28. -0.K.2a. -0.K.2b. -0.K.2l. -6.2m.K. -0.K.2p. -2.2q.K. -2.2r.K. -7.2s.K. -7.2t.K. -2.2u.K. -0.K.2v. -0.K.2w. -e.2x.K. -9.2y.K. -0.K.2z. -3.K.2A. -9.2B.K. -0.K.2C. -0.K.2D. -0.K.2E. -0.K.2F. -0.K.2G. -9.2H.K. -0.K.2I. -3.K.2J. -0.K.2K. -c.2M.K. -9.2N.K. -2.2O.K. -9.2Q.K. -7.2S.K. -3.K.2T. -1.2U.K. -9.2V.K. -1.2W.K. -0.K.2X. -b.2+.K. -0.K.30. -0.K.35. -3.36.K. -0.K.37. -0.K.38. -5.39.K. -0.K.3d. -3.K.3h. -0.K.3i. -0.K.3m. -0.K.3p. -a.3v.K. -a.3y.K. -2.3z.K. -5.3A.K. -2.3B.K. -0.K.3C. -a.3F.K. -5.3G.K. -0.K.3M. -0.K.3Q. -5.40.K. -0.K.47. -0.K.4d. -0.K.4O. -3.K.4Q. -5.4T.K. -3.K.4V. -0.K.51. -0.K.52. -a.5a.K. -2.5b.K. -5.5d.K. -0.K.5B. -0.K.5C. -0.K.5F. -3.K.5H. -0.K.5I. -f.K.5K. -7.5M.K. -0.K.5O. -0.K.5R. -0.K.5U. -0.K.5W. -1.5Y.K. -0.K.5Z. -0.K.5/. -0.K.60. -2.62.K. -2.66.K. -a.6b.K. -c.6e.K. -0.K.6F. -0.K.6M. -0.K.6+. -3.K.71. -5.79.K. -3.K.7D. -0.K.2p. -0.K.2p. +0.m.16. +0.o.14. +0.p.13. +0.Q.F. +0.r.11. +0.s.10. +0.y.Z. +3.J.M. +3.n.15. +4.e.1d. +4.i.1a. +4.k.18. +4.S.D. +4.t./. +4.u.+. +c.T.C. +c.Y.z. +e.9.1h. +i.c.1g. +j.1j.7. +0.13.q. +0.1h.a. +0.A.W. +0.B.V. +0.C.U. +0.h.1c. +0.i.1b. +0.k.19. +0.l.18. 0.L.L. -2.M.L. +0.m.17. +0.N.H. +0.o.15. +0.p.14. +0.r.12. +0.R.F. +0.s.11. +0.t.10. +0.v.+. +3.M.K. +3.n.16. +4.f.1d. +4.G.O. +4.j.1a. +4.S.E. +4.u./. +4.Z.z. +c.T.D. +e.9.1i. +j.1j.8. +0.14.q. +0.1h.b. +0.A.X. +0.B.W. +0.C.V. +0.d.1e. +0.D.U. +0.G.P. +0.i.1c. +0.l.19. +0.o.16. +0.O.H. +0.p.15. +0.s.12. +0.t.11. +0.u.10. +3.I.N. +3.M.L. +3.n.17. +4.1i.a. +4.g.1d. +4.j.1b. +4.k.1a. +4.m.18. +4.S.F. +4.v./. +4.w.+. +c.T.E. +j.1j.9. +0.15.q. +0.1i.b. +0.B.X. +0.C.W. +0.D.V. +0.e.1e. +0.E.U. +0.G.Q. +0.h.1d. +0.H.P. +0.j.1c. +0.k.1b. +0.l.1a. +0.m.19. +0.o.17. +0.p.16. +0.t.12. +0.u.11. +0.v.10. +0.x.+. +3.I.O. +3.J.N. +3.M.M. +3.n.18. +4.d.1f. +4.w./. +c.T.F. +c.Y.A. +i.c.1h. +j.1j.a. +0.16.q. +0.A.Z. +0.C.X. +0.d.1g. +0.D.W. +0.E.V. +0.f.1e. +0.G.R. +0.H.Q. +0.k.1c. +0.l.1b. +0.m.1a. +0.N.K. +0.p.17. +0.r.13. +0.u.12. +0.U.F. +0.v.11. +0.w.10. +0.y.+. +3.I.P. +3.J.O. +3.n.19. +4.e.1f. +4.i.1d. +4.o.18. +4.x./. +c.Y.B. +i.c.1i. +j.1j.b. +0.+.z. +0.17.q. +0.B.Z. +0.D.X. +0.e.1g. +0.E.W. +0.g.1e. +0.H.R. +0.l.1c. 0.L.N. +0.m.1b. +0.o.19. +0.O.K. +0.p.18. +0.r.14. +0.s.13. +0.v.12. +0.V.F. +0.w.11. +0.x.10. +3.I.Q. +3.J.P. +3.n.1a. +4.f.1f. +4.j.1d. +4.S.G. +4.y./. +c.Y.C. +i.c.1j. +0./.z. +0.18.q. +0.C.Z. +0.E.X. +0.f.1g. +0.g.1f. +0.K.P. 0.L.O. +0.m.1c. +0.o.1a. +0.p.19. +0.r.15. +0.s.14. +0.t.13. +0.w.12. +0.W.F. +0.x.11. +0.y.10. +3.I.R. +3.J.Q. +3.M.N. +3.n.1b. +4.h.1e. +4.k.1d. +4.S.H. +c.T.G. +c.Y.D. +0.10.z. +0.19.q. +0.g.1g. +0.h.1f. +0.i.1e. +0.K.Q. 0.L.P. +0.o.1b. +0.p.1a. +0.r.16. +0.s.15. +0.t.14. +0.u.13. +0.x.12. +0.X.F. +0.y.11. +3.J.R. +3.M.O. +3.n.1c. +4.d.1h. +4.D.Z. +4.G.U. +4.I.S. +4.l.1d. +c.T.H. +c.Y.E. +0.11.z. +0.1a.q. +0.A.+. +0.i.1f. +0.j.1e. +0.K.R. 0.L.Q. +0.o.1c. +0.p.1b. +0.s.16. +0.t.15. +0.u.14. +0.U.H. +0.v.13. +0.y.12. +3.I.T. +3.M.P. +4.d.1i. +4.e.1h. +4.E.Z. +4.G.V. +4.h.1g. +4.J.S. +4.m.1d. +c.Y.F. +i.c.1k. +j.17.r. +0.12.z. +0.1b.q. +0.A./. +0.B.+. +0.i.1g. +0.k.1e. 0.L.R. -3.S.L. -b.T.L. -0.L.U. -0.L.V. -0.L.W. -0.L.X. -b.Y.L. -0.L.Z. -0.L.+. -0.L./. -0.L.10. -0.L.11. -0.L.12. -0.L.13. -0.L.14. -0.L.15. -0.L.16. -0.L.17. -0.L.18. -0.L.19. -0.L.1a. -0.L.1b. -0.L.1c. -0.L.1d. -0.L.1e. -0.L.1f. -0.L.1g. -0.L.1h. -0.L.1i. -0.L.1j. -0.L.1k. -2.1l.L. -0.L.1m. -0.L.1n. -0.L.1o. -0.L.1p. -0.L.1q. -0.L.1r. -0.L.1s. -0.L.1t. -2.1u.L. -0.L.1v. -0.L.1w. -0.L.1x. -0.L.1y. -0.L.1z. -0.L.1A. -0.1B.L. -0.L.1C. -0.L.1D. -0.L.1E. -0.L.1F. -0.L.1G. -0.L.1H. -0.L.1I. -0.L.1J. -0.L.1K. -0.L.1L. -0.L.1M. -0.L.1N. -0.L.1O. -0.L.1P. -0.L.1Q. -0.L.1R. -0.L.1S. -0.L.1T. -0.L.1U. -0.L.1V. -0.L.1W. -0.L.1X. -0.L.1Y. -0.L.1Z. -0.L.1+. -0.L.1/. -0.L.20. -0.L.21. -0.L.22. -0.L.23. -0.L.24. -0.L.25. -b.26.L. -0.L.27. -0.L.28. -0.L.2a. -0.L.2b. -0.L.2l. -6.2m.L. -0.L.2p. -2.2q.L. -2.2r.L. -7.2s.L. -7.2t.L. -2.2u.L. -0.L.2v. -0.L.2w. -e.2x.L. -9.2y.L. -0.L.2z. -0.L.2A. -9.2B.L. -0.L.2C. -0.L.2D. -0.L.2E. -0.L.2F. -0.L.2G. -9.2H.L. -0.L.2I. -0.L.2J. -0.L.2K. -c.2M.L. -9.2N.L. -2.2O.L. -9.2Q.L. -7.2S.L. -0.L.2T. -1.2U.L. -9.2V.L. -1.2W.L. -0.L.2X. -b.2+.L. -0.L.30. -0.L.35. -b.36.L. -0.L.37. -0.L.38. -5.39.L. -0.L.3d. -0.L.3h. -0.L.3i. -0.L.3m. -0.L.3p. -a.3v.L. -a.3y.L. -2.3z.L. -5.3A.L. -2.3B.L. -0.L.3C. -a.3F.L. -5.3G.L. -0.L.3M. -0.L.3Q. -5.40.L. -0.L.47. -0.L.4d. -0.L.4O. -0.L.4Q. -5.4T.L. -0.L.4V. -0.L.51. -0.L.52. -a.5a.L. -2.5b.L. -5.5d.L. -0.L.5B. -0.L.5C. -0.L.5F. -0.L.5H. -0.L.5I. -f.L.5K. -7.5M.L. -0.L.5O. -0.L.5R. -0.L.5U. -0.L.5W. -1.5Y.L. -0.L.5Z. -0.L.5/. -0.L.60. -2.62.L. -2.66.L. -a.6b.L. -c.6e.L. -0.L.6F. -0.L.6M. -0.L.6+. -0.L.71. -5.79.L. -0.L.7D. -0.L.2p. -0.L.2p. -2.M.M. -2.M.N. -2.M.O. -2.M.P. -2.M.Q. -2.M.R. -3.S.M. -2.M.T. -2.M.U. -2.M.V. -2.M.W. -2.M.X. -2.M.Y. -2.M.Z. -2.M.+. -2.M./. -2.M.10. -2.M.11. -2.M.12. -2.M.13. -2.M.14. -2.M.15. -2.M.16. -2.M.17. -2.M.18. -2.M.19. -2.M.1a. -2.M.1b. -2.M.1c. -2.M.1d. -2.M.1e. -2.M.1f. -2.M.1g. -2.M.1h. -2.M.1i. -2.M.1j. -2.M.1k. -2.1l.M. -2.M.1m. -2.M.1n. -2.M.1o. -2.M.1p. -2.M.1q. -2.M.1r. -2.M.1s. -2.M.1t. -2.1u.M. -2.M.1v. -2.M.1w. -2.M.1x. -2.M.1y. -2.M.1z. -2.M.1A. -2.M.1B. -2.M.1C. -2.M.1D. -2.M.1E. -2.M.1F. -2.M.1G. -2.M.1H. -2.M.1I. -2.M.1J. -2.M.1K. -2.M.1L. -2.M.1M. -2.M.1N. -2.M.1O. -2.M.1P. -2.M.1Q. -2.M.1R. -2.M.1S. -2.M.1T. -2.M.1U. -2.M.1V. -2.M.1W. -2.M.1X. -2.M.1Y. -2.M.1Z. -2.M.1+. -2.M.1/. -2.M.20. -2.M.21. -2.M.22. -2.M.23. -2.M.24. -2.M.25. -2.M.26. -2.M.27. -2.M.28. -2.M.2a. -2.M.2b. -2.M.2l. -6.2m.M. -2.M.2p. -2.M.2q. -2.M.2r. -7.2s.M. -7.2t.M. -2.2u.M. -2.M.2v. -2.M.2w. -e.2x.M. -2.M.2y. -2.M.2z. -2.M.2A. -2.M.2B. -2.M.2C. -2.M.2D. -2.M.2E. -2.M.2F. -2.M.2G. -2.M.2H. -2.M.2I. -2.M.2J. -2.M.2K. -c.2M.M. -2.M.2N. -2.M.2O. -2.M.2Q. -7.2S.M. -2.M.2T. -1.2U.M. -2.M.2V. -1.2W.M. -2.M.2X. -2.M.2+. -2.M.30. -2.M.35. -2.M.36. -2.M.37. -2.M.38. -2.M.39. -2.M.3d. -2.M.3h. -2.M.3i. -2.M.3m. -2.M.3p. -a.3v.M. -a.3y.M. -2.3z.M. -2.M.3A. -2.3B.M. -2.M.3C. -a.3F.M. -2.M.3G. -2.M.3M. -2.M.3Q. -2.M.40. -2.M.47. -2.M.4d. -2.M.4O. -2.M.4Q. -5.4T.M. -2.M.4V. -2.M.51. -2.M.52. -a.5a.M. -2.5b.M. -2.M.5d. -2.M.5B. -2.M.5C. -2.M.5F. -2.M.5H. -2.M.5I. -2.M.5K. -7.5M.M. -2.M.5O. -2.M.5R. -2.M.5U. -2.M.5W. -1.5Y.M. -2.M.5Z. -2.M.5/. -2.M.60. -2.M.62. -2.M.66. -a.6b.M. -c.6e.M. -2.M.6F. -2.M.6M. -2.M.6+. -2.M.71. -2.M.79. -2.M.7D. -2.M.2p. -2.M.2p. 0.N.N. +0.p.1c. +0.r.18. +0.s.17. +0.t.16. +0.u.15. +0.v.14. +0.V.H. +0.w.13. +3.1l.c. +3.I.U. +3.J.T. +3.M.Q. +3.n.1d. +4.d.1j. +4.e.1i. +4.f.1h. +4.F.Z. +4.G.W. +4.j.1f. +4.S.K. +0.1c.q. +0.A.10. +0.B./. +0.C.+. +0.G.X. +0.j.1g. +0.l.1e. 0.O.N. +0.r.19. +0.t.17. +0.u.16. +0.v.15. +0.w.14. +0.W.H. +0.x.13. +3.I.V. +3.J.U. +3.M.R. +4.e.1j. +4.f.1i. +4.g.1h. +4.k.1f. +4.o.1d. +4.s.18. +4.S.L. +c.T.K. +i.c.1m. +0.A.11. +0.B.10. +0.C./. +0.D.+. +0.h.1h. +0.k.1g. +0.l.1f. +0.m.1e. 0.N.P. +0.r.1a. +0.s.19. +0.t.18. +0.u.17. +0.U.K. +0.v.16. +0.w.15. +0.x.14. +0.X.H. +0.y.13. +3.I.W. +3.J.V. +4.f.1j. +4.g.1i. +4.p.1d. +4.S.M. +c.T.L. +c.Y.G. +e.O.O. +i.c.1n. +0.13.z. +0.1d.q. +0.A.12. +0.B.11. +0.C.10. +0.D./. +0.d.1k. +0.E.+. +0.G.Z. +0.h.1i. +0.l.1g. +0.L.U. +0.m.1f. 0.N.Q. -0.N.R. -3.S.N. -b.T.N. -0.U.N. -0.V.N. -0.W.N. -0.X.N. -b.Y.N. -0.N.Z. -0.+.N. -0./.N. -0.N.10. -0.N.11. -0.N.12. -0.N.13. -0.N.14. -0.N.15. -0.N.16. -0.N.17. -0.N.18. -0.N.19. -0.N.1a. -0.N.1b. -0.N.1c. -3.N.1d. -0.N.1e. -0.N.1f. -0.N.1g. -3.N.1h. -3.N.1i. -0.1j.N. -0.1k.N. -2.1l.N. -0.1m.N. -0.1n.N. -0.1o.N. -0.N.1p. -0.N.1q. -0.N.1r. -0.N.1s. -0.1t.N. -2.1u.N. -0.N.1v. -3.N.1w. -0.N.1x. -0.N.1y. -0.N.1z. -0.N.1A. -0.1B.N. -0.N.1C. -0.N.1D. -0.1E.N. -0.N.1F. -0.N.1G. -0.N.1H. -0.N.1I. -0.N.1J. -0.N.1K. -0.N.1L. -0.N.1M. -0.N.1N. -0.N.1O. -0.N.1P. -0.N.1Q. -0.N.1R. -0.N.1S. -0.N.1T. -0.N.1U. -0.N.1V. -0.N.1W. -0.N.1X. -0.N.1Y. -0.N.1Z. -0.N.1+. -0.N.1/. -0.N.20. -0.N.21. -0.N.22. -0.N.23. -0.N.24. -0.N.25. -b.26.N. -0.N.27. -4.N.28. -0.N.2a. -0.N.2b. -0.N.2l. -6.2m.N. -0.N.2p. -2.2q.N. -2.2r.N. -7.2s.N. -7.2t.N. -2.2u.N. -0.N.2v. -0.N.2w. -e.2x.N. -9.2y.N. -0.N.2z. -3.N.2A. -9.2B.N. -0.N.2C. -0.N.2D. -3.N.2E. -0.N.2F. -0.N.2G. -9.2H.N. -0.N.2I. -3.N.2J. -0.N.2K. -c.2M.N. -9.2N.N. -2.2O.N. -9.2Q.N. -7.2S.N. -0.N.2T. -1.2U.N. -9.2V.N. -1.2W.N. -0.N.2X. -b.2+.N. -0.N.30. -0.N.35. -b.36.N. -0.N.37. -3.N.38. -5.39.N. -0.N.3d. -3.N.3h. -3.N.3i. -0.N.3m. -0.N.3p. -a.3v.N. -a.3y.N. -2.3z.N. -5.3A.N. -2.3B.N. -0.N.3C. -a.3F.N. -5.3G.N. -0.N.3M. -0.N.3Q. -5.40.N. -0.N.47. -0.N.4d. -0.N.4O. -3.N.4Q. -5.4T.N. -3.N.4V. -0.N.51. -0.N.52. -a.5a.N. -2.5b.N. -5.5d.N. -0.N.5B. -0.N.5C. -0.N.5F. -3.N.5H. -0.N.5I. -f.N.5K. -7.5M.N. -0.N.5O. -3.N.5R. -0.N.5U. -0.N.5W. -1.5Y.N. -0.N.5Z. -0.N.5/. -0.N.60. -2.62.N. -2.66.N. -a.6b.N. -c.6e.N. -0.N.6F. -0.N.6M. -0.N.6+. -3.N.71. -5.79.N. -3.N.7D. -0.N.2p. -0.N.2p. -0.O.O. 0.O.P. +0.r.1b. +0.s.1a. +0.t.19. +0.u.18. +0.v.17. +0.V.K. +0.w.16. +0.x.15. +0.y.14. +3.I.X. +3.J.W. +3.M.T. +3.n.1e. +4.g.1j. +4.i.1h. +c.Y.H. +i.c.1o. +0.+.F. +0.14.z. +0.B.12. +0.C.11. +0.D.10. +0.E./. +0.e.1k. +0.H.Z. +0.L.V. +0.m.1g. +0.N.R. +0.o.1e. 0.O.Q. -0.O.R. -3.S.O. -b.T.O. -0.O.U. -0.O.V. -0.O.W. -0.O.X. -b.Y.O. -0.O.Z. -0.O.+. -0.O./. -0.O.10. -0.O.11. -0.O.12. -0.O.13. -0.O.14. -0.O.15. -0.O.16. -0.O.17. -3.O.18. -0.O.19. -0.O.1a. -0.O.1b. -0.O.1c. -3.O.1d. -0.O.1e. -0.O.1f. -0.O.1g. -3.O.1h. -3.O.1i. -3.O.1j. -0.O.1k. -2.1l.O. -0.O.1m. -0.O.1n. -0.O.1o. -3.O.1p. -0.O.1q. -0.O.1r. -0.O.1s. -0.O.1t. -2.1u.O. -0.O.1v. -3.O.1w. -0.O.1x. -0.O.1y. -0.O.1z. -0.O.1A. -0.1B.O. -0.O.1C. -0.O.1D. -0.O.1E. -0.O.1F. -0.O.1G. -0.O.1H. -0.O.1I. -0.O.1J. -0.O.1K. -0.O.1L. -0.O.1M. -0.O.1N. -0.O.1O. -0.O.1P. -3.O.1Q. -0.O.1R. -0.O.1S. -0.O.1T. -3.O.1U. -0.O.1V. -0.O.1W. -0.O.1X. -0.O.1Y. -0.O.1Z. -0.O.1+. -0.O.1/. -0.O.20. -0.O.21. -0.O.22. -0.O.23. -0.O.24. -0.O.25. -b.26.O. -0.O.27. -4.O.28. -0.O.2a. -0.O.2b. -0.O.2l. -6.2m.O. -0.O.2p. -2.2q.O. -2.2r.O. -7.2s.O. -7.2t.O. -2.2u.O. -3.O.2v. -0.O.2w. -e.2x.O. -9.2y.O. -0.O.2z. -3.O.2A. -9.2B.O. -0.O.2C. -0.O.2D. -3.O.2E. -3.O.2F. -0.O.2G. -9.2H.O. -0.O.2I. -3.O.2J. -0.O.2K. -c.2M.O. -9.2N.O. -2.2O.O. -9.2Q.O. -7.2S.O. -0.O.2T. -1.2U.O. -9.2V.O. -1.2W.O. -0.O.2X. -b.2+.O. -0.O.30. -0.O.35. -b.36.O. -0.O.37. -3.O.38. -5.39.O. -0.O.3d. -3.O.3h. -3.O.3i. -0.O.3m. -0.O.3p. -a.3v.O. -a.3y.O. -2.3z.O. -5.3A.O. -2.3B.O. -0.O.3C. -a.3F.O. -5.3G.O. -0.O.3M. -0.O.3Q. -5.40.O. -0.O.47. -0.O.4d. -3.O.4O. -3.O.4Q. -5.4T.O. -3.O.4V. -0.O.51. -0.O.52. -a.5a.O. -2.5b.O. -5.5d.O. -0.O.5B. -0.O.5C. -0.O.5F. -3.O.5H. -0.O.5I. -f.O.5K. -7.5M.O. -0.O.5O. -3.O.5R. -0.O.5U. -0.O.5W. -1.5Y.O. -0.O.5Z. -0.O.5/. -0.O.60. -2.62.O. -2.66.O. -a.6b.O. -c.6e.O. -0.O.6F. -0.O.6M. -0.O.6+. -3.O.71. -5.79.O. -3.O.7D. -0.O.2p. -0.O.2p. 0.P.P. +0.r.1c. +0.s.1b. +0.t.1a. +0.u.19. +0.v.18. +0.w.17. +0.W.K. +0.x.16. +0.y.15. +3.1l.d. +3.I.Y. +3.J.X. +3.M.U. +3.n.1f. +4.h.1j. +4.i.1i. +4.j.1h. +i.c.1p. +0./.F. +0.15.z. +0.1C.0. +0.C.12. +0.D.11. +0.d.1m. +0.E.10. +0.f.1k. +0.L.W. +0.o.1f. +0.O.R. +0.p.1e. 0.P.Q. -0.R.P. -3.S.P. -b.T.P. -0.U.P. -0.V.P. -0.W.P. -0.X.P. -b.Y.P. -0.P.Z. -0.+.P. -0./.P. -0.P.10. -0.P.11. -0.P.12. -0.13.P. -0.14.P. -0.15.P. -0.16.P. -0.17.P. -0.18.P. -0.19.P. -0.1a.P. -0.1b.P. -0.1c.P. -0.1d.P. -0.P.1e. -0.P.1f. -0.P.1g. -3.P.1h. -3.P.1i. -0.1j.P. -0.1k.P. -2.1l.P. -0.1m.P. -0.1n.P. -0.1o.P. -0.1p.P. -0.1q.P. -0.1r.P. -0.1s.P. -0.1t.P. -2.1u.P. -0.1v.P. -0.1w.P. -0.1x.P. -0.1y.P. -0.1z.P. -0.1A.P. -0.1B.P. -0.1C.P. -0.1D.P. -0.1E.P. -0.1F.P. -0.1G.P. -0.1H.P. -0.1I.P. -0.P.1J. -0.P.1K. -0.P.1L. -0.P.1M. -0.P.1N. -0.P.1O. -0.P.1P. -3.P.1Q. -0.P.1R. -0.P.1S. -0.P.1T. -3.P.1U. -0.P.1V. -0.P.1W. -0.P.1X. -0.P.1Y. -0.P.1Z. -0.P.1+. -0.P.1/. -0.P.20. -0.P.21. -0.P.22. -0.P.23. -0.P.24. -0.P.25. -b.26.P. -0.P.27. -4.P.28. -0.P.2a. -0.P.2b. -0.P.2l. -6.2m.P. -0.P.2p. -2.2q.P. -2.2r.P. -7.2s.P. -7.2t.P. -2.2u.P. -0.P.2v. -0.P.2w. -e.2x.P. -9.2y.P. -0.P.2z. -3.P.2A. -9.2B.P. -0.P.2C. -0.P.2D. -3.P.2E. -0.P.2F. -0.P.2G. -9.2H.P. -0.P.2I. -3.P.2J. -0.P.2K. -c.2M.P. -9.2N.P. -2.2O.P. -9.2Q.P. -7.2S.P. -0.P.2T. -1.2U.P. -9.2V.P. -1.2W.P. -0.P.2X. -b.2+.P. -0.P.30. -0.P.35. -b.36.P. -0.P.37. -0.P.38. -5.39.P. -0.P.3d. -3.P.3h. -0.P.3i. -0.P.3m. -0.P.3p. -a.3v.P. -a.3y.P. -2.3z.P. -5.3A.P. -2.3B.P. -0.P.3C. -a.3F.P. -5.3G.P. -0.P.3M. -0.P.3Q. -5.40.P. -0.P.47. -0.P.4d. -0.P.4O. -3.P.4Q. -5.4T.P. -3.P.4V. -0.P.51. -0.P.52. -a.5a.P. -2.5b.P. -5.5d.P. -0.P.5B. -0.P.5C. -0.P.5F. -3.P.5H. -0.P.5I. -f.P.5K. -7.5M.P. -0.P.5O. -3.P.5R. -0.P.5U. -0.P.5W. -1.5Y.P. -0.P.5Z. -0.P.5/. -0.P.60. -2.62.P. -2.66.P. -a.6b.P. -c.6e.P. -0.P.6F. -0.P.6M. -0.P.6+. -3.P.71. -5.79.P. -3.P.7D. -0.P.2p. -0.P.2p. +0.s.1c. +0.t.1b. +0.u.1a. +0.v.19. +0.w.18. +0.x.17. +0.X.K. +0.y.16. +3.1l.e. +3.I.Z. +3.J.Y. +3.M.V. +3.n.1g. +4.i.1j. +4.j.1i. +4.k.1h. +4.S.N. +i.c.1q. +0.10.F. +0.16.z. +0.1C.1. +0.A.13. +0.D.12. +0.d.1n. +0.E.11. +0.e.1m. +0.g.1k. +0.L.X. +0.o.1g. +0.p.1f. +0.q.1e. 0.Q.Q. +0.r.1d. +0.R.P. +0.u.1b. +0.v.1a. +0.w.19. +0.x.18. +0.y.17. +3.1l.f. +3.J.Z. +3.M.W. +4.j.1j. +4.k.1i. +4.l.1h. +4.S.O. +4.t.1c. +c.T.N. +c.Y.K. +i.c.1r. +0.11.F. +0.17.z. +0.A.14. +0.B.13. +0.d.1o. +0.E.12. +0.e.1n. +0.f.1m. +0.h.1k. +0.K.Z. +0.p.1g. +0.q.1f. 0.R.Q. -3.S.Q. -b.T.Q. -0.U.Q. -0.V.Q. -0.W.Q. -0.X.Q. -b.Y.Q. -0.Q.Z. -0.+.Q. -0./.Q. -0.Q.10. -0.Q.11. -0.Q.12. -0.13.Q. -0.14.Q. -0.15.Q. -0.16.Q. -0.17.Q. -0.18.Q. -0.19.Q. -0.1a.Q. -0.1b.Q. -0.1c.Q. -0.1d.Q. -0.Q.1e. -0.Q.1f. -0.Q.1g. -3.Q.1h. -3.Q.1i. -0.1j.Q. -0.1k.Q. -2.1l.Q. -0.1m.Q. -0.1n.Q. -0.1o.Q. -0.1p.Q. -0.1q.Q. -0.1r.Q. -0.1s.Q. -3.1t.Q. -2.1u.Q. -0.1v.Q. -0.1w.Q. -0.1x.Q. -0.1y.Q. -0.1z.Q. -0.1A.Q. -0.1B.Q. -0.1C.Q. -0.1D.Q. -0.1E.Q. -0.1F.Q. -0.1G.Q. -0.1H.Q. -0.1I.Q. -0.Q.1J. -0.Q.1K. -0.Q.1L. -0.Q.1M. -0.Q.1N. -0.Q.1O. -0.Q.1P. -0.Q.1Q. -0.Q.1R. -0.Q.1S. -0.Q.1T. -0.Q.1U. -0.Q.1V. -0.Q.1W. -0.Q.1X. -0.Q.1Y. -0.Q.1Z. -0.Q.1+. -0.Q.1/. -0.Q.20. -0.Q.21. -0.Q.22. -0.Q.23. -0.Q.24. -0.Q.25. -b.26.Q. -0.Q.27. -4.Q.28. -0.Q.2a. -0.Q.2b. -0.Q.2l. -6.2m.Q. -0.Q.2p. -2.2q.Q. -2.2r.Q. -7.2s.Q. -7.2t.Q. -2.2u.Q. -3.Q.2v. -0.Q.2w. -e.2x.Q. -9.2y.Q. -0.Q.2z. -3.Q.2A. -9.2B.Q. -0.Q.2C. -0.Q.2D. -3.Q.2E. -0.Q.2F. -0.Q.2G. -9.2H.Q. -0.Q.2I. -3.Q.2J. -0.Q.2K. -c.2M.Q. -9.2N.Q. -2.2O.Q. -9.2Q.Q. -7.2S.Q. -0.Q.2T. -1.2U.Q. -9.2V.Q. -1.2W.Q. -0.Q.2X. -b.2+.Q. -0.Q.30. -0.Q.35. -b.36.Q. -0.Q.37. -0.Q.38. -5.39.Q. -0.Q.3d. -3.Q.3h. -0.Q.3i. -0.Q.3m. -0.Q.3p. -a.3v.Q. -a.3y.Q. -2.3z.Q. -5.3A.Q. -2.3B.Q. -0.Q.3C. -a.3F.Q. -5.3G.Q. -0.Q.3M. -0.Q.3Q. -5.40.Q. -0.Q.47. -0.Q.4d. -0.Q.4O. -3.Q.4Q. -5.4T.Q. -3.Q.4V. -0.Q.51. -0.Q.52. -a.5a.Q. -2.5b.Q. -5.5d.Q. -0.Q.5B. -0.Q.5C. -0.Q.5F. -3.Q.5H. -0.Q.5I. -f.Q.5K. -7.5M.Q. -0.Q.5O. -3.Q.5R. -0.Q.5U. -0.Q.5W. -1.5Y.Q. -0.Q.5Z. -0.Q.5/. -0.Q.60. -2.62.Q. -2.66.Q. -a.6b.Q. -c.6e.Q. -0.Q.6F. -0.Q.6M. -0.Q.6+. -3.Q.71. -5.79.Q. -3.Q.7D. -0.Q.2p. -0.Q.2p. +0.s.1d. +0.U.N. +0.v.1b. +0.w.1a. +0.x.19. +0.y.18. +3.1l.g. +3.M.X. +4.1C.2. +4.G.+. +4.k.1j. +4.l.1i. +4.m.1h. +4.S.P. +4.u.1c. +c.T.O. +c.Y.L. +i.c.1s. +0.+.H. +0.12.F. +0.18.z. +0.1C.3. +0.A.15. +0.B.14. +0.C.13. +0.d.1p. +0.e.1o. +0.f.1n. +0.g.1m. +0.i.1k. +0.L.Z. +0.O.U. +0.q.1g. 0.R.R. -3.S.R. -b.T.R. +0.t.1d. +0.v.1c. +0.V.N. +0.w.1b. +0.x.1a. +0.y.19. +3.1l.h. +3.M.Y. +3.n.1h. +4.G./. +4.l.1j. +4.m.1i. +4.S.Q. +c.T.P. +i.c.1t. +0./.H. +0.19.z. +0.A.16. +0.B.15. +0.C.14. +0.D.13. +0.e.1p. +0.f.1o. +0.G.10. +0.g.1n. +0.h.1m. +0.O.V. +0.r.1e. +0.u.1d. +0.U.P. +0.w.1c. +0.W.N. +0.x.1b. +0.y.1a. +3.1l.i. +3.1u.c. +3.I.+. +3.M.Z. +3.n.1i. +4.d.1q. +4.j.1k. +4.m.1j. +4.o.1h. +4.S.R. +c.T.Q. +0.1a.z. +0.1C.5. +0.A.17. +0.B.16. +0.C.15. +0.D.14. +0.E.13. +0.e.1q. +0.f.1p. +0.G.11. +0.g.1o. +0.H.10. +0.h.1n. +0.i.1m. +0.k.1k. +0.O.W. +0.r.1f. +0.s.1e. +0.U.Q. +0.v.1d. +0.V.P. +0.X.N. +0.y.1b. +3.1l.j. +3.I./. +3.J.+. +3.n.1j. +4.d.1r. +4.o.1i. +4.p.1h. +4.x.1c. +c.T.R. +e.S.S. +i.c.1v. +0.+.K. +0.13.F. +0.1b.z. +0.1C.6. +0.B.17. +0.C.16. +0.D.15. +0.E.14. +0.e.1r. +0.f.1q. +0.G.12. +0.g.1p. +0.H.11. +0.h.1o. +0.i.1n. +0.j.1m. +0.l.1k. +0.O.X. +0.r.1g. +0.s.1f. +0.t.1e. 0.U.R. +0.V.Q. +0.w.1d. +0.W.P. +0.y.1c. +3.1l.k. +3.I.10. +3.J./. +4.A.18. +4.d.1s. +4.o.1j. +4.p.1i. +4.q.1h. +4.S.T. +c.Y.N. +i.c.1w. +0./.K. +0.14.F. +0.1c.z. +0.A.19. +0.B.18. +0.C.17. +0.D.16. +0.d.1t. +0.E.15. +0.e.1s. +0.f.1r. +0.g.1q. +0.H.12. +0.h.1p. +0.i.1o. +0.j.1n. +0.k.1m. +0.L.+. +0.m.1k. +0.N.Z. +0.s.1g. +0.t.1f. 0.V.R. +0.W.Q. +0.x.1d. +0.X.P. +3.1l.l. +3.I.11. +3.J.10. +4.p.1j. +4.q.1i. +4.S.U. +4.u.1e. +c.T.T. +c.Y.O. +i.c.1x. +0.1C.8. +0.1j.q. +0.A.1a. +0.B.19. +0.D.17. +0.E.16. +0.f.1s. +0.g.1r. +0.h.1q. +0.i.1p. +0.j.1o. +0.K.10. +0.k.1n. +0.L./. +0.l.1m. +0.O.Z. +0.u.1f. +0.v.1e. 0.W.R. +0.X.Q. +0.y.1d. +3.1l.m. +3.1u.d. +3.I.12. +3.J.11. +3.M.+. +3.n.1k. +4.15.F. +4.C.18. +4.e.1t. +4.S.V. +4.t.1g. +c.T.U. +c.Y.P. +i.c.1y. +0.16.F. +0.1d.z. +0.A.1b. +0.B.1a. +0.C.19. +0.d.1v. +0.E.17. +0.f.1t. +0.G.13. +0.g.1s. +0.h.1r. +0.i.1q. +0.K.11. +0.k.1o. +0.L.10. +0.l.1n. +0.m.1m. +0.o.1k. +0.P.Z. +0.r.1h. +0.v.1f. +0.w.1e. 0.X.R. -b.Y.R. -0.R.Z. -0.+.R. -0./.R. -0.R.10. -0.R.11. -0.R.12. -0.13.R. -0.14.R. -0.15.R. -0.16.R. -0.17.R. -0.18.R. -0.19.R. -0.1a.R. -0.1b.R. -0.1c.R. -0.1d.R. -0.R.1e. -0.R.1f. -0.R.1g. -0.R.1h. -3.R.1i. -0.1j.R. -0.1k.R. -2.1l.R. -0.1m.R. -0.1n.R. -0.1o.R. -0.1p.R. -0.1q.R. -0.1r.R. -0.1s.R. -0.1t.R. -2.1u.R. -0.1v.R. -0.1w.R. -0.1x.R. -0.1y.R. -0.1z.R. -0.1A.R. -0.1B.R. -0.1C.R. -0.1D.R. -0.1E.R. -0.1F.R. -0.1G.R. -0.1H.R. -0.1I.R. -0.R.1J. -0.R.1K. -0.R.1L. -0.R.1M. -0.R.1N. -0.R.1O. -0.R.1P. -0.R.1Q. -0.R.1R. -0.R.1S. -0.R.1T. -0.R.1U. -0.R.1V. -0.R.1W. -0.R.1X. -0.R.1Y. -0.R.1Z. -0.R.1+. -0.R.1/. -0.R.20. -0.R.21. -0.R.22. -0.R.23. -0.R.24. -0.R.25. -b.26.R. -0.R.27. -4.R.28. -0.R.2a. -0.R.2b. -0.R.2l. -6.2m.R. -0.R.2p. -2.2q.R. -2.2r.R. -7.2s.R. -7.2t.R. -2.2u.R. -0.R.2v. -0.R.2w. -e.2x.R. -9.2y.R. -0.R.2z. -3.R.2A. -9.2B.R. -0.R.2C. -0.R.2D. -0.R.2E. -0.R.2F. -0.R.2G. -9.2H.R. -0.R.2I. -3.R.2J. -0.R.2K. -c.2M.R. -9.2N.R. -2.2O.R. -9.2Q.R. -7.2S.R. -0.R.2T. -1.2U.R. -9.2V.R. -1.2W.R. -0.R.2X. -b.2+.R. -0.R.30. -0.R.35. -b.36.R. -0.R.37. -0.R.38. -5.39.R. -0.R.3d. -3.R.3h. -0.R.3i. -0.R.3m. -0.R.3p. -a.3v.R. -a.3y.R. -2.3z.R. -5.3A.R. -2.3B.R. -0.R.3C. -a.3F.R. -5.3G.R. -0.R.3M. -0.R.3Q. -5.40.R. -0.R.47. -0.R.4d. -0.R.4O. -3.R.4Q. -5.4T.R. -3.R.4V. -0.R.51. -0.R.52. -a.5a.R. -2.5b.R. -5.5d.R. -0.R.5B. -0.R.5C. -0.R.5F. -3.R.5H. -0.R.5I. -f.R.5K. -7.5M.R. -0.R.5O. -0.R.5R. -0.R.5U. -0.R.5W. -1.5Y.R. -0.R.5Z. -0.R.5/. -0.R.60. -2.62.R. -2.66.R. -a.6b.R. -c.6e.R. -0.R.6F. -0.R.6M. -0.R.6+. -0.R.71. -5.79.R. -3.R.7D. -0.R.2p. -0.R.2p. -3.S.S. -3.S.T. -3.S.U. -3.S.V. -3.S.W. -3.S.X. -3.S.Y. -3.S.Z. -3.S.+. -3.S./. -3.S.10. -3.S.11. -3.S.12. -3.S.13. -3.S.14. -3.S.15. -2.S.16. -3.S.17. -3.S.18. -2.S.19. -2.S.1a. -3.S.1b. -3.S.1c. -3.S.1d. -3.S.1e. -3.S.1f. -3.S.1g. -3.S.1h. -3.S.1i. -3.S.1j. -3.S.1k. -3.1l.S. -3.S.1m. -3.S.1n. -3.S.1o. -3.S.1p. -3.S.1q. -3.S.1r. -3.S.1s. -3.S.1t. -3.1u.S. -3.S.1v. -3.S.1w. -3.S.1x. -3.S.1y. -3.S.1z. -3.S.1A. -3.S.1B. -3.S.1C. -3.S.1D. -3.S.1E. -3.S.1F. -3.S.1G. -3.S.1H. -3.S.1I. -3.S.1J. -3.S.1K. -2.S.1L. -3.S.1M. -2.S.1N. -2.S.1O. -2.S.1P. -3.S.1Q. -2.S.1R. -3.S.1S. -2.S.1T. -3.S.1U. -3.S.1V. -2.S.1W. -2.S.1X. -3.S.1Y. -2.S.1Z. -2.S.1+. -2.S.1/. -3.S.20. -3.S.21. -3.S.22. -3.S.23. -2.S.24. -2.S.25. -2.S.26. -3.S.27. -2.S.28. -3.S.2a. -2.S.2b. -2.S.2l. -6.2m.S. -2.S.2p. -2.S.2q. -2.S.2r. -7.2s.S. -7.2t.S. -2.2u.S. -2.S.2v. -2.S.2w. -e.2x.S. -2.S.2y. -2.S.2z. -2.S.2A. -2.S.2B. -2.S.2C. -2.S.2D. -2.S.2E. -2.S.2F. -2.S.2G. -2.S.2H. -2.S.2I. -2.S.2J. -2.S.2K. -c.2M.S. -2.S.2N. -2.S.2O. -2.S.2Q. -7.2S.S. -2.S.2T. -1.2U.S. -2.S.2V. -1.2W.S. -2.S.2X. -2.S.2+. -2.S.30. -2.S.35. -2.S.36. -2.S.37. -2.S.38. -2.S.39. -2.S.3d. -2.S.3h. -3.S.3i. -2.S.3m. -2.S.3p. -a.3v.S. -a.3y.S. -2.3z.S. -2.S.3A. -2.3B.S. -2.S.3C. -a.3F.S. -2.S.3G. -2.S.3M. -2.S.3Q. -2.S.40. -2.S.47. -2.S.4d. -2.S.4O. -2.S.4Q. -5.4T.S. -2.S.4V. -2.S.51. -2.S.52. -a.5a.S. -2.5b.S. -2.S.5d. -2.S.5B. -2.S.5C. -3.S.5F. -2.S.5H. -2.S.5I. -2.S.5K. -7.5M.S. -2.S.5O. -2.S.5R. -2.S.5U. -2.S.5W. -1.5Y.S. -2.S.5Z. -2.S.5/. -2.S.60. -2.S.62. -2.S.66. -a.6b.S. -c.6e.S. -2.S.6F. -2.S.6M. -2.S.6+. -2.S.71. -3.S.79. -2.S.7D. -2.S.2p. -2.S.2p. -b.T.T. -b.T.U. -b.T.V. -b.T.W. -b.T.X. -b.T.Y. -b.T.Z. -b.T.+. -b.T./. -b.T.10. -b.T.11. -b.T.12. -b.T.13. -b.T.14. -b.T.15. -b.T.16. -b.T.17. -b.T.18. -b.T.19. -b.T.1a. -b.T.1b. -b.T.1c. -b.T.1d. -b.T.1e. -b.T.1f. -b.T.1g. -b.T.1h. -b.T.1i. -b.T.1j. -b.T.1k. -2.1l.T. -b.T.1m. -b.T.1n. -b.T.1o. -b.T.1p. -b.T.1q. -b.T.1r. -b.T.1s. -b.T.1t. -2.1u.T. -b.T.1v. -b.T.1w. -b.T.1x. -b.T.1y. -b.T.1z. -b.T.1A. -b.T.1B. -b.T.1C. -b.T.1D. -b.T.1E. -b.T.1F. -b.T.1G. -b.T.1H. -b.T.1I. -b.T.1J. -b.T.1K. -b.T.1L. -b.T.1M. -b.T.1N. -b.T.1O. -b.T.1P. -b.T.1Q. -b.T.1R. -b.T.1S. -b.T.1T. -b.T.1U. -b.T.1V. -b.T.1W. -b.T.1X. -b.T.1Y. -b.T.1Z. -b.T.1+. -b.T.1/. -b.T.20. -b.T.21. -b.T.22. -b.T.23. -b.T.24. -b.T.25. -b.26.T. -b.T.27. -b.T.28. -b.T.2a. -b.T.2b. -b.T.2l. -6.2m.T. -b.T.2p. -2.2q.T. -2.2r.T. -7.2s.T. -7.2t.T. -2.2u.T. -b.T.2v. -b.T.2w. -e.2x.T. -9.2y.T. -b.T.2z. -b.T.2A. -9.2B.T. -b.T.2C. -3.T.2D. -b.T.2E. -b.T.2F. -b.T.2G. -9.2H.T. -b.T.2I. -b.T.2J. -b.T.2K. -c.2M.T. -9.2N.T. -2.2O.T. -9.2Q.T. -7.2S.T. -b.T.2T. -1.2U.T. -9.2V.T. -1.2W.T. -b.T.2X. -b.2+.T. -b.T.30. -b.T.35. -b.36.T. -b.T.37. -b.T.38. -5.39.T. -b.T.3h. -b.T.3i. -b.T.3m. -b.T.3p. -a.3v.T. -a.3y.T. -2.3z.T. -5.3A.T. -2.3B.T. -b.T.3C. -a.3F.T. -5.3G.T. -b.T.3M. -b.T.3Q. -5.40.T. -b.T.47. -b.T.4d. -b.T.4O. -b.T.4Q. -5.4T.T. -b.T.4V. -b.T.51. -b.T.52. -a.5a.T. -2.5b.T. -5.5d.T. -b.T.5B. -b.T.5C. -b.T.5F. -b.T.5H. -b.T.5I. -b.T.5K. -7.5M.T. -b.T.5O. -b.T.5R. -b.T.5U. -b.T.5W. -1.5Y.T. -b.T.5Z. -b.T.5/. -3.T.60. -2.62.T. -2.66.T. -a.6b.T. -c.6e.T. -b.T.6F. -b.T.6M. -b.T.6+. -b.T.71. -5.79.T. -b.T.7D. -b.T.2p. -b.T.2p. -3.U.U. +3.1l.n. +3.1u.e. +3.J.12. +3.M./. +4.D.18. +4.j.1p. +4.S.W. +4.u.1g. +4.U.U. +c.T.V. +c.Y.Q. +i.c.1z. +0.17.F. +0.A.1c. +0.B.1b. +0.D.19. +0.e.1v. +0.G.14. +0.g.1t. +0.H.13. +0.h.1s. +0.i.1r. +0.j.1q. +0.K.12. +0.k.1p. +0.L.11. +0.l.1o. +0.m.1n. +0.p.1k. +0.Q.Z. +0.r.1i. 0.U.V. -0.W.U. -0.U.X. -b.Y.U. -0.U.Z. -0.U.+. -0.U./. -0.U.10. -0.U.11. -0.U.12. -0.U.13. -0.U.14. -0.U.15. -0.U.16. -0.U.17. -3.U.18. -0.U.19. -0.U.1a. -0.U.1b. -0.U.1c. -3.U.1d. -0.U.1e. -3.U.1f. -0.U.1g. -3.U.1h. -3.U.1i. -0.1j.U. -0.U.1k. -2.1l.U. -0.U.1m. -0.U.1n. -0.U.1o. -0.U.1p. -0.U.1q. -0.U.1r. -0.U.1s. -0.U.1t. -2.1u.U. -0.U.1v. -3.U.1w. -0.U.1x. -0.U.1y. -0.U.1z. -0.U.1A. -3.1B.U. -0.U.1C. -0.U.1D. -0.U.1E. -0.U.1F. -0.U.1G. -0.U.1H. -0.U.1I. -0.U.1J. -0.U.1K. -0.U.1L. -0.U.1M. -0.U.1N. -0.U.1O. -0.U.1P. -3.U.1Q. -0.U.1R. -3.U.1S. -3.U.1T. -3.U.1U. -0.U.1V. -0.U.1W. -0.U.1X. -0.U.1Y. -0.U.1Z. -0.U.1+. -0.U.1/. -0.U.20. -0.U.21. -0.U.22. -0.U.23. -0.U.24. -0.U.25. -b.26.U. -0.U.27. -4.U.28. -0.U.2a. -0.U.2b. -0.U.2l. -6.2m.U. -0.U.2p. -2.2q.U. -2.2r.U. -7.2s.U. -7.2t.U. -2.2u.U. -3.U.2v. -0.U.2w. -e.2x.U. -9.2y.U. -0.U.2z. -3.U.2A. -9.2B.U. -0.U.2C. -0.U.2D. -3.U.2E. -3.U.2F. -0.U.2G. -9.2H.U. -0.U.2I. -3.U.2J. -0.U.2K. -c.2M.U. -9.2N.U. -2.2O.U. -9.2Q.U. -7.2S.U. -0.U.2T. -1.2U.U. -9.2V.U. -1.2W.U. -0.U.2X. -b.2+.U. -0.U.30. -0.U.35. -b.36.U. -0.U.37. -0.U.38. -5.39.U. -0.U.3d. -3.U.3h. -3.U.3i. -0.U.3m. -0.U.3p. -a.3v.U. -a.3y.U. -2.3z.U. -5.3A.U. -2.3B.U. -0.U.3C. -a.3F.U. -5.3G.U. -0.U.3M. -0.U.3Q. -5.40.U. -0.U.47. -0.U.4d. -3.U.4O. -3.U.4Q. -5.4T.U. -3.U.4V. -0.U.51. -0.U.52. -a.5a.U. -2.5b.U. -5.5d.U. -0.U.5B. -0.U.5C. -0.U.5F. -3.U.5H. -0.U.5I. -f.U.5K. -7.5M.U. -0.U.5O. -3.U.5R. -0.U.5U. -0.U.5W. -1.5Y.U. -0.U.5Z. -0.U.5/. -0.U.60. -2.62.U. -2.66.U. -a.6b.U. -c.6e.U. -0.U.6F. -0.U.6M. -0.U.6+. -3.U.71. -5.79.U. -3.U.7D. -0.U.2p. -0.U.2p. +0.w.1f. +0.x.1e. +3.1l.o. +3.1u.f. +3.M.10. +3.n.1m. +4.C.1a. +4.d.1w. +4.E.18. +4.s.1h. +4.S.X. +4.v.1g. +c.T.W. +c.Y.R. +e.1C.a. +i.c.1A. +0.+.N. +0.18.F. +0.1k.q. +0.B.1c. +0.C.1b. +0.E.19. +0.f.1v. +0.G.15. +0.H.14. +0.h.1t. +0.j.1r. +0.k.1q. +0.L.12. +0.l.1p. +0.m.1o. +0.o.1m. +0.r.1j. +0.R.Z. +0.t.1h. 0.V.V. +0.w.1g. +0.W.U. +0.x.1f. +0.y.1e. +3.1l.p. +3.1u.g. +3.I.13. +3.M.11. +3.n.1n. +4.D.1a. +4.d.1x. +4.e.1w. +4.i.1s. +4.s.1i. +4.S.Y. +c.T.X. +e.1C.b. +i.c.1B. +0./.N. +0.19.F. +0.1e.z. +0.C.1c. +0.D.1b. +0.E.1a. +0.e.1x. +0.G.16. +0.g.1v. +0.H.15. +0.i.1t. +0.k.1r. +0.l.1q. +0.m.1p. +0.O.+. +0.o.1n. +0.p.1m. +0.t.1i. +0.u.1h. +0.U.X. 0.W.V. +0.y.1f. +3.1l.q. +3.1u.h. +3.I.14. +3.J.13. +3.M.12. +3.n.1o. +4.A.1d. +4.d.1y. +4.f.1w. +4.j.1s. +4.s.1j. +4.S.Z. +4.x.1g. +c.T.Y. +i.c.1C. +0.+.P. +0.1a.F. +0.1f.z. +0.1m.q. +0.D.1c. +0.d.1z. +0.E.1b. +0.e.1y. +0.f.1x. +0.G.17. +0.H.16. +0.h.1v. +0.j.1t. +0.K.13. +0.l.1r. +0.m.1q. +0.N.10. +0.O./. +0.o.1o. +0.p.1n. +0.u.1i. +0.v.1h. 0.V.X. -b.Y.V. -0.V.Z. -0.V.+. -0.V./. -0.V.10. -0.V.11. -0.V.12. -0.V.13. -0.V.14. -0.V.15. -0.V.16. -0.V.17. -0.V.18. -0.V.19. -0.V.1a. -0.V.1b. -0.V.1c. -3.V.1d. -0.V.1e. -0.V.1f. -0.V.1g. -3.V.1h. -3.V.1i. -0.1j.V. -0.V.1k. -2.1l.V. -0.V.1m. -0.V.1n. -0.V.1o. -0.V.1p. -0.V.1q. -0.V.1r. -0.V.1s. -0.V.1t. -2.1u.V. -0.V.1v. -0.V.1w. -0.V.1x. -0.V.1y. -0.V.1z. -0.V.1A. -3.1B.V. -0.V.1C. -0.V.1D. -0.V.1E. -0.V.1F. -0.V.1G. -0.V.1H. -0.V.1I. -0.V.1J. -0.V.1K. -0.V.1L. -0.V.1M. -0.V.1N. -0.V.1O. -0.V.1P. -0.V.1Q. -0.V.1R. -0.V.1S. -0.V.1T. -3.V.1U. -0.V.1V. -0.V.1W. -0.V.1X. -0.V.1Y. -0.V.1Z. -0.V.1+. -0.V.1/. -0.V.20. -0.V.21. -0.V.22. -0.V.23. -0.V.24. -0.V.25. -b.26.V. -0.V.27. -4.V.28. -0.V.2a. -0.V.2b. -0.V.2l. -6.2m.V. -0.V.2p. -2.2q.V. -2.2r.V. -7.2s.V. -7.2t.V. -2.2u.V. -3.V.2v. -0.V.2w. -e.2x.V. -9.2y.V. -0.V.2z. -3.V.2A. -9.2B.V. -0.V.2C. -0.V.2D. -3.V.2E. -3.V.2F. -0.V.2G. -9.2H.V. -0.V.2I. -3.V.2J. -0.V.2K. -c.2M.V. -9.2N.V. -2.2O.V. -9.2Q.V. -7.2S.V. -0.V.2T. -1.2U.V. -9.2V.V. -1.2W.V. -0.V.2X. -b.2+.V. -0.V.30. -0.V.35. -b.36.V. -0.V.37. -0.V.38. -5.39.V. -0.V.3d. -3.V.3h. -0.V.3i. -0.V.3m. -0.V.3p. -a.3v.V. -a.3y.V. -2.3z.V. -5.3A.V. -2.3B.V. -0.V.3C. -a.3F.V. -5.3G.V. -0.V.3M. -0.V.3Q. -5.40.V. -0.V.47. -0.V.4d. -3.V.4O. -3.V.4Q. -5.4T.V. -3.V.4V. -0.V.51. -0.V.52. -a.5a.V. -2.5b.V. -5.5d.V. -0.V.5B. -0.V.5C. -0.V.5F. -3.V.5H. -0.V.5I. -f.V.5K. -7.5M.V. -0.V.5O. -3.V.5R. -0.V.5U. -0.V.5W. -1.5Y.V. -0.V.5Z. -0.V.5/. -0.V.60. -2.62.V. -2.66.V. -a.6b.V. -c.6e.V. -0.V.6F. -0.V.6M. -0.V.6+. -3.V.71. -5.79.V. -3.V.7D. -0.V.2p. -0.V.2p. 0.W.W. +3.1u.i. +3.I.15. +3.J.14. +3.n.1p. +4.B.1d. +4.g.1w. +4.k.1s. +4.t.1j. +4.y.1g. +c.T.Z. +c.Y.U. +i.c.1D. +0./.P. +0.+.Q. +0.1b.F. +0.1n.q. +0.d.1A. +0.E.1c. +0.e.1z. +0.f.1y. +0.G.18. +0.g.1x. +0.H.17. +0.h.1w. +0.i.1v. +0.K.14. +0.k.1t. +0.L.13. +0.l.1s. +0.m.1r. +0.N.11. +0.O.10. +0.o.1p. +0.p.1o. +0.r.1k. +0.U.Z. +0.v.1i. 0.W.X. -b.Y.W. -0.W.Z. -0.W.+. -0.W./. -0.W.10. -0.W.11. -0.W.12. -0.W.13. -0.W.14. -0.W.15. -0.W.16. -0.W.17. -3.W.18. -0.W.19. -0.W.1a. -0.W.1b. -0.W.1c. -3.W.1d. -0.W.1e. -0.W.1f. -0.W.1g. -3.W.1h. -3.W.1i. -0.1j.W. -0.W.1k. -2.1l.W. -0.W.1m. -0.W.1n. -0.W.1o. -0.W.1p. -0.W.1q. -0.W.1r. -0.W.1s. -0.W.1t. -2.1u.W. -0.W.1v. -3.W.1w. -0.W.1x. -0.W.1y. -0.W.1z. -0.W.1A. -0.1B.W. -0.W.1C. -0.W.1D. -0.W.1E. -0.W.1F. -0.W.1G. -0.W.1H. -0.W.1I. -0.W.1J. -0.W.1K. -0.W.1L. -0.W.1M. -0.W.1N. -0.W.1O. -0.W.1P. -0.W.1Q. -0.W.1R. -0.W.1S. -0.W.1T. -0.W.1U. -0.W.1V. -0.W.1W. -0.W.1X. -0.W.1Y. -0.W.1Z. -0.W.1+. -0.W.1/. -0.W.20. -0.W.21. -0.W.22. -0.W.23. -0.W.24. -0.W.25. -b.26.W. -0.W.27. -4.W.28. -0.W.2a. -0.W.2b. -0.W.2l. -6.2m.W. -0.W.2p. -2.2q.W. -2.2r.W. -7.2s.W. -7.2t.W. -2.2u.W. -0.W.2v. -0.W.2w. -e.2x.W. -9.2y.W. -0.W.2z. -3.W.2A. -9.2B.W. -0.W.2C. -0.W.2D. -3.W.2E. -3.W.2F. -0.W.2G. -9.2H.W. -0.W.2I. -3.W.2J. -0.W.2K. -c.2M.W. -9.2N.W. -2.2O.W. -9.2Q.W. -7.2S.W. -0.W.2T. -1.2U.W. -9.2V.W. -1.2W.W. -0.W.2X. -b.2+.W. -0.W.30. -0.W.35. -b.36.W. -0.W.37. -0.W.38. -5.39.W. -0.W.3d. -3.W.3h. -0.W.3i. -0.W.3m. -0.W.3p. -a.3v.W. -a.3y.W. -2.3z.W. -5.3A.W. -2.3B.W. -0.W.3C. -a.3F.W. -5.3G.W. -0.W.3M. -0.W.3Q. -5.40.W. -0.W.47. -0.W.4d. -0.W.4O. -3.W.4Q. -5.4T.W. -3.W.4V. -0.W.51. -0.W.52. -a.5a.W. -2.5b.W. -5.5d.W. -0.W.5B. -0.W.5C. -0.W.5F. -3.W.5H. -0.W.5I. -f.W.5K. -7.5M.W. -0.W.5O. -0.W.5R. -0.W.5U. -0.W.5W. -1.5Y.W. -0.W.5Z. -0.W.5/. -0.W.60. -2.62.W. -2.66.W. -a.6b.W. -c.6e.W. -0.W.6F. -0.W.6M. -0.W.6+. -3.W.71. -5.79.W. -3.W.7D. -0.W.2p. -0.W.2p. +3.1u.j. +3.I.16. +3.J.15. +3.n.1q. +4.C.1d. +4.u.1j. +4.w.1h. +c.Y.V. +h.1g.z. +0./.Q. +0.+.R. +0.1o.q. +0.A.1e. +0.d.1B. +0.e.1A. +0.f.1z. +0.G.19. +0.g.1y. +0.h.1x. +0.j.1v. +0.K.15. +0.L.14. +0.l.1t. +0.m.1s. +0.N.12. +0.O.11. +0.o.1q. +0.P.10. +0.p.1p. +0.s.1k. +0.V.Z. +0.w.1i. +0.x.1h. 0.X.X. -b.Y.X. +3.1l.r. +3.1u.k. +3.I.17. +3.J.16. +3.M.13. +3.n.1r. +4.1c.F. +4.D.1d. +4.H.18. +4.i.1w. +4.v.1j. +c.Y.W. +i.c.1E. +0./.R. +0.1p.q. +0.A.1f. +0.B.1e. +0.d.1C. +0.e.1B. +0.f.1A. +0.G.1a. +0.g.1z. +0.H.19. +0.h.1y. +0.i.1x. +0.K.16. +0.k.1v. +0.L.15. +0.m.1t. +0.O.12. +0.o.1r. +0.P.11. +0.p.1q. +0.Q.10. +0.r.1m. +0.t.1k. +0.W.Z. +0.x.1i. +0.y.1h. +3.1l.s. +3.1u.l. +3.I.18. +3.J.17. +3.M.14. +3.n.1s. +4.E.1d. +4.j.1w. +4.S.+. +4.w.1j. +c.Y.X. +i.c.1F. +0.1d.F. +0.1h.z. +0.1q.q. +0.A.1g. +0.B.1f. +0.C.1e. +0.d.1D. +0.e.1C. +0.f.1B. +0.g.1A. +0.G.1b. +0.H.1a. +0.h.1z. +0.i.1y. +0.K.17. +0.L.16. +0.l.1v. +0.P.12. +0.p.1r. +0.Q.11. +0.R.10. +0.r.1n. +0.s.1m. +0.u.1k. 0.X.Z. -0.+.X. -0./.X. -0.X.10. -0.X.11. -0.X.12. -0.X.13. -0.X.14. -0.X.15. -0.X.16. -0.X.17. -0.X.18. -0.X.19. -0.X.1a. -0.X.1b. -0.X.1c. -3.X.1d. -0.X.1e. -0.X.1f. -0.X.1g. -3.X.1h. -3.X.1i. -0.1j.X. -0.1k.X. -2.1l.X. -0.1m.X. -0.1n.X. -0.1o.X. -0.X.1p. -0.X.1q. -0.X.1r. -0.X.1s. -0.1t.X. -2.1u.X. -0.X.1v. -3.X.1w. -0.X.1x. -0.X.1y. -0.X.1z. -0.X.1A. -0.1B.X. -0.X.1C. -0.X.1D. -0.1E.X. -0.X.1F. -0.X.1G. -0.X.1H. -0.X.1I. -0.X.1J. -0.X.1K. -0.X.1L. -0.X.1M. -0.X.1N. -0.X.1O. -0.X.1P. -3.X.1Q. -0.X.1R. -0.X.1S. -0.X.1T. -0.X.1U. -0.X.1V. -0.X.1W. -0.X.1X. -0.X.1Y. -0.X.1Z. -0.X.1+. -0.X.1/. -0.X.20. -0.X.21. -0.X.22. -0.X.23. -0.X.24. -0.X.25. -b.26.X. -0.X.27. -4.X.28. -0.X.2a. -0.X.2b. -0.X.2l. -6.2m.X. -0.X.2p. -2.2q.X. -2.2r.X. -7.2s.X. -7.2t.X. -2.2u.X. -0.X.2v. -0.X.2w. -e.2x.X. -9.2y.X. -0.X.2z. -3.X.2A. -9.2B.X. -0.X.2C. -0.X.2D. -3.X.2E. -3.X.2F. -0.X.2G. -9.2H.X. -0.X.2I. -3.X.2J. -0.X.2K. -c.2M.X. -9.2N.X. -2.2O.X. -9.2Q.X. -7.2S.X. -0.X.2T. -1.2U.X. -9.2V.X. -1.2W.X. -0.X.2X. -b.2+.X. -0.X.30. -0.X.35. -b.36.X. -0.X.37. -0.X.38. -5.39.X. -0.X.3d. -3.X.3h. -0.X.3i. -0.X.3m. -0.X.3p. -a.3v.X. -a.3y.X. -2.3z.X. -5.3A.X. -2.3B.X. -0.X.3C. -a.3F.X. -5.3G.X. -0.X.3M. -0.X.3Q. -5.40.X. -0.X.47. -0.X.4d. -0.X.4O. -3.X.4Q. -5.4T.X. -3.X.4V. -0.X.51. -0.X.52. -a.5a.X. -2.5b.X. -5.5d.X. -0.X.5B. -0.X.5C. -0.X.5F. -3.X.5H. -0.X.5I. -f.X.5K. -7.5M.X. -0.X.5O. -0.X.5R. -0.X.5U. -0.X.5W. -1.5Y.X. -0.X.5Z. -0.X.5/. -0.X.60. -2.62.X. -2.66.X. -a.6b.X. -c.6e.X. -0.X.6F. -0.X.6M. -0.X.6+. -3.X.71. -5.79.X. -3.X.7D. -0.X.2p. -0.X.2p. -b.Y.Y. -b.Y.Z. -b.Y.+. -b.Y./. -b.Y.10. -b.Y.11. -b.Y.12. -b.Y.13. -b.Y.14. -b.Y.15. -b.Y.16. -b.Y.17. -b.Y.18. -b.Y.19. -b.Y.1a. -b.Y.1b. -b.Y.1c. -b.Y.1d. -b.Y.1e. -b.Y.1f. -b.Y.1g. -b.Y.1h. -b.Y.1i. -b.Y.1j. -b.Y.1k. -2.1l.Y. -b.Y.1m. -b.Y.1n. -b.Y.1o. -b.Y.1p. -b.Y.1q. -b.Y.1r. -b.Y.1s. -b.Y.1t. -2.1u.Y. -b.Y.1v. -b.Y.1w. -b.Y.1x. -b.Y.1y. -b.Y.1z. -b.Y.1A. -b.Y.1B. -b.Y.1C. -b.Y.1D. -b.Y.1E. -b.Y.1F. -b.Y.1G. -b.Y.1H. -b.Y.1I. -b.Y.1J. -b.Y.1K. -b.Y.1L. -b.Y.1M. -b.Y.1N. -b.Y.1O. -b.Y.1P. -b.Y.1Q. -b.Y.1R. -b.Y.1S. -b.Y.1T. -b.Y.1U. -b.Y.1V. -b.Y.1W. -b.Y.1X. -b.Y.1Y. -b.Y.1Z. -b.Y.1+. -b.Y.1/. -b.Y.20. -b.Y.21. -b.Y.22. -b.Y.23. -b.Y.24. -b.Y.25. -b.26.Y. -b.Y.27. -b.Y.28. -b.Y.2a. -b.Y.2b. -b.Y.2l. -6.2m.Y. -b.Y.2p. -2.2q.Y. -2.2r.Y. -7.2s.Y. -7.2t.Y. -2.2u.Y. -b.Y.2v. -b.Y.2w. -e.2x.Y. -9.2y.Y. -b.Y.2z. -b.Y.2A. -9.2B.Y. -b.Y.2C. -b.Y.2D. -b.Y.2E. -b.Y.2F. -b.Y.2G. -9.2H.Y. -b.Y.2I. -b.Y.2J. -b.Y.2K. -c.2M.Y. -9.2N.Y. -2.2O.Y. -9.2Q.Y. -7.2S.Y. -b.Y.2T. -1.2U.Y. -9.2V.Y. -1.2W.Y. -b.Y.2X. -b.2+.Y. -b.Y.30. -b.Y.35. -b.36.Y. -b.Y.37. -b.Y.38. -5.39.Y. -b.Y.3d. -b.Y.3h. -b.Y.3i. -b.Y.3m. -b.Y.3p. -a.3v.Y. -a.3y.Y. -2.3z.Y. -5.3A.Y. -2.3B.Y. -b.Y.3C. -a.3F.Y. -5.3G.Y. -b.Y.3M. -b.Y.3Q. -5.40.Y. -b.Y.47. -b.Y.4d. -b.Y.4O. -b.Y.4Q. -5.4T.Y. -b.Y.4V. -b.Y.51. -b.Y.52. -a.5a.Y. -2.5b.Y. -5.5d.Y. -b.Y.5B. -b.Y.5C. -b.Y.5F. -b.Y.5H. -b.Y.5I. -b.Y.5K. -7.5M.Y. -b.Y.5O. -b.Y.5R. -b.Y.5U. -b.Y.5W. -1.5Y.Y. -b.Y.5Z. -b.Y.5/. -b.Y.60. -2.62.Y. -2.66.Y. -a.6b.Y. -c.6e.Y. -b.Y.6F. -b.Y.6M. -b.Y.6+. -b.Y.71. -5.79.Y. -b.Y.7D. -b.Y.2p. -b.Y.2p. +0.y.1i. +3.1l.t. +3.1u.m. +3.I.19. +3.J.18. +3.M.15. +3.n.1t. +4.j.1x. +4.k.1w. +4.o.1s. +4.S./. +4.x.1j. +c.T.+. +c.Y.Y. +i.c.1G. +0.1i.z. +0.1r.q. +0.B.1g. +0.C.1f. +0.D.1e. +0.e.1D. +0.f.1C. +0.g.1B. +0.h.1A. +0.H.1b. +0.i.1z. +0.j.1y. +0.k.1x. +0.L.17. +0.m.1v. +0.N.13. +0.o.1t. +0.p.1s. +0.Q.12. +0.R.11. +0.r.1o. +0.s.1n. +0.t.1m. +0.U.+. +0.v.1k. +3.1l.u. +3.1u.n. +3.I.1a. +3.J.19. +3.M.16. +4.G.1c. +4.K.18. +4.l.1w. +4.S.10. +4.y.1j. +c.T./. +c.Y.Z. +i.c.1H. +0.1j.z. +0.1s.q. +0.C.1g. +0.d.1E. +0.D.1f. +0.E.1e. +0.f.1D. +0.g.1C. +0.h.1B. +0.H.1c. +0.i.1A. +0.j.1z. +0.K.19. +0.k.1y. +0.L.18. +0.l.1x. +0.N.14. +0.O.13. +0.p.1t. +0.R.12. +0.r.1p. +0.s.1o. +0.t.1n. +0.U./. +0.u.1m. +0.V.+. +0.w.1k. 0.Z.Z. +3.1l.v. +3.1u.o. +3.I.1b. +3.J.1a. +3.M.17. +3.n.1v. +4.m.1w. +4.S.11. +c.T.10. +i.c.1I. +0.13.P. +0.1e.F. +0.1t.q. +0.d.1F. +0.D.1g. +0.e.1E. +0.E.1f. +0.g.1D. +0.G.1d. +0.h.1C. +0.i.1B. +0.j.1A. +0.K.1a. +0.k.1z. +0.L.19. +0.l.1y. +0.m.1x. +0.N.15. +0.O.14. +0.o.1v. +0.r.1q. +0.s.1p. +0.t.1o. +0.U.10. +0.u.1n. +0.V./. +0.v.1m. +0.W.+. +0.x.1k. +3.1l.w. +3.1u.p. +3.I.1c. +3.J.1b. +3.M.18. +3.n.1w. +4.A.1h. +4.S.12. +c.T.11. +i.c.1J. +0.+.X. +0.13.Q. +0.14.P. +0.d.1G. +0.e.1F. +0.E.1g. +0.f.1E. +0.F.1f. +0.h.1D. +0.i.1C. +0.j.1B. +0.k.1A. +0.K.1b. +0.L.1a. +0.l.1z. +0.m.1y. +0.N.16. +0.O.15. +0.p.1v. +0.r.1r. +0.s.1q. +0.t.1p. +0.U.11. +0.u.1o. +0.V.10. +0.v.1n. +0.W./. +0.w.1m. +0.y.1k. +3.1l.x. +3.1u.q. +3.J.1c. +3.M.19. +3.n.1x. +4.A.1i. +4.B.1h. +4.H.1d. +4.o.1w. +c.T.12. +0./.X. +0.13.R. +0.14.Q. +0.15.P. +0.1k.z. +0.1S.0. +0.1v.q. +0.d.1H. +0.e.1G. +0.f.1F. +0.g.1E. +0.i.1D. +0.j.1C. +0.k.1B. +0.K.1c. +0.l.1A. +0.L.1b. +0.m.1z. +0.N.17. +0.O.16. +0.o.1x. +0.p.1w. +0.r.1s. +0.s.1r. +0.t.1q. +0.U.12. +0.u.1p. +0.V.11. +0.v.1o. +0.W.10. +0.w.1n. +0.x.1m. +3.1l.y. +3.I.1d. +3.M.1a. +3.n.1y. +4.A.1j. +4.B.1i. +4.C.1h. +4.F.1g. +c.Y.+. 0.+.Z. +0.14.R. +0.15.Q. +0.16.P. +0.1B.l. +0.1S.1. +0.1w.q. +0.d.1I. +0.e.1H. +0.f.1G. +0.g.1F. +0.j.1D. +0.k.1C. +0.L.1c. +0.m.1A. +0.N.18. +0.O.17. +0.o.1y. +0.p.1x. +0.r.1t. +0.s.1s. +0.t.1r. +0.u.1q. +0.V.12. +0.v.1p. +0.W.11. +0.w.1o. +0.X.10. +0.x.1n. +0.y.1m. +3.1l.z. +3.J.1d. +3.M.1b. +3.n.1z. +4.B.1j. +4.C.1i. +4.D.1h. +4.G.1e. +4.h.1E. +4.S.13. +c.Y./. +i.c.1K. +j.0.1T. 0./.Z. +0.15.R. +0.16.Q. +0.17.P. +0.1B.m. +0.1m.z. +0.1x.q. +0.d.1J. +0.e.1I. +0.f.1H. +0.G.1f. +0.g.1G. +0.H.1e. +0.h.1F. +0.i.1E. +0.k.1D. +0.l.1C. +0.N.19. +0.o.1z. +0.p.1y. +0.s.1t. +0.u.1r. +0.v.1q. +0.W.12. +0.w.1p. +0.X.11. +0.x.1o. +0.y.1n. +2.1.1T. +3.1u.r. +3.M.1c. +3.n.1A. +4.1S.2. +4.C.1j. +4.D.1i. +4.E.1h. +4.K.1d. +4.O.18. +4.S.14. +4.t.1s. +c.T.13. +c.Y.10. +i.c.1L. +j.0.1U. 0.10.Z. +0.16.R. +0.17.Q. +0.18.P. +0.1n.z. +0.1S.3. +0.1V.0. +0.1y.q. +0.A.1k. +0.e.1J. +0.f.1I. +0.g.1H. +0.H.1f. +0.h.1G. +0.i.1F. +0.j.1E. +0.L.1d. +0.l.1D. +0.m.1C. +0.N.1a. +0.O.19. +0.o.1A. +0.p.1z. +0.r.1v. +0.t.1t. +0.U.13. +0.u.1s. +0.v.1r. +0.w.1q. +0.X.12. +0.x.1p. +0.y.1o. +2.1.1U. +2.2.1T. +3.1u.s. +3.I.1e. +4.D.1j. +4.E.1i. +4.F.1h. +4.G.1g. +4.S.15. +c.T.14. +c.Y.11. +e.n.1B. +i.c.1M. 0.11.Z. -3.12.Z. -0.13.Z. -3.14.Z. -3.15.Z. -0.16.Z. -0.17.Z. -3.18.Z. -0.19.Z. -3.1a.Z. -0.1b.Z. -0.1c.Z. -0.1d.Z. -3.1e.Z. -0.1f.Z. -3.Z.1g. -0.1h.Z. -3.1i.Z. -0.1j.Z. -0.1k.Z. -2.1l.Z. -0.1m.Z. -0.1n.Z. -0.1o.Z. -0.1p.Z. -0.1q.Z. -0.1r.Z. -0.1s.Z. -3.1t.Z. -2.1u.Z. -0.1v.Z. -0.1w.Z. -0.1x.Z. -0.1y.Z. -0.1z.Z. -0.1A.Z. -0.1B.Z. -0.1C.Z. -0.1D.Z. -0.1E.Z. -0.1F.Z. -0.1G.Z. -0.1H.Z. -0.1I.Z. -3.1J.Z. -3.1K.Z. -0.1L.Z. -0.1M.Z. -3.1N.Z. -0.1O.Z. -0.1P.Z. -0.Z.1Q. -0.1R.Z. -3.1S.Z. -3.Z.1T. -0.Z.1U. -0.Z.1V. -0.Z.1W. -0.Z.1X. -0.Z.1Y. -0.Z.1Z. -0.Z.1+. -0.Z.1/. -0.Z.20. -0.Z.21. -0.Z.22. -0.Z.23. -0.Z.24. -0.Z.25. -b.26.Z. -0.Z.27. -4.Z.28. -0.Z.2a. -0.Z.2b. -0.Z.2l. -6.2m.Z. -0.Z.2p. -2.2q.Z. -2.2r.Z. -7.2s.Z. -7.2t.Z. -2.2u.Z. -0.Z.2v. -3.Z.2w. -e.2x.Z. -9.2y.Z. -0.Z.2z. -3.Z.2A. -9.2B.Z. -0.Z.2C. -0.Z.2D. -0.Z.2E. -0.Z.2F. -0.Z.2G. -9.2H.Z. -0.Z.2I. -3.Z.2J. -0.Z.2K. -c.2M.Z. -9.2N.Z. -2.2O.Z. -9.2Q.Z. -7.2S.Z. -0.Z.2T. -1.2U.Z. -9.2V.Z. -1.2W.Z. -3.Z.2X. -b.2+.Z. -0.Z.30. -3.Z.35. -b.36.Z. -0.Z.37. -3.Z.38. -5.39.Z. -0.Z.3d. -3.Z.3h. -0.Z.3i. -0.Z.3m. -0.Z.3p. -a.3v.Z. -a.3y.Z. -2.3z.Z. -5.3A.Z. -2.3B.Z. -0.Z.3C. -a.3F.Z. -5.3G.Z. -0.Z.3M. -0.Z.3Q. -5.40.Z. -0.Z.47. -0.Z.4d. -0.Z.4O. -3.Z.4Q. -5.4T.Z. -3.Z.4V. -3.Z.51. -0.Z.52. -a.5a.Z. -2.5b.Z. -5.5d.Z. -0.Z.5B. -0.Z.5C. -5.5F.Z. -0.Z.5H. -0.Z.5I. -f.Z.5K. -7.5M.Z. -3.Z.5O. -0.Z.5R. -0.Z.5U. -0.Z.5W. -1.5Y.Z. -0.Z.5Z. -0.Z.5/. -0.Z.60. -2.62.Z. -2.66.Z. -a.6b.Z. -c.6e.Z. -0.Z.6F. -0.Z.6M. -0.Z.6+. -3.Z.71. -5.79.Z. -0.Z.7D. -0.Z.2p. -0.Z.2p. +0.17.R. +0.18.Q. +0.19.P. +0.1B.o. +0.1o.z. +0.1V.1. +0.1z.q. +0.B.1k. +0.f.1J. +0.g.1I. +0.H.1g. +0.h.1H. +0.i.1G. +0.j.1F. +0.k.1E. +0.m.1D. +0.N.1b. +0.O.1a. +0.p.1A. +0.r.1w. +0.s.1v. +0.U.14. +0.u.1t. +0.V.13. +0.v.1s. +0.w.1r. +0.x.1q. +0.y.1p. +2.2.1U. +2.3.1T. +3.1l.A. +3.1u.t. +3.I.1f. +3.J.1e. +3.M.1d. +3.n.1C. +3.S.16. +4.E.1j. +4.F.1i. +c.T.15. +c.Y.12. +e.4.1S. +i.c.1N. 0.+.+. +0.18.R. +0.19.Q. +0.1a.P. +0.1A.q. +0.1B.p. +0.1j.F. +0.1p.z. +0.1S.5. +0.1V.2. +0.A.1m. +0.C.1k. +0.d.1K. +0.g.1J. +0.h.1I. +0.i.1H. +0.j.1G. +0.K.1e. +0.k.1F. +0.l.1E. +0.N.1c. +0.O.1b. +0.o.1C. +0.r.1x. +0.t.1v. +0.U.15. +0.V.14. +0.v.1t. +0.W.13. +0.w.1s. +0.x.1r. +0.y.1q. +2.3.1U. +2.4.1T. +3.1l.B. +3.1u.u. +3.I.1g. +3.J.1f. +3.n.1D. +4.12.Z. +4.S.17. +4.s.1w. +c.T.16. +i.c.1O. 0./.+. -0.+.10. -0.+.11. -0.+.12. -0.+.13. -0.+.14. -0.+.15. -0.+.16. -0.+.17. -0.+.18. -0.+.19. -0.+.1a. -0.+.1b. -0.+.1c. -0.+.1d. -0.+.1e. -0.+.1f. -0.+.1g. -0.+.1h. -0.+.1i. -0.1j.+. -0.+.1k. -2.1l.+. -0.+.1m. -0.+.1n. -0.+.1o. -0.+.1p. -0.+.1q. -0.+.1r. -0.+.1s. -0.+.1t. -2.1u.+. -0.+.1v. -0.+.1w. -0.+.1x. -0.+.1y. -0.+.1z. -0.+.1A. -0.1B.+. -0.+.1C. -0.+.1D. -0.1E.+. -0.+.1F. -0.+.1G. -0.+.1H. -0.+.1I. -0.+.1J. -0.+.1K. -0.+.1L. -0.+.1M. -0.+.1N. -0.+.1O. -0.+.1P. -0.+.1Q. -0.+.1R. -0.+.1S. -0.+.1T. -0.+.1U. -0.+.1V. -0.+.1W. -0.+.1X. -0.+.1Y. -0.+.1Z. -0.+.1+. -0.+.1/. -0.+.20. -0.+.21. -0.+.22. -0.+.23. -0.+.24. -0.+.25. -b.26.+. -0.+.27. -4.+.28. -0.+.2a. -0.+.2b. -0.+.2l. -6.2m.+. -0.+.2p. -2.2q.+. -2.2r.+. -7.2s.+. -7.2t.+. -2.2u.+. -0.+.2v. -0.+.2w. -e.2x.+. -9.2y.+. -0.+.2z. -0.+.2A. -9.2B.+. -0.+.2C. -0.+.2D. -0.+.2E. -0.+.2F. -0.+.2G. -9.2H.+. -0.+.2I. -0.+.2J. -0.+.2K. -c.2M.+. -9.2N.+. -2.2O.+. -9.2Q.+. -7.2S.+. -0.+.2T. -1.2U.+. -9.2V.+. -1.2W.+. -0.+.2X. -b.2+.+. -0.+.30. -0.+.35. -b.36.+. -0.+.37. -0.+.38. -5.39.+. -0.+.3d. -0.+.3h. -0.+.3i. -0.+.3m. -0.+.3p. -a.3v.+. -a.3y.+. -2.3z.+. -5.3A.+. -2.3B.+. -0.+.3C. -a.3F.+. -5.3G.+. -0.+.3M. -0.+.3Q. -5.40.+. -0.+.47. -0.+.4d. -0.+.4O. -0.+.4Q. -5.4T.+. -0.+.4V. -0.+.51. -0.+.52. -a.5a.+. -2.5b.+. -5.5d.+. -0.+.5B. -0.+.5C. -0.+.5F. -0.+.5H. -0.+.5I. -f.+.5K. -7.5M.+. -0.+.5O. -0.+.5R. -0.+.5U. -0.+.5W. -1.5Y.+. -0.+.5Z. -0.+.5/. -0.+.60. -2.62.+. -2.66.+. -a.6b.+. -c.6e.+. -0.+.6F. -0.+.6M. -0.+.6+. -0.+.71. -5.79.+. -0.+.7D. -0.+.2p. -0.+.2p. +0.19.R. +0.1a.Q. +0.1b.P. +0.1B.q. +0.1q.z. +0.1S.6. +0.1V.3. +0.A.1n. +0.B.1m. +0.D.1k. +0.d.1L. +0.e.1K. +0.G.1h. +0.h.1J. +0.i.1I. +0.j.1H. +0.K.1f. +0.k.1G. +0.L.1e. +0.l.1F. +0.m.1E. +0.O.1c. +0.o.1D. +0.p.1C. +0.r.1y. +0.s.1x. +0.t.1w. +0.U.16. +0.u.1v. +0.V.15. +0.W.14. +0.w.1t. +0.X.13. +0.x.1s. +0.y.1r. +2.4.1U. +2.5.1T. +3.1l.C. +3.1u.v. +3.J.1g. +4.S.18. +c.T.17. +i.c.1P. 0././. +0.+.10. +0.1a.R. +0.1b.Q. +0.1c.P. +0.1C.q. +0.1r.z. +0.A.1o. +0.B.1n. +0.C.1m. +0.d.1M. +0.E.1k. +0.e.1L. +0.f.1K. +0.G.1i. +0.i.1J. +0.j.1I. +0.K.1g. +0.k.1H. +0.L.1f. +0.l.1G. +0.m.1F. +0.p.1D. +0.r.1z. +0.s.1y. +0.t.1x. +0.U.17. +0.u.1w. +0.V.16. +0.v.1v. +0.W.15. +0.X.14. +0.x.1t. +0.y.1s. +2.5.1U. +2.6.1T. +3.1l.D. +3.1u.w. +3.M.1e. +3.n.1E. +3.S.19. +4.H.1h. +4.N.1d. +c.T.18. +c.Y.13. +e.7.1S. +i.c.1Q. 0./.10. +0.+.11. +0.13.Z. +0.1b.R. +0.1c.Q. +0.1D.q. +0.1k.F. +0.1S.8. +0.1s.z. +0.1V.5. +0.A.1p. +0.B.1o. +0.C.1n. +0.D.1m. +0.d.1N. +0.e.1M. +0.f.1L. +0.g.1K. +0.j.1J. +0.k.1I. +0.L.1g. +0.l.1H. +0.m.1G. +0.o.1E. +0.r.1A. +0.s.1z. +0.t.1y. +0.u.1x. +0.V.17. +0.v.1w. +0.W.16. +0.w.1v. +0.X.15. +0.y.1t. +2.6.1U. +2.7.1T. +3.1l.E. +3.1u.x. +3.I.1h. +3.M.1f. +3.n.1F. +3.S.1a. +4.G.1j. +4.H.1i. +4.O.1d. +4.U.18. +c.T.19. +c.Y.14. 0./.11. -0./.12. -0./.13. -0./.14. -0./.15. -0./.16. -0./.17. -3./.18. -0./.19. -0./.1a. -0./.1b. -0./.1c. -3./.1d. -0./.1e. -0./.1f. -0./.1g. -3./.1h. -3./.1i. -0.1j./. -0./.1k. -2.1l./. -0./.1m. -0./.1n. -0./.1o. -3./.1p. -0./.1q. -0./.1r. -0./.1s. -0./.1t. -2.1u./. -0./.1v. -3./.1w. -0./.1x. -0./.1y. -0./.1z. -0./.1A. -3.1B./. -0./.1C. -0./.1D. -0.1E./. -0./.1F. -0./.1G. -0./.1H. -0./.1I. -0./.1J. -0./.1K. -0./.1L. -0./.1M. -0./.1N. -0./.1O. -0./.1P. -3./.1Q. -0./.1R. -0./.1S. -3./.1T. -3./.1U. -0./.1V. -0./.1W. -0./.1X. -0./.1Y. -0./.1Z. -0./.1+. -0./.1/. -0./.20. -0./.21. -0./.22. -0./.23. -0./.24. -0./.25. -b.26./. -0./.27. -4./.28. -4./.29. -0./.2a. -0./.2b. -4./.2c. -4./.2d. -4./.2f. -4./.2g. -4./.2h. -4./.2i. -4./.2j. -0./.2l. -6.2m./. -1./.2o. -0./.2p. -2.2q./. -2.2r./. -7.2s./. -7.2t./. -2.2u./. -3./.2v. -0./.2w. -e.2x./. -9.2y./. -0./.2z. -3./.2A. -9.2B./. -0./.2C. -0./.2D. -3./.2E. -3./.2F. -0./.2G. -9.2H./. -0./.2I. -3./.2J. -0./.2K. -4./.2L. -c.2M./. -9.2N./. -2.2O./. -4./.2P. -9.2Q./. -8./.2R. -7.2S./. -0./.2T. -1.2U./. -9.2V./. -1.2W./. -0./.2X. -8./.2Y. -8./.2Z. -b.2+./. -8./.2/. -0./.30. -3./.31. -3./.32. -8./.33. -3./.34. -0./.35. -b.36./. -0./.37. -0./.38. -5.39./. -4./.3a. -4./.3c. -0./.3d. -1./.3e. -4./.3f. -8./.3g. -3./.3h. -0./.3i. -1./.3j. -3./.3k. -4./.3l. -0./.3m. -3./.3n. -8./.3o. -0./.3p. -3./.3q. -4./.3r. -4./.3s. -8./.3t. -8./.3u. -a.3v./. -4./.3w. -4./.3x. -a.3y./. -2.3z./. -5.3A./. -2.3B./. -0./.3C. -4./.3D. -4./.3E. -a.3F./. -5.3G./. -4./.3H. -4./.3I. -4./.3J. -4./.3K. -4./.3L. -0./.3M. -4./.3N. -4./.3O. -4./.3P. -0./.3Q. -4./.3R. -4./.3S. -1./.3T. -4./.3U. -4./.3V. -4./.3W. -4./.3X. -4./.3Y. -4./.3Z. -1./.3+. -1./.3/. -5.40./. -4./.41. -4./.42. -4./.43. -4./.44. -4./.45. -0./.47. -4./.48. -4./.49. -1./.4a. -4./.4b. -4./.4c. -0./.4d. -4./.4e. -4./.4f. -4./.4g. -4./.4h. -4./.4i. -4./.4j. -3./.4k. -4./.4l. -4./.4m. -4./.4n. -4./.4o. -4./.4p. -4./.4q. -4./.4r. -4./.4s. -4./.4t. -4./.4u. -4./.4v. -4./.4w. -4./.4x. -4./.4y. -4./.4z. -4./.4A. -4./.4B. -4./.4C. -4./.4D. -4./.4E. -1./.4F. -8./.4G. -8./.4H. -1./.4I. -8./.4J. -8./.4K. -8./.4L. -4./.4M. -4./.4N. -3./.4O. -4./.4P. -3./.4Q. -1./.4R. -4./.4S. -4./.4T. -8./.4U. -3./.4V. -4./.4W. -3./.4Y. -1./.4Z. -4./.4+. -4./.4/. -1./.50. -0./.51. -0./.52. -8./.53. -3./.54. -8./.55. -8./.56. -1./.57. -3./.58. -8./.59. -a.5a./. -2.5b./. -4./.5c. -5.5d./. -8./.5e. -8./.5f. -4./.5g. -8./.5h. -4./.5i. -4./.5j. -4./.5k. -4./.5l. -4./.5m. -4./.5n. -4./.5o. -4./.5p. -4./.5q. -4./.5r. -1./.5s. -4./.5t. -4./.5u. -4./.5v. -1./.5w. -1./.5y. -1./.5z. -8./.5A. -0./.5B. -0./.5C. -1./.5D. -8./.5E. -0./.5F. -8./.5G. -3./.5H. -0./.5I. -f./.5K. -3./.5L. -8./.5M. -3./.5N. -0./.5O. -8./.5P. -4./.5Q. -3./.5R. -4./.5S. -4./.5T. -0./.5U. -8./.5V. -0./.5W. -8./.5X. -8./.5Y. -0./.5Z. -0./.5/. -0./.60. -4./.61. -2.62./. -4./.64. -4./.65. -2.66./. -4./.67. -4./.68. -4./.69. -4./.6a. -a.6b./. -4./.6c. -4./.6d. -4./.6e. -4./.6f. -4./.6g. -4./.6h. -4./.6i. -4./.6j. -4./.6k. -4./.6l. -4./.6m. -4./.6n. -4./.6o. -4./.6p. -4./.6q. -4./.6r. -4./.6s. -4./.6t. -4./.6u. -4./.6v. -4./.6w. -4./.6x. -4./.6y. -4./.6z. -4./.6A. -4./.6B. -4./.6C. -4./.6D. -4./.6E. -0./.6F. -4./.6G. -4./.6H. -4./.6I. -4./.6J. -4./.6K. -3./.6L. -0./.6M. -4./.6N. -4./.6O. -4./.6P. -4./.6Q. -4./.6R. -4./.6S. -4./.6T. -4./.6U. -4./.6V. -4./.6W. -1./.6X. -4./.6Y. -1./.6Z. -0./.6+. -1./.6/. -1./.70. -3./.71. -4./.72. -4./.73. -1./.74. -4./.75. -1./.76. -1./.77. -4./.78. -5.79./. -1./.7a. -4./.7b. -4./.7c. -4./.7d. -4./.7e. -4./.7f. -4./.7g. -4./.7h. -4./.7i. -4./.7j. -4./.7k. -4./.7l. -1./.7m. -4./.7o. -4./.7p. -1./.7r. -1./.7s. -8./.7t. -4./.7u. -4./.7v. -4./.7w. -4./.7x. -4./.7y. -1./.7z. -4./.7A. -8./.7B. -4./.7C. -3./.7D. -4./.7E. -4./.7F. -4./.7G. -4./.7H. -4./.7I. -4./.7J. -1./.7K. -1./.7L. -1./.7M. -1./.7N. -1./.7O. -1./.7P. -4./.7Q. -3./.7R. -4./.7S. -4./.7T. -1./.7U. -8./.7V. -4./.7W. -4./.7X. -4./.7Y. -4./.7Z. -1./.7/. -1./.80. -1./.81. -1./.82. -4./.83. -1./.84. -1./.85. -1./.87. -3./.89. -4./.8a. -3./.8b. -4./.8c. -8./.8d. -4./.8e. -4./.8f. -4./.8g. -4./.8h. -4./.8i. -4./.8j. -4./.8k. -4./.8l. -4./.8m. -4./.8n. -4./.8o. -4./.8p. -4./.8q. -4./.8r. -4./.8s. -4./.8t. -4./.8u. -4./.8v. -4./.8w. -4./.8x. -4./.8y. -4./.8z. -4./.8A. -4./.8B. -4./.8C. -4./.8D. -4./.8E. -4./.8F. -4./.8G. -4./.8H. -4./.8I. -4./.8J. -4./.8K. -4./.8L. -4./.8M. -4./.8N. -4./.8O. -4./.8P. -0./.2p. -0./.2p. -8./.3b. -1./.4X. -1./.5x. -1./.5J. -1./.7n. -1./.7q. +0.+.12. 0.10.10. +0.1B.r. +0.1c.R. +0.1d.P. +0.1j.H. +0.1t.z. +0.1V.6. +0.A.1q. +0.B.1p. +0.C.1o. +0.D.1n. +0.d.1O. +0.E.1m. +0.e.1N. +0.f.1M. +0.g.1L. +0.h.1K. +0.k.1J. +0.l.1I. +0.m.1H. +0.o.1F. +0.p.1E. +0.s.1A. +0.t.1z. +0.U.19. +0.u.1y. +0.V.18. +0.v.1x. +0.W.17. +0.w.1w. +0.X.16. +0.x.1v. +2.7.1U. +3.1l.F. +3.1u.y. +3.I.1i. +3.J.1h. +3.M.1g. +3.n.1G. +4.14.Z. +4.S.1b. +c.T.1a. +c.Y.15. +e.9.1S. +j.8.1T. +0./.12. 0.11.10. -0.12.10. -0.13.10. -0.14.10. -0.15.10. -0.16.10. -0.17.10. -0.18.10. -0.19.10. -0.1a.10. -0.1b.10. -0.1c.10. -0.1d.10. -0.10.1e. -0.10.1f. -0.10.1g. -3.10.1h. -3.10.1i. -0.1j.10. -0.1k.10. -2.1l.10. -0.1m.10. -0.1n.10. -0.1o.10. -0.1p.10. -0.1q.10. -0.1r.10. -0.1s.10. -0.1t.10. -2.1u.10. -0.1v.10. -0.1w.10. -0.1x.10. -0.1y.10. -0.1z.10. -0.1A.10. -0.1B.10. -0.1C.10. -0.1D.10. -0.1E.10. -0.1F.10. -0.1G.10. -0.1H.10. -0.1I.10. -0.1J.10. -0.1K.10. -0.1L.10. -0.1M.10. -3.1N.10. -0.1O.10. -0.1P.10. -3.10.1Q. -0.10.1R. -0.10.1S. -0.10.1T. -3.10.1U. -0.10.1V. -0.10.1W. -0.10.1X. -0.10.1Y. -0.10.1Z. -0.10.1+. -0.10.1/. -0.10.20. -0.10.21. -0.10.22. -0.10.23. -0.10.24. -0.10.25. -b.26.10. -0.10.27. -4.10.28. -0.10.2a. -0.10.2b. -0.10.2l. -6.2m.10. -0.10.2p. -2.2q.10. -2.2r.10. -7.2s.10. -7.2t.10. -2.2u.10. -3.10.2v. -0.10.2w. -e.2x.10. -9.2y.10. -0.10.2z. -3.10.2A. -9.2B.10. -0.10.2C. -0.10.2D. -3.10.2E. -3.10.2F. -0.10.2G. -9.2H.10. -0.10.2I. -3.10.2J. -0.10.2K. -c.2M.10. -9.2N.10. -2.2O.10. -9.2Q.10. -7.2S.10. -0.10.2T. -1.2U.10. -9.2V.10. -1.2W.10. -0.10.2X. -b.2+.10. -0.10.30. -0.10.35. -b.36.10. -0.10.37. -3.10.38. -5.39.10. -0.10.3d. -3.10.3h. -0.10.3i. -0.10.3m. -0.10.3p. -a.3v.10. -a.3y.10. -2.3z.10. -5.3A.10. -2.3B.10. -0.10.3C. -a.3F.10. -5.3G.10. -0.10.3M. -0.10.3Q. -5.40.10. -0.10.47. -0.10.4d. -3.10.4O. -3.10.4Q. -5.4T.10. -3.10.4V. -0.10.51. -0.10.52. -a.5a.10. -2.5b.10. -5.5d.10. -0.10.5B. -0.10.5C. -0.10.5F. -3.10.5H. -0.10.5I. -f.10.5K. -7.5M.10. -0.10.5O. -3.10.5R. -0.10.5U. -0.10.5W. -1.5Y.10. -0.10.5Z. -0.10.5/. -0.10.60. -2.62.10. -2.66.10. -a.6b.10. -c.6e.10. -0.10.6F. -0.10.6M. -0.10.6+. -3.10.71. -5.79.10. -3.10.7D. -0.10.2p. -0.10.2p. +0.1B.s. +0.1d.Q. +0.1E.q. +0.1m.F. +0.A.1r. +0.B.1q. +0.C.1p. +0.D.1o. +0.d.1P. +0.E.1n. +0.e.1O. +0.f.1N. +0.g.1M. +0.h.1L. +0.i.1K. +0.l.1J. +0.m.1I. +0.N.1e. +0.o.1G. +0.p.1F. +0.r.1C. +0.t.1A. +0.U.1a. +0.u.1z. +0.V.19. +0.v.1y. +0.w.1x. +0.X.17. +0.x.1w. +0.y.1v. +2.9.1T. +3.1u.z. +3.I.1j. +3.J.1i. +3.n.1H. +4.15.Z. +4.1S.a. +4.K.1h. +4.S.1c. +4.W.18. +c.T.1b. +c.Y.16. +j.8.1U. 0.11.11. +0.12.10. +0.16.Z. +0.1B.t. +0.1d.R. +0.1F.q. +0.1n.F. +0.1S.b. +0.1V.8. +0.1v.z. +0.A.1s. +0.B.1r. +0.C.1q. +0.D.1p. +0.E.1o. +0.e.1P. +0.f.1O. +0.G.1k. +0.g.1N. +0.h.1M. +0.i.1L. +0.j.1K. +0.L.1h. +0.m.1J. +0.N.1f. +0.O.1e. +0.o.1H. +0.p.1G. +0.r.1D. +0.s.1C. +0.u.1A. +0.U.1b. +0.V.1a. +0.v.1z. +0.W.19. +0.w.1y. +0.X.18. +0.x.1x. +0.y.1w. +2.9.1U. +3.J.1j. +3.n.1I. +4.d.1Q. +4.K.1i. +c.T.1c. +c.Y.17. +i.c.1R. +j.a.1T. +0.+.13. 0.11.12. -0.13.11. -0.14.11. -0.15.11. -0.16.11. -0.17.11. -0.18.11. -0.19.11. -0.1a.11. -0.1b.11. -0.1c.11. -0.1d.11. -0.11.1e. -0.11.1f. -0.11.1g. -3.11.1h. -3.11.1i. -0.1j.11. -0.1k.11. -2.1l.11. -0.1m.11. -0.1n.11. -0.1o.11. -0.1p.11. -0.1q.11. -0.1r.11. -0.1s.11. -0.1t.11. -2.1u.11. -0.1v.11. -0.1w.11. -0.1x.11. -0.1y.11. -0.1z.11. -0.1A.11. -0.1B.11. -0.1C.11. -0.1D.11. -0.1E.11. -0.1F.11. -0.1G.11. -0.1H.11. -0.1I.11. -0.11.1J. -0.1K.11. -0.1L.11. -0.1M.11. -0.1N.11. -0.1O.11. -0.1P.11. -0.11.1Q. -0.11.1R. -0.11.1S. -0.11.1T. -0.11.1U. -0.11.1V. -0.11.1W. -0.11.1X. -0.11.1Y. -0.11.1Z. -0.11.1+. -0.11.1/. -0.11.20. -0.11.21. -0.11.22. -0.11.23. -0.11.24. -0.11.25. -b.26.11. -0.11.27. -4.11.28. -0.11.2a. -0.11.2b. -0.11.2l. -6.2m.11. -0.11.2p. -2.2q.11. -2.2r.11. -7.2s.11. -7.2t.11. -2.2u.11. -3.11.2v. -0.11.2w. -e.2x.11. -9.2y.11. -0.11.2z. -3.11.2A. -9.2B.11. -0.11.2C. -0.11.2D. -3.11.2E. -3.11.2F. -0.11.2G. -9.2H.11. -0.11.2I. -3.11.2J. -0.11.2K. -c.2M.11. -9.2N.11. -2.2O.11. -9.2Q.11. -7.2S.11. -0.11.2T. -1.2U.11. -9.2V.11. -1.2W.11. -0.11.2X. -b.2+.11. -0.11.30. -0.11.35. -b.36.11. -0.11.37. -3.11.38. -5.39.11. -0.11.3d. -3.11.3h. -0.11.3i. -0.11.3m. -0.11.3p. -a.3v.11. -a.3y.11. -2.3z.11. -5.3A.11. -2.3B.11. -0.11.3C. -a.3F.11. -5.3G.11. -0.11.3M. -0.11.3Q. -5.40.11. -0.11.47. -0.11.4d. -3.11.4O. -3.11.4Q. -5.4T.11. -3.11.4V. -0.11.51. -0.11.52. -a.5a.11. -2.5b.11. -5.5d.11. -0.11.5B. -0.11.5C. -0.11.5F. -3.11.5H. -0.11.5I. -f.11.5K. -7.5M.11. -0.11.5O. -3.11.5R. -0.11.5U. -0.11.5W. -1.5Y.11. -0.11.5Z. -0.11.5/. -0.11.60. -2.62.11. -2.66.11. -a.6b.11. -c.6e.11. -0.11.6F. -0.11.6M. -0.11.6+. -3.11.71. -5.79.11. -3.11.7D. -0.11.2p. -0.11.2p. +0.17.Z. +0.1B.u. +0.1G.q. +0.1j.K. +0.1k.H. +0.1o.F. +0.1w.z. +0.A.1t. +0.B.1s. +0.C.1r. +0.D.1q. +0.E.1p. +0.f.1P. +0.g.1O. +0.h.1N. +0.i.1M. +0.j.1L. +0.k.1K. +0.L.1i. +0.N.1g. +0.O.1f. +0.o.1I. +0.P.1e. +0.p.1H. +0.s.1D. +0.t.1C. +0.U.1c. +0.v.1A. +0.V.1b. +0.W.1a. +0.w.1z. +0.X.19. +0.x.1y. +0.y.1x. +2.b.1T. +3.1l.G. +3.M.1h. +3.n.1J. +4.e.1Q. +4.S.1d. +c.Y.18. +i.c.1S. +j.a.1U. +0./.13. +0.+.14. 0.12.12. +0.1B.v. +0.1H.q. +0.1p.F. +0.1V.a. +0.1x.z. +0.B.1t. +0.D.1r. +0.E.1q. +0.G.1m. +0.g.1P. +0.h.1O. +0.i.1N. +0.j.1M. +0.k.1L. +0.L.1j. +0.l.1K. +0.O.1g. +0.o.1J. +0.P.1f. +0.p.1I. +0.Q.1e. +0.r.1E. +0.t.1D. +0.u.1C. +0.V.1c. +0.w.1A. +0.W.1b. +0.X.1a. +0.x.1z. +0.y.1y. +2.b.1U. +3.1l.H. +3.1u.A. +3.I.1k. +3.M.1i. +4.18.Z. +4.C.1s. +4.f.1Q. +c.T.1d. +c.Y.19. +0./.14. +0.+.15. +0.13.10. +0.19.Z. +0.1B.w. +0.1I.q. +0.1m.H. +0.1q.F. +0.1y.z. +0.A.1v. +0.C.1t. +0.D.1s. +0.G.1n. +0.h.1P. +0.i.1O. +0.j.1N. +0.k.1M. +0.l.1L. +0.m.1K. +0.P.1g. +0.p.1J. +0.Q.1f. +0.R.1e. +0.r.1F. +0.s.1E. +0.u.1D. +0.v.1C. +0.W.1c. +0.x.1A. +0.X.1b. +0.y.1z. +3.1l.I. +3.1u.B. +3.J.1k. +3.M.1j. +4.E.1r. +4.g.1Q. +4.U.1d. +c.Y.1a. +f.1V.b. +0./.15. +0.+.16. +0.13.11. +0.14.10. +0.1B.x. +0.1J.q. +0.1k.K. +0.1n.H. +0.1r.F. +0.1z.z. +0.B.1v. +0.d.1R. +0.D.1t. +0.G.1o. +0.h.1Q. +0.i.1P. +0.j.1O. +0.l.1M. +0.m.1L. +0.Q.1g. +0.R.1f. +0.r.1G. +0.s.1F. +0.t.1E. +0.v.1D. +0.w.1C. +0.X.1c. +0.y.1A. +3.1l.J. +3.1u.C. +3.I.1m. +3.n.1K. +4.1a.Z. +4.A.1w. +4.E.1s. +4.k.1N. +4.N.1h. +4.S.1e. +4.V.1d. +c.Y.1b. +i.c.1V. +0./.16. +0.+.17. 0.13.12. +0.14.11. +0.15.10. +0.1A.z. +0.1B.y. +0.1b.Z. +0.1o.H. +0.1s.F. +0.A.1x. +0.C.1v. +0.d.1S. +0.e.1R. +0.E.1t. +0.G.1p. +0.i.1Q. +0.j.1P. +0.k.1O. +0.L.1k. +0.l.1N. +0.m.1M. +0.o.1K. +0.R.1g. +0.r.1H. +0.s.1G. +0.t.1F. +0.w.1D. +0.x.1C. +3.1l.K. +3.1u.D. +3.I.1n. +3.J.1m. +3.n.1L. +4.B.1w. +4.N.1i. +4.O.1h. +4.S.1f. +4.u.1E. +4.W.1d. +c.T.1e. +c.Y.1c. +i.c.1W. +0./.17. +0.+.18. 0.14.12. +0.15.11. +0.16.10. +0.1c.Z. +0.1j.N. +0.1m.K. +0.A.1y. +0.B.1x. +0.D.1v. +0.e.1S. +0.f.1R. +0.G.1q. +0.H.1p. +0.k.1P. +0.l.1O. +0.m.1N. +0.o.1L. +0.p.1K. +0.r.1I. +0.s.1H. +0.t.1G. +0.U.1e. +0.u.1F. +0.x.1D. +0.y.1C. +3.1l.L. +3.1u.E. +3.I.1o. +3.J.1n. +3.M.1k. +3.n.1M. +4.1B.z. +4.1t.F. +4.C.1w. +4.j.1Q. +4.O.1i. +4.P.1h. +4.S.1g. +4.v.1E. +4.X.1d. +c.T.1f. +0.+.19. 0.15.12. -0.16.12. -0.17.12. -0.18.12. -0.19.12. -0.1a.12. -0.1b.12. -0.1c.12. -0.1d.12. -0.12.1e. -0.12.1f. -0.12.1g. -3.12.1h. -3.12.1i. -0.1j.12. -0.1k.12. -2.1l.12. -0.1m.12. -0.1n.12. -0.1o.12. -0.1p.12. -0.1q.12. -0.1r.12. -0.1s.12. -3.1t.12. -2.1u.12. -0.1v.12. -0.1w.12. -0.1x.12. -0.1y.12. -0.1z.12. -0.1A.12. -0.1B.12. -0.1C.12. -0.1D.12. -0.1E.12. -0.1F.12. -0.1G.12. -0.1H.12. -0.1I.12. -0.1J.12. -0.1K.12. -0.1L.12. -0.1M.12. -3.1N.12. -0.1O.12. -0.1P.12. -3.12.1Q. -0.12.1R. -3.12.1S. -0.12.1T. -3.12.1U. -0.12.1V. -3.12.1W. -0.12.1X. -0.12.1Y. -3.12.1Z. -3.12.1+. -0.12.1/. -3.12.20. -3.12.21. -0.12.22. -0.12.23. -0.12.24. -0.12.25. -b.26.12. -0.12.27. -4.12.28. -3.12.2a. -3.12.2b. -0.12.2l. -6.2m.12. -3.12.2p. -2.2q.12. -2.2r.12. -7.2s.12. -7.2t.12. -2.2u.12. -3.12.2v. -3.12.2w. -e.2x.12. -9.2y.12. -3.12.2z. -3.12.2A. -9.2B.12. -3.12.2C. -3.12.2D. -3.12.2E. -3.12.2F. -3.12.2G. -9.2H.12. -3.12.2I. -3.12.2J. -3.12.2K. -c.2M.12. -9.2N.12. -2.2O.12. -9.2Q.12. -7.2S.12. -3.12.2T. -1.2U.12. -9.2V.12. -1.2W.12. -3.12.2X. -b.2+.12. -3.12.30. -0.12.35. -b.36.12. -0.12.37. -0.12.38. -5.39.12. -0.12.3d. -3.12.3h. -3.12.3i. -0.12.3m. -0.12.3p. -a.3v.12. -a.3y.12. -2.3z.12. -5.3A.12. -2.3B.12. -3.12.3C. -a.3F.12. -5.3G.12. -0.12.3M. -0.12.3Q. -5.40.12. -3.12.47. -3.12.4d. -3.12.4O. -3.12.4Q. -5.4T.12. -3.12.4V. -3.12.51. -3.12.52. -a.5a.12. -2.5b.12. -5.5d.12. -3.12.5B. -3.12.5C. -3.12.5F. -3.12.5H. -0.12.5I. -f.12.5K. -7.5M.12. -3.12.5O. -3.12.5R. -3.12.5U. -3.12.5W. -1.5Y.12. -3.12.5Z. -3.12.5/. -3.12.60. -2.62.12. -2.66.12. -a.6b.12. -c.6e.12. -3.12.6F. -3.12.6M. -3.12.6+. -3.12.71. -5.79.12. -3.12.7D. -3.12.2p. -3.12.2p. +0.16.11. +0.17.10. +0.1C.z. +0.1K.q. +0.1n.K. +0.A.1z. +0.B.1y. +0.C.1x. +0.E.1v. +0.f.1S. +0.g.1R. +0.G.1r. +0.L.1m. +0.l.1P. +0.m.1O. +0.o.1M. +0.p.1L. +0.r.1J. +0.s.1I. +0.t.1H. +0.u.1G. +0.V.1e. +0.v.1F. +0.y.1D. +3.1l.M. +3.1u.F. +3.I.1p. +3.J.1o. +3.n.1N. +4./.18. +4.D.1w. +4.H.1q. +4.k.1Q. +4.O.1j. +4.P.1i. +4.Q.1h. +4.U.1f. +4.w.1E. +c.T.1g. +c.Y.1d. +i.c.1X. +0./.19. +0.+.1a. 0.13.13. +0.16.12. +0.17.11. +0.18.10. +0.1D.z. +0.1d.Z. +0.1j.P. +0.1L.q. +0.1o.K. +0.1v.F. +0.A.1A. +0.B.1z. +0.C.1y. +0.d.1V. +0.D.1x. +0.g.1S. +0.h.1R. +0.H.1r. +0.L.1n. +0.l.1Q. +0.m.1P. +0.o.1N. +0.p.1M. +0.R.1h. +0.s.1J. +0.t.1I. +0.U.1g. +0.u.1H. +0.V.1f. +0.v.1G. +0.W.1e. +0.w.1F. +3.I.1q. +3.J.1p. +3.M.1m. +3.n.1O. +4.E.1w. +4.G.1s. +4.Q.1i. +4.x.1E. +0./.1a. +0.+.1b. 0.13.14. +0.17.12. +0.18.11. +0.19.10. +0.1B.A. +0.1j.Q. +0.1k.N. +0.1M.q. +0.1w.F. +0.B.1A. +0.C.1z. +0.D.1y. +0.e.1V. +0.E.1x. +0.G.1t. +0.H.1s. +0.i.1R. +0.K.1p. +0.L.1o. +0.m.1Q. +0.o.1O. +0.p.1N. +0.t.1J. +0.u.1I. +0.V.1g. +0.v.1H. +0.W.1f. +0.w.1G. +0.X.1e. +0.x.1F. +0.y.1E. +3.I.1r. +3.J.1q. +3.M.1n. +3.n.1P. +4.d.1W. +4.h.1S. +4.R.1i. +4.S.1h. +0./.1b. +0.+.1c. 0.13.15. -0.16.13. -0.17.13. -0.18.13. -0.13.19. -0.13.1a. -0.13.1b. -0.1c.13. -0.1d.13. -0.13.1e. -0.13.1f. -0.13.1g. -3.13.1h. -3.13.1i. -0.1j.13. -0.1k.13. -2.1l.13. -0.1m.13. -0.1n.13. -0.1o.13. -0.1p.13. -0.1q.13. -0.1r.13. -0.1s.13. -0.1t.13. -2.1u.13. -0.1v.13. -0.1w.13. -0.1x.13. -0.1y.13. -0.1z.13. -0.1A.13. -0.1B.13. -0.1C.13. -0.1D.13. -0.1E.13. -0.1F.13. -0.1G.13. -0.1H.13. -0.1I.13. -0.13.1J. -0.13.1K. -0.13.1L. -0.13.1M. -0.13.1N. -0.13.1O. -0.13.1P. -3.13.1Q. -0.13.1R. -0.13.1S. -0.13.1T. -0.13.1U. -0.13.1V. -0.13.1W. -0.13.1X. -0.13.1Y. -0.13.1Z. -0.13.1+. -0.13.1/. -0.13.20. -0.13.21. -0.13.22. -0.13.23. -0.13.24. -0.13.25. -b.26.13. -0.13.27. -4.13.28. -0.13.2a. -0.13.2b. -0.13.2l. -6.2m.13. -0.13.2p. -2.2q.13. -2.2r.13. -7.2s.13. -7.2t.13. -2.2u.13. -0.13.2v. -0.13.2w. -e.2x.13. -9.2y.13. -0.13.2z. -3.13.2A. -9.2B.13. -0.13.2C. -0.13.2D. -0.13.2E. -0.13.2F. -0.13.2G. -9.2H.13. -0.13.2I. -3.13.2J. -0.13.2K. -c.2M.13. -9.2N.13. -2.2O.13. -9.2Q.13. -7.2S.13. -0.13.2T. -1.2U.13. -9.2V.13. -1.2W.13. -0.13.2X. -b.2+.13. -0.13.30. -0.13.35. -b.36.13. -0.13.37. -0.13.38. -5.39.13. -0.13.3d. -3.13.3h. -3.13.3i. -0.13.3m. -0.13.3p. -a.3v.13. -a.3y.13. -2.3z.13. -5.3A.13. -2.3B.13. -0.13.3C. -a.3F.13. -5.3G.13. -0.13.3M. -0.13.3Q. -5.40.13. -0.13.47. -0.13.4d. -0.13.4O. -3.13.4Q. -5.4T.13. -3.13.4V. -0.13.51. -0.13.52. -a.5a.13. -2.5b.13. -5.5d.13. -0.13.5B. -0.13.5C. -0.13.5F. -3.13.5H. -0.13.5I. -f.13.5K. -7.5M.13. -0.13.5O. -0.13.5R. -0.13.5U. -0.13.5W. -1.5Y.13. -0.13.5Z. -0.13.5/. -0.13.60. -2.62.13. -2.66.13. -a.6b.13. -c.6e.13. -0.13.6F. -0.13.6M. -0.13.6+. -3.13.71. -5.79.13. -3.13.7D. -0.13.2p. -0.13.2p. -3.14.14. +0.18.12. +0.19.11. +0.1a.10. +0.1B.B. +0.1E.z. +0.1j.R. +0.1N.q. +0.1t.H. +0.1x.F. +0.A.1C. +0.C.1A. +0.D.1z. +0.E.1y. +0.f.1V. +0.i.1S. +0.j.1R. +0.K.1q. +0.L.1p. +0.O.1k. +0.o.1P. +0.p.1O. +0.r.1K. +0.u.1J. +0.v.1I. +0.W.1g. +0.w.1H. +0.X.1f. +0.x.1G. +0.y.1F. +3.1l.N. +3.1u.G. +3.I.1s. +3.J.1r. +3.M.1o. +3.n.1Q. +4.14.14. +4.e.1W. +4.S.1i. +c.T.1h. +c.Y.1e. +0./.1c. 0.14.15. -0.16.14. -0.17.14. -0.18.14. -0.14.19. -3.14.1a. -0.1b.14. -0.1c.14. -0.1d.14. -0.14.1e. -3.14.1f. -0.14.1g. -3.14.1h. -3.14.1i. -0.1j.14. -0.1k.14. -2.1l.14. -0.1m.14. -0.1n.14. -0.1o.14. -0.1p.14. -0.1q.14. -0.1r.14. -0.1s.14. -3.1t.14. -2.1u.14. -0.1v.14. -0.1w.14. -0.1x.14. -0.1y.14. -0.1z.14. -0.1A.14. -0.1B.14. -0.1C.14. -0.1D.14. -0.1E.14. -0.1F.14. -0.1G.14. -0.1H.14. -0.1I.14. -0.14.1J. -0.14.1K. -0.14.1L. -0.14.1M. -0.14.1N. -0.14.1O. -0.14.1P. -3.14.1Q. -0.14.1R. -3.14.1S. -3.14.1T. -3.14.1U. -0.14.1V. -0.14.1W. -0.14.1X. -0.14.1Y. -0.14.1Z. -0.14.1+. -0.14.1/. -0.14.20. -0.14.21. -0.14.22. -0.14.23. -0.14.24. -0.14.25. -b.26.14. -0.14.27. -4.14.28. -0.14.2a. -0.14.2b. -0.14.2l. -6.2m.14. -3.14.2p. -2.2q.14. -2.2r.14. -7.2s.14. -7.2t.14. -2.2u.14. -3.14.2v. -0.14.2w. -e.2x.14. -9.2y.14. -0.14.2z. -3.14.2A. -9.2B.14. -0.14.2C. -0.14.2D. -3.14.2E. -3.14.2F. -0.14.2G. -9.2H.14. -0.14.2I. -3.14.2J. -0.14.2K. -c.2M.14. -9.2N.14. -2.2O.14. -9.2Q.14. -7.2S.14. -0.14.2T. -1.2U.14. -9.2V.14. -1.2W.14. -0.14.2X. -b.2+.14. -0.14.30. -0.14.35. -b.36.14. -0.14.37. -3.14.38. -5.39.14. -0.14.3d. -3.14.3h. -3.14.3i. -0.14.3m. -0.14.3p. -a.3v.14. -a.3y.14. -2.3z.14. -5.3A.14. -2.3B.14. -0.14.3C. -a.3F.14. -5.3G.14. -0.14.3M. -0.14.3Q. -5.40.14. -0.14.47. -0.14.4d. -3.14.4O. -3.14.4Q. -5.4T.14. -3.14.4V. -0.14.51. -0.14.52. -a.5a.14. -2.5b.14. -5.5d.14. -0.14.5B. -0.14.5C. -0.14.5F. -3.14.5H. -0.14.5I. -f.14.5K. -7.5M.14. -0.14.5O. -3.14.5R. -0.14.5U. -3.14.5W. -1.5Y.14. -0.14.5Z. -0.14.5/. -0.14.60. -2.62.14. -2.66.14. -a.6b.14. -c.6e.14. -0.14.6F. -0.14.6M. -0.14.6+. -3.14.71. -5.79.14. -3.14.7D. -3.14.2p. -3.14.2p. +0.16.13. +0.19.12. +0.1a.11. +0.1b.10. +0.1B.C. +0.1F.z. +0.1k.P. +0.1m.N. +0.1O.q. +0.1y.F. +0.A.1D. +0.B.1C. +0.D.1A. +0.d.1X. +0.E.1z. +0.f.1W. +0.G.1v. +0.g.1V. +0.k.1R. +0.K.1r. +0.L.1q. +0.p.1P. +0.r.1L. +0.s.1K. +0.v.1J. +0.w.1I. +0.X.1g. +0.x.1H. +0.y.1G. +3.1l.O. +3.1u.H. +3.I.1t. +3.J.1s. +3.M.1p. +4.1e.Z. +4.j.1S. +4.o.1Q. +4.S.1j. +4.U.1h. +c.T.1i. +c.Y.1f. +0.+.1d. 0.15.15. +0.16.14. +0.17.13. +0.1a.12. +0.1b.11. +0.1B.D. +0.1c.10. +0.1f.Z. +0.1G.z. +0.1k.Q. +0.1n.N. +0.1P.q. +0.1z.F. +0.B.1D. +0.C.1C. +0.E.1A. +0.e.1X. +0.G.1w. +0.H.1v. +0.k.1S. +0.K.1s. +0.l.1R. +0.L.1r. +0.O.1m. +0.p.1Q. +0.r.1M. +0.s.1L. +0.t.1K. +0.w.1J. +0.x.1I. +0.y.1H. +3.1l.P. +3.1u.I. +3.J.1t. +3.M.1q. +4.g.1W. +4.h.1V. +4.U.1i. +4.V.1h. +c.T.1j. +c.Y.1g. 0.16.15. -0.17.15. -0.18.15. -0.15.19. -3.15.1a. -0.1b.15. -0.1c.15. -0.1d.15. -0.15.1e. -3.15.1f. -0.15.1g. -3.15.1h. -3.15.1i. -0.1j.15. -0.1k.15. -2.1l.15. -0.1m.15. -0.1n.15. -0.1o.15. -0.1p.15. -0.1q.15. -0.1r.15. -0.1s.15. -0.1t.15. -2.1u.15. -0.1v.15. -0.1w.15. -0.1x.15. -0.1y.15. -0.1z.15. -0.1A.15. -0.1B.15. -0.1C.15. -0.1D.15. -0.1E.15. -0.1F.15. -0.1G.15. -0.1H.15. -0.1I.15. -0.15.1J. -0.15.1K. -0.15.1L. -0.15.1M. -0.15.1N. -0.15.1O. -0.15.1P. -3.15.1Q. -0.15.1R. -0.15.1S. -3.15.1T. -3.15.1U. -0.15.1V. -0.15.1W. -0.15.1X. -0.15.1Y. -0.15.1Z. -0.15.1+. -0.15.1/. -0.15.20. -0.15.21. -0.15.22. -0.15.23. -0.15.24. -0.15.25. -b.26.15. -0.15.27. -4.15.28. -0.15.2a. -0.15.2b. -0.15.2l. -6.2m.15. -3.15.2p. -2.2q.15. -2.2r.15. -7.2s.15. -7.2t.15. -2.2u.15. -3.15.2v. -0.15.2w. -e.2x.15. -9.2y.15. -0.15.2z. -3.15.2A. -9.2B.15. -0.15.2C. -0.15.2D. -3.15.2E. -3.15.2F. -0.15.2G. -9.2H.15. -0.15.2I. -3.15.2J. -0.15.2K. -c.2M.15. -9.2N.15. -2.2O.15. -9.2Q.15. -7.2S.15. -0.15.2T. -1.2U.15. -9.2V.15. -1.2W.15. -0.15.2X. -b.2+.15. -0.15.30. -0.15.35. -b.36.15. -0.15.37. -3.15.38. -5.39.15. -0.15.3d. -3.15.3h. -0.15.3i. -0.15.3m. -0.15.3p. -a.3v.15. -a.3y.15. -2.3z.15. -5.3A.15. -2.3B.15. -0.15.3C. -a.3F.15. -5.3G.15. -0.15.3M. -0.15.3Q. -5.40.15. -0.15.47. -0.15.4d. -3.15.4O. -3.15.4Q. -5.4T.15. -3.15.4V. -0.15.51. -0.15.52. -a.5a.15. -2.5b.15. -5.5d.15. -0.15.5B. -0.15.5C. -0.15.5F. -3.15.5H. -0.15.5I. -f.15.5K. -7.5M.15. -0.15.5O. -3.15.5R. -0.15.5U. -3.15.5W. -1.5Y.15. -0.15.5Z. -0.15.5/. -0.15.60. -2.62.15. -2.66.15. -a.6b.15. -c.6e.15. -0.15.6F. -0.15.6M. -0.15.6+. -3.15.71. -5.79.15. -3.15.7D. -3.15.2p. -3.15.2p. +0.17.14. +0.18.13. +0.1A.F. +0.1b.12. +0.1B.E. +0.1c.11. +0.1H.z. +0.1j.U. +0.1k.R. +0.1m.P. +0.1o.N. +0.1t.K. +0.A.1E. +0.C.1D. +0.D.1C. +0.f.1X. +0.G.1x. +0.h.1W. +0.l.1S. +0.L.1s. +0.m.1R. +0.O.1n. +0.r.1N. +0.s.1M. +0.t.1L. +0.u.1K. +0.x.1J. +0.y.1I. +3.1l.Q. +3.1u.J. +3.I.1v. +3.M.1r. +4./.1d. +4.H.1w. +4.i.1V. +4.q.1Q. +4.V.1i. +4.W.1h. +4.Z.1g. +0.13.19. 0.16.16. +0.17.15. +0.18.14. +0.1B.F. +0.1c.12. +0.1d.10. +0.1I.z. +0.1j.V. +0.1m.Q. +0.1n.P. +0.A.1F. +0.B.1E. +0.D.1D. +0.E.1C. +0.g.1X. +0.G.1y. +0.H.1x. +0.i.1W. +0.L.1t. +0.m.1S. +0.N.1p. +0.O.1o. +0.r.1O. +0.s.1N. +0.t.1M. +0.u.1L. +0.v.1K. +0.y.1J. +3.1l.R. +3.1u.K. +3.I.1w. +3.J.1v. +3.M.1s. +3.n.1R. +4.j.1V. +4.S.1k. +4.W.1i. +4.X.1h. +e.0.1Z. +0.+.1e. +0.13.1a. +0.14.19. 0.16.17. -3.16.18. -0.16.19. -3.16.1a. -0.16.1b. -0.1c.16. -0.1d.16. -0.16.1e. -0.16.1f. -0.16.1g. -3.16.1h. -3.16.1i. -0.1j.16. -0.1k.16. -2.1l.16. -0.1m.16. -0.1n.16. -0.1o.16. -0.1p.16. -0.1q.16. -0.1r.16. -0.1s.16. -0.1t.16. -2.1u.16. -0.1v.16. -0.1w.16. -0.1x.16. -0.1y.16. -0.1z.16. -0.1A.16. -0.1B.16. -0.1C.16. -0.1D.16. -0.1E.16. -0.1F.16. -0.1G.16. -0.1H.16. -0.1I.16. -0.16.1J. -0.16.1K. -0.16.1L. -0.16.1M. -0.16.1N. -0.16.1O. -0.16.1P. -0.16.1Q. -0.16.1R. -0.16.1S. -0.16.1T. -3.16.1U. -0.16.1V. -0.16.1W. -0.16.1X. -0.16.1Y. -0.16.1Z. -0.16.1+. -0.16.1/. -0.16.20. -0.16.21. -0.16.22. -0.16.23. -0.16.24. -0.16.25. -b.26.16. -0.16.27. -4.16.28. -0.16.2a. -0.16.2b. -0.16.2l. -6.2m.16. -0.16.2p. -2.2q.16. -2.2r.16. -7.2s.16. -7.2t.16. -2.2u.16. -3.16.2v. -0.16.2w. -e.2x.16. -9.2y.16. -0.16.2z. -3.16.2A. -9.2B.16. -0.16.2C. -0.16.2D. -0.16.2E. -3.16.2F. -0.16.2G. -9.2H.16. -0.16.2I. -3.16.2J. -0.16.2K. -c.2M.16. -9.2N.16. -2.2O.16. -9.2Q.16. -7.2S.16. -0.16.2T. -1.2U.16. -9.2V.16. -1.2W.16. -0.16.2X. -b.2+.16. -0.16.30. -0.16.35. -b.36.16. -0.16.37. -0.16.38. -5.39.16. -0.16.3d. -3.16.3h. -3.16.3i. -0.16.3m. -0.16.3p. -a.3v.16. -a.3y.16. -2.3z.16. -5.3A.16. -2.3B.16. -0.16.3C. -a.3F.16. -5.3G.16. -0.16.3M. -0.16.3Q. -5.40.16. -0.16.47. -0.16.4d. -0.16.4O. -3.16.4Q. -5.4T.16. -3.16.4V. -0.16.51. -0.16.52. -a.5a.16. -2.5b.16. -5.5d.16. -0.16.5B. -0.16.5C. -0.16.5F. -3.16.5H. -0.16.5I. -f.16.5K. -7.5M.16. -0.16.5O. -3.16.5R. -0.16.5U. -0.16.5W. -1.5Y.16. -0.16.5Z. -0.16.5/. -0.16.60. -2.62.16. -2.66.16. -a.6b.16. -c.6e.16. -0.16.6F. -0.16.6M. -0.16.6+. -3.16.71. -5.79.16. -0.16.7D. -0.16.2p. -0.16.2p. +0.18.15. +0.1C.F. +0.1d.11. +0.1j.W. +0.1J.z. +0.1m.R. +0.1n.Q. +0.1o.P. +0.A.1G. +0.B.1F. +0.C.1E. +0.E.1D. +0.G.1z. +0.h.1X. +0.H.1y. +0.K.1v. +0.N.1q. +0.o.1R. +0.r.1P. +0.s.1O. +0.t.1N. +0.u.1M. +0.v.1L. +0.w.1K. +3.1u.L. +3.I.1x. +3.J.1w. +3.M.1t. +3.n.1S. +4.1l.S. +4.j.1W. +4.k.1V. +4.O.1p. +4.X.1i. +c.T.1k. +c.Y.1h. +e.0.1+. +e.1.1Z. +0./.1e. +0.+.1f. +0.13.1b. +0.15.19. 0.17.17. +0.1d.12. +0.1D.F. +0.1h.Z. +0.1j.X. +0.1n.R. +0.1o.Q. +0.1p.P. +0.A.1H. +0.B.1G. +0.C.1F. +0.D.1E. +0.G.1A. +0.H.1z. +0.i.1X. +0.l.1V. +0.L.1v. +0.N.1r. +0.O.1q. +0.o.1S. +0.p.1R. +0.r.1Q. +0.s.1P. +0.t.1O. +0.U.1k. +0.u.1N. +0.v.1M. +0.w.1L. +0.x.1K. +3.1l.T. +3.1u.M. +3.I.1y. +3.J.1x. +4.14.1a. +4.16.18. +4.K.1w. +4.k.1W. +4.S.1m. +c.Y.1i. +e.0.1/. +e.1.1+. +e.2.1Z. +0./.1f. +0.+.1g. +0.10.1e. +0.16.19. 0.17.18. +0.1b.14. +0.1B.G. +0.1c.13. +0.1o.R. +0.1p.Q. +0.1q.P. +0.A.1I. +0.B.1H. +0.C.1G. +0.D.1F. +0.E.1E. +0.H.1A. +0.j.1X. +0.K.1x. +0.L.1w. +0.m.1V. +0.N.1s. +0.O.1r. +0.p.1S. +0.s.1Q. +0.t.1P. +0.u.1O. +0.V.1k. +0.v.1N. +0.w.1M. +0.x.1L. +0.y.1K. +3.1l.U. +3.I.1z. +3.J.1y. +3.M.1v. +4.15.1a. +4.1i.Z. +4.l.1W. +4.S.1n. +c.T.1m. +c.Y.1j. +e.0.20. +e.1.1/. +e.2.1+. +e.3.1Z. +f.1R.q. +0./.1g. +0.10.1f. +0.11.1e. 0.17.19. -3.17.1a. -0.17.1b. -0.1c.17. -0.1d.17. -0.17.1e. -3.17.1f. -0.17.1g. -3.17.1h. -3.17.1i. -0.1j.17. -0.1k.17. -2.1l.17. -0.1m.17. -0.1n.17. -0.1o.17. -0.1p.17. -0.1q.17. -0.1r.17. -0.1s.17. -0.1t.17. -2.1u.17. -0.1v.17. -0.1w.17. -0.1x.17. -0.1y.17. -0.1z.17. -0.1A.17. -0.1B.17. -0.1C.17. -0.1D.17. -0.1E.17. -0.1F.17. -0.1G.17. -0.1H.17. -0.1I.17. -0.17.1J. -0.17.1K. -0.17.1L. -0.17.1M. -0.17.1N. -0.17.1O. -0.17.1P. -3.17.1Q. -0.17.1R. -3.17.1S. -3.17.1T. -3.17.1U. -0.17.1V. -0.17.1W. -0.17.1X. -0.17.1Y. -0.17.1Z. -0.17.1+. -0.17.1/. -0.17.20. -0.17.21. -0.17.22. -0.17.23. -0.17.24. -0.17.25. -b.26.17. -0.17.27. -4.17.28. -0.17.2a. -0.17.2b. -0.17.2l. -6.2m.17. -3.17.2p. -2.2q.17. -2.2r.17. -7.2s.17. -7.2t.17. -2.2u.17. -3.17.2v. -0.17.2w. -e.2x.17. -9.2y.17. -0.17.2z. -3.17.2A. -9.2B.17. -0.17.2C. -0.17.2D. -3.17.2E. -3.17.2F. -0.17.2G. -9.2H.17. -0.17.2I. -3.17.2J. -0.17.2K. -c.2M.17. -9.2N.17. -2.2O.17. -9.2Q.17. -7.2S.17. -0.17.2T. -1.2U.17. -9.2V.17. -1.2W.17. -0.17.2X. -b.2+.17. -0.17.30. -0.17.35. -b.36.17. -0.17.37. -3.17.38. -5.39.17. -0.17.3d. -3.17.3h. -3.17.3i. -0.17.3m. -0.17.3p. -a.3v.17. -a.3y.17. -2.3z.17. -5.3A.17. -2.3B.17. -0.17.3C. -a.3F.17. -5.3G.17. -0.17.3M. -0.17.3Q. -5.40.17. -0.17.47. -0.17.4d. -3.17.4O. -3.17.4Q. -5.4T.17. -3.17.4V. -0.17.51. -0.17.52. -a.5a.17. -2.5b.17. -5.5d.17. -l.17.5B. -l.17.5C. -0.17.5F. -3.17.5H. -0.17.5I. -f.17.5K. -7.5M.17. -0.17.5O. -3.17.5R. -0.17.5U. -0.17.5W. -1.5Y.17. -0.17.5Z. -0.17.5/. -0.17.60. -2.62.17. -2.66.17. -a.6b.17. -c.6e.17. -0.17.6F. -0.17.6M. -0.17.6+. -3.17.71. -5.79.17. -3.17.7D. -3.17.2p. -3.17.2p. -3.18.18. +0.1b.15. +0.1B.H. +0.1c.14. +0.1j.Z. +0.1K.z. +0.1p.R. +0.1q.Q. +0.1r.P. +0.1t.N. +0.A.1J. +0.B.1I. +0.C.1H. +0.D.1G. +0.E.1F. +0.G.1C. +0.k.1X. +0.K.1y. +0.L.1x. +0.m.1W. +0.O.1s. +0.q.1S. +0.t.1Q. +0.U.1m. +0.u.1P. +0.v.1O. +0.W.1k. +0.w.1N. +0.x.1M. +0.y.1L. +3.1l.V. +3.I.1A. +3.J.1z. +3.M.1w. +3.n.1V. +4.16.1a. +4.18.18. +4.1E.F. +4.S.1o. +c.T.1n. +e.0.21. +e.1.20. +e.2.1/. +e.3.1+. +e.4.1Z. +0.10.1g. +0.11.1f. +0.12.1e. +0.16.1b. 0.18.19. +0.1c.15. +0.1d.13. +0.1F.F. +0.1k.X. +0.1L.z. +0.1q.R. +0.1r.Q. +0.1s.P. +0.B.1J. +0.C.1I. +0.D.1H. +0.E.1G. +0.G.1D. +0.H.1C. +0.K.1z. +0.l.1X. +0.L.1y. +0.O.1t. +0.o.1V. +0.U.1n. +0.u.1Q. +0.V.1m. +0.v.1P. +0.w.1O. +0.x.1N. +0.y.1M. +3.1l.W. +3.1u.N. +3.I.1B. +3.J.1A. +3.M.1x. +3.n.1W. +4.17.1a. +4.S.1p. +c.T.1o. +e.0.22. +e.1.21. +e.2.20. +e.3.1/. +e.4.1+. +e.5.1Z. +0.+.1h. +0.11.1g. +0.12.1f. +0.17.1b. 0.18.1a. -0.18.1b. -3.1c.18. -3.1d.18. -0.18.1e. -3.18.1f. -0.18.1g. -3.18.1h. -3.18.1i. -0.1j.18. -0.1k.18. -2.1l.18. -3.1m.18. -0.1n.18. -0.1o.18. -0.1p.18. -3.1q.18. -3.1r.18. -3.1s.18. -3.1t.18. -2.1u.18. -0.1v.18. -0.1w.18. -0.1x.18. -3.1y.18. -3.1z.18. -3.1A.18. -0.1B.18. -0.1C.18. -3.1D.18. -3.1E.18. -0.1F.18. -0.1G.18. -3.1H.18. -3.1I.18. -0.18.1J. -0.18.1K. -0.18.1L. -0.18.1M. -0.18.1N. -0.18.1O. -0.18.1P. -3.18.1Q. -0.18.1R. -3.18.1S. -0.18.1T. -3.18.1U. -0.18.1V. -0.18.1W. -0.18.1X. -0.18.1Y. -0.18.1Z. -0.18.1+. -0.18.1/. -0.18.20. -0.18.21. -0.18.22. -0.18.23. -0.18.24. -0.18.25. -b.26.18. -0.18.27. -4.18.28. -0.18.2a. -0.18.2b. -0.18.2l. -6.2m.18. -3.18.2p. -2.2q.18. -2.2r.18. -7.2s.18. -7.2t.18. -2.2u.18. -3.18.2v. -0.18.2w. -e.2x.18. -9.2y.18. -0.18.2z. -3.18.2A. -9.2B.18. -0.18.2C. -0.18.2D. -3.18.2E. -3.18.2F. -0.18.2G. -9.2H.18. -0.18.2I. -3.18.2J. -0.18.2K. -c.2M.18. -9.2N.18. -2.2O.18. -9.2Q.18. -7.2S.18. -0.18.2T. -1.2U.18. -9.2V.18. -1.2W.18. -0.18.2X. -b.2+.18. -0.18.30. -0.18.35. -b.36.18. -0.18.37. -3.18.38. -5.39.18. -0.18.3d. -3.18.3h. -3.18.3i. -0.18.3m. -0.18.3p. -a.3v.18. -a.3y.18. -2.3z.18. -5.3A.18. -2.3B.18. -0.18.3C. -a.3F.18. -5.3G.18. -0.18.3M. -0.18.3Q. -5.40.18. -0.18.47. -0.18.4d. -3.18.4O. -3.18.4Q. -5.4T.18. -3.18.4V. -0.18.51. -0.18.52. -a.5a.18. -2.5b.18. -5.5d.18. -0.18.5B. -0.18.5C. -0.18.5F. -3.18.5H. -0.18.5I. -f.18.5K. -7.5M.18. -0.18.5O. -3.18.5R. -0.18.5U. -0.18.5W. -1.5Y.18. -0.18.5Z. -0.18.5/. -0.18.60. -2.62.18. -2.66.18. -a.6b.18. -c.6e.18. -0.18.6F. -0.18.6M. -0.18.6+. -3.18.71. -5.79.18. -3.18.7D. -3.18.2p. -3.18.2p. 0.19.19. +0.1c.16. +0.1d.14. +0.1G.F. +0.1M.z. +0.1r.R. +0.1s.Q. +0.1t.P. +0.C.1J. +0.D.1I. +0.E.1H. +0.H.1D. +0.K.1A. +0.L.1z. +0.m.1X. +0.N.1v. +0.p.1V. +0.r.1R. +0.U.1o. +0.V.1n. +0.v.1Q. +0.W.1m. +0.w.1P. +0.x.1O. +0.y.1N. +3.1l.X. +3.1u.O. +3.I.1C. +3.J.1B. +3.M.1y. +4.o.1W. +4.S.1q. +c.T.1p. +c.Y.1k. +e.0.23. +e.1.22. +e.2.21. +e.3.20. +e.4.1/. +e.5.1+. +e.6.1Z. +0.+.1i. +0.12.1g. +0.18.1b. 0.19.1a. +0.1B.K. +0.1c.17. +0.1d.15. +0.1H.F. +0.1k.Z. +0.1m.X. +0.1N.z. +0.1s.R. +0.A.1K. +0.D.1J. +0.E.1I. +0.L.1A. +0.O.1v. +0.p.1W. +0.q.1V. +0.s.1R. +0.U.1p. +0.V.1o. +0.W.1n. +0.w.1Q. +0.x.1P. +0.y.1O. +3.1l.Y. +3.1u.P. +3.I.1D. +3.J.1C. +3.M.1z. +3.n.1X. +4./.1h. +4.1t.Q. +4.G.1E. +4.N.1w. +4.r.1S. +4.S.1r. +c.T.1q. +e.0.24. +e.1.23. +e.2.22. +e.3.21. +e.4.20. +e.5.1/. +e.6.1+. +e.7.1Z. +i.c.1Y. +0.13.1e. 0.1b.19. -0.1c.19. -0.1d.19. -0.19.1e. -0.19.1f. -0.19.1g. -0.19.1h. -0.19.1i. -0.1j.19. -0.1k.19. -2.1l.19. -0.1m.19. -0.1n.19. -0.1o.19. -0.1p.19. -0.1q.19. -0.1r.19. -0.1s.19. -0.1t.19. -2.1u.19. -0.1v.19. -0.1w.19. -0.1x.19. -0.1y.19. -0.1z.19. -0.1A.19. -0.1B.19. -0.1C.19. -0.1D.19. -0.1E.19. -0.1F.19. -0.1G.19. -0.1H.19. -0.1I.19. -0.19.1J. -0.19.1K. -0.19.1L. -0.19.1M. -0.19.1N. -0.19.1O. -0.19.1P. -0.19.1Q. -0.19.1R. -0.19.1S. -0.19.1T. -0.19.1U. -0.19.1V. -0.19.1W. -0.19.1X. -0.19.1Y. -0.19.1Z. -0.19.1+. -0.19.1/. -0.19.20. -0.19.21. -0.19.22. -0.19.23. -0.19.24. -0.19.25. -b.26.19. -0.19.27. -4.19.28. -0.19.2a. -0.19.2b. -0.19.2l. -6.2m.19. -0.19.2p. -2.2q.19. -2.2r.19. -7.2s.19. -7.2t.19. -2.2u.19. -0.19.2v. -0.19.2w. -e.2x.19. -9.2y.19. -0.19.2z. -0.19.2A. -9.2B.19. -0.19.2C. -0.19.2D. -0.19.2E. -0.19.2F. -0.19.2G. -9.2H.19. -0.19.2I. -3.19.2J. -0.19.2K. -c.2M.19. -9.2N.19. -2.2O.19. -9.2Q.19. -7.2S.19. -0.19.2T. -1.2U.19. -9.2V.19. -1.2W.19. -0.19.2X. -b.2+.19. -0.19.30. -0.19.35. -3.36.19. -0.19.37. -0.19.38. -5.39.19. -0.19.3d. -0.19.3h. -0.19.3i. -0.19.3m. -0.19.3p. -a.3v.19. -a.3y.19. -2.3z.19. -5.3A.19. -2.3B.19. -0.19.3C. -a.3F.19. -5.3G.19. -0.19.3M. -0.19.3Q. -5.40.19. -0.19.47. -0.19.4d. -0.19.4O. -0.19.4Q. -5.4T.19. -0.19.4V. -0.19.51. -0.19.52. -a.5a.19. -2.5b.19. -5.5d.19. -0.19.5B. -0.19.5C. -0.19.5F. -0.19.5H. -0.19.5I. -f.19.5K. -7.5M.19. -0.19.5O. -0.19.5R. -0.19.5U. -0.19.5W. -1.5Y.19. -0.19.5Z. -0.19.5/. -0.19.60. -2.62.19. -2.66.19. -a.6b.19. -c.6e.19. -0.19.6F. -0.19.6M. -0.19.6+. -0.19.71. -5.79.19. -0.19.7D. -0.19.2p. -0.19.2p. -3.1a.1a. +0.1B.L. +0.1d.16. +0.1E.H. +0.1I.F. +0.1j.+. +0.1n.X. +0.1O.z. +0.1t.R. +0.1v.P. +0.A.1L. +0.B.1K. +0.E.1J. +0.G.1F. +0.K.1C. +0.N.1x. +0.o.1X. +0.s.1S. +0.t.1R. +0.U.1q. +0.V.1p. +0.W.1o. +0.x.1Q. +0.y.1P. +3.1l.Z. +3.1u.Q. +3.J.1D. +3.M.1A. +4./.1i. +4.10.1h. +4.1a.1a. +4.1c.18. +4.O.1w. +4.q.1W. +4.S.1s. +c.T.1r. +c.Y.1m. +e.0.25. +e.1.24. +e.2.23. +e.3.22. +e.4.21. +e.5.20. +e.6.1/. +e.7.1+. +e.8.1Z. +0.13.1f. +0.14.1e. 0.1b.1a. -0.1c.1a. -3.1d.1a. -0.1a.1e. -3.1a.1f. -0.1a.1g. -3.1a.1h. -3.1a.1i. -0.1j.1a. -0.1k.1a. -2.1l.1a. -0.1m.1a. -0.1n.1a. -0.1o.1a. -0.1p.1a. -0.1q.1a. -0.1r.1a. -3.1s.1a. -3.1t.1a. -2.1u.1a. -0.1v.1a. -0.1w.1a. -0.1x.1a. -0.1y.1a. -0.1z.1a. -0.1A.1a. -0.1B.1a. -3.1C.1a. -0.1D.1a. -0.1E.1a. -0.1F.1a. -0.1G.1a. -0.1H.1a. -0.1I.1a. -0.1a.1J. -0.1a.1K. -0.1a.1L. -0.1a.1M. -3.1a.1N. -0.1a.1O. -0.1a.1P. -3.1a.1Q. -0.1a.1R. -0.1a.1S. -0.1a.1T. -3.1a.1U. -0.1a.1V. -0.1a.1W. -0.1a.1X. -0.1a.1Y. -0.1a.1Z. -0.1a.1+. -0.1a.1/. -0.1a.20. -0.1a.21. -0.1a.22. -0.1a.23. -0.1a.24. -0.1a.25. -b.26.1a. -0.1a.27. -4.1a.28. -0.1a.2a. -0.1a.2b. -0.1a.2l. -6.2m.1a. -3.1a.2p. -2.2q.1a. -2.2r.1a. -7.2s.1a. -7.2t.1a. -2.2u.1a. -3.1a.2v. -3.1a.2w. -e.2x.1a. -9.2y.1a. -0.1a.2z. -3.1a.2A. -9.2B.1a. -0.1a.2C. -3.1a.2D. -3.1a.2E. -3.1a.2F. -0.1a.2G. -9.2H.1a. -0.1a.2I. -3.1a.2J. -0.1a.2K. -c.2M.1a. -9.2N.1a. -2.2O.1a. -9.2Q.1a. -7.2S.1a. -3.1a.2T. -1.2U.1a. -9.2V.1a. -1.2W.1a. -0.1a.2X. -d.2+.1a. -3.1a.30. -0.1a.35. -b.36.1a. -0.1a.37. -3.1a.38. -5.39.1a. -0.1a.3d. -3.1a.3h. -3.1a.3i. -0.1a.3m. -0.1a.3p. -a.3v.1a. -a.3y.1a. -2.3z.1a. -5.3A.1a. -2.3B.1a. -3.1a.3C. -a.3F.1a. -5.3G.1a. -0.1a.3M. -0.1a.3Q. -5.40.1a. -0.1a.47. -0.1a.4d. -3.1a.4O. -3.1a.4Q. -5.4T.1a. -3.1a.4V. -0.1a.51. -0.1a.52. -a.5a.1a. -2.5b.1a. -5.5d.1a. -0.1a.5B. -0.1a.5C. -0.1a.5F. -3.1a.5H. -0.1a.5I. -f.1a.5K. -7.5M.1a. -0.1a.5O. -3.1a.5R. -3.1a.5U. -0.1a.5W. -1.5Y.1a. -0.1a.5Z. -3.1a.5/. -3.1a.60. -2.62.1a. -2.66.1a. -a.6b.1a. -c.6e.1a. -0.1a.6F. -0.1a.6M. -0.1a.6+. -3.1a.71. -5.79.1a. -3.1a.7D. -3.1a.2p. -3.1a.2p. +0.1c.19. +0.1d.17. +0.1j./. +0.1J.F. +0.1m.Z. +0.1o.X. +0.1P.z. +0.1v.Q. +0.1w.P. +0.A.1M. +0.B.1L. +0.C.1K. +0.G.1G. +0.H.1F. +0.K.1D. +0.L.1C. +0.N.1y. +0.O.1x. +0.p.1X. +0.t.1S. +0.u.1R. +0.U.1r. +0.V.1q. +0.W.1p. +0.y.1Q. +3.1u.R. +3.I.1E. +3.M.1B. +4.10.1i. +4.11.1h. +4.S.1t. +c.T.1s. +c.Y.1n. +e.0.26. +e.1.25. +e.2.24. +e.3.23. +e.4.22. +e.5.21. +e.6.20. +e.7.1/. +e.8.1+. +e.9.1Z. +0.13.1g. +0.15.1e. 0.1b.1b. +0.1c.1a. +0.1j.10. +0.1n.Z. +0.1Q.z. +0.1v.R. +0.1w.Q. +0.1x.P. +0.A.1N. +0.B.1M. +0.C.1L. +0.D.1K. +0.G.1H. +0.H.1G. +0.L.1D. +0.N.1z. +0.O.1y. +0.q.1X. +0.r.1V. +0.U.1s. +0.u.1S. +0.V.1r. +0.v.1R. +0.W.1q. +0.X.1p. +3.I.1F. +3.J.1E. +3.M.1C. +4.11.1i. +4.12.1h. +4.14.1f. +4.1d.18. +4.1u.S. +c.T.1t. +c.Y.1o. +e.0.27. +e.1.26. +e.2.25. +e.3.24. +e.4.23. +e.5.22. +e.6.21. +e.7.20. +e.8.1/. +e.9.1+. +e.a.1Z. +0.+.1k. +0.14.1g. +0.16.1e. 0.1c.1b. -0.1d.1b. -0.1b.1e. -3.1b.1f. -0.1b.1g. -3.1b.1h. -3.1b.1i. -0.1j.1b. -0.1k.1b. -2.1l.1b. -0.1m.1b. -0.1n.1b. -0.1o.1b. -0.1p.1b. -0.1q.1b. -0.1r.1b. -0.1s.1b. -3.1t.1b. -2.1u.1b. -0.1v.1b. -0.1w.1b. -0.1x.1b. -0.1y.1b. -0.1z.1b. -0.1A.1b. -0.1B.1b. -0.1C.1b. -0.1D.1b. -0.1E.1b. -0.1F.1b. -0.1G.1b. -0.1H.1b. -0.1I.1b. -0.1b.1J. -0.1b.1K. -0.1b.1L. -0.1b.1M. -0.1b.1N. -0.1b.1O. -0.1b.1P. -3.1b.1Q. -0.1b.1R. -3.1b.1S. -3.1b.1T. -3.1b.1U. -0.1b.1V. -0.1b.1W. -0.1b.1X. -0.1b.1Y. -0.1b.1Z. -0.1b.1+. -0.1b.1/. -0.1b.20. -0.1b.21. -0.1b.22. -0.1b.23. -0.1b.24. -0.1b.25. -b.26.1b. -0.1b.27. -4.1b.28. -0.1b.2a. -0.1b.2b. -0.1b.2l. -6.2m.1b. -0.1b.2p. -2.2q.1b. -2.2r.1b. -7.2s.1b. -7.2t.1b. -2.2u.1b. -3.1b.2v. -0.1b.2w. -e.2x.1b. -9.2y.1b. -0.1b.2z. -3.1b.2A. -9.2B.1b. -0.1b.2C. -0.1b.2D. -3.1b.2E. -3.1b.2F. -0.1b.2G. -9.2H.1b. -0.1b.2I. -3.1b.2J. -0.1b.2K. -c.2M.1b. -9.2N.1b. -2.2O.1b. -9.2Q.1b. -7.2S.1b. -0.1b.2T. -1.2U.1b. -9.2V.1b. -1.2W.1b. -0.1b.2X. -b.2+.1b. -0.1b.30. -0.1b.35. -b.36.1b. -0.1b.37. -3.1b.38. -5.39.1b. -0.1b.3d. -3.1b.3h. -3.1b.3i. -0.1b.3m. -0.1b.3p. -a.3v.1b. -a.3y.1b. -2.3z.1b. -5.3A.1b. -2.3B.1b. -0.1b.3C. -a.3F.1b. -5.3G.1b. -0.1b.3M. -0.1b.3Q. -5.40.1b. -0.1b.47. -0.1b.4d. -3.1b.4O. -3.1b.4Q. -5.4T.1b. -3.1b.4V. -0.1b.51. -0.1b.52. -a.5a.1b. -2.5b.1b. -5.5d.1b. -0.1b.5B. -0.1b.5C. -0.1b.5F. -3.1b.5H. -0.1b.5I. -f.1b.5K. -7.5M.1b. -0.1b.5O. -3.1b.5R. -0.1b.5U. -3.1b.5W. -1.5Y.1b. -0.1b.5Z. -0.1b.5/. -0.1b.60. -2.62.1b. -2.66.1b. -a.6b.1b. -c.6e.1b. -0.1b.6F. -0.1b.6M. -0.1b.6+. -3.1b.71. -5.79.1b. -3.1b.7D. -0.1b.2p. -0.1b.2p. +0.1d.19. +0.1E.K. +0.1j.11. +0.1o.Z. +0.1w.R. +0.1x.Q. +0.1y.P. +0.A.1O. +0.B.1N. +0.C.1M. +0.D.1L. +0.d.1Y. +0.E.1K. +0.G.1I. +0.H.1H. +0.N.1A. +0.O.1z. +0.r.1W. +0.s.1V. +0.U.1t. +0.v.1S. +0.V.1s. +0.w.1R. +0.W.1r. +0.X.1q. +3.1u.T. +3.I.1G. +3.J.1F. +3.M.1D. +4.12.1i. +4.15.1f. +4.S.1v. +c.Y.1p. +e.1.27. +e.2.26. +e.3.25. +e.4.24. +e.5.23. +e.6.22. +e.7.21. +e.8.20. +e.9.1/. +e.a.1+. +e.b.1Z. +0./.1k. +0.15.1g. +0.16.1f. +0.17.1e. +0.1B.N. 0.1c.1c. +0.1j.12. +0.1p.Z. +0.1x.R. +0.1y.Q. +0.1z.P. +0.A.1P. +0.B.1O. +0.C.1N. +0.D.1M. +0.E.1L. +0.e.1Y. +0.G.1J. +0.H.1I. +0.K.1F. +0.L.1E. +0.O.1A. +0.s.1W. +0.V.1t. +0.W.1s. +0.x.1R. +0.X.1r. +3.1l.+. +3.1u.U. +3.I.1H. +3.J.1G. +4.1d.1a. +4.1K.F. +4.S.1w. +4.t.1V. +4.w.1S. +c.28.0. +c.T.1v. +c.Y.1q. +e.2.27. +e.3.26. +e.4.25. +e.5.24. +e.6.23. +e.7.22. +e.8.21. +e.9.20. +e.a.1/. +e.b.1+. +i.c.1Z. +0.+.1m. +0.16.1g. +0.18.1e. +0.1A.P. +0.1B.O. +0.1d.1b. +0.1k.10. +0.1L.F. +0.1q.Z. +0.1y.R. +0.1z.Q. +0.B.1P. +0.C.1O. +0.D.1N. +0.E.1M. +0.f.1Y. +0.H.1J. +0.K.1G. +0.L.1F. +0.N.1C. +0.r.1X. +0.t.1W. +0.U.1v. +0.W.1t. +0.X.1s. +0.y.1R. +3.1l./. +3.1u.V. +3.I.1I. +3.J.1H. +3.M.1E. +4.13.1h. +4.17.1f. +4.A.1Q. +4.S.1x. +4.u.1V. +4.x.1S. +c.28.1. +c.T.1w. +c.Y.1r. +e.3.27. +e.4.26. +e.5.25. +e.6.24. +e.7.23. +e.8.22. +e.9.21. +e.a.20. +e.b.1/. +i.c.1+. +0./.1m. +0.+.1n. +0.17.1g. +0.19.1e. +0.1A.Q. +0.1B.P. 0.1d.1c. +0.1k.11. +0.1M.F. +0.1r.Z. +0.1R.z. +0.1t.X. +0.1z.R. +0.B.1Q. +0.C.1P. +0.D.1O. +0.E.1N. +0.g.1Y. +0.K.1H. +0.L.1G. +0.N.1D. +0.O.1C. +0.s.1X. +0.u.1W. +0.V.1v. +0.y.1S. +3.1l.10. +3.1u.W. +3.I.1J. +3.J.1I. +3.M.1F. +4.13.1i. +4.14.1h. +4.18.1f. +4.S.1y. +4.U.1w. +4.v.1V. +c.28.2. +c.T.1x. +c.Y.1s. +e.4.27. +e.5.26. +e.6.25. +e.7.24. +e.8.23. +e.9.22. +e.a.21. +e.b.20. +i.c.1/. +0./.1n. +0.+.1o. +0.18.1g. +0.19.1f. +0.1a.1e. +0.1A.R. +0.1B.Q. +0.1C.P. +0.1j.13. +0.1k.12. +0.1m.10. +0.1S.z. +0.1s.Z. +0.C.1Q. +0.D.1P. +0.E.1O. +0.G.1K. +0.h.1Y. +0.K.1I. +0.L.1H. +0.O.1D. +0.t.1X. +0.U.1x. +0.V.1w. +0.v.1W. +0.w.1V. +0.W.1v. +3.1l.11. +3.1u.X. +3.J.1J. +3.M.1G. +4.14.1i. +4.15.1h. +4.1N.F. +4.S.1z. +c.28.3. +c.T.1y. +c.Y.1t. +e.5.27. +e.6.26. +e.7.25. +e.8.24. +e.9.23. +e.a.22. +e.b.21. +i.c.20. +0./.1o. +0.+.1p. +0.19.1g. +0.1b.1e. +0.1B.R. +0.1C.Q. +0.1D.P. +0.1E.N. +0.1j.14. +0.1m.11. +0.1n.10. +0.1O.F. +0.D.1Q. +0.d.1Z. +0.E.1P. +0.G.1L. +0.H.1K. +0.i.1Y. +0.K.1J. +0.L.1I. +0.u.1X. +0.U.1y. +0.V.1x. +0.w.1W. +0.X.1v. +3.1l.12. +3.1u.Y. +3.M.1H. +4.15.1i. +4.16.1h. +4.1a.1f. +4.1d.1d. +4.1t.Z. +4.S.1A. +4.W.1w. +4.x.1V. +c.T.1z. +e.4.28. +e.6.27. +e.7.26. +e.8.25. +e.9.24. +e.a.23. +e.b.22. +i.c.21. +0.+.1q. +0.1a.1g. 0.1c.1e. -3.1c.1f. +0.1C.R. +0.1D.Q. +0.1j.15. +0.1m.12. +0.1n.11. +0.1o.10. +0.1P.F. +0.A.1R. +0.d.1+. +0.E.1Q. +0.e.1Z. +0.G.1M. +0.H.1L. +0.j.1Y. +0.L.1J. +0.N.1F. +0.O.1E. +0.U.1z. +0.v.1X. +0.V.1y. +0.W.1x. +0.x.1W. +3.1u.Z. +3.I.1K. +3.M.1I. +4./.1p. +4.16.1i. +4.17.1h. +4.1b.1f. +4.S.1B. +4.X.1w. +4.y.1V. +c.28.5. +c.T.1A. +c.Y.1v. +e.7.27. +e.8.26. +e.9.25. +e.a.24. +e.b.23. +i.c.22. +0./.1q. +0.+.1r. +0.1b.1g. +0.1D.R. +0.1E.P. +0.1j.16. +0.1k.13. +0.1n.12. +0.1o.11. +0.1p.10. +0.1v.Z. +0.A.1S. +0.B.1R. +0.d.1/. +0.e.1+. +0.f.1Z. +0.G.1N. +0.H.1M. +0.k.1Y. +0.N.1G. +0.O.1F. +0.U.1A. +0.V.1z. +0.w.1X. +0.W.1y. +0.X.1x. +0.y.1W. +3.I.1L. +3.J.1K. +3.M.1J. +4.17.1i. +4.18.1h. +4.1c.1f. +4.F.1Q. +4.S.1C. +c.28.6. +c.T.1B. +c.Y.1w. +e.8.27. +e.9.26. +e.a.25. +e.b.24. +f.1V.z. +i.c.23. +0./.1r. +0.+.1s. +0.19.1h. 0.1c.1g. -3.1c.1h. -3.1c.1i. -0.1j.1c. -0.1k.1c. -2.1l.1c. -0.1m.1c. -0.1n.1c. -0.1o.1c. -0.1p.1c. -0.1q.1c. -0.1r.1c. -0.1s.1c. -0.1t.1c. -2.1u.1c. -0.1v.1c. -0.1w.1c. -0.1x.1c. -0.1y.1c. -0.1z.1c. -0.1A.1c. -3.1B.1c. -0.1c.1C. -0.1D.1c. -0.1E.1c. -0.1F.1c. -0.1G.1c. -0.1H.1c. -0.1I.1c. -0.1c.1J. -0.1c.1K. -0.1c.1L. -0.1c.1M. -0.1c.1N. -0.1c.1O. -0.1c.1P. -3.1c.1Q. -0.1c.1R. -3.1c.1S. -3.1c.1T. -3.1c.1U. -0.1c.1V. -0.1c.1W. -0.1c.1X. -0.1c.1Y. -0.1c.1Z. -0.1c.1+. -0.1c.1/. -0.1c.20. -0.1c.21. -0.1c.22. -0.1c.23. -0.1c.24. -0.1c.25. -b.26.1c. -0.1c.27. -4.1c.28. -0.1c.2a. -0.1c.2b. -0.1c.2l. -6.2m.1c. -3.1c.2p. -2.2q.1c. -2.2r.1c. -7.2s.1c. -7.2t.1c. -2.2u.1c. -3.1c.2v. -0.1c.2w. -e.2x.1c. -9.2y.1c. -0.1c.2z. -3.1c.2A. -9.2B.1c. -0.1c.2C. -0.1c.2D. -3.1c.2E. -3.1c.2F. -0.1c.2G. -9.2H.1c. -0.1c.2I. -3.1c.2J. -0.1c.2K. -c.2M.1c. -9.2N.1c. -2.2O.1c. -9.2Q.1c. -7.2S.1c. -0.1c.2T. -1.2U.1c. -9.2V.1c. -1.2W.1c. -0.1c.2X. -b.2+.1c. -0.1c.30. -0.1c.35. -b.36.1c. -0.1c.37. -3.1c.38. -5.39.1c. -0.1c.3d. -3.1c.3h. -3.1c.3i. -0.1c.3m. -0.1c.3p. -a.3v.1c. -a.3y.1c. -2.3z.1c. -5.3A.1c. -2.3B.1c. -0.1c.3C. -a.3F.1c. -5.3G.1c. -0.1c.3M. -0.1c.3Q. -5.40.1c. -0.1c.47. -0.1c.4d. -3.1c.4O. -3.1c.4Q. -5.4T.1c. -3.1c.4V. -0.1c.51. -0.1c.52. -a.5a.1c. -2.5b.1c. -5.5d.1c. -0.1c.5B. -0.1c.5C. -0.1c.5F. -3.1c.5H. -0.1c.5I. -f.1c.5K. -7.5M.1c. -0.1c.5O. -3.1c.5R. -0.1c.5U. -0.1c.5W. -1.5Y.1c. -0.1c.5Z. -0.1c.5/. -0.1c.60. -2.62.1c. -2.66.1c. -a.6b.1c. -c.6e.1c. -0.1c.6F. -0.1c.6M. -0.1c.6+. -3.1c.71. -5.79.1c. -3.1c.7D. -3.1c.2p. -3.1c.2p. -3.1d.1d. 0.1d.1e. -3.1d.1f. +0.1E.Q. +0.1F.P. +0.1j.17. +0.1k.14. +0.1o.12. +0.1p.11. +0.1q.10. +0.1W.z. +0.1w.Z. +0.B.1S. +0.C.1R. +0.d.20. +0.e.1/. +0.f.1+. +0.G.1O. +0.g.1Z. +0.H.1N. +0.K.1K. +0.l.1Y. +0.N.1H. +0.O.1G. +0.V.1A. +0.W.1z. +0.x.1X. +0.X.1y. +3.1l.13. +3.I.1M. +3.J.1L. +4.18.1i. +4.1B.U. +4.S.1D. +c.T.1C. +c.Y.1x. +e.7.28. +e.9.27. +e.a.26. +e.b.25. +i.c.24. +0./.1s. +0.+.1t. +0.19.1i. +0.1E.R. +0.1F.Q. +0.1G.P. +0.1j.18. +0.1k.15. +0.1m.13. +0.1p.12. +0.1q.11. +0.1r.10. +0.1x.Z. +0.C.1S. +0.D.1R. +0.d.21. +0.e.20. +0.f.1/. +0.g.1+. +0.G.1P. +0.H.1O. +0.h.1Z. +0.K.1L. +0.L.1K. +0.m.1Y. +0.N.1I. +0.O.1H. +0.U.1C. +0.W.1A. +0.X.1z. +0.y.1X. +3.1l.14. +3.I.1N. +3.J.1M. +4.1a.1h. +4.1B.V. +4.1d.1f. +c.28.8. +c.T.1D. +c.Y.1y. +e.a.27. +e.b.26. +i.c.25. +0./.1t. +0.1B.W. 0.1d.1g. -3.1d.1h. -3.1d.1i. -0.1j.1d. -3.1k.1d. -2.1l.1d. -3.1m.1d. -3.1n.1d. -0.1o.1d. -3.1p.1d. -3.1q.1d. -3.1r.1d. -3.1s.1d. -3.1t.1d. -2.1u.1d. -3.1v.1d. -3.1w.1d. -3.1x.1d. -3.1y.1d. -0.1d.1z. -0.1d.1A. -0.1B.1d. -0.1d.1C. -0.1d.1D. -3.1E.1d. -0.1d.1F. -0.1d.1G. -0.1d.1H. -0.1d.1I. -0.1d.1J. -0.1d.1K. -0.1d.1L. -0.1d.1M. -0.1d.1N. -0.1d.1O. -0.1d.1P. -3.1d.1Q. -0.1d.1R. -0.1d.1S. -0.1d.1T. -0.1d.1U. -0.1d.1V. -0.1d.1W. -0.1d.1X. -0.1d.1Y. -0.1d.1Z. -0.1d.1+. -0.1d.1/. -0.1d.20. -0.1d.21. -0.1d.22. -0.1d.23. -0.1d.24. -0.1d.25. -b.26.1d. -0.1d.27. -4.1d.28. -0.1d.2a. -0.1d.2b. -0.1d.2l. -6.2m.1d. -3.1d.2p. -2.2q.1d. -2.2r.1d. -7.2s.1d. -7.2t.1d. -2.2u.1d. -3.1d.2v. -0.1d.2w. -e.2x.1d. -9.2y.1d. -0.1d.2z. -3.1d.2A. -9.2B.1d. -0.1d.2C. -0.1d.2D. -3.1d.2E. -3.1d.2F. -0.1d.2G. -9.2H.1d. -0.1d.2I. -3.1d.2J. -0.1d.2K. -c.2M.1d. -9.2N.1d. -2.2O.1d. -9.2Q.1d. -7.2S.1d. -0.1d.2T. -1.2U.1d. -9.2V.1d. -1.2W.1d. -0.1d.2X. -b.2+.1d. -0.1d.30. -0.1d.35. -b.36.1d. -0.1d.37. -3.1d.38. -5.39.1d. -0.1d.3d. -3.1d.3h. -3.1d.3i. -0.1d.3m. -0.1d.3p. -a.3v.1d. -a.3y.1d. -2.3z.1d. -5.3A.1d. -2.3B.1d. -3.1d.3C. -a.3F.1d. -5.3G.1d. -0.1d.3M. -0.1d.3Q. -5.40.1d. -0.1d.47. -0.1d.4d. -0.1d.4O. -3.1d.4Q. -5.4T.1d. -3.1d.4V. -3.1d.51. -0.1d.52. -a.5a.1d. -2.5b.1d. -5.5d.1d. -0.1d.5B. -0.1d.5C. -0.1d.5F. -3.1d.5H. -0.1d.5I. -f.1d.5K. -7.5M.1d. -0.1d.5O. -3.1d.5R. -0.1d.5U. -0.1d.5W. -1.5Y.1d. -0.1d.5Z. -0.1d.5/. -0.1d.60. -2.62.1d. -2.66.1d. -a.6b.1d. -c.6e.1d. -0.1d.6F. -0.1d.6M. -0.1d.6+. -3.1d.71. -5.79.1d. -0.1d.7D. -3.1d.2p. -3.1d.2p. +0.1F.R. +0.1G.Q. +0.1H.P. +0.1j.19. +0.1k.16. +0.1m.14. +0.1n.13. +0.1q.12. +0.1r.11. +0.1s.10. +0.1X.z. +0.1y.Z. +0.A.1V. +0.D.1S. +0.d.22. +0.E.1R. +0.e.21. +0.f.20. +0.g.1/. +0.H.1P. +0.i.1Z. +0.K.1M. +0.L.1L. +0.N.1J. +0.O.1I. +0.U.1D. +0.V.1C. +0.X.1A. +3.1l.15. +3.1u.+. +3.I.1O. +3.J.1N. +3.M.1K. +3.n.1Y. +4.1a.1i. +4.1b.1h. +4.G.1Q. +4.h.1+. +4.S.1E. +c.Y.1z. +e.9.28. +e.b.27. +i.c.26. +0.+.1v. +0.1B.X. 0.1e.1e. +0.1G.R. +0.1H.Q. +0.1I.P. +0.1j.1a. +0.1k.17. +0.1m.15. +0.1n.14. +0.1o.13. +0.1r.12. +0.1R.F. +0.1s.11. +0.1t.10. +0.1z.Z. +0.A.1W. +0.B.1V. +0.d.23. +0.E.1S. +0.e.22. +0.f.21. +0.g.20. +0.h.1/. +0.i.1+. +0.j.1Z. +0.K.1N. +0.L.1M. +0.O.1J. +0.o.1Y. +0.V.1D. +0.W.1C. +3.1l.16. +3.1u./. +3.I.1P. +3.J.1O. +3.M.1L. +4.1b.1i. +4.1c.1h. +4.H.1Q. +4.S.1F. +c.28.a. +c.T.1E. +c.Y.1A. +i.c.27. +0./.1v. +0.+.1w. +0.1A.Z. 0.1e.1f. +0.1H.R. +0.1I.Q. +0.1j.1b. +0.1k.18. +0.1m.16. +0.1n.15. +0.1o.14. +0.1p.13. +0.1s.12. +0.1t.11. +0.B.1W. +0.C.1V. +0.d.24. +0.e.23. +0.f.22. +0.g.21. +0.i.1/. +0.j.1+. +0.K.1O. +0.k.1Z. +0.L.1N. +0.P.1J. +0.p.1Y. +0.U.1E. +0.W.1D. +0.X.1C. +3.1l.17. +3.1u.10. +3.I.1Q. +3.J.1P. +3.M.1M. +4.1c.1i. +4.1S.F. +4.h.20. +4.S.1G. +c.28.b. +c.T.1F. +c.Y.1B. +0.+.1x. +0.1B.Z. 0.1e.1g. -3.1e.1h. -3.1e.1i. -0.1j.1e. -0.1k.1e. -2.1l.1e. -0.1m.1e. -0.1n.1e. -0.1o.1e. -0.1p.1e. -0.1q.1e. -0.1r.1e. -0.1s.1e. -0.1t.1e. -2.1u.1e. -0.1v.1e. -0.1w.1e. -0.1x.1e. -0.1y.1e. -0.1z.1e. -0.1A.1e. -3.1B.1e. -0.1C.1e. -0.1D.1e. -0.1E.1e. -0.1F.1e. -0.1G.1e. -0.1H.1e. -0.1I.1e. -0.1J.1e. -0.1K.1e. -0.1L.1e. -0.1M.1e. -3.1N.1e. -0.1O.1e. -0.1P.1e. -0.1e.1Q. -d.1R.1e. -0.1e.1S. -0.1e.1T. -0.1e.1U. -0.1e.1V. -0.1e.1W. -0.1e.1X. -0.1e.1Y. -0.1e.1Z. -0.1e.1+. -0.1e.1/. -0.1e.20. -0.1e.21. -0.1e.22. -0.1e.23. -0.1e.24. -0.1e.25. -b.26.1e. -0.1e.27. -0.1e.28. -0.1e.2a. -0.1e.2b. -0.1e.2l. -6.2m.1e. -0.1e.2p. -2.2q.1e. -2.2r.1e. -7.2s.1e. -7.2t.1e. -2.2u.1e. -3.1e.2v. -0.1e.2w. -e.2x.1e. -9.2y.1e. -0.1e.2z. -3.1e.2A. -9.2B.1e. -0.1e.2C. -0.1e.2D. -3.1e.2E. -0.1e.2F. -0.1e.2G. -9.2H.1e. -3.1e.2I. -3.1e.2J. -0.1e.2K. -c.2M.1e. -9.2N.1e. -2.2O.1e. -9.2Q.1e. -7.2S.1e. -0.1e.2T. -1.2U.1e. -9.2V.1e. -1.2W.1e. -0.1e.2X. -d.2+.1e. -0.1e.30. -0.1e.35. -b.36.1e. -0.1e.37. -3.1e.38. -5.39.1e. -0.1e.3d. -3.1e.3h. -0.1e.3i. -0.1e.3m. -0.1e.3p. -a.3v.1e. -a.3y.1e. -2.3z.1e. -5.3A.1e. -2.3B.1e. -0.1e.3C. -a.3F.1e. -5.3G.1e. -0.1e.3M. -0.1e.3Q. -5.40.1e. -0.1e.47. -0.1e.4d. -0.1e.4O. -0.1e.4Q. -5.4T.1e. -3.1e.4V. -0.1e.51. -0.1e.52. -a.5a.1e. -2.5b.1e. -5.5d.1e. -0.1e.5B. -0.1e.5C. -5.5F.1e. -3.1e.5H. -0.1e.5I. -f.1e.5K. -7.5M.1e. -0.1e.5O. -3.1e.5R. -0.1e.5U. -0.1e.5W. -1.5Y.1e. -0.1e.5Z. -0.1e.5/. -0.1e.60. -2.62.1e. -2.66.1e. -a.6b.1e. -c.6e.1e. -0.1e.6F. -0.1e.6M. -0.1e.6+. -3.1e.71. -5.79.1e. -3.1e.7D. -0.1e.2p. -0.1e.2p. 0.1f.1f. +0.1I.R. +0.1j.1c. +0.1k.19. +0.1m.17. +0.1n.16. +0.1o.15. +0.1p.14. +0.1q.13. +0.1v.10. +0.A.1X. +0.D.1V. +0.d.25. +0.e.24. +0.f.23. +0.g.22. +0.h.21. +0.i.20. +0.j.1/. +0.k.1+. +0.K.1P. +0.L.1O. +0.l.1Z. +0.N.1K. +0.Q.1J. +0.q.1Y. +0.U.1F. +0.V.1E. +0.X.1D. +3.1l.18. +3.1u.11. +3.J.1Q. +3.M.1N. +4./.1w. +4.1d.1h. +4.1t.12. +4.C.1W. +4.S.1H. +c.T.1G. +c.Y.1C. +i.c.28. +0./.1x. +0.+.1y. +0.1C.Z. 0.1f.1g. -3.1f.1h. -3.1f.1i. -0.1j.1f. -0.1k.1f. -2.1l.1f. -0.1m.1f. -0.1n.1f. -0.1o.1f. -0.1p.1f. -0.1q.1f. -0.1r.1f. -0.1s.1f. -3.1t.1f. -2.1u.1f. -0.1v.1f. -0.1w.1f. -0.1x.1f. -0.1y.1f. -0.1z.1f. -0.1A.1f. -0.1B.1f. -0.1C.1f. -3.1D.1f. -3.1E.1f. -0.1F.1f. -0.1G.1f. -0.1H.1f. -3.1I.1f. -0.1J.1f. -3.1K.1f. -0.1L.1f. -0.1M.1f. -3.1N.1f. -0.1O.1f. -0.1P.1f. -3.1f.1Q. -d.1R.1f. -3.1S.1f. -0.1f.1T. -3.1f.1U. -0.1f.1V. -0.1f.1W. -0.1f.1X. -0.1f.1Y. -0.1f.1Z. -0.1f.1+. -0.1f.1/. -0.1f.20. -0.1f.21. -0.1f.22. -0.1f.23. -0.1f.24. -0.1f.25. -b.26.1f. -0.1f.27. -4.1f.28. -0.1f.2a. -0.1f.2b. -0.1f.2l. -6.2m.1f. -0.1f.2p. -2.2q.1f. -2.2r.1f. -7.2s.1f. -7.2t.1f. -2.2u.1f. -3.1f.2v. -0.1f.2w. -e.2x.1f. -9.2y.1f. -0.1f.2z. -3.1f.2A. -9.2B.1f. -0.1f.2C. -0.1f.2D. -3.1f.2E. -3.1f.2F. -0.1f.2G. -9.2H.1f. -0.1f.2I. -3.1f.2J. -0.1f.2K. -c.2M.1f. -9.2N.1f. -2.2O.1f. -9.2Q.1f. -7.2S.1f. -0.1f.2T. -1.2U.1f. -9.2V.1f. -1.2W.1f. -0.1f.2X. -d.2+.1f. -0.1f.30. -0.1f.35. -3.36.1f. -0.1f.37. -3.1f.38. -5.39.1f. -0.1f.3d. -3.1f.3h. -0.1f.3i. -0.1f.3m. -0.1f.3p. -a.3v.1f. -a.3y.1f. -2.3z.1f. -5.3A.1f. -2.3B.1f. -0.1f.3C. -a.3F.1f. -5.3G.1f. -0.1f.3M. -0.1f.3Q. -5.40.1f. -0.1f.47. -0.1f.4d. -3.1f.4O. -3.1f.4Q. -5.4T.1f. -3.1f.4V. -0.1f.51. -0.1f.52. -a.5a.1f. -2.5b.1f. -5.5d.1f. -0.1f.5B. -0.1f.5C. -5.5F.1f. -3.1f.5H. -0.1f.5I. -f.1f.5K. -7.5M.1f. -0.1f.5O. -3.1f.5R. -0.1f.5U. -0.1f.5W. -1.5Y.1f. -0.1f.5Z. -0.1f.5/. -0.1f.60. -2.62.1f. -2.66.1f. -a.6b.1f. -c.6e.1f. -0.1f.6F. -0.1f.6M. -0.1f.6+. -3.1f.71. -5.79.1f. -3.1f.7D. -0.1f.2p. -0.1f.2p. -f.1g.1g. +0.1k.1a. +0.1n.17. +0.1o.16. +0.1p.15. +0.1q.14. +0.1r.13. +0.1v.11. +0.1w.10. +0.B.1X. +0.d.26. +0.e.25. +0.f.24. +0.G.1R. +0.g.23. +0.h.22. +0.i.21. +0.j.20. +0.k.1/. +0.l.1+. +0.L.1P. +0.m.1Z. +0.N.1L. +0.O.1K. +0.R.1J. +0.U.1G. +0.V.1F. +0.W.1E. +3.1l.19. +3.1u.12. +3.M.1O. +4.1d.1i. +4.1m.18. +4.D.1W. +4.E.1V. +4.K.1Q. +4.S.1I. +c.T.1H. +c.Y.1D. +0./.1y. +0.+.1z. +0.1D.Z. +0.1E.X. +0.1j.1d. +0.1k.1b. +0.1m.19. +0.1n.18. +0.1o.17. +0.1p.16. +0.1q.15. +0.1r.14. +0.1s.13. +0.1v.12. +0.1w.11. +0.1x.10. +0.C.1X. +0.d.27. +0.e.26. +0.f.25. +0.g.24. +0.H.1R. +0.i.22. +0.j.21. +0.k.20. +0.l.1/. +0.L.1Q. +0.m.1+. +0.N.1M. +0.O.1L. +0.P.1K. +0.U.1H. +0.V.1G. +0.W.1F. +3.1l.1a. +3.M.1P. +3.n.1Z. +4.E.1W. +4.F.1V. +4.G.1S. +4.h.23. +4.S.1J. +c.T.1I. +e.1g.1g. +0./.1z. +0.+.1A. +0.1k.1c. +0.1m.1a. +0.1n.19. +0.1o.18. +0.1p.17. +0.1q.16. +0.1r.15. +0.1s.14. +0.1t.13. +0.1w.12. +0.1x.11. +0.1y.10. +0.D.1X. +0.e.27. +0.f.26. +0.g.25. +0.H.1S. +0.h.24. +0.i.23. +0.j.22. +0.k.21. +0.l.20. +0.m.1/. +0.N.1N. +0.O.1M. +0.o.1Z. +0.P.1L. +0.Q.1K. +0.r.1Y. +0.U.1I. +0.V.1H. +0.W.1G. +0.X.1F. +3.1l.1b. +3.I.1R. +3.M.1Q. +3.n.1+. +4.1e.1h. +4.F.1W. +c.T.1J. +c.Y.1E. +0./.1A. +0.1B.+. +0.1E.Z. +0.1m.1b. +0.1n.1a. +0.1o.19. +0.1p.18. +0.1q.17. +0.1r.16. +0.1s.15. +0.1x.12. +0.1y.11. +0.1z.10. +0.E.1X. +0.f.27. +0.g.26. +0.h.25. +0.i.24. +0.j.23. +0.k.22. +0.l.21. +0.m.20. +0.N.1O. +0.o.1+. +0.O.1N. +0.P.1M. +0.p.1Z. +0.Q.1L. +0.R.1K. +0.s.1Y. +0.U.1J. +0.V.1I. +0.W.1H. +0.X.1G. +3.1l.1c. +3.1u.13. +3.I.1S. +3.J.1R. +3.n.1/. +4.1e.1i. +4.1f.1h. +4.1t.14. +c.28.d. +c.Y.1F. +e.0.29. +0.+.1C. +0.1A.10. +0.1F.Z. 0.1h.1g. +0.1j.1e. +0.1m.1c. +0.1n.1b. +0.1o.1a. +0.1p.19. +0.1r.17. +0.1s.16. +0.1t.15. +0.1v.13. +0.1y.12. +0.1z.11. +0.F.1X. +0.g.27. +0.h.26. +0.i.25. +0.j.24. +0.K.1R. +0.k.23. +0.l.22. +0.m.21. +0.N.1P. +0.o.1/. +0.O.1O. +0.p.1+. +0.P.1N. +0.Q.1M. +0.q.1Z. +0.R.1L. +0.t.1Y. +0.V.1J. +0.W.1I. +0.X.1H. +3.1u.14. +3.J.1S. +4.1B./. +4.1f.1i. +4.1k.1d. +4.1q.18. +4.G.1V. +4.S.1K. +c.28.e. +c.Y.1G. +e.0.2a. +e.1.29. +e.n.20. +0./.1C. +0.+.1D. +0.1A.11. +0.1B.10. +0.1G.Z. 0.1i.1g. +0.1j.1f. +0.1n.1c. +0.1o.1b. +0.1p.1a. +0.1q.19. +0.1s.17. +0.1t.16. +0.1v.14. +0.1w.13. +0.1z.12. +0.G.1W. +0.H.1V. +0.h.27. +0.i.26. +0.j.25. +0.K.1S. +0.k.24. +0.L.1R. +0.l.23. +0.m.22. +0.N.1Q. +0.O.1P. +0.o.20. +0.p.1/. +0.P.1O. +0.q.1+. +0.Q.1N. +0.R.1M. +0.u.1Y. +0.W.1J. +0.X.1I. +3.1l.1d. +3.1u.15. +3.n.21. +3.S.1L. +4.1r.18. +c.28.f. +c.T.1K. +c.Y.1H. +e.1.2a. +e.2.29. +0./.1D. +0.1A.12. +0.1B.11. +0.1C.10. +0.1H.Z. 0.1j.1g. -0.1k.1g. -2.1l.1g. -0.1m.1g. -0.1n.1g. -0.1o.1g. -0.1p.1g. -0.1q.1g. -0.1r.1g. -0.1s.1g. -0.1t.1g. -2.1u.1g. -0.1v.1g. -0.1w.1g. -0.1x.1g. -0.1y.1g. -0.1z.1g. -0.1A.1g. -3.1B.1g. -0.1C.1g. -0.1D.1g. -0.1E.1g. -0.1F.1g. -0.1G.1g. -0.1H.1g. -0.1I.1g. -0.1J.1g. -3.1K.1g. -0.1L.1g. -0.1M.1g. -3.1N.1g. -0.1O.1g. -0.1P.1g. -d.1Q.1g. -0.1R.1g. -0.1S.1g. -d.1T.1g. -0.1U.1g. -0.1V.1g. -f.1g.1W. -0.1X.1g. -0.1Y.1g. -0.1Z.1g. -d.1+.1g. -0.1/.1g. -0.20.1g. -d.21.1g. -0.22.1g. -0.23.1g. -0.24.1g. -0.25.1g. -b.26.1g. -0.27.1g. -4.28.1g. -0.2a.1g. -3.2b.1g. -0.2l.1g. -6.2m.1g. -d.2p.1g. -2.2q.1g. -2.2r.1g. -7.2s.1g. -7.2t.1g. -2.2u.1g. -d.2v.1g. -0.2w.1g. -e.2x.1g. -9.2y.1g. -3.2z.1g. -d.2A.1g. -9.2B.1g. -3.2C.1g. -d.2D.1g. -d.2E.1g. -0.2F.1g. -0.2G.1g. -9.2H.1g. -d.2I.1g. -d.2J.1g. -0.2K.1g. -c.2M.1g. -9.2N.1g. -2.2O.1g. -9.2Q.1g. -7.2S.1g. -d.2T.1g. -1.2U.1g. -9.2V.1g. -1.2W.1g. -0.2X.1g. -b.2+.1g. -d.30.1g. -0.35.1g. -b.36.1g. -f.1g.37. -d.38.1g. -5.39.1g. -5.3d.1g. -0.3h.1g. -5.3i.1g. -f.1g.3m. -5.3p.1g. -a.3v.1g. -a.3y.1g. -2.3z.1g. -5.3A.1g. -2.3B.1g. -5.3C.1g. -a.3F.1g. -5.3G.1g. -f.1g.3M. -f.1g.3Q. -5.40.1g. -f.1g.47. -5.4d.1g. -f.1g.4O. -3.4Q.1g. -5.4T.1g. -f.1g.4V. -5.51.1g. -0.52.1g. -a.5a.1g. -2.5b.1g. -5.5d.1g. -5.5B.1g. -5.5C.1g. -5.5F.1g. -0.5H.1g. -0.5I.1g. -f.1g.5K. -7.5M.1g. -5.5O.1g. -5.5R.1g. -f.1g.5U. -3.5W.1g. -1.5Y.1g. -5.5Z.1g. -5.5/.1g. -0.60.1g. -2.62.1g. -2.66.1g. -a.6b.1g. -c.6e.1g. -f.1g.6F. -0.6M.1g. -d.6+.1g. -d.71.1g. -5.79.1g. -f.1g.7D. -d.2p.1g. -d.2p.1g. -3.1h.1h. -3.1h.1i. +0.1o.1c. +0.1p.1b. +0.1q.1a. +0.1r.19. +0.1t.17. +0.1v.15. +0.1w.14. +0.1x.13. +0.i.27. +0.j.26. +0.k.25. +0.L.1S. +0.l.24. +0.m.23. +0.o.21. +0.P.1P. +0.p.20. +0.q.1/. +0.Q.1O. +0.R.1N. +0.U.1K. +0.v.1Y. +0.X.1J. +3.1u.16. +3.I.1V. +3.M.1R. +3.n.22. +4.1m.1d. +4.1s.18. +4.H.1W. +4.O.1Q. +4.S.1M. +c.28.g. +c.T.1L. +c.Y.1I. +e.2.2a. +e.3.29. +0.1B.12. +0.1C.11. +0.1D.10. +0.1E.+. +0.1I.Z. +0.1k.1e. +0.1p.1c. +0.1q.1b. +0.1r.1a. +0.1s.19. +0.1v.16. +0.1w.15. +0.1x.14. +0.1y.13. +0.G.1X. +0.j.27. +0.k.26. +0.l.25. +0.m.24. +0.o.22. +0.p.21. +0.Q.1P. +0.q.20. +0.R.1O. +0.r.1Z. +0.U.1L. +0.V.1K. +0.w.1Y. +3.1u.17. +3.I.1W. +3.J.1V. +3.M.1S. +3.n.23. +3.S.1N. +4.1n.1d. +4.1t.18. +4.P.1Q. +c.28.h. +c.T.1M. +c.Y.1J. +e.0.2c. +e.1h.1h. +e.3.2a. +e.4.29. +0.+.1F. +0.1C.12. +0.1D.11. +0.1E./. +0.1k.1f. +0.1o.1d. +0.1q.1c. +0.1r.1b. +0.1t.19. +0.1v.17. +0.1w.16. +0.1x.15. +0.1y.14. +0.1z.13. +0.H.1X. +0.K.1V. +0.k.27. +0.l.26. +0.m.25. +0.o.23. +0.p.22. +0.Q.1Q. +0.q.21. +0.r.1+. +0.R.1P. +0.s.1Z. +0.U.1M. +0.V.1L. +0.W.1K. +0.x.1Y. +3.1l.1e. +3.1u.18. +3.J.1W. +3.n.24. +3.S.1O. +4.1h.1i. +4.1J.Z. +4.1s.1a. +c.28.i. +c.T.1N. +e.1.2c. +e.4.2a. +e.5.29. +0./.1F. +0.+.1G. +0.1A.13. +0.1D.12. +0.1E.10. 0.1j.1h. -3.1k.1h. -2.1l.1h. -3.1m.1h. -3.1n.1h. -0.1o.1h. -3.1p.1h. -3.1q.1h. -3.1r.1h. -3.1s.1h. -3.1t.1h. -2.1u.1h. -3.1v.1h. -3.1w.1h. -3.1x.1h. -3.1y.1h. -3.1z.1h. -3.1A.1h. -0.1B.1h. -3.1C.1h. -3.1D.1h. -3.1E.1h. -3.1F.1h. -3.1G.1h. -3.1H.1h. -3.1I.1h. -3.1J.1h. -3.1K.1h. -0.1L.1h. -3.1M.1h. -3.1N.1h. -0.1O.1h. -0.1P.1h. -3.1h.1Q. -0.1R.1h. -3.1S.1h. -3.1h.1T. -3.1h.1U. -0.1h.1V. -0.1h.1W. -0.1h.1X. -0.1h.1Y. -0.1h.1Z. -0.1h.1+. -0.1h.1/. -0.1h.20. -0.1h.21. -0.1h.22. -0.1h.23. -0.1h.24. -0.1h.25. -b.26.1h. -0.1h.27. -4.1h.28. -4.1h.29. -0.1h.2a. -0.1h.2b. -4.1h.2c. -4.1h.2d. -4.1h.2f. -4.1h.2g. -4.1h.2h. -4.1h.2i. -4.1h.2j. -0.1h.2l. -6.2m.1h. -1.1h.2o. -0.1h.2p. -2.2q.1h. -2.2r.1h. -7.2s.1h. -7.2t.1h. -2.2u.1h. -3.1h.2v. -0.1h.2w. -e.2x.1h. -9.2y.1h. -0.1h.2z. -3.1h.2A. -9.2B.1h. -0.1h.2C. -0.1h.2D. -3.1h.2E. -3.1h.2F. -0.1h.2G. -9.2H.1h. -0.1h.2I. -3.1h.2J. -0.1h.2K. -4.1h.2L. -c.2M.1h. -9.2N.1h. -2.2O.1h. -4.1h.2P. -9.2Q.1h. -4.1h.2R. -7.2S.1h. -0.1h.2T. -1.2U.1h. -9.2V.1h. -1.2W.1h. -0.1h.2X. -4.1h.2Y. -4.1h.2Z. -b.2+.1h. -4.1h.2/. -0.1h.30. -4.1h.31. -4.1h.32. -4.1h.33. -4.1h.34. -0.1h.35. -b.36.1h. -0.1h.37. -3.1h.38. -5.39.1h. -4.1h.3a. -4.1h.3c. -0.1h.3d. -1.1h.3e. -4.1h.3f. -4.1h.3g. -3.1h.3h. -3.1h.3i. -1.1h.3j. -4.1h.3k. -4.1h.3l. -0.1h.3m. -4.1h.3n. -4.1h.3o. -0.1h.3p. -4.1h.3q. -4.1h.3r. -4.1h.3s. -4.1h.3t. -4.1h.3u. -a.3v.1h. -4.1h.3w. -4.1h.3x. -a.3y.1h. -2.3z.1h. -5.3A.1h. -2.3B.1h. -0.1h.3C. -4.1h.3D. -4.1h.3E. -a.3F.1h. -5.3G.1h. -4.1h.3H. -4.1h.3I. -4.1h.3J. -4.1h.3K. -4.1h.3L. -0.1h.3M. -4.1h.3N. -4.1h.3O. -4.1h.3P. -0.1h.3Q. -4.1h.3R. -4.1h.3S. -1.1h.3T. -4.1h.3U. -4.1h.3V. -4.1h.3W. -4.1h.3X. -4.1h.3Y. -4.1h.3Z. -1.1h.3+. -1.1h.3/. -5.40.1h. -4.1h.41. -4.1h.42. -4.1h.43. -4.1h.44. -4.1h.45. -0.1h.47. -4.1h.48. -4.1h.49. -1.1h.4a. -4.1h.4b. -4.1h.4c. -0.1h.4d. -4.1h.4e. -4.1h.4f. -4.1h.4g. -4.1h.4h. -4.1h.4i. -4.1h.4j. -4.1h.4k. -4.1h.4l. -4.1h.4m. -4.1h.4n. -4.1h.4o. -4.1h.4p. -4.1h.4q. -4.1h.4r. -4.1h.4s. -4.1h.4t. -4.1h.4u. -4.1h.4v. -4.1h.4w. -4.1h.4x. -4.1h.4y. -4.1h.4z. -4.1h.4A. -4.1h.4B. -4.1h.4C. -4.1h.4D. -4.1h.4E. -1.1h.4F. -4.1h.4G. -4.1h.4H. -1.1h.4I. -4.1h.4J. -4.1h.4K. -4.1h.4L. -4.1h.4M. -4.1h.4N. -3.1h.4O. -4.1h.4P. -3.1h.4Q. -1.1h.4R. -4.1h.4S. -4.1h.4T. -4.1h.4U. -3.1h.4V. -4.1h.4W. -4.1h.4Y. -1.1h.4Z. -4.1h.4+. -4.1h.4/. -1.1h.50. -0.1h.51. -0.1h.52. -4.1h.53. -4.1h.54. -4.1h.55. -4.1h.56. -1.1h.57. -4.1h.58. -4.1h.59. -a.5a.1h. -2.5b.1h. -4.1h.5c. -5.5d.1h. -4.1h.5e. -4.1h.5f. -4.1h.5g. -4.1h.5h. -4.1h.5i. -4.1h.5j. -4.1h.5k. -4.1h.5l. -4.1h.5m. -4.1h.5n. -4.1h.5o. -4.1h.5p. -4.1h.5q. -4.1h.5r. -1.1h.5s. -4.1h.5t. -4.1h.5u. -4.1h.5v. -1.1h.5w. -1.1h.5y. -1.1h.5z. -4.1h.5A. -0.1h.5B. -0.1h.5C. -1.1h.5D. -4.1h.5E. -5.5F.1h. -4.1h.5G. -3.1h.5H. -0.1h.5I. -f.1h.5K. -4.1h.5L. -4.1h.5M. -4.1h.5N. -0.1h.5O. -4.1h.5P. -4.1h.5Q. -3.1h.5R. -4.1h.5S. -4.1h.5T. -0.1h.5U. -4.1h.5V. -3.1h.5W. -4.1h.5X. -4.1h.5Y. -0.1h.5Z. -h.5+.1h. -0.1h.5/. -0.1h.60. -4.1h.61. -2.62.1h. -4.1h.64. -4.1h.65. -2.66.1h. -4.1h.67. -4.1h.68. -4.1h.69. -4.1h.6a. -a.6b.1h. -4.1h.6c. -4.1h.6d. -4.1h.6e. -4.1h.6f. -4.1h.6g. -4.1h.6h. -4.1h.6i. -4.1h.6j. -4.1h.6k. -4.1h.6l. -4.1h.6m. -4.1h.6n. -4.1h.6o. -4.1h.6p. -4.1h.6q. -4.1h.6r. -4.1h.6s. -4.1h.6t. -4.1h.6u. -4.1h.6v. -4.1h.6w. -4.1h.6x. -4.1h.6y. -4.1h.6z. -4.1h.6A. -4.1h.6B. -4.1h.6C. -4.1h.6D. -4.1h.6E. -0.1h.6F. -4.1h.6G. -4.1h.6H. -4.1h.6I. -4.1h.6J. -4.1h.6K. -4.1h.6L. -0.1h.6M. -4.1h.6N. -4.1h.6O. -4.1h.6P. -4.1h.6Q. -4.1h.6R. -4.1h.6S. -4.1h.6T. -4.1h.6U. -4.1h.6V. -4.1h.6W. -1.1h.6X. -4.1h.6Y. -1.1h.6Z. -0.1h.6+. -1.1h.6/. -1.1h.70. -3.1h.71. -4.1h.72. -4.1h.73. -1.1h.74. -4.1h.75. -1.1h.76. -1.1h.77. -4.1h.78. -5.79.1h. -1.1h.7a. -4.1h.7b. -4.1h.7c. -4.1h.7d. -4.1h.7e. -4.1h.7f. -4.1h.7g. -4.1h.7h. -4.1h.7i. -4.1h.7j. -4.1h.7k. -4.1h.7l. -1.1h.7m. -4.1h.7o. -4.1h.7p. -1.1h.7r. -1.1h.7s. -4.1h.7t. -4.1h.7u. -4.1h.7v. -4.1h.7w. -4.1h.7x. -4.1h.7y. -1.1h.7z. -4.1h.7A. -4.1h.7B. -4.1h.7C. -3.1h.7D. -4.1h.7E. -4.1h.7F. -4.1h.7G. -4.1h.7H. -4.1h.7I. -4.1h.7J. -1.1h.7K. -1.1h.7L. -1.1h.7M. -1.1h.7N. -1.1h.7O. -1.1h.7P. -4.1h.7Q. -4.1h.7R. -4.1h.7S. -4.1h.7T. -1.1h.7U. -4.1h.7V. -4.1h.7W. -4.1h.7X. -4.1h.7Y. -4.1h.7Z. -1.1h.7+. -1.1h.7/. -1.1h.80. -1.1h.81. -1.1h.82. -4.1h.83. -1.1h.84. -1.1h.85. -h.86.1h. -1.1h.87. -1.1h.88. -4.1h.89. -4.1h.8a. -4.1h.8b. -4.1h.8c. -4.1h.8d. -4.1h.8e. -4.1h.8f. -4.1h.8g. -4.1h.8h. -4.1h.8i. -4.1h.8j. -4.1h.8k. -4.1h.8l. -4.1h.8m. -4.1h.8n. -4.1h.8o. -4.1h.8p. -4.1h.8q. -4.1h.8r. -4.1h.8s. -4.1h.8t. -4.1h.8u. -4.1h.8v. -4.1h.8w. -4.1h.8x. -4.1h.8y. -4.1h.8z. -4.1h.8A. -4.1h.8B. -4.1h.8C. -4.1h.8D. -4.1h.8E. -4.1h.8F. -4.1h.8G. -4.1h.8H. -4.1h.8I. -4.1h.8J. -4.1h.8K. -4.1h.8L. -4.1h.8M. -4.1h.8N. -4.1h.8O. -4.1h.8P. -0.1h.2p. -0.1h.2p. -4.1h.3b. -1.1h.46. -1.1h.4X. -1.1h.5x. -1.1h.5J. -1.1h.7n. -1.1h.7q. -3.1i.1i. +0.1k.1g. +0.1m.1e. +0.1r.1c. +0.1s.1b. +0.1v.18. +0.1w.17. +0.1x.16. +0.1y.15. +0.1z.14. +0.K.1W. +0.L.1V. +0.l.27. +0.m.26. +0.N.1R. +0.o.24. +0.p.23. +0.q.22. +0.r.1/. +0.R.1Q. +0.s.1+. +0.t.1Z. +0.U.1N. +0.V.1M. +0.W.1L. +0.X.1K. +0.y.1Y. +3.1l.1f. +3.1u.19. +3.I.1X. +3.n.25. +3.S.1P. +4.1i.1i. +4.1p.1d. +4.1t.1a. +c.28.j. +c.T.1O. +e.2.2c. +e.5.2a. +e.6.29. +0./.1G. +0.+.1H. +0.1A.14. +0.1B.13. +0.1E.11. +0.1F.10. 0.1j.1i. -3.1k.1i. -2.1l.1i. -3.1m.1i. -3.1n.1i. -0.1o.1i. -3.1p.1i. -3.1q.1i. -3.1r.1i. -3.1s.1i. -3.1t.1i. -2.1u.1i. -3.1v.1i. -3.1w.1i. -3.1x.1i. -0.1y.1i. -3.1z.1i. -3.1A.1i. -0.1B.1i. -3.1C.1i. -3.1D.1i. -3.1E.1i. -3.1F.1i. -3.1G.1i. -3.1H.1i. -3.1I.1i. -3.1J.1i. -3.1K.1i. -0.1L.1i. -3.1M.1i. -3.1N.1i. -0.1O.1i. -0.1P.1i. -3.1i.1Q. -d.1R.1i. -3.1S.1i. -3.1i.1T. -3.1i.1U. -3.1i.1V. -0.1i.1W. -0.1i.1X. -0.1i.1Y. -0.1i.1Z. -0.1i.1+. -0.1i.1/. -0.1i.20. -0.1i.21. -0.1i.22. -0.1i.23. -0.1i.24. -0.1i.25. -b.26.1i. -3.1i.27. -0.1i.28. -0.1i.2a. -3.1i.2b. -3.1i.2l. -6.2m.1i. -3.1i.2p. -2.2q.1i. -2.2r.1i. -7.2s.1i. -7.2t.1i. -2.2u.1i. -3.1i.2v. -3.1i.2w. -e.2x.1i. -9.2y.1i. -0.1i.2z. -3.1i.2A. -9.2B.1i. -0.1i.2C. -3.1i.2D. -3.1i.2E. -3.1i.2F. -0.1i.2G. -9.2H.1i. -3.1i.2I. -3.1i.2J. -3.1i.2K. -c.2M.1i. -9.2N.1i. -2.2O.1i. -9.2Q.1i. -7.2S.1i. -0.1i.2T. -1.2U.1i. -9.2V.1i. -1.2W.1i. -0.1i.2X. -b.2+.1i. -0.1i.30. -0.1i.35. -b.36.1i. -0.1i.37. -3.1i.38. -5.39.1i. -3.1i.3d. -3.1i.3h. -3.1i.3i. -0.1i.3m. -0.1i.3p. -a.3v.1i. -a.3y.1i. -2.3z.1i. -5.3A.1i. -2.3B.1i. -3.1i.3C. -a.3F.1i. -5.3G.1i. -0.1i.3M. -0.1i.3Q. -5.40.1i. -0.1i.47. -0.1i.4d. -3.1i.4O. -3.1i.4Q. -5.4T.1i. -3.1i.4V. -3.1i.51. -0.1i.52. -a.5a.1i. -2.5b.1i. -5.5d.1i. -3.1i.5B. -0.1i.5C. -5.5F.1i. -3.1i.5H. -0.1i.5I. -f.1i.5K. -7.5M.1i. -0.1i.5O. -3.1i.5R. -3.1i.5U. -3.1i.5W. -1.5Y.1i. -0.1i.5Z. -0.1i.5/. -0.1i.60. -2.62.1i. -2.66.1i. -a.6b.1i. -c.6e.1i. -0.1i.6F. -0.1i.6M. -0.1i.6+. -3.1i.71. -5.79.1i. -3.1i.7D. -3.1i.2p. -3.1i.2p. +0.1m.1f. +0.1n.1e. +0.1s.1c. +0.1v.19. +0.1w.18. +0.1x.17. +0.1y.16. +0.1z.15. +0.L.1W. +0.m.27. +0.N.1S. +0.O.1R. +0.o.25. +0.p.24. +0.q.23. +0.r.20. +0.s.1/. +0.U.1O. +0.u.1Z. +0.V.1N. +0.W.1M. +0.X.1L. +0.z.1Y. +3.1l.1g. +3.1u.1a. +3.J.1X. +3.M.1V. +3.n.26. +4.1q.1d. +4.1t.1b. +4.S.1Q. +4.t.1+. +c.28.k. +c.T.1P. +c.Y.1K. +e.0.2d. +e.3.2c. +e.6.2a. +e.7.29. +0./.1H. +0.+.1I. +0.1A.15. +0.1B.14. +0.1C.13. +0.1E.12. +0.1F.11. +0.1G.10. 0.1j.1j. +0.1m.1g. +0.1n.1f. +0.1o.1e. +0.1t.1c. +0.1v.1a. +0.1w.19. +0.1x.18. +0.1y.17. +0.1z.16. +0.K.1X. +0.O.1S. +0.o.26. +0.P.1R. +0.p.25. +0.q.24. +0.r.21. +0.s.20. +0.U.1P. +0.V.1O. +0.v.1Z. +0.W.1N. +0.X.1M. +3.1u.1b. +3.M.1W. +3.n.27. +4.1K.Z. +4.1r.1d. +4.t.1/. +4.u.1+. +c.28.l. +c.T.1Q. +c.Y.1L. +e.1.2d. +e.4.2c. +e.7.2a. +e.8.29. +0./.1I. +0.+.1J. +0.1A.16. +0.1B.15. +0.1C.14. +0.1D.13. +0.1F.12. +0.1G.11. +0.1H.10. +0.1L.Z. +0.1n.1g. +0.1o.1f. +0.1p.1e. +0.1v.1b. +0.1w.1a. +0.1x.19. +0.1z.17. +0.L.1X. +0.o.27. +0.P.1S. +0.p.26. +0.Q.1R. +0.q.25. +0.r.22. +0.s.21. +0.u.1/. +0.v.1+. +0.V.1P. +0.W.1O. +0.w.1Z. +0.X.1N. +3.1u.1c. +4.1k.1h. +4.1s.1d. +4.1y.18. +4.t.20. +4.U.1Q. +c.28.m. +c.Y.1M. +e.2.2d. +e.5.2c. +e.8.2a. +e.9.29. +0./.1J. +0.1A.17. +0.1B.16. +0.1C.15. +0.1D.14. +0.1G.12. +0.1H.11. +0.1I.10. +0.1M.Z. +0.1o.1g. +0.1p.1f. +0.1q.1e. +0.1v.1c. +0.1w.1b. +0.1x.1a. +0.1y.19. +0.A.1Y. +0.N.1V. +0.p.27. +0.Q.1S. +0.q.26. +0.R.1R. +0.r.23. +0.s.22. +0.t.21. +0.u.20. +0.v.1/. +0.V.1Q. +0.W.1P. +0.X.1O. +0.x.1Z. +3.1l.1h. +3.M.1X. +3.n.28. +4.1k.1i. +4.1t.1d. +4.1z.18. +4.w.1+. +c.Y.1N. +e.3.2d. +e.6.2c. +e.9.2a. +e.a.29. +0.1B.17. +0.1C.16. +0.1D.15. +0.1E.13. +0.1H.12. +0.1I.11. +0.1J.10. 0.1j.1k. -2.1l.1j. +0.1p.1g. +0.1q.1f. +0.1r.1e. +0.1w.1c. +0.1x.1b. +0.1y.1a. +0.1z.19. +0.B.1Y. +0.N.1W. +0.O.1V. +0.q.27. +0.R.1S. +0.r.24. +0.s.23. +0.u.21. +0.v.20. +0.w.1/. +0.W.1Q. +0.X.1P. +0.y.1Z. +3.1l.1i. +3.1u.1d. +3.S.1R. +4.1A.18. +4.1m.1h. +4.1N.Z. +4.t.22. +4.x.1+. +c.28.o. +c.Y.1O. +e.4.2d. +e.7.2c. +e.a.2a. +e.b.29. +0.+.1K. +0.11.1J. +0.1A.19. +0.1B.18. +0.1C.17. +0.1D.16. +0.1E.14. +0.1F.13. +0.1I.12. +0.1O.Z. +0.1q.1g. +0.1r.1f. +0.1s.1e. +0.1x.1c. +0.1y.1b. +0.1z.1a. +0.1Z.z. +0.C.1Y. +0.P.1V. +0.r.25. +0.s.24. +0.u.22. +0.v.21. +0.w.20. +0.x.1/. +0.y.1+. +3.1l.1j. +4.1m.1i. +4.1n.1h. +4.1v.1d. +4.O.1W. +4.S.1S. +4.t.23. +4.X.1Q. +c.28.p. +c.T.1R. +c.Y.1P. +e.5.2d. +e.8.2c. +e.b.2a. +i.c.29. +0./.1K. +0.+.1L. +0.1+.z. +0.1A.1a. +0.1B.19. +0.1C.18. +0.1D.17. +0.1E.15. +0.1F.14. +0.1G.13. +0.1J.12. 0.1j.1m. +0.1o.1h. +0.1P.Z. +0.1r.1g. +0.1s.1f. +0.1t.1e. +0.1y.1c. +0.1z.1b. +0.D.1Y. +0.N.1X. +0.Q.1V. +0.r.26. +0.s.25. +0.t.24. +0.U.1R. +0.u.23. +0.v.22. +0.w.21. +0.x.20. +0.y.1/. +4.1n.1i. +4.1w.1d. +4.P.1W. +c.28.q. +c.T.1S. +c.Y.1Q. +e.6.2d. +e.9.2c. +i.c.2a. +0./.1L. +0.+.1M. +0.1/.z. +0.1A.1b. +0.1B.1a. +0.1C.19. +0.1E.16. +0.1F.15. +0.1G.14. +0.1H.13. 0.1j.1n. -0.1j.1o. -0.1j.1p. -0.1j.1q. -0.1j.1r. -0.1j.1s. -0.1j.1t. -2.1u.1j. -0.1j.1v. -0.1j.1w. -0.1j.1x. -0.1j.1y. -0.1j.1z. -0.1j.1A. -3.1B.1j. -0.1j.1C. -0.1j.1D. -0.1j.1E. -0.1j.1F. -0.1j.1G. -0.1j.1H. -0.1j.1I. -0.1j.1J. -0.1j.1K. -0.1j.1L. -0.1j.1M. -0.1j.1N. -0.1j.1O. -0.1j.1P. -0.1j.1Q. -0.1j.1R. -0.1j.1S. -0.1j.1T. -0.1j.1U. -0.1j.1V. -0.1j.1W. -0.1j.1X. -0.1j.1Y. -0.1j.1Z. -0.1j.1+. -0.1j.1/. -0.1j.20. -0.1j.21. -0.1j.22. -0.1j.23. -0.1j.24. -0.1j.25. -b.26.1j. -0.1j.27. -0.1j.28. -0.1j.2a. -0.1j.2b. -3.1j.2l. -6.2m.1j. -0.1j.2p. -2.2q.1j. -2.2r.1j. -7.2s.1j. -7.2t.1j. -2.2u.1j. -0.1j.2v. -0.1j.2w. -e.2x.1j. -9.2y.1j. -0.1j.2z. -0.1j.2A. -9.2B.1j. -0.1j.2C. -0.1j.2D. -0.1j.2E. -0.1j.2F. -0.1j.2G. -9.2H.1j. -0.1j.2I. -0.1j.2J. -0.1j.2K. -c.2M.1j. -9.2N.1j. -2.2O.1j. -9.2Q.1j. -7.2S.1j. -0.1j.2T. -1.2U.1j. -9.2V.1j. -1.2W.1j. -0.1j.2X. -d.2+.1j. -0.1j.30. -0.1j.35. -b.36.1j. -0.1j.37. -0.1j.38. -5.39.1j. -0.1j.3d. -0.1j.3h. -0.1j.3i. -0.1j.3m. -0.1j.3p. -a.3v.1j. -a.3y.1j. -2.3z.1j. -5.3A.1j. -2.3B.1j. -0.1j.3C. -a.3F.1j. -5.3G.1j. -0.1j.3M. -0.1j.3Q. -5.40.1j. -0.1j.47. -0.1j.4d. -0.1j.4O. -0.1j.4Q. -5.4T.1j. -0.1j.4V. -0.1j.51. -0.1j.52. -a.5a.1j. -2.5b.1j. -5.5d.1j. -0.1j.5B. -0.1j.5C. -0.1j.5F. -0.1j.5H. -0.1j.5I. -f.1j.5K. -7.5M.1j. -0.1j.5O. -0.1j.5R. -0.1j.5U. -0.1j.5W. -1.5Y.1j. -0.1j.5Z. -0.1j.5/. -0.1j.60. -2.62.1j. -2.66.1j. -a.6b.1j. -c.6e.1j. -0.1j.6F. -0.1j.6M. -0.1j.6+. -0.1j.71. -5.79.1j. -0.1j.7D. -0.1j.2p. -0.1j.2p. +0.1K.10. 0.1k.1k. -2.1l.1k. +0.1o.1i. +0.1s.1g. +0.1z.1c. +0.E.1Y. +0.O.1X. +0.Q.1W. +0.R.1V. +0.r.27. +0.s.26. +0.t.25. +0.u.24. +0.V.1R. +0.v.23. +0.w.22. +0.x.21. +0.y.20. +0.Z.1Q. +3.1u.1e. +4.1D.18. +4.1p.1h. +4.1t.1f. +4.1x.1d. +4.U.1S. +e.7.2d. +e.a.2c. +0./.1M. +0.+.1N. +0.1A.1c. +0.1B.1b. +0.1D.19. +0.1E.17. +0.1F.16. +0.1G.15. +0.1H.14. +0.1I.13. +0.1j.1o. +0.1K.11. +0.1L.10. +0.1t.1g. +0.1v.1e. +0.A.1Z. +0.F.1Y. +0.P.1X. +0.R.1W. +0.s.27. +0.t.26. +0.u.25. +0.V.1S. +0.v.24. +0.W.1R. +0.w.23. +0.x.22. +0.y.21. +3.1l.1k. +3.1u.1f. +3.S.1V. +4.1C.1a. +4.1p.1i. +4.1q.1h. +4.1y.1d. +5.c.2b. +e.8.2d. +e.b.2c. +f.20.z. +0./.1N. +0.+.1O. +0.13.1J. +0.1C.1b. +0.1D.1a. +0.1d.1z. +0.1F.17. +0.1G.16. +0.1H.15. +0.1I.14. +0.1j.1p. +0.1K.12. 0.1k.1m. +0.1L.11. +0.1M.10. +0.1v.1f. +0.1w.1e. +0.A.1+. +0.B.1Z. +0.d.29. +0.Q.1X. +0.t.27. +0.u.26. +0.v.25. +0.W.1S. +0.w.24. +0.X.1R. +0.x.23. +0.y.22. +3.1l.1l. +3.1u.1g. +4.1B.1c. +4.1E.18. +4.1q.1i. +4.1r.1h. +4.S.1W. +c.28.r. +c.T.1V. +e.21.z. +e.9.2d. +i.c.2c. +0./.1O. +0.+.1P. +0.14.1J. +0.1c.1C. +0.1d.1A. +0.1D.1b. +0.1E.19. +0.1F.18. +0.1G.17. +0.1H.16. +0.1I.15. +0.1j.1q. +0.1L.12. +0.1M.11. 0.1n.1k. -0.1o.1k. -0.1k.1p. -0.1k.1q. -0.1k.1r. -0.1k.1s. -0.1k.1t. -2.1u.1k. -0.1k.1v. -3.1k.1w. -0.1k.1x. -0.1k.1y. -0.1k.1z. -0.1k.1A. -0.1B.1k. -0.1k.1C. -0.1k.1D. -0.1E.1k. -0.1k.1F. -0.1k.1G. -0.1k.1H. -0.1k.1I. -0.1k.1J. -0.1k.1K. -0.1k.1L. -0.1k.1M. -0.1k.1N. -0.1k.1O. -0.1k.1P. -3.1k.1Q. -0.1k.1R. -0.1k.1S. -0.1k.1T. -3.1k.1U. -0.1k.1V. -0.1k.1W. -0.1k.1X. -0.1k.1Y. -0.1k.1Z. -0.1k.1+. -0.1k.1/. -0.1k.20. -0.1k.21. -0.1k.22. -0.1k.23. -0.1k.24. -0.1k.25. -b.26.1k. -0.1k.27. -0.1k.28. -0.1k.2a. -0.1k.2b. -0.1k.2l. -6.2m.1k. -0.1k.2p. -2.2q.1k. -2.2r.1k. -7.2s.1k. -7.2t.1k. -2.2u.1k. -0.1k.2v. -0.1k.2w. -e.2x.1k. -9.2y.1k. -0.1k.2z. -3.1k.2A. -9.2B.1k. -0.1k.2C. -0.1k.2D. -3.1k.2E. -0.1k.2F. -0.1k.2G. -9.2H.1k. -0.1k.2I. -3.1k.2J. -0.1k.2K. -c.2M.1k. -9.2N.1k. -2.2O.1k. -9.2Q.1k. -7.2S.1k. -0.1k.2T. -1.2U.1k. -9.2V.1k. -1.2W.1k. -0.1k.2X. -b.2+.1k. -0.1k.30. -0.1k.35. -b.36.1k. -0.1k.37. -0.1k.38. -5.39.1k. -0.1k.3d. -3.1k.3h. -0.1k.3i. -0.1k.3m. -0.1k.3p. -a.3v.1k. -a.3y.1k. -2.3z.1k. -5.3A.1k. -2.3B.1k. -0.1k.3C. -a.3F.1k. -5.3G.1k. -0.1k.3M. -0.1k.3Q. -5.40.1k. -0.1k.47. -0.1k.4d. -0.1k.4O. -3.1k.4Q. -5.4T.1k. -3.1k.4V. -0.1k.51. -0.1k.52. -a.5a.1k. -2.5b.1k. -5.5d.1k. -0.1k.5B. -0.1k.5C. -0.1k.5F. -3.1k.5H. -0.1k.5I. -f.1k.5K. -7.5M.1k. -0.1k.5O. -3.1k.5R. -0.1k.5U. -0.1k.5W. -1.5Y.1k. -0.1k.5Z. -0.1k.5/. -0.1k.60. -2.62.1k. -2.66.1k. -a.6b.1k. -c.6e.1k. -0.1k.6F. -0.1k.6M. -0.1k.6+. -3.1k.71. -5.79.1k. -3.1k.7D. -0.1k.2p. -0.1k.2p. -2.1l.1l. -2.1l.1m. -2.1l.1n. -2.1l.1o. -2.1l.1p. -2.1l.1q. -2.1l.1r. -2.1l.1s. -2.1l.1t. -2.1u.1l. -2.1l.1v. -2.1l.1w. -2.1l.1x. -2.1l.1y. -2.1l.1z. -2.1l.1A. -2.1l.1B. -2.1l.1C. -2.1l.1D. -2.1l.1E. -2.1l.1F. -2.1l.1G. -2.1l.1H. -2.1l.1I. -2.1l.1J. -2.1l.1K. -2.1l.1L. -2.1l.1M. -2.1l.1N. -2.1l.1O. -2.1l.1P. -2.1l.1Q. -2.1l.1R. -2.1l.1S. -2.1l.1T. -2.1l.1U. -2.1l.1V. -2.1l.1W. -2.1l.1X. -2.1l.1Y. -2.1l.1Z. -2.1l.1+. -2.1l.1/. -2.1l.20. -2.1l.21. -2.1l.22. -2.1l.23. -2.1l.24. -2.1l.25. -2.1l.26. -2.1l.27. -2.1l.28. -2.1l.2a. -2.1l.2b. -2.1l.2l. -6.2m.1l. -2.1l.2p. -2.1l.2q. -2.1l.2r. -7.2s.1l. -7.2t.1l. -2.2u.1l. -2.1l.2v. -2.1l.2w. -e.2x.1l. -2.1l.2y. -2.1l.2z. -2.1l.2A. -2.1l.2B. -2.1l.2C. -2.1l.2D. -2.1l.2E. -2.1l.2F. -2.1l.2G. -2.1l.2H. -2.1l.2I. -2.1l.2J. -2.1l.2K. -c.2M.1l. -2.1l.2N. -2.1l.2O. -2.1l.2Q. -7.2S.1l. -2.1l.2T. -1.2U.1l. -2.1l.2V. -1.2W.1l. -2.1l.2X. -2.1l.2+. -2.1l.30. -2.1l.35. -2.1l.36. -2.1l.37. -2.1l.38. -2.1l.39. -2.1l.3d. -2.1l.3h. -2.1l.3i. -2.1l.3m. -2.1l.3p. -a.3v.1l. -a.3y.1l. -2.3z.1l. -2.1l.3A. -2.3B.1l. -2.1l.3C. -a.3F.1l. -2.1l.3G. -2.1l.3M. -2.1l.3Q. -2.1l.40. -2.1l.47. -2.1l.4d. -2.1l.4O. -2.1l.4Q. -5.4T.1l. -2.1l.4V. -2.1l.51. -2.1l.52. -a.5a.1l. -2.5b.1l. -2.1l.5d. -2.1l.5B. -2.1l.5C. -2.1l.5F. -2.1l.5H. -2.1l.5I. -f.1l.5K. -7.5M.1l. -2.1l.5O. -2.1l.5R. -2.1l.5U. -2.1l.5W. -1.5Y.1l. -2.1l.5Z. -2.1l.5/. -2.1l.60. -2.1l.62. -2.1l.66. -a.6b.1l. -c.6e.1l. -2.1l.6F. -2.1l.6M. -2.1l.6+. -2.1l.71. -2.1l.79. -2.1l.7D. -2.1l.2p. -2.1l.2p. +0.1v.1g. +0.1w.1f. +0.1x.1e. +0.22.z. +0.A.1/. +0.B.1+. +0.C.1Z. +0.e.29. +0.R.1X. +0.u.27. +0.v.26. +0.w.25. +0.X.1S. +0.x.24. +0.y.23. +3.1l.1m. +4.1N.10. +4.1r.1i. +4.1s.1h. +4.U.1V. +5.d.2a. +c.28.s. +c.T.1W. +c.Y.1R. +e.a.2d. +0./.1P. +0.+.1Q. +0.15.1J. +0.1B.1d. +0.1D.1c. +0.1E.1a. +0.1F.19. +0.1G.18. +0.1H.17. +0.1I.16. +0.1j.1r. +0.1M.12. 0.1m.1m. +0.1N.11. +0.1O.10. +0.1o.1k. +0.1R.Z. +0.1w.1g. +0.1x.1f. +0.1y.1e. +0.A.20. +0.B.1/. +0.C.1+. +0.D.1Z. +0.f.29. +0.G.1Y. +0.V.1V. +0.v.27. +0.w.26. +0.x.25. +0.y.24. +3.1l.1n. +4.1s.1i. +4.1t.1h. +4.S.1X. +4.U.1W. +5.e.2a. +c.28.t. +c.Y.1S. +e.b.2d. +f.23.z. +0.13.1K. +0.16.1J. +0.1d.1C. +0.1E.1b. +0.1F.1a. +0.1G.19. +0.1I.17. +0.1j.1s. +0.1k.1p. 0.1n.1m. -0.1o.1m. -0.1m.1p. -0.1m.1q. -0.1m.1r. -0.1m.1s. -0.1m.1t. -2.1u.1m. -0.1m.1v. -3.1m.1w. -0.1m.1x. -0.1m.1y. -0.1m.1z. -0.1m.1A. -0.1B.1m. -0.1m.1C. -0.1m.1D. -0.1E.1m. -0.1m.1F. -0.1m.1G. -0.1m.1H. -0.1m.1I. -0.1m.1J. -0.1m.1K. -0.1m.1L. -0.1m.1M. -0.1m.1N. -0.1m.1O. -0.1m.1P. -3.1m.1Q. -0.1m.1R. -0.1m.1S. -0.1m.1T. -3.1m.1U. -0.1m.1V. -0.1m.1W. -0.1m.1X. -0.1m.1Y. -0.1m.1Z. -0.1m.1+. -0.1m.1/. -0.1m.20. -0.1m.21. -0.1m.22. -0.1m.23. -0.1m.24. -0.1m.25. -b.26.1m. -0.1m.27. -4.1m.28. -0.1m.2a. -0.1m.2b. -0.1m.2l. -6.2m.1m. -0.1m.2p. -2.2q.1m. -2.2r.1m. -7.2s.1m. -7.2t.1m. -2.2u.1m. -3.1m.2v. -0.1m.2w. -e.2x.1m. -9.2y.1m. -0.1m.2z. -3.1m.2A. -9.2B.1m. -0.1m.2C. -0.1m.2D. -3.1m.2E. -3.1m.2F. -0.1m.2G. -9.2H.1m. -0.1m.2I. -3.1m.2J. -0.1m.2K. -c.2M.1m. -9.2N.1m. -2.2O.1m. -9.2Q.1m. -7.2S.1m. -0.1m.2T. -1.2U.1m. -9.2V.1m. -1.2W.1m. -0.1m.2X. -b.2+.1m. -0.1m.30. -0.1m.35. -b.36.1m. -0.1m.37. -0.1m.38. -5.39.1m. -0.1m.3d. -3.1m.3h. -3.1m.3i. -0.1m.3m. -0.1m.3p. -a.3v.1m. -a.3y.1m. -2.3z.1m. -5.3A.1m. -2.3B.1m. -0.1m.3C. -a.3F.1m. -5.3G.1m. -0.1m.3M. -0.1m.3Q. -5.40.1m. -0.1m.47. -0.1m.4d. -3.1m.4O. -3.1m.4Q. -5.4T.1m. -3.1m.4V. -0.1m.51. -0.1m.52. -a.5a.1m. -2.5b.1m. -5.5d.1m. -0.1m.5B. -0.1m.5C. -0.1m.5F. -3.1m.5H. -0.1m.5I. -f.1m.5K. -7.5M.1m. -0.1m.5O. -3.1m.5R. -0.1m.5U. -0.1m.5W. -1.5Y.1m. -0.1m.5Z. -0.1m.5/. -0.1m.60. -2.62.1m. -2.66.1m. -a.6b.1m. -c.6e.1m. -0.1m.6F. -0.1m.6M. -0.1m.6+. -3.1m.71. -5.79.1m. -3.1m.7D. -0.1m.2p. -0.1m.2p. +0.1O.11. +0.1P.10. +0.1x.1g. +0.1y.1f. +0.1z.1e. +0.24.z. +0.A.21. +0.B.20. +0.C.1/. +0.D.1+. +0.E.1Z. +0.g.29. +0.H.1Y. +0.W.1V. +0.w.27. +0.x.26. +0.y.25. +3.1l.1o. +3.1u.1h. +4./.1Q. +4.1H.18. +4.1N.12. +4.1S.Z. +4.1t.1i. +4.V.1W. +5.f.2a. +c.28.u. +c.T.1X. +i.c.2d. +0.13.1L. +0.14.1K. +0.17.1J. +0.1A.1e. +0.1d.1D. +0.1E.1c. +0.1F.1b. +0.1G.1a. +0.1H.19. +0.1j.1t. +0.1k.1q. 0.1n.1n. +0.1O.12. +0.1o.1m. +0.1P.11. +0.1y.1g. +0.1z.1f. +0.25.z. +0.A.22. +0.B.21. +0.C.20. +0.D.1/. +0.d.2c. +0.E.1+. +0.F.1Z. +0.U.1X. +0.W.1W. +0.X.1V. +0.x.27. +0.y.26. +3.1l.1p. +3.1u.1i. +3.I.1Y. +4.10.1Q. +4.1I.18. +4.1v.1h. +4.h.29. +5.c.2e. +5.g.2a. +c.28.v. +0.11.1Q. +0.13.1M. +0.14.1L. +0.15.1K. +0.18.1J. +0.1A.1f. +0.1F.1c. +0.1G.1b. +0.1H.1a. +0.1I.19. +0.1k.1r. +0.1m.1p. 0.1o.1n. +0.1P.12. +0.1z.1g. +0.26.z. +0.A.23. +0.B.22. +0.C.21. +0.D.20. +0.E.1/. +0.e.2c. +0.F.1+. +0.i.29. +0.V.1X. +0.X.1W. +0.y.27. +3.1l.1q. +3.1u.1j. +3.J.1Y. +4.1B.1e. +4.1v.1i. +4.1w.1h. +5.h.2a. +c.28.w. +c.Y.1V. +0.+.1R. +0.13.1N. +0.14.1M. +0.15.1L. +0.16.1K. +0.19.1J. +0.1A.1g. +0.1B.1f. +0.1C.1e. +0.1G.1c. +0.1H.1b. +0.1I.1a. +0.1j.1v. +0.1k.1s. +0.1m.1q. 0.1n.1p. -0.1n.1q. -0.1n.1r. -0.1n.1s. -0.1n.1t. -2.1u.1n. -0.1n.1v. -3.1n.1w. -0.1n.1x. -0.1n.1y. -0.1n.1z. -0.1n.1A. -0.1B.1n. -0.1n.1C. -0.1n.1D. -0.1E.1n. -0.1n.1F. -0.1n.1G. -0.1n.1H. -0.1n.1I. -0.1n.1J. -0.1n.1K. -0.1n.1L. -0.1n.1M. -0.1n.1N. -0.1n.1O. -0.1n.1P. -3.1n.1Q. -0.1n.1R. -0.1n.1S. -0.1n.1T. -0.1n.1U. -0.1n.1V. -0.1n.1W. -0.1n.1X. -0.1n.1Y. -0.1n.1Z. -0.1n.1+. -0.1n.1/. -0.1n.20. -0.1n.21. -0.1n.22. -0.1n.23. -0.1n.24. -0.1n.25. -b.26.1n. -0.1n.27. -0.1n.28. -0.1n.2a. -0.1n.2b. -0.1n.2l. -6.2m.1n. -0.1n.2p. -2.2q.1n. -2.2r.1n. -7.2s.1n. -7.2t.1n. -2.2u.1n. -0.1n.2v. -0.1n.2w. -e.2x.1n. -9.2y.1n. -0.1n.2z. -3.1n.2A. -9.2B.1n. -0.1n.2C. -0.1n.2D. -3.1n.2E. -0.1n.2F. -0.1n.2G. -9.2H.1n. -0.1n.2I. -3.1n.2J. -0.1n.2K. -c.2M.1n. -9.2N.1n. -2.2O.1n. -9.2Q.1n. -7.2S.1n. -0.1n.2T. -1.2U.1n. -9.2V.1n. -1.2W.1n. -0.1n.2X. -b.2+.1n. -0.1n.30. -0.1n.35. -b.36.1n. -0.1n.37. -0.1n.38. -5.39.1n. -0.1n.3d. -3.1n.3h. -0.1n.3i. -0.1n.3m. -0.1n.3p. -a.3v.1n. -a.3y.1n. -2.3z.1n. -5.3A.1n. -2.3B.1n. -0.1n.3C. -a.3F.1n. -5.3G.1n. -0.1n.3M. -0.1n.3Q. -5.40.1n. -0.1n.47. -0.1n.4d. -0.1n.4O. -3.1n.4Q. -5.4T.1n. -3.1n.4V. -0.1n.51. -0.1n.52. -a.5a.1n. -2.5b.1n. -5.5d.1n. -0.1n.5B. -0.1n.5C. -0.1n.5F. -3.1n.5H. -0.1n.5I. -f.1n.5K. -7.5M.1n. -0.1n.5O. -3.1n.5R. -0.1n.5U. -0.1n.5W. -1.5Y.1n. -0.1n.5Z. -0.1n.5/. -0.1n.60. -2.62.1n. -2.66.1n. -a.6b.1n. -c.6e.1n. -0.1n.6F. -0.1n.6M. -0.1n.6+. -3.1n.71. -5.79.1n. -3.1n.7D. -0.1n.2p. -0.1n.2p. 0.1o.1o. +0.27.z. +0.A.24. +0.B.23. +0.C.22. +0.D.21. +0.E.20. +0.F.1/. +0.f.2c. +0.K.1Y. +0.W.1X. +3.1l.1r. +4.12.1Q. +4.1E.1d. +4.1w.1i. +4.1x.1h. +4.j.29. +4.Z.1V. +5.i.2a. +c.28.x. +c.Y.1W. +0./.1R. +0.+.1S. +0.13.1O. +0.14.1N. +0.15.1M. +0.16.1L. +0.17.1K. +0.1a.1J. +0.1C.1f. +0.1D.1e. +0.1d.1F. +0.1H.1c. +0.1I.1b. +0.1j.1w. +0.1k.1t. +0.1m.1r. +0.1n.1q. 0.1o.1p. +0.A.25. +0.B.24. +0.C.23. +0.D.22. +0.d.2d. +0.E.21. +0.F.20. +0.G.1Z. +0.g.2c. +0.k.29. +0.L.1Y. +0.X.1X. +0.Z.1W. +3.1l.1s. +4.1B.1g. +4.1x.1i. +4.1y.1h. +5.c.2f. +5.j.2a. +c.28.y. +0./.1S. +0.10.1R. +0.13.1P. +0.14.1O. +0.15.1N. +0.16.1M. +0.17.1L. +0.18.1K. +0.1b.1J. +0.1C.1g. +0.1d.1G. +0.1I.1c. +0.1j.1x. +0.1m.1s. +0.1n.1r. 0.1o.1q. -0.1o.1r. -0.1o.1s. -0.1o.1t. -2.1u.1o. -0.1o.1v. -0.1o.1w. -0.1o.1x. -0.1o.1y. -0.1o.1z. -0.1o.1A. -0.1B.1o. -0.1o.1C. -0.1o.1D. -0.1E.1o. -0.1o.1F. -0.1o.1G. -0.1o.1H. -0.1o.1I. -0.1o.1J. -0.1o.1K. -0.1o.1L. -0.1o.1M. -0.1o.1N. -0.1o.1O. -0.1o.1P. -0.1o.1Q. -0.1o.1R. -0.1o.1S. -0.1o.1T. -0.1o.1U. -0.1o.1V. -0.1o.1W. -0.1o.1X. -0.1o.1Y. -0.1o.1Z. -0.1o.1+. -0.1o.1/. -0.1o.20. -0.1o.21. -0.1o.22. -0.1o.23. -0.1o.24. -0.1o.25. -b.26.1o. -0.1o.27. -0.1o.28. -0.1o.2a. -0.1o.2b. -0.1o.2l. -6.2m.1o. -0.1o.2p. -2.2q.1o. -2.2r.1o. -7.2s.1o. -7.2t.1o. -2.2u.1o. -0.1o.2v. -0.1o.2w. -e.2x.1o. -9.2y.1o. -0.1o.2z. -0.1o.2A. -9.2B.1o. -0.1o.2C. -0.1o.2D. -0.1o.2E. -0.1o.2F. -0.1o.2G. -9.2H.1o. -0.1o.2I. -0.1o.2J. -0.1o.2K. -c.2M.1o. -9.2N.1o. -2.2O.1o. -9.2Q.1o. -7.2S.1o. -0.1o.2T. -1.2U.1o. -9.2V.1o. -1.2W.1o. -0.1o.2X. -b.2+.1o. -0.1o.30. -0.1o.35. -b.36.1o. -0.1o.37. -0.1o.38. -5.39.1o. -0.1o.3d. -0.1o.3h. -0.1o.3i. -0.1o.3m. -0.1o.3p. -a.3v.1o. -a.3y.1o. -2.3z.1o. -5.3A.1o. -2.3B.1o. -0.1o.3C. -a.3F.1o. -5.3G.1o. -0.1o.3M. -0.1o.3Q. -5.40.1o. -0.1o.47. -0.1o.4d. -0.1o.4O. -0.1o.4Q. -5.4T.1o. -0.1o.4V. -0.1o.51. -0.1o.52. -a.5a.1o. -2.5b.1o. -5.5d.1o. -0.1o.5B. -0.1o.5C. -0.1o.5F. -0.1o.5H. -0.1o.5I. -f.1o.5K. -7.5M.1o. -0.1o.5O. -0.1o.5R. -0.1o.5U. -0.1o.5W. -1.5Y.1o. -0.1o.5Z. -0.1o.5/. -0.1o.60. -2.62.1o. -2.66.1o. -a.6b.1o. -c.6e.1o. -0.1o.6F. -0.1o.6M. -0.1o.6+. -0.1o.71. -5.79.1o. -0.1o.7D. -0.1o.2p. -0.1o.2p. 0.1p.1p. +0.1y.1i. +0.A.26. +0.B.25. +0.C.24. +0.D.23. +0.E.22. +0.e.2d. +0.F.21. +0.H.1Z. +0.h.2c. +0.k.2a. +0.l.29. +3.1l.1t. +3.1u.1k. +3.M.1Y. +4.1D.1f. +4.1z.1h. +4.G.1+. +c.28.z. +c.Y.1X. +0.10.1S. +0.11.1R. +0.14.1P. +0.15.1O. +0.16.1N. +0.17.1M. +0.18.1L. +0.19.1K. +0.1c.1J. +0.1D.1g. +0.1d.1H. +0.1E.1e. +0.1j.1y. +0.1k.1v. +0.1m.1t. +0.1n.1s. +0.1o.1r. 0.1p.1q. +0.A.27. +0.B.26. +0.C.25. +0.D.24. +0.E.23. +0.F.22. +0.f.2d. +0.H.1+. +0.i.2c. +0.m.29. +0.Z.1X. +3.1u.1l. +3.I.1Z. +4.13.1Q. +4.1A.1h. +4.1z.1i. +4.G.1/. +5.l.2a. +0.+.1V. +0.11.1S. +0.12.1R. +0.15.1P. +0.16.1O. +0.17.1N. +0.18.1M. +0.19.1L. +0.1a.1K. +0.1B.1h. +0.1d.1I. +0.1F.1e. +0.1j.1z. +0.1n.1t. +0.1o.1s. 0.1p.1r. -0.1p.1s. -3.1t.1p. -2.1u.1p. -0.1v.1p. -0.1w.1p. -0.1x.1p. -0.1y.1p. -0.1p.1z. -0.1p.1A. -0.1B.1p. -0.1p.1C. -0.1p.1D. -3.1E.1p. -0.1p.1F. -0.1p.1G. -0.1p.1H. -0.1p.1I. -0.1p.1J. -0.1p.1K. -0.1p.1L. -0.1p.1M. -0.1p.1N. -0.1p.1O. -0.1p.1P. -3.1p.1Q. -0.1p.1R. -0.1p.1S. -0.1p.1T. -0.1p.1U. -0.1p.1V. -0.1p.1W. -0.1p.1X. -0.1p.1Y. -0.1p.1Z. -0.1p.1+. -0.1p.1/. -0.1p.20. -0.1p.21. -0.1p.22. -0.1p.23. -0.1p.24. -0.1p.25. -b.26.1p. -0.1p.27. -4.1p.28. -0.1p.2a. -0.1p.2b. -0.1p.2l. -6.2m.1p. -0.1p.2p. -2.2q.1p. -2.2r.1p. -7.2s.1p. -7.2t.1p. -2.2u.1p. -0.1p.2v. -0.1p.2w. -e.2x.1p. -9.2y.1p. -0.1p.2z. -3.1p.2A. -9.2B.1p. -0.1p.2C. -0.1p.2D. -3.1p.2E. -0.1p.2F. -0.1p.2G. -9.2H.1p. -0.1p.2I. -3.1p.2J. -0.1p.2K. -c.2M.1p. -9.2N.1p. -2.2O.1p. -9.2Q.1p. -7.2S.1p. -0.1p.2T. -1.2U.1p. -9.2V.1p. -1.2W.1p. -0.1p.2X. -b.2+.1p. -0.1p.30. -0.1p.35. -b.36.1p. -0.1p.37. -0.1p.38. -5.39.1p. -0.1p.3d. -3.1p.3h. -0.1p.3i. -0.1p.3m. -0.1p.3p. -a.3v.1p. -a.3y.1p. -2.3z.1p. -5.3A.1p. -2.3B.1p. -0.1p.3C. -a.3F.1p. -5.3G.1p. -0.1p.3M. -0.1p.3Q. -5.40.1p. -0.1p.47. -0.1p.4d. -0.1p.4O. -3.1p.4Q. -5.4T.1p. -3.1p.4V. -0.1p.51. -0.1p.52. -a.5a.1p. -2.5b.1p. -5.5d.1p. -0.1p.5B. -0.1p.5C. -0.1p.5F. -3.1p.5H. -0.1p.5I. -f.1p.5K. -7.5M.1p. -0.1p.5O. -0.1p.5R. -0.1p.5U. -0.1p.5W. -1.5Y.1p. -0.1p.5Z. -0.1p.5/. -0.1p.60. -2.62.1p. -2.66.1p. -a.6b.1p. -c.6e.1p. -0.1p.6F. -0.1p.6M. -0.1p.6+. -3.1p.71. -5.79.1p. -3.1p.7D. -0.1p.2p. -0.1p.2p. 0.1q.1q. +0.B.27. +0.C.26. +0.D.25. +0.E.24. +0.F.23. +0.G.20. +0.g.2d. +0.H.1/. +0.j.2c. +0.m.2a. +3.1l.1v. +3.1u.1m. +3.I.1+. +3.J.1Z. +3.n.29. +4.14.1Q. +4.1A.1i. +4.1E.1f. +4.1k.1w. +5.k.2b. +0.+.1W. +0.16.1P. +0.17.1O. +0.18.1N. +0.19.1M. +0.1a.1L. +0.1B.1i. +0.1b.1K. +0.1d.1J. +0.1E.1g. +0.1F.1f. +0.1G.1e. +0.1j.1A. +0.1k.1x. +0.1m.1v. +0.1o.1t. +0.1p.1s. 0.1q.1r. +0.C.27. +0.D.26. +0.E.25. +0.F.24. +0.G.21. +0.H.20. +0.h.2d. +0.K.1Z. +0.k.2c. +0.N.1Y. +0.o.29. +3.1l.1w. +3.1u.1n. +3.I.1/. +3.J.1+. +3.n.2a. +4./.1V. +4.12.1S. +4.15.1Q. +4.1C.1h. +c.28.A. +0.10.1V. +0.16.1Q. +0.17.1P. +0.18.1O. +0.19.1N. +0.1a.1M. +0.1b.1L. +0.1c.1K. +0.1F.1g. +0.1G.1f. +0.1H.1e. +0.1k.1y. +0.1n.1v. 0.1q.1s. -3.1t.1q. -2.1u.1q. -0.1v.1q. -0.1w.1q. -0.1x.1q. -0.1y.1q. -0.1q.1z. -0.1q.1A. -0.1B.1q. -0.1q.1C. -0.1q.1D. -3.1E.1q. -0.1q.1F. -0.1q.1G. -0.1q.1H. -0.1q.1I. -0.1q.1J. -0.1q.1K. -0.1q.1L. -0.1q.1M. -0.1q.1N. -0.1q.1O. -0.1q.1P. -3.1q.1Q. -0.1q.1R. -0.1q.1S. -0.1q.1T. -3.1q.1U. -0.1q.1V. -0.1q.1W. -0.1q.1X. -0.1q.1Y. -0.1q.1Z. -0.1q.1+. -0.1q.1/. -0.1q.20. -0.1q.21. -0.1q.22. -0.1q.23. -0.1q.24. -0.1q.25. -b.26.1q. -0.1q.27. -4.1q.28. -0.1q.2a. -0.1q.2b. -0.1q.2l. -6.2m.1q. -0.1q.2p. -2.2q.1q. -2.2r.1q. -7.2s.1q. -7.2t.1q. -2.2u.1q. -0.1q.2v. -0.1q.2w. -e.2x.1q. -9.2y.1q. -0.1q.2z. -3.1q.2A. -9.2B.1q. -0.1q.2C. -0.1q.2D. -3.1q.2E. -3.1q.2F. -0.1q.2G. -9.2H.1q. -0.1q.2I. -3.1q.2J. -0.1q.2K. -c.2M.1q. -9.2N.1q. -2.2O.1q. -9.2Q.1q. -7.2S.1q. -0.1q.2T. -1.2U.1q. -9.2V.1q. -1.2W.1q. -0.1q.2X. -b.2+.1q. -0.1q.30. -0.1q.35. -b.36.1q. -0.1q.37. -0.1q.38. -5.39.1q. -0.1q.3d. -3.1q.3h. -0.1q.3i. -0.1q.3m. -0.1q.3p. -a.3v.1q. -a.3y.1q. -2.3z.1q. -5.3A.1q. -2.3B.1q. -0.1q.3C. -a.3F.1q. -5.3G.1q. -0.1q.3M. -0.1q.3Q. -5.40.1q. -0.1q.47. -0.1q.4d. -3.1q.4O. -3.1q.4Q. -5.4T.1q. -3.1q.4V. -0.1q.51. -0.1q.52. -a.5a.1q. -2.5b.1q. -5.5d.1q. -0.1q.5B. -0.1q.5C. -0.1q.5F. -3.1q.5H. -0.1q.5I. -f.1q.5K. -7.5M.1q. -0.1q.5O. -3.1q.5R. -0.1q.5U. -0.1q.5W. -1.5Y.1q. -0.1q.5Z. -0.1q.5/. -0.1q.60. -2.62.1q. -2.66.1q. -a.6b.1q. -c.6e.1q. -0.1q.6F. -0.1q.6M. -0.1q.6+. -3.1q.71. -5.79.1q. -3.1q.7D. -0.1q.2p. -0.1q.2p. 0.1r.1r. +0.D.27. +0.E.26. +0.F.25. +0.G.22. +0.H.21. +0.i.2d. +0.K.1+. +0.L.1Z. +0.l.2c. +0.O.1Y. +0.p.29. +3.1l.1x. +3.1u.1o. +3.I.20. +3.J.1/. +4./.1W. +4.1B.1j. +4.1C.1i. +4.1D.1h. +4.1m.1w. +4.1t.1p. +5.o.2a. +c.28.B. +0.+.1X. +0.11.1V. +0.13.1R. +0.18.1P. +0.19.1O. +0.1b.1M. +0.1c.1L. +0.1G.1g. +0.1H.1f. +0.1I.1e. +0.1j.1C. +0.1k.1z. +0.1m.1x. +0.1o.1v. 0.1r.1s. -3.1t.1r. -2.1u.1r. -0.1v.1r. -0.1w.1r. -0.1x.1r. -0.1y.1r. -0.1r.1z. -0.1r.1A. -0.1B.1r. -0.1r.1C. -0.1r.1D. -3.1E.1r. -0.1r.1F. -0.1r.1G. -0.1r.1H. -0.1r.1I. -0.1r.1J. -0.1r.1K. -0.1r.1L. -0.1r.1M. -0.1r.1N. -0.1r.1O. -0.1r.1P. -3.1r.1Q. -0.1r.1R. -3.1r.1S. -0.1r.1T. -3.1r.1U. -0.1r.1V. -0.1r.1W. -0.1r.1X. -0.1r.1Y. -0.1r.1Z. -0.1r.1+. -0.1r.1/. -0.1r.20. -0.1r.21. -0.1r.22. -0.1r.23. -0.1r.24. -0.1r.25. -b.26.1r. -0.1r.27. -4.1r.28. -0.1r.2a. -0.1r.2b. -0.1r.2l. -6.2m.1r. -0.1r.2p. -2.2q.1r. -2.2r.1r. -7.2s.1r. -7.2t.1r. -2.2u.1r. -3.1r.2v. -0.1r.2w. -e.2x.1r. -9.2y.1r. -0.1r.2z. -3.1r.2A. -9.2B.1r. -0.1r.2C. -0.1r.2D. -3.1r.2E. -3.1r.2F. -0.1r.2G. -9.2H.1r. -0.1r.2I. -3.1r.2J. -0.1r.2K. -c.2M.1r. -9.2N.1r. -2.2O.1r. -9.2Q.1r. -7.2S.1r. -0.1r.2T. -1.2U.1r. -9.2V.1r. -1.2W.1r. -0.1r.2X. -b.2+.1r. -0.1r.30. -0.1r.35. -b.36.1r. -0.1r.37. -3.1r.38. -5.39.1r. -0.1r.3d. -3.1r.3h. -3.1r.3i. -0.1r.3m. -0.1r.3p. -a.3v.1r. -a.3y.1r. -2.3z.1r. -5.3A.1r. -2.3B.1r. -0.1r.3C. -a.3F.1r. -5.3G.1r. -0.1r.3M. -0.1r.3Q. -5.40.1r. -0.1r.47. -0.1r.4d. -3.1r.4O. -3.1r.4Q. -5.4T.1r. -3.1r.4V. -0.1r.51. -0.1r.52. -a.5a.1r. -2.5b.1r. -5.5d.1r. -0.1r.5B. -0.1r.5C. -0.1r.5F. -3.1r.5H. -0.1r.5I. -f.1r.5K. -7.5M.1r. -0.1r.5O. -3.1r.5R. -0.1r.5U. -0.1r.5W. -1.5Y.1r. -0.1r.5Z. -0.1r.5/. -0.1r.60. -2.62.1r. -2.66.1r. -a.6b.1r. -c.6e.1r. -0.1r.6F. -0.1r.6M. -0.1r.6+. -3.1r.71. -5.79.1r. -3.1r.7D. -0.1r.2p. -0.1r.2p. +0.E.27. +0.F.26. +0.G.23. +0.H.22. +0.j.2d. +0.K.1/. +0.L.1+. +0.m.2c. +0.P.1Y. +0.q.29. +3.1l.1y. +3.1u.1p. +3.I.21. +3.J.20. +3.M.1Z. +4.10.1W. +4.17.1Q. +4.1a.1N. +4.1D.1i. +4.1n.1w. +4.1t.1q. +5.p.2a. +c.28.C. +0./.1X. +0.11.1W. +0.12.1V. +0.13.1S. +0.14.1R. +0.19.1P. +0.1a.1O. +0.1b.1N. +0.1c.1M. +0.1d.1K. +0.1H.1g. +0.1j.1D. +0.1J.1e. +0.1k.1A. +0.1m.1y. +0.1n.1x. +0.1o.1w. 0.1s.1s. +0.1v.1p. +0.F.27. +0.G.24. +0.H.23. +0.K.20. +0.k.2d. +0.L.1/. +0.Q.1Y. +3.1l.1z. +3.1u.1q. +3.I.22. +3.J.21. +3.M.1+. +3.n.2c. +4.18.1Q. +4.1E.1h. +4.1I.1f. +4.1t.1r. +5.q.2a. +c.28.D. +0.10.1X. +0.15.1R. +0.19.1Q. +0.1a.1P. +0.1B.1k. +0.1b.1O. +0.1c.1N. +0.1d.1L. +0.1I.1g. +0.1J.1f. +0.1m.1z. +0.1n.1y. +0.1o.1x. 0.1t.1s. -2.1u.1s. +0.1v.1q. +0.1w.1p. +0.G.25. +0.H.24. +0.K.21. +0.L.20. +0.l.2d. +0.o.2c. +0.R.1Y. +3.1l.1A. +3.1u.1r. +3.I.23. +3.J.22. +3.M.1/. +4.12.1W. +4.14.1S. +4.1E.1i. +4.1F.1h. +5.k.2e. +c.28.E. +0.11.1X. +0.15.1S. +0.16.1R. +0.1b.1P. +0.1c.1O. +0.1d.1M. +0.1j.1E. +0.1J.1g. +0.1k.1C. +0.1m.1A. +0.1n.1z. +0.1o.1y. +0.1v.1r. +0.1w.1q. +0.1x.1p. +0.G.26. +0.H.25. +0.K.22. +0.L.21. +0.m.2d. +0.N.1Z. +0.p.2c. +0.r.29. +3.1l.1B. +3.1u.1s. +3.I.24. +3.J.23. +3.M.20. +3.S.1Y. +4.1a.1Q. +4.1F.1i. +4.1G.1h. +4.1t.1t. +c.28.F. +0.12.1X. +0.13.1V. +0.16.1S. +0.17.1R. +0.1B.1m. +0.1c.1P. +0.1d.1N. +0.1j.1F. +0.1k.1D. +0.1K.1e. +0.1n.1A. +0.1o.1z. 0.1v.1s. +0.1w.1r. +0.1x.1q. +0.1y.1p. +0.G.27. +0.H.26. +0.K.23. +0.L.22. +0.N.1+. +0.O.1Z. +0.q.2c. +0.r.2a. +0.s.29. +3.1l.1C. +3.1u.1t. +3.I.25. +3.J.24. +3.M.21. +3.n.2d. +4.1b.1Q. +4.1G.1i. +4.1H.1h. +c.T.1Y. +0.13.1W. +0.18.1R. +0.1B.1n. +0.1d.1O. +0.1j.1G. +0.1L.1e. +0.1m.1C. +0.1o.1A. +0.1p.1z. 0.1w.1s. +0.1x.1r. +0.1y.1q. +0.H.27. +0.K.24. +0.L.23. +0.N.1/. +0.O.1+. +0.o.2d. +0.P.1Z. +0.s.2a. +0.U.1Y. +3.1l.1D. +3.1u.1u. +3.I.26. +3.J.25. +3.M.22. +4.14.1V. +4.17.1S. +4.1c.1Q. +4.1H.1i. +4.1I.1h. +4.1K.1f. +4.1t.1v. +4.t.29. +5.k.2f. +0.19.1R. +0.1B.1o. +0.1d.1P. +0.1E.1k. +0.1j.1H. +0.1L.1f. +0.1m.1D. +0.1M.1e. +0.1n.1C. +0.1p.1A. +0.1q.1z. 0.1x.1s. -0.1y.1s. -0.1s.1z. -0.1s.1A. -3.1B.1s. -0.1s.1C. -0.1s.1D. -0.1E.1s. -0.1s.1F. -0.1s.1G. -0.1s.1H. -0.1s.1I. -0.1s.1J. -0.1s.1K. -0.1s.1L. -0.1s.1M. -0.1s.1N. -0.1s.1O. -0.1s.1P. -3.1s.1Q. -0.1s.1R. -0.1s.1S. -0.1s.1T. -3.1s.1U. -0.1s.1V. -0.1s.1W. -0.1s.1X. -0.1s.1Y. -0.1s.1Z. -0.1s.1+. -0.1s.1/. -0.1s.20. -0.1s.21. -0.1s.22. -0.1s.23. -0.1s.24. -0.1s.25. -b.26.1s. -0.1s.27. -4.1s.28. -0.1s.2a. -0.1s.2b. -0.1s.2l. -6.2m.1s. -0.1s.2p. -2.2q.1s. -2.2r.1s. -7.2s.1s. -7.2t.1s. -2.2u.1s. -3.1s.2v. -0.1s.2w. -e.2x.1s. -9.2y.1s. -0.1s.2z. -3.1s.2A. -9.2B.1s. -0.1s.2C. -0.1s.2D. -3.1s.2E. -3.1s.2F. -0.1s.2G. -9.2H.1s. -0.1s.2I. -3.1s.2J. -0.1s.2K. -c.2M.1s. -9.2N.1s. -2.2O.1s. -9.2Q.1s. -7.2S.1s. -0.1s.2T. -1.2U.1s. -9.2V.1s. -1.2W.1s. -0.1s.2X. -b.2+.1s. -0.1s.30. -0.1s.35. -b.36.1s. -0.1s.37. -0.1s.38. -5.39.1s. -0.1s.3d. -3.1s.3h. -0.1s.3i. -0.1s.3m. -0.1s.3p. -a.3v.1s. -a.3y.1s. -2.3z.1s. -5.3A.1s. -2.3B.1s. -0.1s.3C. -a.3F.1s. -5.3G.1s. -0.1s.3M. -0.1s.3Q. -5.40.1s. -0.1s.47. -0.1s.4d. -3.1s.4O. -3.1s.4Q. -5.4T.1s. -3.1s.4V. -0.1s.51. -0.1s.52. -a.5a.1s. -2.5b.1s. -5.5d.1s. -0.1s.5B. -0.1s.5C. -0.1s.5F. -3.1s.5H. -0.1s.5I. -f.1s.5K. -7.5M.1s. -0.1s.5O. -3.1s.5R. -0.1s.5U. -0.1s.5W. -1.5Y.1s. -0.1s.5Z. -0.1s.5/. -0.1s.60. -2.62.1s. -2.66.1s. -a.6b.1s. -c.6e.1s. -0.1s.6F. -0.1s.6M. -0.1s.6+. -3.1s.71. -5.79.1s. -3.1s.7D. -0.1s.2p. -0.1s.2p. -3.1t.1t. -2.1u.1t. -3.1t.1v. -3.1t.1w. -3.1t.1x. -3.1t.1y. -0.1t.1z. -0.1t.1A. -0.1B.1t. -3.1t.1C. -0.1t.1D. -0.1E.1t. -0.1t.1F. -3.1t.1G. -0.1t.1H. -0.1t.1I. -0.1t.1J. -0.1t.1K. -0.1t.1L. -0.1t.1M. -3.1t.1N. -0.1t.1O. -0.1t.1P. -3.1t.1Q. -0.1t.1R. -3.1t.1S. -3.1t.1T. -3.1t.1U. -0.1t.1V. -0.1t.1W. -0.1t.1X. -0.1t.1Y. -0.1t.1Z. -0.1t.1+. -0.1t.1/. -0.1t.20. -0.1t.21. -0.1t.22. -0.1t.23. -0.1t.24. -0.1t.25. -b.26.1t. -3.1t.27. -0.1t.28. -4.1t.29. -0.1t.2a. -3.1t.2b. -4.1t.2c. -4.1t.2d. -4.1t.2f. -4.1t.2g. -4.1t.2h. -4.1t.2i. -4.1t.2j. -0.1t.2l. -6.2m.1t. -1.1t.2o. -3.1t.2p. -2.2q.1t. -2.2r.1t. -7.2s.1t. -7.2t.1t. -2.2u.1t. -3.1t.2v. -3.1t.2w. -e.2x.1t. -9.2y.1t. -0.1t.2z. -3.1t.2A. -9.2B.1t. -0.1t.2C. -3.1t.2D. -3.1t.2E. -3.1t.2F. -0.1t.2G. -9.2H.1t. -3.1t.2I. -3.1t.2J. -0.1t.2K. -4.1t.2L. -c.2M.1t. -9.2N.1t. -2.2O.1t. -4.1t.2P. -9.2Q.1t. -8.1t.2R. -7.2S.1t. -0.1t.2T. -1.2U.1t. -9.2V.1t. -1.2W.1t. -0.1t.2X. -8.1t.2Y. -3.1t.2Z. -b.2+.1t. -8.1t.2/. -0.1t.30. -3.1t.31. -3.1t.32. -3.1t.33. -3.1t.34. -0.1t.35. -b.36.1t. -0.1t.37. -3.1t.38. -5.39.1t. -4.1t.3a. -4.1t.3c. -3.1t.3d. -1.1t.3e. -4.1t.3f. -3.1t.3g. -3.1t.3h. -3.1t.3i. -1.1t.3j. -3.1t.3k. -4.1t.3l. -0.1t.3m. -3.1t.3n. -3.1t.3o. -0.1t.3p. -3.1t.3q. -4.1t.3r. -4.1t.3s. -8.1t.3t. -8.1t.3u. -a.3v.1t. -4.1t.3w. -4.1t.3x. -a.3y.1t. -2.3z.1t. -5.3A.1t. -2.3B.1t. -3.1t.3C. -4.1t.3D. -4.1t.3E. -a.3F.1t. -5.3G.1t. -4.1t.3H. -4.1t.3I. -4.1t.3J. -4.1t.3K. -4.1t.3L. -0.1t.3M. -4.1t.3N. -4.1t.3O. -4.1t.3P. -0.1t.3Q. -4.1t.3R. -4.1t.3S. -1.1t.3T. -4.1t.3U. -4.1t.3V. -4.1t.3W. -4.1t.3X. -4.1t.3Y. -4.1t.3Z. -1.1t.3+. -1.1t.3/. -5.40.1t. -4.1t.41. -4.1t.42. -4.1t.43. -4.1t.44. -4.1t.45. -0.1t.47. -4.1t.48. -4.1t.49. -1.1t.4a. -4.1t.4b. -4.1t.4c. -0.1t.4d. -4.1t.4e. -4.1t.4f. -4.1t.4g. -4.1t.4h. -4.1t.4i. -4.1t.4j. -3.1t.4k. -4.1t.4l. -4.1t.4m. -4.1t.4n. -4.1t.4o. -4.1t.4p. -4.1t.4q. -4.1t.4r. -4.1t.4s. -4.1t.4t. -4.1t.4u. -4.1t.4v. -4.1t.4w. -4.1t.4x. -4.1t.4y. -4.1t.4z. -4.1t.4A. -4.1t.4B. -4.1t.4C. -4.1t.4D. -4.1t.4E. -1.1t.4F. -8.1t.4G. -3.1t.4H. -1.1t.4I. -8.1t.4J. -8.1t.4K. -3.1t.4L. -4.1t.4M. -4.1t.4N. -3.1t.4O. -4.1t.4P. -3.1t.4Q. -1.1t.4R. -4.1t.4S. -4.1t.4T. -8.1t.4U. -3.1t.4V. -4.1t.4W. -3.1t.4Y. -1.1t.4Z. -4.1t.4+. -4.1t.4/. -1.1t.50. -0.1t.51. -0.1t.52. -8.1t.53. -3.1t.54. -8.1t.55. -8.1t.56. -1.1t.57. -3.1t.58. -8.1t.59. -a.5a.1t. -2.5b.1t. -4.1t.5c. -5.5d.1t. -8.1t.5e. -8.1t.5f. -4.1t.5g. -8.1t.5h. -4.1t.5i. -4.1t.5j. -4.1t.5k. -4.1t.5l. -4.1t.5m. -4.1t.5n. -4.1t.5o. -4.1t.5p. -4.1t.5q. -4.1t.5r. -1.1t.5s. -4.1t.5t. -4.1t.5u. -4.1t.5v. -1.1t.5w. -1.1t.5y. -1.1t.5z. -3.1t.5A. -0.1t.5B. -0.1t.5C. -1.1t.5D. -3.1t.5E. -0.1t.5F. -8.1t.5G. -3.1t.5H. -0.1t.5I. -f.1t.5K. -8.1t.5L. -8.1t.5M. -3.1t.5N. -0.1t.5O. -8.1t.5P. -4.1t.5Q. -3.1t.5R. -4.1t.5S. -4.1t.5T. -0.1t.5U. -8.1t.5V. -3.1t.5W. -8.1t.5X. -8.1t.5Y. -0.1t.5Z. -0.1t.5/. -0.1t.60. -4.1t.61. -2.62.1t. -4.1t.64. -4.1t.65. -5.66.1t. -4.1t.67. -4.1t.68. -4.1t.69. -4.1t.6a. -a.6b.1t. -4.1t.6c. -4.1t.6d. -4.1t.6e. -4.1t.6f. -4.1t.6g. -4.1t.6h. -4.1t.6i. -4.1t.6j. -4.1t.6k. -4.1t.6l. -4.1t.6m. -4.1t.6n. -4.1t.6o. -4.1t.6p. -4.1t.6q. -4.1t.6r. -4.1t.6s. -4.1t.6t. -4.1t.6u. -4.1t.6v. -4.1t.6w. -4.1t.6x. -4.1t.6y. -4.1t.6z. -4.1t.6A. -4.1t.6B. -4.1t.6C. -4.1t.6D. -4.1t.6E. -3.1t.6F. -4.1t.6G. -4.1t.6H. -4.1t.6I. -4.1t.6J. -4.1t.6K. -3.1t.6L. -0.1t.6M. -4.1t.6N. -4.1t.6O. -4.1t.6P. -4.1t.6Q. -4.1t.6R. -4.1t.6S. -4.1t.6T. -4.1t.6U. -4.1t.6V. -4.1t.6W. -1.1t.6X. -4.1t.6Y. -1.1t.6Z. -0.1t.6+. -1.1t.6/. -1.1t.70. -3.1t.71. -4.1t.72. -4.1t.73. -1.1t.74. -4.1t.75. -1.1t.76. -1.1t.77. -4.1t.78. -5.79.1t. -1.1t.7a. -4.1t.7b. -4.1t.7c. -4.1t.7d. -4.1t.7e. -4.1t.7f. -4.1t.7g. -4.1t.7h. -4.1t.7i. -4.1t.7j. -4.1t.7k. -4.1t.7l. -1.1t.7m. -4.1t.7o. -4.1t.7p. -1.1t.7r. -1.1t.7s. -3.1t.7t. -4.1t.7u. -4.1t.7v. -4.1t.7w. -4.1t.7x. -4.1t.7y. -1.1t.7z. -4.1t.7A. -8.1t.7B. -4.1t.7C. -3.1t.7D. -4.1t.7E. -4.1t.7F. -4.1t.7G. -4.1t.7H. -4.1t.7I. -4.1t.7J. -1.1t.7K. -1.1t.7L. -1.1t.7M. -1.1t.7N. -1.1t.7O. -1.1t.7P. -4.1t.7Q. -3.1t.7R. -4.1t.7S. -4.1t.7T. -1.1t.7U. -8.1t.7V. -4.1t.7W. -4.1t.7X. -4.1t.7Y. -4.1t.7Z. -1.1t.7+. -1.1t.7/. -1.1t.80. -1.1t.81. -1.1t.82. -4.1t.83. -1.1t.84. -1.1t.85. -1.1t.87. -1.1t.88. -3.1t.89. -4.1t.8a. -3.1t.8b. -4.1t.8c. -8.1t.8d. -4.1t.8e. -4.1t.8f. -4.1t.8g. -4.1t.8h. -4.1t.8i. -4.1t.8j. -4.1t.8k. -4.1t.8l. -4.1t.8m. -4.1t.8n. -4.1t.8o. -4.1t.8p. -4.1t.8q. -4.1t.8r. -4.1t.8s. -4.1t.8t. -4.1t.8u. -4.1t.8v. -4.1t.8w. -4.1t.8x. -4.1t.8y. -4.1t.8z. -4.1t.8A. -4.1t.8B. -4.1t.8C. -4.1t.8D. -4.1t.8E. -4.1t.8F. -4.1t.8G. -4.1t.8H. -4.1t.8I. -4.1t.8J. -4.1t.8K. -4.1t.8L. -4.1t.8M. -4.1t.8N. -4.1t.8O. -4.1t.8P. -3.1t.2p. -3.1t.2p. -8.1t.3b. -1.1t.46. -1.1t.4X. -1.1t.5x. -1.1t.5J. -1.1t.7n. -1.1t.7q. -2.1u.1u. -2.1u.1v. -2.1u.1w. -2.1u.1x. -2.1u.1y. -2.1u.1z. -2.1u.1A. -2.1u.1B. -2.1u.1C. -2.1u.1D. -2.1u.1E. -2.1u.1F. -2.1u.1G. -2.1u.1H. -2.1u.1I. -2.1u.1J. -2.1u.1K. -2.1u.1L. -2.1u.1M. -2.1u.1N. -2.1u.1O. -2.1u.1P. -2.1u.1Q. -2.1u.1R. -2.1u.1S. -2.1u.1T. -2.1u.1U. -2.1u.1V. -2.1u.1W. -2.1u.1X. -2.1u.1Y. -2.1u.1Z. -2.1u.1+. -2.1u.1/. -2.1u.20. -2.1u.21. -2.1u.22. -2.1u.23. -2.1u.24. -2.1u.25. -2.1u.26. -2.1u.27. -2.1u.28. -2.1u.2a. -2.1u.2b. -2.1u.2l. -6.2m.1u. -2.1u.2p. -2.1u.2q. -2.1u.2r. -7.2s.1u. -7.2t.1u. -2.2u.1u. -2.1u.2v. -2.1u.2w. -e.2x.1u. -2.1u.2y. -2.1u.2z. -2.1u.2A. -2.1u.2B. -2.1u.2C. -2.1u.2D. -2.1u.2E. -2.1u.2F. -2.1u.2G. -2.1u.2H. -2.1u.2I. -2.1u.2J. -2.1u.2K. -c.2M.1u. -2.1u.2N. -2.1u.2O. -2.1u.2Q. -7.2S.1u. -2.1u.2T. -1.2U.1u. -2.1u.2V. -1.2W.1u. -2.1u.2X. -2.1u.2+. -2.1u.30. -2.1u.35. -2.1u.36. -2.1u.37. -2.1u.38. -2.1u.39. -2.1u.3d. -2.1u.3h. -2.1u.3i. -2.1u.3m. -2.1u.3p. -a.3v.1u. -a.3y.1u. -2.3z.1u. -2.1u.3A. -2.3B.1u. -2.1u.3C. -a.3F.1u. -2.1u.3G. -2.1u.3M. -2.1u.3Q. -2.1u.40. -2.1u.47. -2.1u.4d. -2.1u.4O. -2.1u.4Q. -5.4T.1u. -2.1u.4V. -2.1u.51. -2.1u.52. -a.5a.1u. -2.5b.1u. -2.1u.5d. -2.1u.5B. -2.1u.5C. -2.1u.5F. -2.1u.5H. -2.1u.5I. -f.1u.5K. -7.5M.1u. -2.1u.5O. -2.1u.5R. -2.1u.5U. -2.1u.5W. -1.5Y.1u. -2.1u.5Z. -2.1u.5/. -2.1u.60. -2.1u.62. -2.1u.66. -a.6b.1u. -c.6e.1u. -2.1u.6F. -2.1u.6M. -2.1u.6+. -2.1u.71. -2.1u.79. -2.1u.7D. -2.1u.2p. -2.1u.2p. +0.1y.1r. +0.K.25. +0.L.24. +0.N.20. +0.O.1/. +0.P.1+. +0.p.2d. +0.Q.1Z. +0.t.2a. +0.V.1Y. +3.1u.1v. +3.I.27. +3.J.26. +3.M.23. +4.14.1W. +4.15.1V. +4.18.1S. +4.1I.1i. +4.1J.1h. +4.1K.1g. +4.1t.1w. +4.u.29. +7.r.2b. +c.28.G. +0.13.1X. +0.16.1V. +0.19.1S. +0.1a.1R. +0.1B.1p. +0.1j.1I. +0.1k.1F. +0.1L.1g. +0.1M.1f. +0.1n.1D. +0.1o.1C. +0.1q.1A. +0.1r.1z. 0.1v.1v. +0.1y.1s. +0.K.26. +0.L.25. +0.N.21. +0.O.20. +0.P.1/. +0.Q.1+. +0.q.2d. +0.R.1Z. +0.r.2c. +0.u.2a. +0.v.29. +0.W.1Y. +3.1l.1E. +3.1u.1w. +3.J.27. +3.M.24. +4.15.1W. +4.1d.1Q. +4.1J.1i. +4.1N.1e. +4.1t.1x. +7.s.2b. +c.28.H. +0.14.1X. +0.1a.1S. +0.1B.1q. +0.1b.1R. +0.1E.1m. +0.1j.1J. +0.1k.1G. +0.1M.1g. +0.1o.1D. +0.1O.1e. +0.1p.1C. +0.1r.1A. +0.1s.1z. 0.1w.1v. -0.1x.1v. -0.1y.1v. -0.1v.1z. -0.1v.1A. -0.1B.1v. -0.1v.1C. -0.1v.1D. -0.1E.1v. -0.1v.1F. -0.1v.1G. -0.1v.1H. -0.1v.1I. -0.1v.1J. -0.1v.1K. -0.1v.1L. -0.1v.1M. -0.1v.1N. -0.1v.1O. -0.1v.1P. -3.1v.1Q. -0.1v.1R. -0.1v.1S. -0.1v.1T. -0.1v.1U. -0.1v.1V. -0.1v.1W. -0.1v.1X. -0.1v.1Y. -0.1v.1Z. -0.1v.1+. -0.1v.1/. -0.1v.20. -0.1v.21. -0.1v.22. -0.1v.23. -0.1v.24. -0.1v.25. -b.26.1v. -0.1v.27. -4.1v.28. -0.1v.2a. -0.1v.2b. -0.1v.2l. -6.2m.1v. -0.1v.2p. -2.2q.1v. -2.2r.1v. -7.2s.1v. -7.2t.1v. -2.2u.1v. -0.1v.2v. -0.1v.2w. -e.2x.1v. -9.2y.1v. -0.1v.2z. -3.1v.2A. -9.2B.1v. -0.1v.2C. -0.1v.2D. -3.1v.2E. -0.1v.2F. -0.1v.2G. -9.2H.1v. -0.1v.2I. -3.1v.2J. -0.1v.2K. -c.2M.1v. -9.2N.1v. -2.2O.1v. -9.2Q.1v. -7.2S.1v. -0.1v.2T. -1.2U.1v. -9.2V.1v. -1.2W.1v. -0.1v.2X. -b.2+.1v. -0.1v.30. -0.1v.35. -b.36.1v. -0.1v.37. -0.1v.38. -5.39.1v. -0.1v.3d. -3.1v.3h. -0.1v.3i. -0.1v.3m. -0.1v.3p. -a.3v.1v. -a.3y.1v. -2.3z.1v. -5.3A.1v. -2.3B.1v. -0.1v.3C. -a.3F.1v. -5.3G.1v. -0.1v.3M. -0.1v.3Q. -5.40.1v. -0.1v.47. -0.1v.4d. -0.1v.4O. -3.1v.4Q. -5.4T.1v. -3.1v.4V. -0.1v.51. -0.1v.52. -a.5a.1v. -2.5b.1v. -5.5d.1v. -0.1v.5B. -0.1v.5C. -0.1v.5F. -3.1v.5H. -0.1v.5I. -f.1v.5K. -7.5M.1v. -0.1v.5O. -0.1v.5R. -0.1v.5U. -0.1v.5W. -1.5Y.1v. -0.1v.5Z. -0.1v.5/. -0.1v.60. -2.62.1v. -2.66.1v. -a.6b.1v. -c.6e.1v. -0.1v.6F. -0.1v.6M. -0.1v.6+. -3.1v.71. -5.79.1v. -3.1v.7D. -0.1v.2p. -0.1v.2p. +0.K.27. +0.L.26. +0.N.22. +0.O.21. +0.P.20. +0.Q.1/. +0.R.1+. +0.s.2c. +0.X.1Y. +3.1l.1F. +3.1u.1x. +3.I.28. +3.M.25. +3.S.1Z. +4.16.1W. +4.17.1V. +4.1N.1f. +4.1t.1y. +4.w.29. +5.v.2a. +0.15.1X. +0.18.1V. +0.1B.1r. +0.1c.1R. +0.1E.1n. +0.1k.1H. +0.1m.1F. +0.1O.1f. +0.1p.1D. +0.1P.1e. +0.1q.1C. +0.1s.1A. +0.1t.1z. 0.1w.1w. -3.1x.1w. -3.1y.1w. -0.1w.1z. -0.1w.1A. -0.1B.1w. -0.1w.1C. -0.1w.1D. -3.1E.1w. -0.1w.1F. -0.1w.1G. -0.1w.1H. -0.1w.1I. -0.1w.1J. -0.1w.1K. -0.1w.1L. -0.1w.1M. -0.1w.1N. -0.1w.1O. -0.1w.1P. -3.1w.1Q. -0.1w.1R. -0.1w.1S. -0.1w.1T. -3.1w.1U. -0.1w.1V. -0.1w.1W. -0.1w.1X. -0.1w.1Y. -0.1w.1Z. -0.1w.1+. -0.1w.1/. -0.1w.20. -0.1w.21. -0.1w.22. -0.1w.23. -0.1w.24. -0.1w.25. -b.26.1w. -0.1w.27. -4.1w.28. -0.1w.2a. -0.1w.2b. -0.1w.2l. -6.2m.1w. -0.1w.2p. -2.2q.1w. -2.2r.1w. -7.2s.1w. -7.2t.1w. -2.2u.1w. -3.1w.2v. -0.1w.2w. -e.2x.1w. -9.2y.1w. -0.1w.2z. -3.1w.2A. -9.2B.1w. -0.1w.2C. -0.1w.2D. -3.1w.2E. -3.1w.2F. -0.1w.2G. -9.2H.1w. -0.1w.2I. -3.1w.2J. -0.1w.2K. -c.2M.1w. -9.2N.1w. -2.2O.1w. -9.2Q.1w. -7.2S.1w. -0.1w.2T. -1.2U.1w. -9.2V.1w. -1.2W.1w. -0.1w.2X. -b.2+.1w. -0.1w.30. -0.1w.35. -b.36.1w. -0.1w.37. -3.1w.38. -5.39.1w. -0.1w.3d. -3.1w.3h. -0.1w.3i. -0.1w.3m. -0.1w.3p. -a.3v.1w. -a.3y.1w. -2.3z.1w. -5.3A.1w. -2.3B.1w. -0.1w.3C. -a.3F.1w. -5.3G.1w. -0.1w.3M. -0.1w.3Q. -5.40.1w. -0.1w.47. -0.1w.4d. -3.1w.4O. -3.1w.4Q. -5.4T.1w. -3.1w.4V. -0.1w.51. -0.1w.52. -a.5a.1w. -2.5b.1w. -5.5d.1w. -0.1w.5B. -0.1w.5C. -0.1w.5F. -3.1w.5H. -0.1w.5I. -f.1w.5K. -7.5M.1w. -0.1w.5O. -3.1w.5R. -0.1w.5U. -0.1w.5W. -1.5Y.1w. -0.1w.5Z. -0.1w.5/. -0.1w.60. -2.62.1w. -2.66.1w. -a.6b.1w. -c.6e.1w. -0.1w.6F. -0.1w.6M. -0.1w.6+. -3.1w.71. -5.79.1w. -3.1w.7D. -0.1w.2p. -0.1w.2p. +0.1x.1v. +0.L.27. +0.N.23. +0.O.22. +0.P.21. +0.Q.20. +0.R.1/. +0.t.2c. +3.1l.1G. +3.1u.1y. +3.J.28. +3.M.26. +4.17.1W. +4.1b.1S. +4.1K.1h. +4.1N.1g. +4.S.1+. +4.u.2b. +4.x.29. +5.w.2a. +c.T.1Z. +c.Y.1Y. +0.16.1X. +0.19.1V. +0.1E.1o. +0.1e.1Q. +0.1k.1I. +0.1L.1h. +0.1m.1G. +0.1n.1F. +0.1O.1g. +0.1P.1f. +0.1q.1D. +0.1r.1C. +0.1t.1A. +0.1y.1v. +0.N.24. +0.O.23. +0.P.22. +0.Q.21. +0.R.20. +0.r.2d. +0.U.1Z. +0.u.2c. +0.y.29. +0.Z.1Y. +3.1l.1H. +3.1u.1z. +3.M.27. +3.S.1/. +4.18.1W. +4.1B.1s. +4.1c.1S. +4.1K.1i. +4.1x.1w. +5.x.2a. +c.28.K. +c.T.1+. +0.17.1X. +0.19.1W. +0.1a.1V. +0.1B.1t. +0.1d.1R. +0.1j.1K. +0.1k.1J. +0.1L.1i. +0.1m.1H. +0.1n.1G. +0.1o.1F. +0.1P.1g. +0.1r.1D. +0.1s.1C. +0.1v.1z. 0.1x.1x. +0.29.z. +0.N.25. +0.O.24. +0.P.23. +0.Q.22. +0.R.21. +0.s.2d. +0.U.1+. +0.V.1Z. +0.v.2c. +3.1l.1I. +3.1u.1A. +3.S.20. +4.1E.1p. +4.1f.1Q. +4.1M.1h. +4.1y.1w. +4.r.2e. +5.y.2a. +c.28.L. +c.T.1/. +0.18.1X. +0.1d.1S. +0.1j.1L. +0.1m.1I. +0.1n.1H. +0.1o.1G. +0.1p.1F. +0.1s.1D. +0.1v.1A. +0.1w.1z. 0.1y.1x. +0.N.26. +0.O.25. +0.P.24. +0.Q.23. +0.R.22. +0.t.2d. +0.U.1/. +0.V.1+. +0.W.1Z. +0.w.2c. +3.1l.1J. +3.1u.1B. +3.M.28. +3.S.21. +4.1a.1W. +4.1b.1V. +4.1E.1q. +4.1M.1i. +4.1N.1h. +4.1t.1C. +4.s.2e. +5.2a.z. +c.T.20. +f.1Q.1g. +0.19.1X. +0.1B.1v. +0.1j.1M. +0.1m.1J. +0.1n.1I. +0.1O.1h. +0.1o.1H. +0.1p.1G. +0.1q.1F. +0.1t.1D. +0.1w.1A. 0.1x.1z. -0.1x.1A. -0.1B.1x. -0.1x.1C. -0.1x.1D. -0.1E.1x. -0.1x.1F. -0.1x.1G. -0.1x.1H. -0.1x.1I. -0.1x.1J. -0.1x.1K. -0.1x.1L. -0.1x.1M. -0.1x.1N. -0.1x.1O. -0.1x.1P. -0.1x.1Q. -0.1x.1R. -0.1x.1S. -0.1x.1T. -0.1x.1U. -0.1x.1V. -0.1x.1W. -0.1x.1X. -0.1x.1Y. -0.1x.1Z. -0.1x.1+. -0.1x.1/. -0.1x.20. -0.1x.21. -0.1x.22. -0.1x.23. -0.1x.24. -0.1x.25. -b.26.1x. -0.1x.27. -4.1x.28. -0.1x.2a. -0.1x.2b. -0.1x.2l. -6.2m.1x. -0.1x.2p. -2.2q.1x. -2.2r.1x. -7.2s.1x. -7.2t.1x. -2.2u.1x. -0.1x.2v. -0.1x.2w. -e.2x.1x. -9.2y.1x. -0.1x.2z. -3.1x.2A. -9.2B.1x. -0.1x.2C. -0.1x.2D. -3.1x.2E. -0.1x.2F. -0.1x.2G. -9.2H.1x. -0.1x.2I. -3.1x.2J. -0.1x.2K. -c.2M.1x. -9.2N.1x. -2.2O.1x. -9.2Q.1x. -7.2S.1x. -0.1x.2T. -1.2U.1x. -9.2V.1x. -1.2W.1x. -0.1x.2X. -b.2+.1x. -0.1x.30. -0.1x.35. -b.36.1x. -0.1x.37. -0.1x.38. -5.39.1x. -0.1x.3d. -3.1x.3h. -0.1x.3i. -0.1x.3m. -0.1x.3p. -a.3v.1x. -a.3y.1x. -2.3z.1x. -5.3A.1x. -2.3B.1x. -0.1x.3C. -a.3F.1x. -5.3G.1x. -0.1x.3M. -0.1x.3Q. -5.40.1x. -0.1x.47. -0.1x.4d. -0.1x.4O. -3.1x.4Q. -5.4T.1x. -3.1x.4V. -0.1x.51. -0.1x.52. -a.5a.1x. -2.5b.1x. -5.5d.1x. -0.1x.5B. -0.1x.5C. -0.1x.5F. -3.1x.5H. -0.1x.5I. -f.1x.5K. -7.5M.1x. -0.1x.5O. -3.1x.5R. -0.1x.5U. -0.1x.5W. -1.5Y.1x. -0.1x.5Z. -0.1x.5/. -0.1x.60. -2.62.1x. -2.66.1x. -a.6b.1x. -c.6e.1x. -0.1x.6F. -0.1x.6M. -0.1x.6+. -0.1x.71. -5.79.1x. -0.1x.7D. -0.1x.2p. -0.1x.2p. 0.1y.1y. +0.N.27. +0.O.26. +0.P.25. +0.Q.24. +0.R.23. +0.U.20. +0.u.2d. +0.V.1/. +0.W.1+. +0.X.1Z. +0.x.2c. +3.1u.1C. +4.1b.1W. +4.1c.1V. +4.1E.1r. +4.1N.1i. +4.S.22. +c.T.21. +0.+.1Y. +0.1a.1X. +0.1B.1w. +0.1E.1s. +0.1j.1N. +0.1k.1K. +0.1n.1J. +0.1O.1i. +0.1o.1I. +0.1p.1H. +0.1P.1h. +0.1q.1G. +0.1r.1F. +0.1v.1C. +0.1x.1A. 0.1y.1z. +0.A.29. +0.O.27. +0.P.26. +0.Q.25. +0.R.24. +0.U.21. +0.V.20. +0.v.2d. +0.W.1/. +0.X.1+. +0.y.2c. +3.1u.1D. +4.1c.1W. +4.r.2f. +4.S.23. +4.u.2e. +c.T.22. +c.Y.1Z. +f.1R.1e. +0./.1Y. +0.1b.1X. +0.1B.1x. +0.1d.1V. +0.1e.1S. +0.1E.1t. +0.1j.1O. +0.1k.1L. +0.1o.1J. +0.1p.1I. +0.1P.1i. +0.1q.1H. +0.1r.1G. +0.1s.1F. +0.1v.1D. +0.1w.1C. 0.1y.1A. -0.1B.1y. -0.1y.1C. -0.1y.1D. -0.1E.1y. -0.1y.1F. -0.1y.1G. -0.1y.1H. -0.1y.1I. -0.1y.1J. -0.1y.1K. -0.1y.1L. -0.1y.1M. -0.1y.1N. -0.1y.1O. -0.1y.1P. -0.1y.1Q. -0.1y.1R. -0.1y.1S. -0.1y.1T. -0.1y.1U. -0.1y.1V. -0.1y.1W. -0.1y.1X. -0.1y.1Y. -0.1y.1Z. -0.1y.1+. -0.1y.1/. -0.1y.20. -0.1y.21. -0.1y.22. -0.1y.23. -0.1y.24. -0.1y.25. -b.26.1y. -0.1y.27. -4.1y.28. -0.1y.2a. -0.1y.2b. -0.1y.2l. -6.2m.1y. -0.1y.2p. -2.2q.1y. -2.2r.1y. -7.2s.1y. -7.2t.1y. -2.2u.1y. -0.1y.2v. -0.1y.2w. -e.2x.1y. -9.2y.1y. -0.1y.2z. -3.1y.2A. -9.2B.1y. -0.1y.2C. -0.1y.2D. -3.1y.2E. -0.1y.2F. -0.1y.2G. -9.2H.1y. -0.1y.2I. -3.1y.2J. -0.1y.2K. -c.2M.1y. -9.2N.1y. -2.2O.1y. -9.2Q.1y. -7.2S.1y. -0.1y.2T. -1.2U.1y. -9.2V.1y. -1.2W.1y. -0.1y.2X. -b.2+.1y. -0.1y.30. -0.1y.35. -b.36.1y. -0.1y.37. -0.1y.38. -5.39.1y. -0.1y.3d. -3.1y.3h. -0.1y.3i. -0.1y.3m. -0.1y.3p. -a.3v.1y. -a.3y.1y. -2.3z.1y. -5.3A.1y. -2.3B.1y. -0.1y.3C. -a.3F.1y. -5.3G.1y. -0.1y.3M. -0.1y.3Q. -5.40.1y. -0.1y.47. -0.1y.4d. -0.1y.4O. -3.1y.4Q. -5.4T.1y. -3.1y.4V. -0.1y.51. -0.1y.52. -a.5a.1y. -2.5b.1y. -5.5d.1y. -0.1y.5B. -0.1y.5C. -0.1y.5F. -3.1y.5H. -0.1y.5I. -f.1y.5K. -7.5M.1y. -0.1y.5O. -3.1y.5R. -0.1y.5U. -0.1y.5W. -1.5Y.1y. -0.1y.5Z. -0.1y.5/. -0.1y.60. -2.62.1y. -2.66.1y. -a.6b.1y. -c.6e.1y. -0.1y.6F. -0.1y.6M. -0.1y.6+. -0.1y.71. -5.79.1y. -0.1y.7D. -0.1y.2p. -0.1y.2p. 0.1z.1z. +0.2c.z. +0.B.29. +0.P.27. +0.Q.26. +0.R.25. +0.U.22. +0.V.21. +0.W.20. +0.w.2d. +0.X.1/. +0.Z.1Z. +3.1l.1K. +4.1h.1Q. +4.S.24. +4.s.2f. +5.A.2a. +c.28.N. +c.T.23. +c.Y.1+. +f.1R.1f. +0.10.1Y. 0.1A.1z. -0.1B.1z. -0.1z.1C. -0.1z.1D. -0.1E.1z. -0.1z.1F. -0.1z.1G. -0.1z.1H. -0.1z.1I. -0.1z.1J. -0.1z.1K. -0.1z.1L. -0.1z.1M. -0.1z.1N. -0.1z.1O. -0.1z.1P. -3.1z.1Q. -0.1z.1R. -3.1z.1S. -0.1z.1T. -3.1z.1U. -0.1z.1V. -0.1z.1W. -0.1z.1X. -0.1z.1Y. -0.1z.1Z. -0.1z.1+. -0.1z.1/. -0.1z.20. -0.1z.21. -0.1z.22. -0.1z.23. -0.1z.24. -0.1z.25. -b.26.1z. -0.1z.27. -4.1z.28. -0.1z.2a. -0.1z.2b. -0.1z.2l. -6.2m.1z. -0.1z.2p. -2.2q.1z. -2.2r.1z. -7.2s.1z. -7.2t.1z. -2.2u.1z. -3.1z.2v. -0.1z.2w. -e.2x.1z. -9.2y.1z. -0.1z.2z. -3.1z.2A. -9.2B.1z. -0.1z.2C. -0.1z.2D. -3.1z.2E. -3.1z.2F. -0.1z.2G. -9.2H.1z. -0.1z.2I. -3.1z.2J. -0.1z.2K. -c.2M.1z. -9.2N.1z. -2.2O.1z. -9.2Q.1z. -7.2S.1z. -0.1z.2T. -1.2U.1z. -9.2V.1z. -1.2W.1z. -0.1z.2X. -b.2+.1z. -0.1z.30. -0.1z.35. -b.36.1z. -0.1z.37. -3.1z.38. -5.39.1z. -0.1z.3d. -3.1z.3h. -3.1z.3i. -0.1z.3m. -0.1z.3p. -a.3v.1z. -a.3y.1z. -2.3z.1z. -5.3A.1z. -2.3B.1z. -0.1z.3C. -a.3F.1z. -5.3G.1z. -0.1z.3M. -0.1z.3Q. -5.40.1z. -0.1z.47. -0.1z.4d. -3.1z.4O. -3.1z.4Q. -5.4T.1z. -3.1z.4V. -0.1z.51. -0.1z.52. -a.5a.1z. -2.5b.1z. -5.5d.1z. -0.1z.5B. -0.1z.5C. -0.1z.5F. -3.1z.5H. -0.1z.5I. -f.1z.5K. -7.5M.1z. -0.1z.5O. -3.1z.5R. -0.1z.5U. -0.1z.5W. -1.5Y.1z. -0.1z.5Z. -0.1z.5/. -0.1z.60. -2.62.1z. -2.66.1z. -a.6b.1z. -c.6e.1z. -0.1z.6F. -0.1z.6M. -0.1z.6+. -3.1z.71. -5.79.1z. -3.1z.7D. -0.1z.2p. -0.1z.2p. +0.1B.1y. +0.1c.1X. +0.1d.1W. +0.1j.1P. +0.1k.1M. +0.1m.1K. +0.1p.1J. +0.1q.1I. +0.1R.1g. +0.1r.1H. +0.1s.1G. +0.1t.1F. +0.1w.1D. +0.1x.1C. +0.C.29. +0.Q.27. +0.R.26. +0.U.23. +0.V.22. +0.W.21. +0.X.20. +0.x.2d. +0.Z.1+. +3.1l.1L. +3.1u.1E. +4.1i.1Q. +4.1S.1f. +5.B.2a. +c.28.O. +c.T.24. +c.Y.1/. +e.S.25. +0.11.1Y. 0.1A.1A. +0.1B.1z. +0.1E.1v. +0.1j.1Q. +0.1k.1N. +0.1m.1L. +0.1n.1K. +0.1q.1J. +0.1r.1I. +0.1S.1g. +0.1s.1H. +0.1x.1D. +0.1y.1C. +0.D.29. +0.R.27. +0.U.24. +0.V.23. +0.W.22. +0.X.21. +0.y.2d. +0.Z.1/. +3.1l.1M. +3.1u.1F. +3.S.26. +4.1t.1G. +4.u.2f. +5.C.2a. +c.28.P. +c.T.25. +c.Y.20. 0.1B.1A. +0.1d.1X. +0.1e.1V. +0.1k.1O. +0.1m.1M. +0.1n.1L. +0.1o.1K. +0.1r.1J. +0.1s.1I. +0.1t.1H. +0.1v.1F. +0.1y.1D. +0.1z.1C. +0.A.2c. +0.E.29. +0.U.25. +0.V.24. +0.W.23. +0.X.22. +0.Z.20. +3.1l.1N. +3.1u.1G. +4.12.1Y. +4.1E.1w. +4.2d.z. +5.D.2a. +c.28.Q. +c.T.26. +c.Y.21. +e.S.27. +0.+.1Z. 0.1A.1C. -0.1A.1D. -0.1E.1A. -0.1A.1F. -0.1A.1G. -0.1A.1H. -0.1A.1I. -0.1A.1J. -0.1A.1K. -0.1A.1L. -0.1A.1M. -0.1A.1N. -0.1A.1O. -0.1A.1P. -3.1A.1Q. -0.1A.1R. -3.1A.1S. -0.1A.1T. -3.1A.1U. -0.1A.1V. -0.1A.1W. -0.1A.1X. -0.1A.1Y. -0.1A.1Z. -0.1A.1+. -0.1A.1/. -0.1A.20. -0.1A.21. -0.1A.22. -0.1A.23. -0.1A.24. -0.1A.25. -b.26.1A. -0.1A.27. -4.1A.28. -0.1A.2a. -0.1A.2b. -0.1A.2l. -6.2m.1A. -0.1A.2p. -2.2q.1A. -2.2r.1A. -7.2s.1A. -7.2t.1A. -2.2u.1A. -3.1A.2v. -0.1A.2w. -e.2x.1A. -9.2y.1A. -0.1A.2z. -3.1A.2A. -9.2B.1A. -0.1A.2C. -0.1A.2D. -3.1A.2E. -3.1A.2F. -0.1A.2G. -9.2H.1A. -0.1A.2I. -3.1A.2J. -0.1A.2K. -c.2M.1A. -9.2N.1A. -2.2O.1A. -9.2Q.1A. -7.2S.1A. -0.1A.2T. -1.2U.1A. -9.2V.1A. -1.2W.1A. -0.1A.2X. -b.2+.1A. -0.1A.30. -0.1A.35. -b.36.1A. -0.1A.37. -3.1A.38. -5.39.1A. -0.1A.3d. -3.1A.3h. -3.1A.3i. -0.1A.3m. -0.1A.3p. -a.3v.1A. -a.3y.1A. -2.3z.1A. -5.3A.1A. -2.3B.1A. -0.1A.3C. -a.3F.1A. -5.3G.1A. -0.1A.3M. -0.1A.3Q. -5.40.1A. -0.1A.47. -0.1A.4d. -3.1A.4O. -3.1A.4Q. -5.4T.1A. -3.1A.4V. -0.1A.51. -0.1A.52. -a.5a.1A. -2.5b.1A. -5.5d.1A. -0.1A.5B. -0.1A.5C. -0.1A.5F. -3.1A.5H. -0.1A.5I. -f.1A.5K. -7.5M.1A. -0.1A.5O. -3.1A.5R. -0.1A.5U. -0.1A.5W. -1.5Y.1A. -0.1A.5Z. -0.1A.5/. -0.1A.60. -2.62.1A. -2.66.1A. -a.6b.1A. -c.6e.1A. -0.1A.6F. -0.1A.6M. -0.1A.6+. -3.1A.71. -5.79.1A. -3.1A.7D. -0.1A.2p. -0.1A.2p. 0.1B.1B. +0.1e.1W. +0.1E.1x. +0.1f.1V. +0.1k.1P. +0.1m.1N. +0.1n.1M. +0.1o.1L. +0.1p.1K. +0.1R.1h. +0.1s.1J. +0.1t.1I. +0.1v.1G. +0.1w.1F. +0.1z.1D. +0.B.2c. +0.F.29. +0.U.26. +0.V.25. +0.W.24. +0.X.23. +0.Z.21. +3.1l.1O. +3.1u.1H. +5.E.2a. +c.28.R. +c.T.27. +c.Y.22. +0./.1Z. +0.+.1+. +0.1A.1D. 0.1B.1C. +0.1E.1y. +0.1m.1O. +0.1n.1N. +0.1o.1M. +0.1p.1L. +0.1q.1K. +0.1t.1J. +0.1v.1H. +0.1w.1G. +0.1x.1F. +0.C.2c. +0.U.27. +0.V.26. +0.W.25. +0.X.24. +0.Z.22. +3.1l.1P. +3.1u.1I. +3.S.28. +4.1f.1W. +4.1k.1Q. +4.1S.1h. +5.F.2a. +c.Y.23. +f.1R.1i. +f.1V.1g. +0./.1+. +0.+.1/. +0.10.1Z. +0.13.1Y. 0.1B.1D. -3.1B.1E. -0.1B.1F. -0.1B.1G. -0.1B.1H. -0.1B.1I. -0.1B.1J. -0.1B.1K. -0.1B.1L. -0.1B.1M. -0.1B.1N. -0.1B.1O. -0.1B.1P. -0.1B.1Q. -0.1B.1R. -0.1B.1S. -3.1B.1T. -0.1B.1U. -0.1B.1V. -0.1B.1W. -0.1B.1X. -3.1B.1Y. -0.1B.1Z. -0.1B.1+. -0.1B.1/. -0.1B.20. -3.1B.21. -0.1B.22. -0.1B.23. -0.1B.24. -0.1B.25. -b.26.1B. -0.1B.27. -4.1B.28. -0.1B.2a. -0.1B.2b. -0.1B.2l. -6.2m.1B. -0.1B.2p. -2.2q.1B. -2.2r.1B. -7.2s.1B. -7.2t.1B. -2.2u.1B. -3.1B.2v. -3.1B.2w. -e.2x.1B. -9.2y.1B. -0.1B.2z. -3.1B.2A. -9.2B.1B. -0.1B.2C. -0.1B.2D. -0.1B.2E. -0.1B.2F. -0.1B.2G. -9.2H.1B. -0.1B.2I. -3.1B.2J. -0.1B.2K. -c.2M.1B. -9.2N.1B. -2.2O.1B. -9.2Q.1B. -7.2S.1B. -0.1B.2T. -1.2U.1B. -9.2V.1B. -1.2W.1B. -0.1B.2X. -b.2+.1B. -0.1B.30. -3.1B.35. -b.36.1B. -0.1B.37. -3.1B.38. -5.39.1B. -0.1B.3d. -3.1B.3h. -0.1B.3i. -0.1B.3m. -0.1B.3p. -a.3v.1B. -a.3y.1B. -2.3z.1B. -5.3A.1B. -2.3B.1B. -0.1B.3C. -a.3F.1B. -5.3G.1B. -0.1B.3M. -3.1B.3Q. -5.40.1B. -0.1B.47. -0.1B.4d. -0.1B.4O. -0.1B.4Q. -5.4T.1B. -3.1B.4V. -3.1B.51. -0.1B.52. -a.5a.1B. -2.5b.1B. -5.5d.1B. -0.1B.5B. -0.1B.5C. -0.1B.5F. -0.1B.5H. -0.1B.5I. -f.1B.5K. -7.5M.1B. -3.1B.5O. -0.1B.5R. -3.1B.5U. -0.1B.5W. -1.5Y.1B. -0.1B.5Z. -0.1B.5/. -0.1B.60. -2.62.1B. -2.66.1B. -a.6b.1B. -c.6e.1B. -0.1B.6F. -0.1B.6M. -0.1B.6+. -0.1B.71. -5.79.1B. -3.1B.7D. -0.1B.2p. -0.1B.2p. 0.1C.1C. +0.1e.1X. +0.1E.1z. +0.1j.1R. +0.1m.1P. +0.1n.1O. +0.1o.1N. +0.1p.1M. +0.1q.1L. +0.1r.1K. +0.1v.1I. +0.1W.1g. +0.1w.1H. +0.1x.1G. +0.1y.1F. +0.A.2d. +0.D.2c. +0.V.27. +0.W.26. +0.X.25. +0.Z.23. +3.1l.1Q. +3.1u.1J. +4.1S.1i. +c.28.T. +c.Y.24. +0./.1/. +0.+.20. +0.10.1+. +0.11.1Z. +0.14.1Y. 0.1D.1C. -0.1E.1C. -0.1F.1C. -0.1G.1C. -0.1H.1C. -0.1I.1C. -0.1C.1J. -0.1C.1K. -0.1C.1L. -0.1C.1M. -0.1C.1N. -0.1C.1O. -0.1C.1P. -0.1C.1Q. -0.1C.1R. -0.1C.1S. -0.1C.1T. -0.1C.1U. -0.1C.1V. -0.1C.1W. -0.1C.1X. -0.1C.1Y. -0.1C.1Z. -0.1C.1+. -0.1C.1/. -0.1C.20. -0.1C.21. -0.1C.22. -0.1C.23. -0.1C.24. -0.1C.25. -b.26.1C. -0.1C.27. -4.1C.28. -0.1C.2a. -0.1C.2b. -0.1C.2l. -6.2m.1C. -0.1C.2p. -2.2q.1C. -2.2r.1C. -7.2s.1C. -7.2t.1C. -2.2u.1C. -0.1C.2v. -0.1C.2w. -e.2x.1C. -9.2y.1C. -0.1C.2z. -3.1C.2A. -9.2B.1C. -0.1C.2C. -0.1C.2D. -3.1C.2E. -3.1C.2F. -0.1C.2G. -9.2H.1C. -0.1C.2I. -3.1C.2J. -0.1C.2K. -c.2M.1C. -9.2N.1C. -2.2O.1C. -9.2Q.1C. -7.2S.1C. -0.1C.2T. -1.2U.1C. -9.2V.1C. -1.2W.1C. -0.1C.2X. -b.2+.1C. -0.1C.30. -0.1C.35. -b.36.1C. -0.1C.37. -0.1C.38. -5.39.1C. -0.1C.3d. -3.1C.3h. -3.1C.3i. -0.1C.3m. -0.1C.3p. -a.3v.1C. -a.3y.1C. -2.3z.1C. -5.3A.1C. -2.3B.1C. -0.1C.3C. -a.3F.1C. -5.3G.1C. -0.1C.3M. -0.1C.3Q. -5.40.1C. -0.1C.47. -0.1C.4d. -0.1C.4O. -0.1C.4Q. -5.4T.1C. -3.1C.4V. -0.1C.51. -0.1C.52. -a.5a.1C. -2.5b.1C. -5.5d.1C. -0.1C.5B. -0.1C.5C. -0.1C.5F. -3.1C.5H. -0.1C.5I. -f.1C.5K. -7.5M.1C. -0.1C.5O. -0.1C.5R. -0.1C.5U. -0.1C.5W. -1.5Y.1C. -0.1C.5Z. -0.1C.5/. -0.1C.60. -2.62.1C. -2.66.1C. -a.6b.1C. -c.6e.1C. -0.1C.6F. -0.1C.6M. -0.1C.6+. -3.1C.71. -5.79.1C. -0.1C.7D. -0.1C.2p. -0.1C.2p. +0.1E.1A. +0.1f.1X. +0.1j.1S. +0.1n.1P. +0.1o.1O. +0.1p.1N. +0.1q.1M. +0.1r.1L. +0.1s.1K. +0.1v.1J. +0.1w.1I. +0.1x.1H. +0.1y.1G. +0.1z.1F. +0.B.2d. +0.E.2c. +0.W.27. +0.X.26. +0.Z.24. +4.1m.1Q. +4.G.29. +c.28.U. +c.Y.25. +0./.20. +0.+.21. +0.10.1/. +0.11.1+. +0.12.1Z. +0.15.1Y. +0.1A.1F. 0.1D.1D. +0.1o.1P. +0.1p.1O. +0.1q.1N. +0.1r.1M. +0.1s.1L. +0.1t.1K. +0.1w.1J. +0.1X.1g. +0.1x.1I. +0.1y.1H. +0.1z.1G. +0.C.2d. +0.F.2c. +0.H.29. +0.X.27. +0.Z.25. +4.1B.1E. +4.1h.1V. +4.1n.1Q. +5.G.2a. +c.28.V. +c.Y.26. +0./.21. +0.+.22. +0.10.20. +0.11.1/. +0.12.1+. +0.16.1Y. +0.1A.1G. +0.1B.1F. +0.1E.1C. +0.1k.1R. +0.1o.1Q. +0.1p.1P. +0.1q.1O. +0.1r.1N. +0.1s.1M. +0.1t.1L. +0.1x.1J. +0.1y.1I. +0.1z.1H. +0.D.2d. +0.Z.26. +3.1u.1K. +3.I.29. +4.1h.1W. +4.1i.1V. +5.H.2a. +c.28.W. +c.Y.27. +0./.22. +0.+.23. +0.10.21. +0.11.20. +0.17.1Y. +0.1A.1H. +0.1B.1G. 0.1E.1D. +0.1F.1C. +0.1j.1V. +0.1k.1S. +0.1q.1P. +0.1r.1O. +0.1s.1N. +0.1t.1M. +0.1v.1K. +0.1y.1J. +0.1z.1I. +0.E.2d. +0.Z.27. +3.1l.1R. +3.1u.1L. +3.I.2a. +3.J.29. +4.12.1/. +4.1i.1W. +4.1p.1Q. +c.28.X. +0./.23. +0.+.24. +0.10.22. +0.11.21. +0.13.1Z. +0.18.1Y. +0.1A.1I. +0.1B.1H. 0.1F.1D. +0.1G.1C. +0.1h.1X. +0.1j.1W. +0.1m.1R. +0.1r.1P. +0.1s.1O. +0.1v.1L. +0.1w.1K. +0.1z.1J. +0.F.2d. +0.G.2c. +0.K.29. +3.1l.1S. +3.1u.1M. +4.12.20. +4.1q.1Q. +4.1t.1N. +5.J.2a. +c.28.Y. +0./.24. +0.+.25. +0.10.23. +0.11.22. +0.12.21. +0.13.1+. +0.14.1Z. +0.19.1Y. +0.1A.1J. +0.1B.1I. 0.1G.1D. +0.1H.1C. +0.1m.1S. +0.1n.1R. +0.1s.1P. +0.1t.1O. +0.1v.1M. +0.1w.1L. +0.1x.1K. +0.H.2c. +0.L.29. +3.1u.1N. +4.1i.1X. +4.1r.1Q. +5.K.2a. +c.28.Z. +e.1E.1E. +0./.25. +0.+.26. +0.10.24. +0.11.23. +0.13.1/. +0.14.1+. +0.15.1Z. +0.1a.1Y. +0.1B.1J. 0.1D.1H. -0.1D.1I. -0.1D.1J. -0.1D.1K. -0.1D.1L. -0.1D.1M. -0.1D.1N. -0.1D.1O. -0.1D.1P. -3.1D.1Q. -0.1D.1R. -3.1D.1S. -3.1D.1T. -3.1D.1U. -0.1D.1V. -0.1D.1W. -0.1D.1X. -0.1D.1Y. -0.1D.1Z. -0.1D.1+. -0.1D.1/. -0.1D.20. -0.1D.21. -0.1D.22. -0.1D.23. -0.1D.24. -0.1D.25. -b.26.1D. -0.1D.27. -4.1D.28. -0.1D.2a. -0.1D.2b. -0.1D.2l. -6.2m.1D. -0.1D.2p. -2.2q.1D. -2.2r.1D. -7.2s.1D. -7.2t.1D. -2.2u.1D. -3.1D.2v. -0.1D.2w. -e.2x.1D. -9.2y.1D. -0.1D.2z. -3.1D.2A. -9.2B.1D. -0.1D.2C. -0.1D.2D. -3.1D.2E. -3.1D.2F. -0.1D.2G. -9.2H.1D. -0.1D.2I. -3.1D.2J. -0.1D.2K. -c.2M.1D. -9.2N.1D. -2.2O.1D. -9.2Q.1D. -7.2S.1D. -0.1D.2T. -1.2U.1D. -9.2V.1D. -1.2W.1D. -0.1D.2X. -b.2+.1D. -0.1D.30. -0.1D.35. -b.36.1D. -0.1D.37. -3.1D.38. -5.39.1D. -0.1D.3d. -3.1D.3h. -3.1D.3i. -0.1D.3m. -0.1D.3p. -a.3v.1D. -a.3y.1D. -2.3z.1D. -5.3A.1D. -2.3B.1D. -0.1D.3C. -a.3F.1D. -5.3G.1D. -0.1D.3M. -0.1D.3Q. -5.40.1D. -0.1D.47. -0.1D.4d. -3.1D.4O. -3.1D.4Q. -5.4T.1D. -3.1D.4V. -0.1D.51. -0.1D.52. -a.5a.1D. -2.5b.1D. -5.5d.1D. -0.1D.5B. -0.1D.5C. -0.1D.5F. -3.1D.5H. -0.1D.5I. -f.1D.5K. -7.5M.1D. -0.1D.5O. -3.1D.5R. -0.1D.5U. -3.1D.5W. -1.5Y.1D. -0.1D.5Z. -0.1D.5/. -0.1D.60. -2.62.1D. -2.66.1D. -a.6b.1D. -c.6e.1D. -0.1D.6F. -0.1D.6M. -0.1D.6+. -3.1D.71. -5.79.1D. -3.1D.7D. -0.1D.2p. -0.1D.2p. -0.1E.1E. 0.1E.1F. +0.1I.1C. +0.1j.1X. +0.1k.1V. +0.1n.1S. +0.1o.1R. +0.1t.1P. +0.1v.1N. +0.1w.1M. +0.1x.1L. +0.1y.1K. +0.L.2a. +3.1u.1O. +3.I.2c. +3.M.29. +4.12.22. +4.1s.1Q. +0./.26. +0.+.27. +0.10.25. +0.11.24. +0.13.20. +0.14.1/. +0.15.1+. +0.16.1Z. +0.1b.1Y. +0.1C.1J. +0.1D.1I. 0.1E.1G. -0.1E.1H. -0.1E.1I. -0.1E.1J. -0.1E.1K. -0.1E.1L. -0.1E.1M. -0.1E.1N. -0.1E.1O. -0.1E.1P. -3.1E.1Q. -0.1E.1R. -0.1E.1S. -3.1E.1T. -3.1E.1U. -0.1E.1V. -0.1E.1W. -0.1E.1X. -0.1E.1Y. -0.1E.1Z. -0.1E.1+. -0.1E.1/. -0.1E.20. -0.1E.21. -0.1E.22. -0.1E.23. -0.1E.24. -0.1E.25. -b.26.1E. -0.1E.27. -4.1E.28. -0.1E.2a. -0.1E.2b. -0.1E.2l. -6.2m.1E. -0.1E.2p. -2.2q.1E. -2.2r.1E. -7.2s.1E. -7.2t.1E. -2.2u.1E. -3.1E.2v. -0.1E.2w. -e.2x.1E. -9.2y.1E. -0.1E.2z. -3.1E.2A. -9.2B.1E. -0.1E.2C. -0.1E.2D. -3.1E.2E. -3.1E.2F. -0.1E.2G. -9.2H.1E. -0.1E.2I. -3.1E.2J. -0.1E.2K. -c.2M.1E. -9.2N.1E. -2.2O.1E. -9.2Q.1E. -7.2S.1E. -0.1E.2T. -1.2U.1E. -9.2V.1E. -1.2W.1E. -0.1E.2X. -b.2+.1E. -0.1E.30. -0.1E.35. -b.36.1E. -0.1E.37. -3.1E.38. -5.39.1E. -0.1E.3d. -3.1E.3h. -3.1E.3i. -0.1E.3m. -0.1E.3p. -a.3v.1E. -a.3y.1E. -2.3z.1E. -5.3A.1E. -2.3B.1E. -0.1E.3C. -a.3F.1E. -5.3G.1E. -0.1E.3M. -0.1E.3Q. -5.40.1E. -0.1E.47. -0.1E.4d. -3.1E.4O. -3.1E.4Q. -5.4T.1E. -3.1E.4V. -0.1E.51. -0.1E.52. -a.5a.1E. -2.5b.1E. -5.5d.1E. -0.1E.5B. -0.1E.5C. -0.1E.5F. -3.1E.5H. -0.1E.5I. -f.1E.5K. -7.5M.1E. -0.1E.5O. -3.1E.5R. -0.1E.5U. -3.1E.5W. -1.5Y.1E. -0.1E.5Z. -0.1E.5/. -0.1E.60. -2.62.1E. -2.66.1E. -a.6b.1E. -c.6e.1E. -0.1E.6F. -0.1E.6M. -0.1E.6+. -3.1E.71. -5.79.1E. -3.1E.7D. -0.1E.2p. -0.1E.2p. 0.1F.1F. +0.1o.1S. +0.1p.1R. +0.1v.1O. +0.1w.1N. +0.1x.1M. +0.1y.1L. +0.1z.1K. +0.G.2d. +3.1l.1V. +3.1u.1P. +3.J.2c. +3.M.2a. +4.12.23. +4.1k.1W. +4.1t.1Q. +0./.27. +0.10.26. +0.11.25. +0.12.24. +0.13.21. +0.14.20. +0.15.1/. +0.16.1+. +0.17.1Z. +0.1A.1K. +0.1c.1Y. +0.1D.1J. +0.1E.1H. 0.1G.1F. +0.1m.1V. +0.1p.1S. +0.1q.1R. +0.1v.1P. +0.1w.1O. +0.1x.1N. +0.1y.1M. +0.1z.1L. +0.H.2d. +0.K.2c. +3.1l.1W. +3.1u.1Q. +0.10.27. +0.11.26. +0.12.25. +0.13.22. +0.14.21. +0.15.20. +0.16.1/. +0.17.1+. +0.18.1Z. +0.1A.1L. +0.1B.1K. +0.1E.1I. 0.1F.1H. -0.1F.1I. -0.1F.1J. -0.1F.1K. -0.1F.1L. -0.1F.1M. -0.1F.1N. -0.1F.1O. -0.1F.1P. -0.1F.1Q. -0.1F.1R. -0.1F.1S. -0.1F.1T. -0.1F.1U. -0.1F.1V. -0.1F.1W. -0.1F.1X. -0.1F.1Y. -0.1F.1Z. -0.1F.1+. -0.1F.1/. -0.1F.20. -0.1F.21. -0.1F.22. -0.1F.23. -0.1F.24. -0.1F.25. -b.26.1F. -0.1F.27. -4.1F.28. -0.1F.2a. -0.1F.2b. -0.1F.2l. -6.2m.1F. -0.1F.2p. -2.2q.1F. -2.2r.1F. -7.2s.1F. -7.2t.1F. -2.2u.1F. -3.1F.2v. -0.1F.2w. -e.2x.1F. -9.2y.1F. -0.1F.2z. -3.1F.2A. -9.2B.1F. -0.1F.2C. -0.1F.2D. -3.1F.2E. -0.1F.2F. -0.1F.2G. -9.2H.1F. -0.1F.2I. -3.1F.2J. -0.1F.2K. -c.2M.1F. -9.2N.1F. -2.2O.1F. -9.2Q.1F. -7.2S.1F. -0.1F.2T. -1.2U.1F. -9.2V.1F. -1.2W.1F. -0.1F.2X. -b.2+.1F. -0.1F.30. -0.1F.35. -b.36.1F. -0.1F.37. -0.1F.38. -5.39.1F. -0.1F.3d. -3.1F.3h. -0.1F.3i. -0.1F.3m. -0.1F.3p. -a.3v.1F. -a.3y.1F. -2.3z.1F. -5.3A.1F. -2.3B.1F. -0.1F.3C. -a.3F.1F. -5.3G.1F. -0.1F.3M. -0.1F.3Q. -5.40.1F. -0.1F.47. -0.1F.4d. -0.1F.4O. -3.1F.4Q. -5.4T.1F. -3.1F.4V. -0.1F.51. -0.1F.52. -a.5a.1F. -2.5b.1F. -5.5d.1F. -0.1F.5B. -0.1F.5C. -0.1F.5F. -3.1F.5H. -0.1F.5I. -f.1F.5K. -7.5M.1F. -0.1F.5O. -3.1F.5R. -0.1F.5U. -0.1F.5W. -1.5Y.1F. -0.1F.5Z. -0.1F.5/. -0.1F.60. -2.62.1F. -2.66.1F. -a.6b.1F. -c.6e.1F. -0.1F.6F. -0.1F.6M. -0.1F.6+. -3.1F.71. -5.79.1F. -3.1F.7D. -0.1F.2p. -0.1F.2p. 0.1G.1G. +0.1k.1X. +0.1n.1V. +0.1q.1S. +0.1r.1R. +0.1w.1P. +0.1x.1O. +0.1y.1N. +0.1z.1M. +0.L.2c. +0.N.29. +3.I.2d. +4.1m.1W. +4.1v.1Q. +c.28.+. +0.11.27. +0.12.26. +0.13.23. +0.14.22. +0.15.21. +0.16.20. +0.17.1/. +0.18.1+. +0.19.1Z. +0.1A.1M. +0.1B.1L. +0.1C.1K. +0.1d.1Y. +0.1E.1J. +0.1F.1I. 0.1G.1H. +0.1n.1W. +0.1o.1V. +0.1s.1R. +0.1x.1P. +0.1y.1O. +0.1z.1N. +0.O.29. +3.1l.1X. +3.J.2d. +3.M.2c. +4.1r.1S. +4.1w.1Q. +5.N.2a. +c.28./. +0.12.27. +0.13.24. +0.14.23. +0.15.22. +0.16.21. +0.17.20. +0.18.1/. +0.19.1+. +0.1A.1N. +0.1a.1Z. +0.1B.1M. +0.1C.1L. +0.1D.1K. +0.1F.1J. 0.1G.1I. -0.1G.1J. -0.1G.1K. -0.1G.1L. -0.1G.1M. -0.1G.1N. -0.1G.1O. -0.1G.1P. -0.1G.1Q. -0.1G.1R. -0.1G.1S. -0.1G.1T. -0.1G.1U. -0.1G.1V. -0.1G.1W. -0.1G.1X. -0.1G.1Y. -0.1G.1Z. -0.1G.1+. -0.1G.1/. -0.1G.20. -0.1G.21. -0.1G.22. -0.1G.23. -0.1G.24. -0.1G.25. -b.26.1G. -0.1G.27. -4.1G.28. -0.1G.2a. -0.1G.2b. -0.1G.2l. -6.2m.1G. -0.1G.2p. -2.2q.1G. -2.2r.1G. -7.2s.1G. -7.2t.1G. -2.2u.1G. -3.1G.2v. -0.1G.2w. -e.2x.1G. -9.2y.1G. -0.1G.2z. -3.1G.2A. -9.2B.1G. -0.1G.2C. -0.1G.2D. -3.1G.2E. -0.1G.2F. -0.1G.2G. -9.2H.1G. -0.1G.2I. -3.1G.2J. -0.1G.2K. -c.2M.1G. -9.2N.1G. -2.2O.1G. -9.2Q.1G. -7.2S.1G. -0.1G.2T. -1.2U.1G. -9.2V.1G. -1.2W.1G. -0.1G.2X. -b.2+.1G. -0.1G.30. -0.1G.35. -b.36.1G. -0.1G.37. -0.1G.38. -5.39.1G. -0.1G.3d. -3.1G.3h. -0.1G.3i. -0.1G.3m. -0.1G.3p. -a.3v.1G. -a.3y.1G. -2.3z.1G. -5.3A.1G. -2.3B.1G. -0.1G.3C. -a.3F.1G. -5.3G.1G. -0.1G.3M. -0.1G.3Q. -5.40.1G. -0.1G.47. -0.1G.4d. -0.1G.4O. -3.1G.4Q. -5.4T.1G. -3.1G.4V. -0.1G.51. -0.1G.52. -a.5a.1G. -2.5b.1G. -5.5d.1G. -0.1G.5B. -0.1G.5C. -0.1G.5F. -3.1G.5H. -0.1G.5I. -f.1G.5K. -7.5M.1G. -0.1G.5O. -3.1G.5R. -0.1G.5U. -0.1G.5W. -1.5Y.1G. -0.1G.5Z. -0.1G.5/. -0.1G.60. -2.62.1G. -2.66.1G. -a.6b.1G. -c.6e.1G. -0.1G.6F. -0.1G.6M. -0.1G.6+. -3.1G.71. -5.79.1G. -3.1G.7D. -0.1G.2p. -0.1G.2p. 0.1H.1H. +0.1m.1X. +0.1o.1W. +0.1p.1V. +0.1s.1S. +0.1t.1R. +0.1x.1Q. +0.1y.1P. +0.1z.1O. +0.K.2d. +0.P.29. +5.O.2a. +c.28.10. +0.13.25. +0.14.24. +0.15.23. +0.16.22. +0.17.21. +0.18.20. +0.19.1/. +0.1a.1+. +0.1A.1O. +0.1B.1N. +0.1b.1Z. +0.1C.1M. +0.1D.1L. +0.1G.1J. 0.1H.1I. +0.1n.1X. +0.1p.1W. +0.1q.1V. +0.1y.1Q. +0.1z.1P. +0.L.2d. +0.Q.29. +3.1u.1R. +4.1t.1S. +5.P.2a. +c.28.11. +0.13.26. +0.14.25. +0.15.24. +0.16.23. +0.17.22. +0.18.21. +0.19.20. +0.1a.1/. +0.1A.1P. +0.1b.1+. +0.1B.1O. +0.1C.1N. +0.1c.1Z. +0.1D.1M. +0.1E.1K. +0.1e.1Y. 0.1H.1J. -0.1H.1K. -0.1H.1L. -0.1H.1M. -0.1H.1N. -0.1H.1O. -0.1H.1P. -3.1H.1Q. -0.1H.1R. -3.1H.1S. -0.1H.1T. -0.1H.1U. -0.1H.1V. -0.1H.1W. -0.1H.1X. -0.1H.1Y. -0.1H.1Z. -0.1H.1+. -0.1H.1/. -0.1H.20. -0.1H.21. -0.1H.22. -0.1H.23. -0.1H.24. -0.1H.25. -b.26.1H. -0.1H.27. -4.1H.28. -0.1H.2a. -0.1H.2b. -0.1H.2l. -6.2m.1H. -0.1H.2p. -2.2q.1H. -2.2r.1H. -7.2s.1H. -7.2t.1H. -2.2u.1H. -3.1H.2v. -0.1H.2w. -e.2x.1H. -9.2y.1H. -0.1H.2z. -3.1H.2A. -9.2B.1H. -0.1H.2C. -0.1H.2D. -3.1H.2E. -3.1H.2F. -0.1H.2G. -9.2H.1H. -0.1H.2I. -3.1H.2J. -0.1H.2K. -c.2M.1H. -9.2N.1H. -2.2O.1H. -9.2Q.1H. -7.2S.1H. -0.1H.2T. -1.2U.1H. -9.2V.1H. -1.2W.1H. -0.1H.2X. -b.2+.1H. -0.1H.30. -0.1H.35. -b.36.1H. -0.1H.37. -3.1H.38. -5.39.1H. -0.1H.3d. -3.1H.3h. -0.1H.3i. -0.1H.3m. -0.1H.3p. -a.3v.1H. -a.3y.1H. -2.3z.1H. -5.3A.1H. -2.3B.1H. -0.1H.3C. -a.3F.1H. -5.3G.1H. -0.1H.3M. -0.1H.3Q. -5.40.1H. -0.1H.47. -0.1H.4d. -3.1H.4O. -3.1H.4Q. -5.4T.1H. -3.1H.4V. -0.1H.51. -0.1H.52. -a.5a.1H. -2.5b.1H. -5.5d.1H. -0.1H.5B. -0.1H.5C. -0.1H.5F. -3.1H.5H. -0.1H.5I. -f.1H.5K. -7.5M.1H. -0.1H.5O. -3.1H.5R. -0.1H.5U. -0.1H.5W. -1.5Y.1H. -0.1H.5Z. -0.1H.5/. -0.1H.60. -2.62.1H. -2.66.1H. -a.6b.1H. -c.6e.1H. -0.1H.6F. -0.1H.6M. -0.1H.6+. -3.1H.71. -5.79.1H. -3.1H.7D. -0.1H.2p. -0.1H.2p. -3.1I.1I. +0.1o.1X. +0.1r.1V. +0.1v.1R. +0.N.2c. +0.R.29. +3.1u.1S. +3.M.2d. +4.1I.1I. +4.1q.1W. +4.1z.1Q. +5.Q.2a. +c.28.12. +0.13.27. +0.14.26. +0.15.25. +0.16.24. +0.17.23. +0.18.22. +0.19.21. +0.1a.20. +0.1b.1/. +0.1B.1P. +0.1c.1+. +0.1C.1O. +0.1D.1N. +0.1E.1L. +0.1F.1K. +0.1f.1Y. 0.1I.1J. -0.1I.1K. -0.1I.1L. -0.1I.1M. -0.1I.1N. -0.1I.1O. -0.1I.1P. -3.1I.1Q. -0.1I.1R. -0.1I.1S. -3.1I.1T. -3.1I.1U. -0.1I.1V. -0.1I.1W. -0.1I.1X. -0.1I.1Y. -0.1I.1Z. -0.1I.1+. -0.1I.1/. -0.1I.20. -0.1I.21. -0.1I.22. -0.1I.23. -0.1I.24. -0.1I.25. -b.26.1I. -0.1I.27. -4.1I.28. -0.1I.2a. -0.1I.2b. -0.1I.2l. -6.2m.1I. -3.1I.2p. -2.2q.1I. -2.2r.1I. -7.2s.1I. -7.2t.1I. -2.2u.1I. -3.1I.2v. -0.1I.2w. -e.2x.1I. -9.2y.1I. -0.1I.2z. -3.1I.2A. -9.2B.1I. -0.1I.2C. -0.1I.2D. -3.1I.2E. -3.1I.2F. -0.1I.2G. -9.2H.1I. -3.1I.2I. -3.1I.2J. -0.1I.2K. -c.2M.1I. -9.2N.1I. -2.2O.1I. -9.2Q.1I. -7.2S.1I. -0.1I.2T. -1.2U.1I. -9.2V.1I. -1.2W.1I. -0.1I.2X. -b.2+.1I. -0.1I.30. -0.1I.35. -b.36.1I. -0.1I.37. -3.1I.38. -5.39.1I. -0.1I.3d. -3.1I.3h. -0.1I.3i. -0.1I.3m. -0.1I.3p. -a.3v.1I. -a.3y.1I. -2.3z.1I. -5.3A.1I. -2.3B.1I. -0.1I.3C. -a.3F.1I. -5.3G.1I. -0.1I.3M. -0.1I.3Q. -5.40.1I. -0.1I.47. -0.1I.4d. -3.1I.4O. -3.1I.4Q. -5.4T.1I. -3.1I.4V. -0.1I.51. -0.1I.52. -a.5a.1I. -2.5b.1I. -5.5d.1I. -0.1I.5B. -0.1I.5C. -0.1I.5F. -3.1I.5H. -0.1I.5I. -f.1I.5K. -7.5M.1I. -0.1I.5O. -3.1I.5R. -0.1I.5U. -0.1I.5W. -1.5Y.1I. -0.1I.5Z. -0.1I.5/. -0.1I.60. -2.62.1I. -2.66.1I. -a.6b.1I. -c.6e.1I. -0.1I.6F. -0.1I.6M. -0.1I.6+. -3.1I.71. -5.79.1I. -3.1I.7D. -3.1I.2p. -3.1I.2p. +0.1p.1X. +0.1s.1V. +0.1v.1S. +0.1w.1R. +0.O.2c. +4.1A.1Q. +4.1r.1W. +4.S.29. +5.R.2a. +0.14.27. +0.15.26. +0.16.25. +0.17.24. +0.18.23. +0.19.22. +0.1a.21. +0.1B.1Q. +0.1b.20. +0.1c.1/. +0.1C.1P. +0.1D.1O. +0.1d.1Z. +0.1E.1M. +0.1F.1L. +0.1G.1K. 0.1J.1J. +0.1q.1X. +0.1w.1S. +0.1x.1R. +0.P.2c. +3.S.2a. +4.1s.1W. +4.1t.1V. +c.T.29. +h.1g.1Y. +0.15.27. +0.16.26. +0.17.25. +0.18.24. +0.19.23. +0.1a.22. +0.1b.21. +0.1C.1Q. +0.1c.20. +0.1d.1+. +0.1D.1P. +0.1E.1N. +0.1F.1M. +0.1G.1L. +0.1H.1K. +0.1r.1X. +0.1x.1S. +0.1y.1R. +0.N.2d. +0.Q.2c. +0.U.29. +3.1u.1V. +4.1t.1W. +c.28.13. +c.T.2a. +0.16.27. +0.17.26. +0.18.25. +0.19.24. +0.1a.23. +0.1b.22. +0.1c.21. +0.1d.1/. +0.1E.1O. +0.1F.1N. +0.1G.1M. +0.1H.1L. +0.1I.1K. +0.1s.1X. +0.1v.1V. +0.1y.1S. +0.1z.1R. +0.O.2d. +0.R.2c. +0.V.29. +3.1u.1W. +4.1D.1Q. +5.U.2a. +c.28.14. +0.17.27. +0.18.26. +0.19.25. +0.1A.1R. +0.1a.24. +0.1b.23. +0.1c.22. +0.1d.20. +0.1E.1P. +0.1e.1Z. +0.1F.1O. +0.1G.1N. +0.1H.1M. +0.1h.1Y. +0.1I.1L. 0.1K.1J. +0.1t.1X. +0.1v.1W. +0.1w.1V. +0.P.2d. +0.W.29. +4.1z.1S. +4.S.2c. +5.V.2a. +c.28.15. +0.18.27. +0.19.26. +0.1a.25. +0.1B.1R. +0.1b.24. +0.1c.23. +0.1d.21. +0.1e.1+. +0.1F.1P. +0.1f.1Z. +0.1G.1O. +0.1H.1N. +0.1I.1M. +0.1i.1Y. 0.1L.1J. +0.1x.1V. +0.Q.2d. +0.X.29. +3.1u.1X. +4.1A.1S. +4.1E.1Q. +4.1w.1W. +5.W.2a. +c.28.16. +c.T.2c. +0.19.27. +0.1a.26. +0.1B.1S. +0.1b.25. +0.1C.1R. +0.1c.24. +0.1d.22. +0.1e.1/. +0.1f.1+. +0.1F.1Q. +0.1G.1P. +0.1H.1O. +0.1I.1N. +0.1j.1Y. 0.1M.1J. -3.1N.1J. -0.1O.1J. -0.1P.1J. -3.1J.1Q. -0.1J.1R. -3.1J.1S. -0.1J.1T. -3.1J.1U. -0.1J.1V. -0.1J.1W. -0.1J.1X. -0.1J.1Y. -0.1J.1Z. -0.1J.1+. -0.1J.1/. -0.1J.20. -0.1J.21. -0.1J.22. -0.1J.23. -0.1J.24. -0.1J.25. -b.26.1J. -0.1J.27. -4.1J.28. -0.1J.2a. -0.1J.2b. -0.1J.2l. -6.2m.1J. -0.1J.2p. -2.2q.1J. -2.2r.1J. -7.2s.1J. -7.2t.1J. -2.2u.1J. -0.1J.2v. -0.1J.2w. -e.2x.1J. -9.2y.1J. -0.1J.2z. -3.1J.2A. -9.2B.1J. -0.1J.2C. -0.1J.2D. -3.1J.2E. -0.1J.2F. -0.1J.2G. -9.2H.1J. -0.1J.2I. -3.1J.2J. -0.1J.2K. -c.2M.1J. -9.2N.1J. -2.2O.1J. -9.2Q.1J. -7.2S.1J. -0.1J.2T. -1.2U.1J. -9.2V.1J. -1.2W.1J. -0.1J.2X. -d.2+.1J. -0.1J.30. -0.1J.35. -b.36.1J. -0.1J.37. -0.1J.38. -5.39.1J. -0.1J.3d. -3.1J.3h. -3.1J.3i. -0.1J.3m. -0.1J.3p. -a.3v.1J. -a.3y.1J. -2.3z.1J. -5.3A.1J. -2.3B.1J. -0.1J.3C. -a.3F.1J. -5.3G.1J. -0.1J.3M. -0.1J.3Q. -5.40.1J. -0.1J.47. -0.1J.4d. -3.1J.4O. -3.1J.4Q. -5.4T.1J. -3.1J.4V. -0.1J.51. -0.1J.52. -a.5a.1J. -2.5b.1J. -5.5d.1J. -0.1J.5B. -0.1J.5C. -0.1J.5F. -3.1J.5H. -0.1J.5I. -f.1J.5K. -7.5M.1J. -0.1J.5O. -3.1J.5R. -0.1J.5U. -0.1J.5W. -1.5Y.1J. -0.1J.5Z. -0.1J.5/. -0.1J.60. -2.62.1J. -2.66.1J. -a.6b.1J. -c.6e.1J. -0.1J.6F. -0.1J.6M. -0.1J.6+. -3.1J.71. -5.79.1J. -3.1J.7D. -0.1J.2p. -0.1J.2p. +0.1v.1X. +0.1x.1W. +0.1y.1V. +0.1Z.1g. +0.R.2d. +0.U.2c. +5.X.2a. +c.28.17. +c.Y.29. +0.1+.1g. +0.1a.27. +0.1b.26. +0.1C.1S. +0.1c.25. +0.1D.1R. +0.1d.23. +0.1e.20. +0.1f.1/. +0.1G.1Q. +0.1H.1P. +0.1I.1O. 0.1K.1K. +0.1w.1X. +0.1y.1W. +0.1z.1V. +0.V.2c. +0.Z.29. +3.S.2d. +4.1N.1J. +c.28.18. +c.Y.2a. +0.1/.1g. +0.1A.1V. +0.1b.27. +0.1c.26. +0.1d.24. +0.1e.21. +0.1f.20. +0.1I.1P. 0.1K.1L. +0.1O.1J. +0.1x.1X. +0.W.2c. +4.1D.1S. +4.1H.1Q. +4.1z.1W. +5.Z.2a. +c.28.19. +c.T.2d. +0.1c.27. +0.1d.25. +0.1E.1R. +0.1e.22. +0.1f.21. +0.1h.1Z. 0.1K.1M. -0.1K.1N. -0.1K.1O. -0.1K.1P. -3.1K.1Q. -0.1K.1R. -0.1K.1S. -3.1K.1T. -3.1K.1U. -0.1K.1V. -0.1K.1W. -0.1K.1X. -0.1K.1Y. -0.1K.1Z. -0.1K.1+. -0.1K.1/. -0.1K.20. -0.1K.21. -0.1K.22. -0.1K.23. -0.1K.24. -0.1K.25. -b.26.1K. -0.1K.27. -4.1K.28. -0.1K.2a. -0.1K.2b. -0.1K.2l. -6.2m.1K. -3.1K.2p. -2.2q.1K. -2.2r.1K. -7.2s.1K. -7.2t.1K. -2.2u.1K. -3.1K.2v. -0.1K.2w. -e.2x.1K. -9.2y.1K. -0.1K.2z. -3.1K.2A. -9.2B.1K. -0.1K.2C. -3.1K.2D. -3.1K.2E. -3.1K.2F. -0.1K.2G. -9.2H.1K. -0.1K.2I. -3.1K.2J. -0.1K.2K. -c.2M.1K. -9.2N.1K. -2.2O.1K. -9.2Q.1K. -7.2S.1K. -0.1K.2T. -1.2U.1K. -9.2V.1K. -1.2W.1K. -0.1K.2X. -b.2+.1K. -0.1K.30. -0.1K.35. -b.36.1K. -0.1K.37. -3.1K.38. -5.39.1K. -0.1K.3d. -3.1K.3h. -3.1K.3i. -0.1K.3m. -0.1K.3p. -a.3v.1K. -a.3y.1K. -2.3z.1K. -5.3A.1K. -2.3B.1K. -0.1K.3C. -a.3F.1K. -5.3G.1K. -0.1K.3M. -0.1K.3Q. -5.40.1K. -0.1K.47. -0.1K.4d. -3.1K.4O. -3.1K.4Q. -5.4T.1K. -3.1K.4V. -0.1K.51. -0.1K.52. -a.5a.1K. -2.5b.1K. -5.5d.1K. -0.1K.5B. -0.1K.5C. -0.1K.5F. -3.1K.5H. -0.1K.5I. -f.1K.5K. -7.5M.1K. -0.1K.5O. -3.1K.5R. -0.1K.5U. -3.1K.5W. -1.5Y.1K. -0.1K.5Z. -0.1K.5/. -0.1K.60. -2.62.1K. -2.66.1K. -a.6b.1K. -c.6e.1K. -0.1K.6F. -0.1K.6M. -0.1K.6+. -3.1K.71. -5.79.1K. -3.1K.7D. -3.1K.2p. -3.1K.2p. +0.1k.1Y. 0.1L.1L. +0.1P.1J. +0.1y.1X. +0.U.2d. +0.X.2c. +4.1A.1W. +4.1B.1V. +4.1I.1Q. +c.28.1a. +f.20.1g. +0.1B.1W. +0.1C.1V. +0.1d.26. +0.1E.1S. +0.1e.23. +0.1F.1R. +0.1f.22. +0.1h.1+. +0.1i.1Z. +0.1K.1N. 0.1M.1L. +0.1z.1X. +0.V.2d. +3.1l.1Y. +4.1J.1Q. +c.28.1b. +c.Y.2c. +e.21.1g. +0.+.29. +0.1A.1X. +0.1C.1W. +0.1d.27. +0.1e.24. +0.1F.1S. +0.1f.23. +0.1G.1R. +0.1h.1/. +0.1i.1+. +0.1j.1Z. +0.1K.1O. 0.1L.1N. +0.1m.1Y. +0.22.1g. +0.W.2d. +0.Z.2c. +4.1D.1V. +4.1M.1M. +c.28.1c. +0./.29. +0.1B.1X. +0.1e.25. +0.1f.24. +0.1G.1S. +0.1H.1R. +0.1h.20. +0.1i.1/. +0.1j.1+. +0.1K.1P. 0.1L.1O. +0.1n.1Y. +0.X.2d. +4.1D.1W. +4.1M.1N. +5.+.2a. +f.23.1g. +0.10.29. +0.1C.1X. +0.1e.26. +0.1f.25. +0.1h.21. +0.1I.1R. +0.1i.20. +0.1j.1/. 0.1L.1P. -0.1L.1Q. -0.1L.1R. -0.1L.1S. -0.1L.1T. -0.1L.1U. -0.1L.1V. -0.1L.1W. -0.1L.1X. -0.1L.1Y. -0.1L.1Z. -0.1L.1+. -0.1L.1/. -0.1L.20. -0.1L.21. -0.1L.22. -0.1L.23. -0.1L.24. -0.1L.25. -b.26.1L. -0.1L.27. -4.1L.28. -0.1L.2a. -0.1L.2b. -0.1L.2l. -6.2m.1L. -0.1L.2p. -2.2q.1L. -2.2r.1L. -7.2s.1L. -7.2t.1L. -2.2u.1L. -0.1L.2v. -0.1L.2w. -e.2x.1L. -9.2y.1L. -0.1L.2z. -0.1L.2A. -9.2B.1L. -0.1L.2C. -0.1L.2D. -3.1L.2E. -0.1L.2F. -0.1L.2G. -9.2H.1L. -0.1L.2I. -3.1L.2J. -0.1L.2K. -c.2M.1L. -9.2N.1L. -2.2O.1L. -9.2Q.1L. -7.2S.1L. -0.1L.2T. -1.2U.1L. -9.2V.1L. -1.2W.1L. -0.1L.2X. -b.2+.1L. -0.1L.30. -0.1L.35. -3.36.1L. -0.1L.37. -0.1L.38. -5.39.1L. -0.1L.3d. -0.1L.3h. -0.1L.3i. -0.1L.3m. -0.1L.3p. -a.3v.1L. -a.3y.1L. -2.3z.1L. -5.3A.1L. -2.3B.1L. -0.1L.3C. -a.3F.1L. -5.3G.1L. -0.1L.3M. -0.1L.3Q. -5.40.1L. -0.1L.47. -0.1L.4d. -0.1L.4O. -3.1L.4Q. -5.4T.1L. -0.1L.4V. -0.1L.51. -0.1L.52. -a.5a.1L. -2.5b.1L. -5.5d.1L. -0.1L.5B. -0.1L.5C. -0.1L.5F. -0.1L.5H. -0.1L.5I. -f.1L.5K. -7.5M.1L. -0.1L.5O. -0.1L.5R. -0.1L.5U. -0.1L.5W. -1.5Y.1L. -0.1L.5Z. -0.1L.5/. -0.1L.60. -2.62.1L. -2.66.1L. -a.6b.1L. -c.6e.1L. -0.1L.6F. -0.1L.6M. -0.1L.6+. -3.1L.71. -5.79.1L. -0.1L.7D. -0.1L.2p. -0.1L.2p. -3.1M.1M. -3.1M.1N. 0.1M.1O. -0.1M.1P. -3.1M.1Q. -0.1M.1R. -0.1M.1S. -0.1M.1T. -3.1M.1U. -0.1M.1V. -0.1M.1W. -0.1M.1X. -0.1M.1Y. -0.1M.1Z. -0.1M.1+. -0.1M.1/. -0.1M.20. -0.1M.21. -0.1M.22. -0.1M.23. -0.1M.24. -0.1M.25. -b.26.1M. -0.1M.27. -4.1M.28. -0.1M.2a. -0.1M.2b. -0.1M.2l. -6.2m.1M. -0.1M.2p. -2.2q.1M. -2.2r.1M. -7.2s.1M. -7.2t.1M. -2.2u.1M. -0.1M.2v. -0.1M.2w. -e.2x.1M. -9.2y.1M. -0.1M.2z. -3.1M.2A. -9.2B.1M. -0.1M.2C. -0.1M.2D. -3.1M.2E. -3.1M.2F. -0.1M.2G. -9.2H.1M. -0.1M.2I. -3.1M.2J. -0.1M.2K. -c.2M.1M. -9.2N.1M. -2.2O.1M. -9.2Q.1M. -7.2S.1M. -0.1M.2T. -1.2U.1M. -9.2V.1M. -1.2W.1M. -0.1M.2X. -b.2+.1M. -0.1M.30. -0.1M.35. -b.36.1M. -0.1M.37. -0.1M.38. -5.39.1M. -0.1M.3d. -3.1M.3h. -0.1M.3i. -0.1M.3m. -0.1M.3p. -a.3v.1M. -a.3y.1M. -2.3z.1M. -5.3A.1M. -2.3B.1M. -0.1M.3C. -a.3F.1M. -5.3G.1M. -0.1M.3M. -0.1M.3Q. -5.40.1M. -0.1M.47. -0.1M.4d. -0.1M.4O. -3.1M.4Q. -5.4T.1M. -3.1M.4V. -0.1M.51. -0.1M.52. -a.5a.1M. -2.5b.1M. -5.5d.1M. -0.1M.5B. -0.1M.5C. -0.1M.5F. -3.1M.5H. -0.1M.5I. -f.1M.5K. -7.5M.1M. -0.1M.5O. -3.1M.5R. -0.1M.5U. -0.1M.5W. -1.5Y.1M. -0.1M.5Z. -0.1M.5/. -0.1M.60. -2.62.1M. -2.66.1M. -a.6b.1M. -c.6e.1M. -0.1M.6F. -0.1M.6M. -0.1M.6+. -3.1M.71. -5.79.1M. -3.1M.7D. -0.1M.2p. -0.1M.2p. 0.1N.1N. -3.1N.1O. +0.1o.1Y. +0.24.1g. +4.1E.1V. +4.1H.1S. +4.1K.1Q. +5./.2a. +c.28.1d. +c.Y.2d. +0.11.29. +0.1D.1X. +0.1e.27. +0.1F.1V. +0.1f.26. +0.1h.22. +0.1I.1S. +0.1i.21. +0.1J.1R. +0.1j.20. +0.1k.1Z. +0.1L.1Q. +0.1M.1P. +0.1p.1Y. +0.25.1g. +0.Z.2d. +4.1E.1W. +4.1N.1O. +5.10.2a. +0.+.2c. +0.12.29. +0.1F.1W. +0.1f.27. +0.1G.1V. +0.1h.23. +0.1i.22. +0.1j.21. +0.1k.1+. 0.1N.1P. -3.1N.1Q. -0.1N.1R. -3.1N.1S. -3.1N.1T. -3.1N.1U. -3.1N.1V. -0.1N.1W. -3.1N.1X. -0.1N.1Y. -3.1N.1Z. -3.1N.1+. -3.1N.1/. -3.1N.20. -3.1N.21. -3.1N.22. -0.1N.23. -3.1N.24. -3.1N.25. -b.26.1N. -3.1N.27. -4.1N.28. -0.1N.2a. -3.1N.2b. -0.1N.2l. -6.2m.1N. -3.1N.2p. -2.2q.1N. -2.2r.1N. -7.2s.1N. -7.2t.1N. -2.2u.1N. -3.1N.2v. -3.1N.2w. -e.2x.1N. -9.2y.1N. -3.1N.2z. -3.1N.2A. -9.2B.1N. -3.1N.2C. -3.1N.2D. -3.1N.2E. -3.1N.2F. -3.1N.2G. -9.2H.1N. -3.1N.2I. -3.1N.2J. -3.1N.2K. -c.2M.1N. -9.2N.1N. -2.2O.1N. -9.2Q.1N. -7.2S.1N. -3.1N.2T. -1.2U.1N. -9.2V.1N. -1.2W.1N. -3.1N.2X. -d.2+.1N. -3.1N.30. -0.1N.35. -3.36.1N. -0.1N.37. -3.1N.38. -5.39.1N. -3.1N.3d. -3.1N.3h. -3.1N.3i. -0.1N.3m. -0.1N.3p. -a.3v.1N. -a.3y.1N. -2.3z.1N. -5.3A.1N. -2.3B.1N. -3.1N.3C. -a.3F.1N. -5.3G.1N. -3.1N.3M. -3.1N.3Q. -5.40.1N. -3.1N.47. -3.1N.4d. -3.1N.4O. -3.1N.4Q. -5.4T.1N. -3.1N.4V. -0.1N.51. -0.1N.52. -a.5a.1N. -2.5b.1N. -5.5d.1N. -3.1N.5B. -3.1N.5C. -3.1N.5F. -3.1N.5H. -0.1N.5I. -f.1N.5K. -7.5M.1N. -0.1N.5O. -3.1N.5R. -3.1N.5U. -3.1N.5W. -1.5Y.1N. -3.1N.5Z. -3.1N.5/. -3.1N.60. -2.62.1N. -2.66.1N. -a.6b.1N. -c.6e.1N. -3.1N.6F. -3.1N.6M. -3.1N.6+. -3.1N.71. -5.79.1N. -3.1N.7D. -3.1N.2p. -3.1N.2p. 0.1O.1O. +0.1q.1Y. +0.26.1g. +3.1l.1Z. +4.1J.1S. +4.1M.1Q. +5./.2b. +5.11.2a. +0./.2c. +0.1E.1X. +0.1G.1W. +0.1H.1V. +0.1h.24. +0.1i.23. +0.1j.22. +0.1k.1/. +0.1m.1Z. 0.1P.1O. +0.1r.1Y. +0.27.1g. +3.1l.1+. +4.1N.1Q. +5.12.2a. +c.28.1e. +0.10.2c. +0.1F.1X. +0.1H.1W. +0.1h.25. +0.1i.24. +0.1j.23. +0.1K.1R. +0.1k.20. +0.1m.1+. +0.1n.1Z. 0.1O.1Q. -0.1O.1R. -0.1O.1S. -0.1O.1T. -0.1O.1U. -0.1O.1V. -0.1O.1W. -0.1O.1X. -0.1O.1Y. -0.1O.1Z. -0.1O.1+. -0.1O.1/. -0.1O.20. -0.1O.21. -0.1O.22. -0.1O.23. -0.1O.24. -0.1O.25. -b.26.1O. -0.1O.27. -4.1O.28. -0.1O.2a. -0.1O.2b. -4.2e.1O. -0.1O.2l. -6.2m.1O. -4.2n.1O. -0.1O.2p. -2.2q.1O. -2.2r.1O. -7.2s.1O. -7.2t.1O. -2.2u.1O. -0.1O.2v. -0.1O.2w. -e.2x.1O. -9.2y.1O. -0.1O.2z. -0.1O.2A. -9.2B.1O. -0.1O.2C. -0.1O.2D. -3.1O.2E. -0.1O.2F. -0.1O.2G. -9.2H.1O. -0.1O.2I. -0.1O.2J. -0.1O.2K. -c.2M.1O. -9.2N.1O. -2.2O.1O. -9.2Q.1O. -7.2S.1O. -0.1O.2T. -1.2U.1O. -9.2V.1O. -1.2W.1O. -0.1O.2X. -d.2+.1O. -0.1O.30. -0.1O.35. -b.36.1O. -0.1O.37. -0.1O.38. -5.39.1O. -0.1O.3d. -3.1O.3h. -0.1O.3i. -0.1O.3m. -0.1O.3p. -a.3v.1O. -a.3y.1O. -2.3z.1O. -5.3A.1O. -2.3B.1O. -0.1O.3C. -a.3F.1O. -5.3G.1O. -0.1O.3M. -0.1O.3Q. -5.40.1O. -0.1O.47. -0.1O.4d. -0.1O.4O. -0.1O.4Q. -5.4T.1O. -0.1O.4V. -0.1O.51. -0.1O.52. -a.5a.1O. -2.5b.1O. -5.5d.1O. -0.1O.5B. -0.1O.5C. -0.1O.5F. -0.1O.5H. -0.1O.5I. -f.1O.5K. -7.5M.1O. -0.1O.5O. -0.1O.5R. -0.1O.5U. -0.1O.5W. -1.5Y.1O. -0.1O.5Z. -0.1O.5/. -0.1O.60. -2.62.1O. -2.66.1O. -a.6b.1O. -c.6e.1O. -0.1O.6F. -0.1O.6M. -0.1O.6+. -0.1O.71. -5.79.1O. -0.1O.7D. -0.1O.2p. -0.1O.2p. 0.1P.1P. +0.1s.1Y. +3.1l.1/. +4.1I.1V. +c.28.1f. +0.+.2d. +0.11.2c. +0.13.29. +0.1G.1X. +0.1h.26. +0.1i.25. +0.1J.1V. +0.1j.24. +0.1K.1S. +0.1k.21. +0.1L.1R. +0.1m.1/. +0.1n.1+. +0.1o.1Z. 0.1P.1Q. -0.1P.1R. -0.1P.1S. -0.1P.1T. -0.1P.1U. -0.1P.1V. -0.1P.1W. -0.1P.1X. -0.1P.1Y. -0.1P.1Z. -0.1P.1+. -0.1P.1/. -0.1P.20. -0.1P.21. -0.1P.22. -0.1P.23. -0.1P.24. -0.1P.25. -b.26.1P. -0.1P.27. -4.1P.28. -0.1P.2a. -0.1P.2b. -d.2e.1P. -0.1P.2l. -6.2m.1P. -4.2n.1P. -0.1P.2p. -2.2q.1P. -2.2r.1P. -7.2s.1P. -7.2t.1P. -2.2u.1P. -0.1P.2v. -0.1P.2w. -e.2x.1P. -9.2y.1P. -0.1P.2z. -0.1P.2A. -9.2B.1P. -0.1P.2C. -0.1P.2D. -d.1P.2E. -0.1P.2F. -0.1P.2G. -9.2H.1P. -0.1P.2I. -0.1P.2J. -0.1P.2K. -c.2M.1P. -9.2N.1P. -2.2O.1P. -9.2Q.1P. -7.2S.1P. -0.1P.2T. -1.2U.1P. -9.2V.1P. -1.2W.1P. -0.1P.2X. -d.2+.1P. -0.1P.30. -0.1P.35. -b.36.1P. -0.1P.37. -0.1P.38. -5.39.1P. -0.1P.3d. -d.1P.3h. -0.1P.3i. -0.1P.3m. -0.1P.3p. -a.3v.1P. -a.3y.1P. -2.3z.1P. -5.3A.1P. -2.3B.1P. -0.1P.3C. -a.3F.1P. -5.3G.1P. -0.1P.3M. -0.1P.3Q. -5.40.1P. -0.1P.47. -0.1P.4d. -0.1P.4O. -0.1P.4Q. -5.4T.1P. -0.1P.4V. -0.1P.51. -0.1P.52. -a.5a.1P. -2.5b.1P. -5.5d.1P. -0.1P.5B. -0.1P.5C. -0.1P.5F. -0.1P.5H. -0.1P.5I. -f.1P.5K. -7.5M.1P. -0.1P.5O. -0.1P.5R. -0.1P.5U. -0.1P.5W. -1.5Y.1P. -0.1P.5Z. -0.1P.5/. -0.1P.60. -2.62.1P. -2.66.1P. -a.6b.1P. -c.6e.1P. -0.1P.6F. -0.1P.6M. -0.1P.6+. -0.1P.71. -5.79.1P. -0.1P.7D. -0.1P.2p. -0.1P.2p. +0.1t.1Y. +3.1l.20. +4.1I.1W. +c.28.1g. +0./.2d. +0.14.29. +0.1H.1X. +0.1h.27. +0.1i.26. +0.1j.25. +0.1k.22. +0.1L.1S. +0.1M.1R. +0.1m.20. +0.1n.1/. +0.1o.1+. +0.1p.1Z. 0.1Q.1Q. +3.1l.21. +3.1u.1Y. +4.12.2c. +4.1J.1W. +5.13.2a. +0.10.2d. +0.15.29. +0.1I.1X. +0.1i.27. +0.1j.26. +0.1k.23. +0.1M.1S. +0.1m.21. +0.1N.1R. +0.1n.20. +0.1o.1/. +0.1p.1+. +0.1q.1Z. +0.1v.1Y. +3.1l.22. +5./.2e. +5.14.2a. +e.0.2m. +0.11.2d. +0.16.29. +0.1J.1X. +0.1j.27. +0.1k.24. +0.1m.22. +0.1n.21. +0.1O.1R. +0.1o.20. +0.1p.1/. +0.1q.1+. +0.1r.1Z. +0.1w.1Y. +3.1l.23. +4.1K.1V. +4.1N.1S. +5.15.2a. +c.28.1h. +e.0.2n. +e.1.2m. +0.13.2c. +0.17.29. +0.1k.25. +0.1L.1V. +0.1m.23. +0.1n.22. +0.1O.1S. +0.1o.21. +0.1P.1R. +0.1p.20. +0.1q.1/. +0.1r.1+. +0.1s.1Z. +0.1x.1Y. +3.1l.24. +4.12.2d. +4.1K.1W. +5.16.2a. +c.28.1i. +e.1.2n. +e.2.2m. +0.14.2c. +0.18.29. +0.1k.26. +0.1L.1W. +0.1M.1V. +0.1m.24. +0.1n.23. +0.1o.22. +0.1P.1S. +0.1p.21. +0.1q.20. +0.1r.1/. 0.1R.1Q. +0.1s.1+. +0.1t.1Z. +0.1y.1Y. +2.0.2o. +3.1l.25. +5./.2f. +5.17.2a. +c.28.1j. +e.2.2n. +e.3.2m. +0.15.2c. +0.19.29. +0.1K.1X. +0.1k.27. +0.1m.25. +0.1n.24. +0.1o.23. +0.1p.22. +0.1q.21. +0.1r.20. +0.1s.1/. 0.1S.1Q. -d.1Q.1T. -d.1Q.1U. -0.1Q.1V. -0.1Q.1W. -0.1Q.1X. -0.1Q.1Y. -0.1Q.1Z. -0.1Q.1+. -0.1Q.1/. -d.1Q.20. -0.1Q.21. -0.1Q.22. -0.1Q.23. -0.1Q.24. -0.1Q.25. -b.26.1Q. -0.1Q.27. -0.1Q.28. -0.1Q.2a. -0.1Q.2b. -d.2e.1Q. -0.1Q.2l. -6.2m.1Q. -4.2n.1Q. -0.1Q.2p. -2.2q.1Q. -2.2r.1Q. -7.2s.1Q. -7.2t.1Q. -2.2u.1Q. -d.1Q.2v. -0.1Q.2w. -e.2x.1Q. -9.2y.1Q. -0.1Q.2z. -d.1Q.2A. -9.2B.1Q. -0.1Q.2C. -d.1Q.2D. -d.1Q.2E. -0.1Q.2F. -0.1Q.2G. -9.2H.1Q. -d.1Q.2I. -0.1Q.2J. -0.1Q.2K. -c.2M.1Q. -9.2N.1Q. -2.2O.1Q. -9.2Q.1Q. -7.2S.1Q. -0.1Q.2T. -1.2U.1Q. -9.2V.1Q. -1.2W.1Q. -0.1Q.2X. -b.2+.1Q. -0.1Q.30. -0.1Q.35. -3.36.1Q. -0.1Q.37. -d.1Q.38. -5.39.1Q. -0.1Q.3d. -d.1Q.3h. -d.1Q.3i. -0.1Q.3m. -0.1Q.3p. -a.3v.1Q. -a.3y.1Q. -2.3z.1Q. -5.3A.1Q. -2.3B.1Q. -0.1Q.3C. -a.3F.1Q. -5.3G.1Q. -0.1Q.3M. -0.1Q.3Q. -5.40.1Q. -0.1Q.47. -0.1Q.4d. -0.1Q.4O. -0.1Q.4Q. -5.4T.1Q. -d.1Q.4V. -0.1Q.51. -0.1Q.52. -a.5a.1Q. -2.5b.1Q. -5.5d.1Q. -0.1Q.5B. -0.1Q.5C. -5.5F.1Q. -d.1Q.5H. -0.1Q.5I. -f.1Q.5K. -7.5M.1Q. -0.1Q.5O. -0.1Q.5R. -0.1Q.5U. -0.1Q.5W. -1.5Y.1Q. -0.1Q.5Z. -0.1Q.5/. -d.1Q.60. -2.62.1Q. -2.66.1Q. -a.6b.1Q. -c.6e.1Q. -0.1Q.6F. -0.1Q.6M. -0.1Q.6+. -d.1Q.71. -5.79.1Q. -0.1Q.7D. -0.1Q.2p. -0.1Q.2p. +0.1t.1+. +0.1z.1Y. +2.1.2o. +3.1l.26. +3.1u.1Z. +4.1M.1W. +4.1N.1V. +5.18.2a. +7.2p.0. +e.3.2n. +e.4.2m. +0.13.2d. +0.16.2c. +0.1A.1Y. +0.1a.29. +0.1L.1X. +0.1m.26. +0.1n.25. +0.1O.1V. +0.1o.24. +0.1p.23. +0.1q.22. +0.1r.21. +0.1s.20. +0.1t.1/. +0.1v.1Z. +2.2.2o. +3.1l.27. +3.1u.1+. +4.1N.1W. +5.19.2a. +7.2p.1. +e.4.2n. +e.5.2m. +0.14.2d. +0.17.2c. +0.1B.1Y. +0.1b.29. +0.1M.1X. +0.1m.27. +0.1n.26. +0.1O.1W. +0.1o.25. +0.1P.1V. +0.1p.24. +0.1q.23. +0.1r.22. +0.1s.21. +0.1t.20. +0.1v.1+. +0.1w.1Z. +2.3.2o. +3.1u.1/. +5.1a.2a. +7.2p.2. +c.28.1k. +e.5.2n. +e.6.2m. +0.15.2d. +0.18.2c. +0.1C.1Y. +0.1c.29. +0.1n.27. +0.1o.26. +0.1P.1W. +0.1p.25. +0.1q.24. 0.1R.1R. +0.1r.23. +0.1s.22. +0.1t.21. +0.1v.1/. +0.1w.1+. +0.1x.1Z. +2.4.2o. +3.1l.28. +3.1u.20. +4.1N.1X. +5.1b.2a. +5.c.2h. +7.2p.3. +e.6.2n. +e.7.2m. +f.1Q.1V. +0.16.2d. +0.19.2c. +0.1D.1Y. +0.1O.1X. +0.1o.27. +0.1p.26. +0.1q.25. 0.1R.1S. -0.1R.1T. -0.1R.1U. -0.1R.1V. -d.1R.1W. -d.1R.1X. -0.1R.1Y. -0.1R.1Z. -d.1R.1+. -d.1R.1/. -d.1R.20. -0.1R.21. -d.1R.22. -0.1R.23. -0.1R.24. -d.1R.25. -b.26.1R. -d.1R.27. -4.1R.28. -0.1R.2a. -0.1R.2b. -d.2e.1R. -d.1R.2l. -6.2m.1R. -4.2n.1R. -d.1R.2p. -2.2q.1R. -2.2r.1R. -7.2s.1R. -7.2t.1R. -2.2u.1R. -d.1R.2v. -0.1R.2w. -e.2x.1R. -9.2y.1R. -0.1R.2z. -d.1R.2A. -9.2B.1R. -0.1R.2C. -d.1R.2D. -d.1R.2E. -d.1R.2F. -d.1R.2G. -9.2H.1R. -d.1R.2I. -d.1R.2J. -d.1R.2K. -c.2M.1R. -9.2N.1R. -2.2O.1R. -9.2Q.1R. -7.2S.1R. -d.1R.2T. -1.2U.1R. -9.2V.1R. -1.2W.1R. -d.1R.2X. -b.2+.1R. -d.1R.30. -d.1R.35. -b.36.1R. -0.1R.37. -d.1R.38. -5.39.1R. -d.1R.3d. -d.1R.3h. -d.1R.3i. -d.1R.3m. -d.1R.3p. -a.3v.1R. -a.3y.1R. -2.3z.1R. -5.3A.1R. -2.3B.1R. -0.1R.3C. -a.3F.1R. -5.3G.1R. -d.1R.3M. -d.1R.3Q. -5.40.1R. -d.1R.47. -0.1R.4d. -d.1R.4O. -d.1R.4Q. -5.4T.1R. -0.1R.4V. -d.1R.51. -0.1R.52. -a.5a.1R. -2.5b.1R. -5.5d.1R. -d.1R.5B. -d.1R.5C. -5.5F.1R. -d.1R.5H. -d.1R.5I. -d.1R.5K. -7.5M.1R. -0.1R.5O. -0.1R.5R. -d.1R.5U. -d.1R.5W. -1.5Y.1R. -d.1R.5Z. -d.1R.5/. -d.1R.60. -2.62.1R. -2.66.1R. -a.6b.1R. -c.6e.1R. -0.1R.6F. -0.1R.6M. -d.1R.6+. -d.1R.71. -5.79.1R. -d.1R.7D. -d.1R.2p. -d.1R.2p. +0.1r.24. +0.1s.23. +0.1t.22. +0.1v.20. +0.1w.1/. +0.1x.1+. +0.1y.1Z. +2.5.2o. +3.1u.21. +5.1c.2a. +5.c.2i. +c.28.1m. +e.4.2p. +e.7.2n. +e.8.2m. +f.1Q.1W. +0.17.2d. +0.1a.2c. +0.1d.29. +0.1P.1X. +0.1p.27. +0.1q.26. +0.1r.25. 0.1S.1S. -0.1S.1T. -0.1S.1U. +0.1s.24. +0.1t.23. +0.1v.21. +0.1w.20. +0.1x.1/. +0.1y.1+. +0.1z.1Z. +2.6.2o. +3.1u.22. +5.c.2j. +7.2p.5. +c.28.1n. +e.8.2n. +e.9.2m. +0.18.2d. +0.1A.1Z. +0.1b.2c. +0.1E.1Y. +0.1Q.1X. +0.1q.27. +0.1r.26. +0.1s.25. +0.1t.24. +0.1v.22. +0.1w.21. +0.1x.20. +0.1y.1/. +0.1z.1+. +2.7.2o. +3.1u.23. +5.1d.2a. +5.c.2k. +7.2p.6. +c.28.1o. +e.9.2n. +e.a.2m. +0.19.2d. +0.1A.1+. +0.1B.1Z. +0.1c.2c. +0.1F.1Y. +0.1R.1V. +0.1r.27. +0.1s.26. +0.1t.25. +0.1v.23. +0.1w.22. +0.1x.21. +0.1y.20. +0.1z.1/. +2.8.2o. +3.1u.24. +5.c.2l. +c.28.1p. +e.7.2p. +e.a.2n. +e.b.2m. +0.1A.1/. +0.1a.2d. +0.1C.1Z. +0.1e.29. +0.1G.1Y. +0.1R.1W. 0.1S.1V. +0.1s.27. +0.1t.26. +0.1v.24. +0.1w.23. +0.1x.22. +0.1y.21. +0.1z.20. +2.9.2o. +3.1u.25. +4.1B.1+. +7.2p.8. +c.28.1q. +e.b.2n. +0.1A.20. +0.1B.1/. +0.1b.2d. +0.1C.1+. +0.1D.1Z. +0.1d.2c. +0.1e.2a. +0.1f.29. +0.1H.1Y. 0.1S.1W. +0.1t.27. +0.1v.25. +0.1w.24. +0.1x.23. +0.1y.22. +0.1z.21. +2.a.2o. +3.1u.26. +c.28.1r. +e.9.2p. +i.c.2n. +0.1A.21. +0.1B.20. +0.1C.1/. +0.1c.2d. +0.1D.1+. +0.1I.1Y. +0.1R.1X. +0.1v.26. +0.1w.25. +0.1x.24. +0.1y.23. +0.1z.22. +0.29.1g. +2.b.2o. +3.1u.27. +5.1f.2a. +7.2p.a. +c.28.1s. +0.1A.22. +0.1B.21. +0.1C.20. +0.1D.1/. +0.1E.1Z. +0.1J.1Y. 0.1S.1X. -0.1S.1Y. -0.1S.1Z. -0.1S.1+. -0.1S.1/. -0.1S.20. -0.1S.21. -0.1S.22. -0.1S.23. -0.1S.24. -0.1S.25. -b.26.1S. -3.1S.27. -0.1S.28. -0.1S.2a. -0.1S.2b. -0.1S.2l. -6.2m.1S. -4.2n.1S. -3.1S.2p. -2.2q.1S. -2.2r.1S. -7.2s.1S. -7.2t.1S. -2.2u.1S. -3.1S.2v. -3.1S.2w. -e.2x.1S. -9.2y.1S. -0.1S.2z. -3.1S.2A. -9.2B.1S. -0.1S.2C. -3.1S.2D. -3.1S.2E. -3.1S.2F. -0.1S.2G. -9.2H.1S. -3.1S.2I. -3.1S.2J. -0.1S.2K. -c.2M.1S. -9.2N.1S. -2.2O.1S. -9.2Q.1S. -7.2S.1S. -3.1S.2T. -1.2U.1S. -9.2V.1S. -1.2W.1S. -0.1S.2X. -d.2+.1S. -0.1S.30. -0.1S.35. -b.36.1S. -0.1S.37. -3.1S.38. -5.39.1S. -0.1S.3d. -3.1S.3h. -3.1S.3i. -0.1S.3m. -0.1S.3p. -a.3v.1S. -a.3y.1S. -2.3z.1S. -5.3A.1S. -2.3B.1S. -0.1S.3C. -a.3F.1S. -5.3G.1S. -0.1S.3M. -0.1S.3Q. -5.40.1S. -0.1S.47. -0.1S.4d. -3.1S.4O. -3.1S.4Q. -5.4T.1S. -3.1S.4V. -0.1S.51. -0.1S.52. -a.5a.1S. -2.5b.1S. -5.5d.1S. -0.1S.5B. -0.1S.5C. -5.5F.1S. -3.1S.5H. -0.1S.5I. -f.1S.5K. -7.5M.1S. -0.1S.5O. -3.1S.5R. -0.1S.5U. -0.1S.5W. -1.5Y.1S. -0.1S.5Z. -3.1S.5/. -0.1S.60. -2.62.1S. -2.66.1S. -a.6b.1S. -c.6e.1S. -0.1S.6F. -0.1S.6M. -0.1S.6+. -3.1S.71. -5.79.1S. -0.1S.7D. -3.1S.2p. -3.1S.2p. -d.1T.1T. -0.1T.1U. -0.1V.1T. -0.1T.1W. -0.1T.1X. -d.1T.1Y. -0.1T.1Z. -d.1T.1+. -0.1T.1/. -d.1T.20. -d.1T.21. -0.1T.22. -0.1T.23. -0.1T.24. -0.1T.25. -b.26.1T. -d.1T.27. -d.1T.28. -4.1T.29. -0.1T.2a. -0.1T.2b. -4.1T.2c. -4.1T.2d. -4.2e.1T. -4.1T.2f. -4.1T.2i. -4.1T.2j. -0.1T.2l. -6.2m.1T. -4.2n.1T. -1.1T.2o. -0.1T.2p. -2.2q.1T. -2.2r.1T. -7.2s.1T. -7.2t.1T. -2.2u.1T. -d.1T.2v. -d.1T.2w. -e.2x.1T. -9.2y.1T. -0.1T.2z. -0.1T.2A. -9.2B.1T. -d.1T.2C. -0.1T.2D. -0.1T.2E. -0.1T.2F. -0.1T.2G. -9.2H.1T. -0.1T.2I. -d.1T.2J. -0.1T.2K. -4.1T.2L. -c.2M.1T. -9.2N.1T. -2.2O.1T. -4.1T.2P. -9.2Q.1T. -d.1T.2R. -7.2S.1T. -0.1T.2T. -1.2U.1T. -9.2V.1T. -1.2W.1T. -0.1T.2X. -d.1T.2Y. -8.1T.2Z. -b.2+.1T. -8.1T.2/. -d.1T.30. -d.1T.31. -d.1T.32. -8.1T.33. -8.1T.34. -d.1T.35. -b.36.1T. -d.1T.37. -d.1T.38. -5.39.1T. -4.1T.3a. -4.1T.3c. -0.1T.3d. -1.1T.3e. -4.1T.3f. -8.1T.3g. -d.1T.3h. -0.1T.3i. -1.1T.3j. -8.1T.3k. -4.1T.3l. -0.1T.3m. -4.1T.3n. -8.1T.3o. -0.1T.3p. -8.1T.3q. -4.1T.3r. -4.1T.3s. -8.1T.3t. -d.1T.3u. -a.3v.1T. -4.1T.3w. -4.1T.3x. -a.3y.1T. -2.3z.1T. -5.3A.1T. -2.3B.1T. -0.1T.3C. -4.1T.3D. -4.1T.3E. -a.3F.1T. -5.3G.1T. -4.1T.3H. -4.1T.3I. -4.1T.3J. -4.1T.3K. -4.1T.3L. -0.1T.3M. -4.1T.3N. -4.1T.3O. -4.1T.3P. -0.1T.3Q. -4.1T.3R. -4.1T.3S. -1.1T.3T. -4.1T.3U. -4.1T.3V. -4.1T.3W. -4.1T.3X. -4.1T.3Y. -4.1T.3Z. -1.1T.3+. -1.1T.3/. -5.40.1T. -4.1T.41. -4.1T.42. -4.1T.43. -4.1T.44. -4.1T.45. -0.1T.47. -4.1T.48. -4.1T.49. -1.1T.4a. -4.1T.4b. -4.1T.4c. -0.1T.4d. -4.1T.4e. -4.1T.4f. -4.1T.4g. -4.1T.4h. -4.1T.4i. -4.1T.4j. -8.1T.4k. -4.1T.4l. -4.1T.4m. -4.1T.4n. -4.1T.4o. -4.1T.4p. -4.1T.4q. -4.1T.4r. -4.1T.4s. -4.1T.4t. -4.1T.4u. -4.1T.4v. -4.1T.4w. -4.1T.4x. -4.1T.4y. -4.1T.4z. -4.1T.4A. -4.1T.4B. -4.1T.4C. -4.1T.4D. -4.1T.4E. -1.1T.4F. -d.1T.4G. -d.1T.4H. -1.1T.4I. -d.1T.4J. -8.1T.4K. -d.1T.4L. -4.1T.4M. -4.1T.4N. -0.1T.4O. -4.1T.4P. -0.1T.4Q. -1.1T.4R. -4.1T.4S. -4.1T.4T. -4.1T.4U. -0.1T.4V. -4.1T.4W. -d.1T.4Y. -1.1T.4Z. -4.1T.4+. -4.1T.4/. -1.1T.50. -d.1T.51. -0.1T.52. -8.1T.53. -d.1T.54. -8.1T.55. -8.1T.56. -1.1T.57. -d.1T.58. -8.1T.59. -a.5a.1T. -2.5b.1T. -4.1T.5c. -5.5d.1T. -4.1T.5e. -8.1T.5f. -4.1T.5g. -4.1T.5h. -4.1T.5l. -4.1T.5m. -4.1T.5n. -4.1T.5o. -4.1T.5p. -4.1T.5q. -4.1T.5r. -1.1T.5s. -4.1T.5t. -4.1T.5u. -4.1T.5v. -1.1T.5w. -1.1T.5y. -1.1T.5z. -4.1T.5A. -0.1T.5B. -0.1T.5C. -1.1T.5D. -4.1T.5E. -5.5F.1T. -8.1T.5G. -d.1T.5H. -0.1T.5I. -0.1T.5K. -8.1T.5L. -8.1T.5M. -d.1T.5N. -d.1T.5O. -8.1T.5P. -4.1T.5Q. -0.1T.5R. -4.1T.5S. -4.1T.5T. -0.1T.5U. -8.1T.5V. -0.1T.5W. -8.1T.5X. -4.1T.5Y. -0.1T.5Z. -d.1T.5/. -0.1T.60. -4.1T.61. -2.62.1T. -4.1T.64. -4.1T.65. -2.66.1T. -4.1T.67. -4.1T.68. -4.1T.69. -4.1T.6a. -a.6b.1T. -4.1T.6c. -4.1T.6d. -4.1T.6e. -4.1T.6f. -4.1T.6g. -4.1T.6h. -4.1T.6i. -4.1T.6j. -4.1T.6k. -4.1T.6l. -4.1T.6m. -4.1T.6n. -4.1T.6o. -4.1T.6p. -4.1T.6q. -4.1T.6r. -4.1T.6s. -4.1T.6t. -4.1T.6u. -4.1T.6v. -4.1T.6w. -4.1T.6x. -4.1T.6y. -4.1T.6z. -4.1T.6A. -4.1T.6B. -4.1T.6C. -4.1T.6D. -4.1T.6E. -0.1T.6F. -4.1T.6G. -4.1T.6H. -4.1T.6I. -4.1T.6J. -4.1T.6K. -4.1T.6L. -d.1T.6M. -4.1T.6N. -4.1T.6O. -4.1T.6P. -4.1T.6Q. -4.1T.6R. -4.1T.6S. -4.1T.6T. -4.1T.6U. -4.1T.6V. -4.1T.6W. -1.1T.6X. -4.1T.6Y. -n.1T.6Z. -d.1T.6+. -1.1T.6/. -1.1T.70. -d.1T.71. -4.1T.72. -4.1T.73. -1.1T.74. -4.1T.75. -1.1T.76. -1.1T.77. -4.1T.78. -5.79.1T. -1.1T.7a. -4.1T.7b. -4.1T.7c. -4.1T.7d. -4.1T.7e. -4.1T.7f. -4.1T.7g. -4.1T.7h. -4.1T.7i. -4.1T.7j. -4.1T.7k. -4.1T.7l. -1.1T.7m. -4.1T.7o. -4.1T.7p. -1.1T.7r. -1.1T.7s. -8.1T.7t. -4.1T.7u. -4.1T.7v. -4.1T.7w. -4.1T.7x. -4.1T.7y. -1.1T.7z. -4.1T.7A. -8.1T.7B. -4.1T.7C. -0.1T.7D. -4.1T.7E. -4.1T.7F. -d.1T.7G. -4.1T.7H. -4.1T.7I. -4.1T.7J. -1.1T.7K. -1.1T.7L. -1.1T.7M. -1.1T.7N. -1.1T.7O. -1.1T.7P. -4.1T.7Q. -8.1T.7R. -4.1T.7S. -4.1T.7T. -1.1T.7U. -d.1T.7V. -4.1T.7W. -4.1T.7X. -4.1T.7Y. -4.1T.7Z. -1.1T.7+. -1.1T.7/. -1.1T.80. -1.1T.81. -1.1T.82. -4.1T.83. -1.1T.84. -1.1T.85. -1.1T.87. -1.1T.88. -8.1T.89. -4.1T.8a. -8.1T.8b. -4.1T.8c. -8.1T.8d. -4.1T.8e. -4.1T.8f. -4.1T.8g. -4.1T.8h. -4.1T.8i. -4.1T.8j. -4.1T.8k. -4.1T.8l. -4.1T.8m. -4.1T.8n. -4.1T.8o. -4.1T.8p. -4.1T.8q. -4.1T.8r. -4.1T.8s. -4.1T.8t. -4.1T.8u. -4.1T.8v. -4.1T.8w. -4.1T.8x. -4.1T.8y. -4.1T.8z. -4.1T.8A. -4.1T.8B. -4.1T.8C. -4.1T.8D. -4.1T.8E. -4.1T.8F. -4.1T.8G. -4.1T.8H. -4.1T.8I. -4.1T.8J. -4.1T.8K. -4.1T.8L. -4.1T.8M. -4.1T.8N. -4.1T.8O. -4.1T.8P. -0.1T.2p. -0.1T.2p. -d.1T.3b. -1.1T.46. -1.1T.4X. -1.1T.5x. -1.1T.5J. -1.1T.7n. -1.1T.7q. -d.1U.1U. -0.1V.1U. -0.1U.1W. -0.1U.1X. -0.1U.1Y. -0.1U.1Z. -0.1U.1+. -0.1U.1/. -0.1U.20. -0.1U.21. -0.1U.22. -0.1U.23. -0.1U.24. -0.1U.25. -b.26.1U. -0.1U.27. -4.1U.28. -0.1U.2a. -0.1U.2b. -d.2e.1U. -0.1U.2l. -6.2m.1U. -4.2n.1U. -d.1U.2p. -2.2q.1U. -2.2r.1U. -7.2s.1U. -7.2t.1U. -2.2u.1U. -0.1U.2v. -d.1U.2w. -e.2x.1U. -9.2y.1U. -0.1U.2z. -d.1U.2A. -9.2B.1U. -0.1U.2C. -0.1U.2D. -0.1U.2E. -d.1U.2F. -0.1U.2G. -9.2H.1U. -0.1U.2I. -0.1U.2J. -0.1U.2K. -c.2M.1U. -9.2N.1U. -2.2O.1U. -9.2Q.1U. -7.2S.1U. -0.1U.2T. -1.2U.1U. -9.2V.1U. -1.2W.1U. -0.1U.2X. -d.2+.1U. -0.1U.30. -0.1U.35. -b.36.1U. -0.1U.37. -0.1U.38. -5.39.1U. -0.1U.3d. -d.1U.3h. -d.1U.3i. -0.1U.3m. -0.1U.3p. -a.3v.1U. -a.3y.1U. -2.3z.1U. -5.3A.1U. -2.3B.1U. -0.1U.3C. -a.3F.1U. -5.3G.1U. -0.1U.3M. -0.1U.3Q. -5.40.1U. -0.1U.47. -0.1U.4d. -0.1U.4O. -d.1U.4Q. -5.4T.1U. -0.1U.4V. -0.1U.51. -0.1U.52. -a.5a.1U. -2.5b.1U. -5.5d.1U. -0.1U.5B. -0.1U.5C. -5.5F.1U. -d.1U.5H. -0.1U.5I. -d.1U.5K. -7.5M.1U. -0.1U.5O. -d.1U.5R. -0.1U.5U. -0.1U.5W. -1.5Y.1U. -0.1U.5Z. -0.1U.5/. -0.1U.60. -2.62.1U. -2.66.1U. -a.6b.1U. -c.6e.1U. -0.1U.6F. -0.1U.6M. -0.1U.6+. -d.1U.71. -5.79.1U. -d.1U.7D. -d.1U.2p. -d.1U.2p. -0.1V.1V. +0.1v.27. +0.1w.26. +0.1x.25. +0.1y.24. +0.1z.23. +5.2a.1g. +7.2p.b. +c.28.1t. +f.1V.1V. +0.1A.23. +0.1B.22. +0.1C.21. +0.1D.20. +0.1d.2d. +0.1E.1+. +0.1e.2c. +0.1F.1Z. 0.1V.1W. -0.1V.1X. -0.1V.1Y. -0.1V.1Z. -0.1V.1+. -0.1V.1/. -0.1V.20. -0.1V.21. -0.1V.22. -0.1V.23. -0.1V.24. -0.1V.25. -b.26.1V. -0.1V.27. -0.1V.28. -0.1V.2a. -0.1V.2b. -d.2e.1V. -0.1V.2l. -6.2m.1V. -4.2n.1V. -3.1V.2p. -2.2q.1V. -2.2r.1V. -7.2s.1V. -7.2t.1V. -2.2u.1V. -0.1V.2v. -0.1V.2w. -e.2x.1V. -9.2y.1V. -0.1V.2z. -0.1V.2A. -9.2B.1V. -0.1V.2C. -0.1V.2D. -0.1V.2E. -0.1V.2F. -0.1V.2G. -9.2H.1V. -0.1V.2I. -0.1V.2J. -0.1V.2K. -c.2M.1V. -9.2N.1V. -2.2O.1V. -9.2Q.1V. -7.2S.1V. -0.1V.2T. -1.2U.1V. -9.2V.1V. -1.2W.1V. -0.1V.2X. -b.2+.1V. -0.1V.30. -0.1V.35. -b.36.1V. -0.1V.37. -0.1V.38. -5.39.1V. -0.1V.3d. -3.1V.3h. -3.1V.3i. -0.1V.3m. -0.1V.3p. -a.3v.1V. -a.3y.1V. -2.3z.1V. -5.3A.1V. -2.3B.1V. -0.1V.3C. -a.3F.1V. -5.3G.1V. -0.1V.3M. -0.1V.3Q. -5.40.1V. -0.1V.47. -0.1V.4d. -0.1V.4O. -0.1V.4Q. -5.4T.1V. -0.1V.4V. -0.1V.51. -0.1V.52. -a.5a.1V. -2.5b.1V. -5.5d.1V. -0.1V.5B. -0.1V.5C. -5.5F.1V. -0.1V.5H. -0.1V.5I. -f.1V.5K. -7.5M.1V. -0.1V.5O. -3.1V.5R. -0.1V.5U. -0.1V.5W. -1.5Y.1V. -0.1V.5Z. -0.1V.5/. -3.1V.60. -2.62.1V. -2.66.1V. -a.6b.1V. -c.6e.1V. -0.1V.6F. -0.1V.6M. -0.1V.6+. -3.1V.71. -5.79.1V. -0.1V.7D. -3.1V.2p. -3.1V.2p. -0.1W.1W. +0.1w.27. +0.1x.26. +0.1y.25. +0.1z.24. +3.1u.28. +7.2p.c. +0.1A.24. +0.1C.22. +0.1D.21. +0.1E.1/. +0.1F.1+. +0.1f.2c. +0.1G.1Z. +0.1h.29. +0.1x.27. +0.1y.26. +0.1z.25. +0.d.2n. +4.1B.23. +c.28.1v. +f.1W.1W. +0.1A.25. +0.1B.24. +0.1C.23. +0.1D.22. +0.1E.20. +0.1F.1/. +0.1G.1+. +0.1H.1Z. +0.1K.1Y. +0.1X.1V. +0.1y.27. +0.1z.26. +0.2c.1g. +0.e.2n. +4.1i.29. +5.1h.2a. +5.k.2h. +c.28.1w. +0.1A.26. +0.1B.25. +0.1C.24. +0.1D.23. +0.1E.21. +0.1e.2d. +0.1F.20. +0.1G.1/. +0.1H.1+. +0.1I.1Z. +0.1i.2a. +0.1j.29. +0.1L.1Y. 0.1X.1W. -0.1Y.1W. -0.1Z.1W. -0.1+.1W. -0.20.1W. -0.21.1W. -0.22.1W. -d.23.1W. -0.24.1W. -0.25.1W. -b.26.1W. -0.27.1W. -4.28.1W. -0.2a.1W. -3.2b.1W. -0.2l.1W. -6.2m.1W. -4.2n.1W. -0.2p.1W. -2.2q.1W. -2.2r.1W. -7.2s.1W. -7.2t.1W. -2.2u.1W. -0.2v.1W. -0.2w.1W. -e.2x.1W. -9.2y.1W. -0.2z.1W. -0.2A.1W. -9.2B.1W. -0.2C.1W. -0.2D.1W. -0.2E.1W. -0.2F.1W. -0.2G.1W. -9.2H.1W. -0.2I.1W. -d.2J.1W. -0.2K.1W. -c.2M.1W. -9.2N.1W. -2.2O.1W. -9.2Q.1W. -7.2S.1W. -0.2T.1W. -1.2U.1W. -9.2V.1W. -1.2W.1W. -0.2X.1W. -b.2+.1W. -d.30.1W. -0.35.1W. -3.36.1W. -0.1W.37. -0.38.1W. -5.39.1W. -5.3d.1W. -0.3h.1W. -5.3i.1W. -0.1W.3m. -5.3p.1W. -a.3v.1W. -a.3y.1W. -2.3z.1W. -5.3A.1W. -2.3B.1W. -5.3C.1W. -a.3F.1W. -5.3G.1W. -0.1W.3M. -0.1W.3Q. -5.40.1W. -0.1W.47. -5.4d.1W. -0.1W.4O. -0.4Q.1W. -7.4T.1W. -3.1W.4V. -5.51.1W. -0.52.1W. -a.5a.1W. -2.5b.1W. -9.5d.1W. -5.5B.1W. -5.5C.1W. -5.5F.1W. -0.5H.1W. -0.5I.1W. -f.1W.5K. -7.5M.1W. -5.5O.1W. -0.1W.5U. -3.5W.1W. -1.5Y.1W. -5.5Z.1W. -5.5/.1W. -0.60.1W. -2.62.1W. -2.66.1W. -a.6b.1W. -c.6e.1W. -0.1W.6F. -0.6M.1W. -0.6+.1W. -0.71.1W. -5.79.1W. -0.1W.7D. -0.2p.1W. -0.2p.1W. +0.1z.27. +0.f.2n. +5.k.2i. +c.28.1x. +0.1A.27. +0.1B.26. +0.1C.25. +0.1D.24. +0.1E.22. +0.1F.21. +0.1f.2d. +0.1G.20. +0.1H.1/. +0.1I.1+. +0.1J.1Z. +0.1j.2a. +0.1M.1Y. +0.g.2n. +5.1h.2b. +5.k.2j. +7.2p.d. +c.28.1y. +0.1B.27. +0.1C.26. +0.1D.25. +0.1E.23. +0.1F.22. +0.1G.21. +0.1H.20. +0.1h.2c. +0.1I.1/. +0.1J.1+. +0.1N.1Y. 0.1X.1X. -0.1Y.1X. -0.1Z.1X. -0.1+.1X. -0.1/.1X. -0.20.1X. -0.21.1X. -0.22.1X. +0.h.2n. +4.2d.1g. +5.k.2k. +7.2p.e. +c.28.1z. +0.1C.27. +0.1D.26. +0.1E.24. +0.1F.23. +0.1G.22. +0.1H.21. +0.1I.20. +0.1i.2c. +0.1J.1/. +0.1k.29. +0.1O.1Y. +0.i.2n. +5.k.2l. +7.2p.f. +c.28.1A. +0.1D.27. +0.1E.25. +0.1F.24. +0.1G.23. +0.1H.22. +0.1I.21. +0.1J.20. +0.1j.2c. +0.1K.1Z. +0.1k.2a. +0.1P.1Y. +0.j.2n. +3.1l.29. +7.2p.g. +c.28.1B. +0.1E.26. +0.1F.25. +0.1G.24. +0.1H.23. +0.1h.2d. +0.1I.22. +0.1J.21. +0.1K.1+. +0.1L.1Z. +0.1m.29. +0.1Q.1Y. +0.k.2n. +3.1l.2a. +7.2p.h. +c.28.1C. +0.1E.27. +0.1F.26. +0.1G.25. +0.1H.24. +0.1I.23. +0.1J.22. +0.1K.1/. +0.1L.1+. +0.1M.1Z. +0.1n.29. +0.l.2n. +4.1i.2d. +5.1h.2e. +5.1m.2a. +7.2p.i. +c.28.1D. +0.1F.27. +0.1G.26. +0.1H.25. +0.1I.24. +0.1J.23. +0.1j.2d. +0.1K.20. +0.1k.2c. +0.1L.1/. +0.1M.1+. +0.1n.2a. +0.1o.29. +0.m.2n. +4.1N.1Z. +7.2p.j. +j.n.2m. +0.1G.27. +0.1H.26. +0.1I.25. +0.1J.24. +0.1K.21. +0.1L.20. +0.1M.1/. +0.1N.1+. +0.1O.1Z. +0.1o.2a. +0.1p.29. +3.1l.2c. +3.n.2n. +7.2p.k. +7.r.2h. +c.28.1E. +0.1H.27. +0.1I.26. +0.1J.25. +0.1K.22. +0.1L.21. +0.1M.20. +0.1m.2c. +0.1O.1+. +0.1P.1Z. +0.1q.29. +0.o.2n. +4.1N.1/. +5.1h.2f. +5.1p.2a. +7.2p.l. +7.r.2i. +7.s.2h. +c.28.1F. +f.1R.1Y. +0.1I.27. +0.1J.26. +0.1K.23. +0.1k.2d. +0.1L.22. +0.1M.21. +0.1n.2c. +0.1O.1/. +0.1P.1+. +0.1Q.1Z. +0.1r.29. +0.1S.1Y. +0.p.2n. +4.1N.20. +4.r.2j. +4.s.2i. +5.1q.2a. +7.2p.m. +c.28.1G. +0.1J.27. +0.1K.24. +0.1L.23. +0.1M.22. +0.1O.20. +0.1o.2c. +0.1P.1/. +0.1Q.1+. +0.1s.29. +0.q.2n. +3.1l.2d. +4.1N.21. +4.s.2j. +5.1r.2a. +7.2p.n. +7.r.2k. +7.u.2h. +c.28.1H. +0.1K.25. +0.1L.24. +0.1M.23. +0.1m.2d. +0.1O.21. +0.1P.20. +0.1p.2c. +0.1Q.1/. +4.1N.22. +4.1t.29. +4.r.2l. +5.1s.2a. +7.2p.o. +7.s.2k. +7.u.2i. +c.28.1I. +0.1K.26. +0.1L.25. +0.1M.24. +0.1n.2d. +0.1O.22. +0.1P.21. +0.1Q.20. +0.1q.2c. +0.1t.2a. +0.1V.1Y. +3.1u.29. +4.1N.23. +4.s.2l. +7.2p.p. +7.u.2j. +c.28.1J. +0.1K.27. +0.1L.26. +0.1M.25. +0.1O.23. +0.1o.2d. +0.1P.22. +0.1Q.21. +0.1r.2c. +0.1v.29. +0.1W.1Y. +0.r.2n. +3.1u.2a. +4.1N.24. +7.2p.q. +7.u.2k. +f.1R.1Z. +0.1L.27. +0.1M.26. +0.1N.25. +0.1O.24. +0.1P.23. +0.1p.2d. +0.1R.1+. +0.1S.1Z. +0.1s.2c. +0.1w.29. +0.s.2n. +5.1t.2b. +5.1v.2a. +7.u.2l. +f.1Q.22. +0.1M.27. +0.1O.25. +0.1P.24. +0.1Q.23. +0.1q.2d. +0.1R.1/. +0.1S.1+. +0.1t.2c. +0.1X.1Y. +0.1x.29. +0.t.2n. +4.1N.26. +5.1w.2a. +c.28.1K. +0.1O.26. +0.1P.25. +0.1Q.24. +0.1r.2d. +0.1S.1/. +0.1y.29. +0.u.2n. +3.1u.2c. +4.1N.27. +5.1x.2a. +7.2p.r. +c.28.1L. +f.1R.20. +0.1O.27. +0.1P.26. +0.1Q.25. +0.1S.20. +0.1s.2d. +0.1V.1Z. +0.1v.2c. +0.1z.29. +0.v.2n. +5.1y.2a. +7.2p.s. +c.28.1M. +f.1R.21. +0.1A.29. +0.1P.27. +0.1Q.26. +0.1S.21. +0.1W.1Z. +0.1w.2c. +0.w.2n. +4.1t.2d. +5.1z.2a. +7.2p.t. +c.28.1N. +f.1R.22. +f.1V.1+. +0.1B.29. +0.1Q.27. +0.1R.23. +0.1S.22. +0.1V.1/. +0.1W.1+. +0.1x.2c. +0.x.2n. +3.1u.2d. +5.1A.2a. +5.1t.2e. +7.2p.u. +c.28.1O. +0.1C.29. +0.1S.23. +0.1v.2d. +0.1W.1/. +0.1X.1Z. +0.1y.2c. +0.y.2n. +5.1B.2a. +7.2p.v. +c.28.1P. +f.1R.24. +f.1V.20. +0.1D.29. +0.1R.25. +0.1S.24. +0.1V.21. +0.1W.20. +0.1w.2d. +0.1X.1+. +0.1z.2c. +0.2n.z. +2.A.2k. +5.1C.2a. +7.2p.w. +c.28.1Q. +0.1A.2c. +0.1R.26. +0.1S.25. +0.1W.21. +0.1X.1/. +0.1x.2d. +2.A.2l. +5.1D.2a. +5.1t.2f. +7.2p.x. +f.1V.22. +0.1B.2c. +0.1E.29. +0.1S.26. +0.1W.22. +0.1X.20. +0.1y.2d. +7.2p.y. +f.1R.27. +f.1V.23. +0.1C.2c. +0.1F.29. +0.1S.27. +0.1V.24. +0.1W.23. +0.1X.21. +0.1z.2d. +0.A.2n. +5.1E.2a. +7.2p.z. +0.1A.2d. +0.1D.2c. +0.1G.29. +0.1V.25. +0.1W.24. +0.1X.22. +0.B.2n. +5.1F.2a. +c.28.1R. +0.1B.2d. +0.1H.29. +0.1V.26. +0.1W.25. 0.1X.23. +0.C.2n. +5.1G.2a. +c.28.1S. +0.1C.2d. +0.1E.2c. +0.1I.29. +0.1V.27. +0.1W.26. 0.1X.24. +0.D.2n. +5.1H.2a. +7.2p.A. +0.1D.2d. +0.1F.2c. +0.1J.29. +0.1W.27. 0.1X.25. -b.26.1X. -0.27.1X. -0.1X.28. -0.1X.2a. -0.1X.2b. -d.1X.2l. -6.2m.1X. -4.2n.1X. -0.1X.2p. -2.2q.1X. -2.2r.1X. -7.2s.1X. -7.2t.1X. -2.2u.1X. -d.1X.2v. -0.1X.2w. -e.2x.1X. -9.2y.1X. -0.1X.2z. -d.1X.2A. -9.2B.1X. -0.1X.2C. -0.1X.2D. -d.1X.2E. -d.1X.2F. -0.1X.2G. -9.2H.1X. -d.1X.2I. -d.1X.2J. -0.1X.2K. -c.2M.1X. -9.2N.1X. -2.2O.1X. -9.2Q.1X. -7.2S.1X. -0.1X.2T. -1.2U.1X. -9.2V.1X. -1.2W.1X. -0.1X.2X. -d.2+.1X. -0.1X.30. -0.1X.35. -b.36.1X. -0.1X.37. -0.1X.38. -5.39.1X. -0.1X.3d. -d.1X.3h. -d.1X.3i. -d.1X.3m. -0.1X.3p. -a.3v.1X. -a.3y.1X. -2.3z.1X. -5.3A.1X. -2.3B.1X. -d.1X.3C. -a.3F.1X. -5.3G.1X. -d.1X.3M. -0.1X.3Q. -5.40.1X. -0.1X.47. -0.1X.4d. -0.1X.4O. -d.1X.4Q. -5.4T.1X. -d.1X.4V. -d.1X.51. -0.1X.52. -a.5a.1X. -2.5b.1X. -5.5d.1X. -0.1X.5B. -0.1X.5C. -5.5F.1X. -d.1X.5H. -0.1X.5I. -f.1X.5K. -7.5M.1X. -0.1X.5O. -d.1X.5R. -0.1X.5U. -d.1X.5W. -1.5Y.1X. -0.1X.5Z. -d.1X.5/. -0.1X.60. -2.62.1X. -2.66.1X. -a.6b.1X. -c.6e.1X. -0.1X.6F. -0.6M.1X. -0.1X.6+. -d.1X.71. -5.79.1X. -d.1X.7D. -0.1X.2p. -0.1X.2p. +0.E.2n. +5.1I.2a. +7.2p.B. +0.1G.2c. +0.1X.26. 0.1Y.1Y. -0.1Y.1Z. -0.1Y.1+. -0.1Y.1/. -0.1Y.20. -0.1Y.21. -0.1Y.22. -0.1Y.23. -0.1Y.24. -0.1Y.25. -b.26.1Y. -0.1Y.27. -0.1Y.28. -6.1Y.29. -0.1Y.2a. -0.1Y.2b. -6.1Y.2c. -6.1Y.2d. -6.1Y.2f. -d.1Y.2g. -6.1Y.2h. -6.1Y.2i. -6.1Y.2j. -0.1Y.2l. -6.2m.1Y. -4.2n.1Y. -1.1Y.2o. -0.1Y.2p. -2.2q.1Y. -2.2r.1Y. -7.2s.1Y. -7.2t.1Y. -2.2u.1Y. -d.1Y.2v. -0.1Y.2w. -e.2x.1Y. -9.2y.1Y. -0.1Y.2z. -d.1Y.2A. -9.2B.1Y. -0.1Y.2C. -0.1Y.2D. -0.1Y.2E. -0.1Y.2F. -0.1Y.2G. -9.2H.1Y. -0.1Y.2I. -d.1Y.2J. -0.1Y.2K. -6.1Y.2L. -c.2M.1Y. -9.2N.1Y. -2.2O.1Y. -6.1Y.2P. -9.2Q.1Y. -8.1Y.2R. -7.2S.1Y. -0.1Y.2T. -1.2U.1Y. -9.2V.1Y. -1.2W.1Y. -0.1Y.2X. -8.1Y.2Y. -8.1Y.2Z. -b.2+.1Y. -8.1Y.2/. -0.1Y.30. -8.1Y.31. -8.1Y.32. -8.1Y.33. -8.1Y.34. -0.1Y.35. -3.36.1Y. -0.1Y.37. -0.1Y.38. -5.39.1Y. -6.1Y.3a. -6.1Y.3c. -0.1Y.3d. -1.1Y.3e. -6.1Y.3f. -8.1Y.3g. -d.1Y.3h. -0.1Y.3i. -1.1Y.3j. -8.1Y.3k. -6.1Y.3l. -0.1Y.3m. -8.1Y.3n. -8.1Y.3o. -0.1Y.3p. -8.1Y.3q. -6.1Y.3r. -4.1Y.3s. -8.1Y.3t. -8.1Y.3u. -6.1Y.3v. -6.1Y.3w. -6.1Y.3x. -a.3y.1Y. -2.3z.1Y. -5.3A.1Y. -2.3B.1Y. -0.1Y.3C. -6.1Y.3D. -6.1Y.3E. -a.3F.1Y. -5.3G.1Y. -6.1Y.3H. -6.1Y.3I. -6.1Y.3J. -6.1Y.3K. -6.1Y.3L. -0.1Y.3M. -6.1Y.3N. -d.1Y.3O. -6.1Y.3P. -0.1Y.3Q. -6.1Y.3R. -6.1Y.3S. -1.1Y.3T. -6.1Y.3U. -6.1Y.3V. -6.1Y.3W. -6.1Y.3X. -6.1Y.3Y. -6.1Y.3Z. -1.1Y.3+. -1.1Y.3/. -5.40.1Y. -6.1Y.41. -6.1Y.42. -6.1Y.43. -6.1Y.44. -6.1Y.45. -0.1Y.47. -6.1Y.48. -6.1Y.49. -1.1Y.4a. -6.1Y.4b. -6.1Y.4c. -0.1Y.4d. -6.1Y.4e. -6.1Y.4f. -6.1Y.4g. -6.1Y.4h. -6.1Y.4i. -6.1Y.4j. -8.1Y.4k. -6.1Y.4l. -6.1Y.4m. -6.1Y.4n. -6.1Y.4o. -6.1Y.4p. -6.1Y.4q. -6.1Y.4r. -6.1Y.4s. -6.1Y.4t. -6.1Y.4u. -6.1Y.4v. -6.1Y.4w. -6.1Y.4x. -6.1Y.4y. -6.1Y.4z. -6.1Y.4A. -6.1Y.4B. -6.1Y.4C. -6.1Y.4D. -6.1Y.4E. -1.1Y.4F. -8.1Y.4G. -8.1Y.4H. -1.1Y.4I. -8.1Y.4J. -8.1Y.4K. -8.1Y.4L. -6.1Y.4M. -6.1Y.4N. -0.1Y.4O. -6.1Y.4P. -d.1Y.4Q. -1.1Y.4R. -6.1Y.4S. -4.1Y.4T. -8.1Y.4U. -d.1Y.4V. -6.1Y.4W. -8.1Y.4Y. -1.1Y.4Z. -6.1Y.4+. -6.1Y.4/. -1.1Y.50. -0.1Y.51. -0.1Y.52. -8.1Y.53. -8.1Y.54. -8.1Y.55. -8.1Y.56. -1.1Y.57. -8.1Y.58. -8.1Y.59. -a.5a.1Y. -2.5b.1Y. -6.1Y.5c. -5.5d.1Y. -8.1Y.5e. -8.1Y.5f. -6.1Y.5g. -8.1Y.5h. -6.1Y.5i. -6.1Y.5j. -6.1Y.5k. -6.1Y.5l. -6.1Y.5m. -6.1Y.5n. -6.1Y.5o. -6.1Y.5p. -6.1Y.5q. -6.1Y.5r. -1.1Y.5s. -6.1Y.5t. -d.1Y.5u. -d.1Y.5v. -1.1Y.5w. -1.1Y.5y. -1.1Y.5z. -8.1Y.5A. -0.1Y.5B. -0.1Y.5C. -1.1Y.5D. -8.1Y.5E. -5.5F.1Y. -8.1Y.5G. -0.1Y.5H. -0.1Y.5I. -f.1Y.5K. -8.1Y.5L. -8.1Y.5M. -8.1Y.5N. -0.1Y.5O. -8.1Y.5P. -6.1Y.5Q. -0.1Y.5R. -6.1Y.5S. -d.1Y.5T. -0.1Y.5U. -8.1Y.5V. -0.1Y.5W. -8.1Y.5X. -8.1Y.5Y. -0.1Y.5Z. -h.5+.1Y. -0.1Y.5/. -0.1Y.60. -6.1Y.61. -2.62.1Y. -6.1Y.64. -6.1Y.65. -2.66.1Y. -6.1Y.67. -6.1Y.68. -6.1Y.69. -6.1Y.6a. -6.1Y.6b. -6.1Y.6c. -6.1Y.6d. -6.1Y.6e. -6.1Y.6f. -6.1Y.6g. -6.1Y.6h. -d.1Y.6i. -6.1Y.6j. -6.1Y.6k. -6.1Y.6l. -d.1Y.6m. -6.1Y.6n. -6.1Y.6o. -6.1Y.6p. -6.1Y.6q. -6.1Y.6r. -6.1Y.6s. -6.1Y.6t. -6.1Y.6u. -6.1Y.6v. -6.1Y.6w. -6.1Y.6x. -6.1Y.6y. -6.1Y.6z. -6.1Y.6A. -6.1Y.6B. -6.1Y.6C. -6.1Y.6D. -6.1Y.6E. -0.1Y.6F. -6.1Y.6G. -6.1Y.6H. -6.1Y.6I. -6.1Y.6J. -6.1Y.6K. -8.1Y.6L. -0.1Y.6M. -6.1Y.6N. -6.1Y.6O. -6.1Y.6P. -6.1Y.6Q. -6.1Y.6R. -6.1Y.6S. -6.1Y.6T. -6.1Y.6U. -6.1Y.6V. -6.1Y.6W. -1.1Y.6X. -6.1Y.6Y. -1.1Y.6Z. -0.1Y.6+. -1.1Y.6/. -1.1Y.70. -0.1Y.71. -6.1Y.72. -6.1Y.73. -1.1Y.74. -6.1Y.75. -1.1Y.76. -1.1Y.77. -6.1Y.78. -5.79.1Y. -1.1Y.7a. -6.1Y.7b. -6.1Y.7c. -6.1Y.7d. -6.1Y.7e. -6.1Y.7f. -6.1Y.7g. -6.1Y.7h. -6.1Y.7i. -6.1Y.7j. -6.1Y.7k. -6.1Y.7l. -1.1Y.7m. -6.1Y.7o. -6.1Y.7p. -1.1Y.7r. -1.1Y.7s. -8.1Y.7t. -6.1Y.7u. -6.1Y.7v. -6.1Y.7w. -6.1Y.7x. -6.1Y.7y. -1.1Y.7z. -6.1Y.7A. -8.1Y.7B. -6.1Y.7C. -0.1Y.7D. -6.1Y.7E. -6.1Y.7F. -6.1Y.7G. -6.1Y.7H. -d.1Y.7I. -6.1Y.7J. -1.1Y.7K. -1.1Y.7L. -1.1Y.7M. -1.1Y.7N. -1.1Y.7O. -1.1Y.7P. -6.1Y.7Q. -8.1Y.7R. -6.1Y.7S. -6.1Y.7T. -1.1Y.7U. -8.1Y.7V. -6.1Y.7W. -6.1Y.7X. -6.1Y.7Y. -6.1Y.7Z. -1.1Y.7+. -1.1Y.7/. -1.1Y.80. -1.1Y.81. -1.1Y.82. -6.1Y.83. -1.1Y.84. -1.1Y.85. -h.86.1Y. -1.1Y.87. -1.1Y.88. -8.1Y.89. -6.1Y.8a. -8.1Y.8b. -6.1Y.8c. -8.1Y.8d. -6.1Y.8e. -6.1Y.8f. -6.1Y.8g. -6.1Y.8h. -6.1Y.8i. -6.1Y.8j. -6.1Y.8k. -6.1Y.8l. -6.1Y.8m. -6.1Y.8n. -6.1Y.8o. -6.1Y.8p. -6.1Y.8q. -6.1Y.8r. -6.1Y.8s. -6.1Y.8t. -6.1Y.8u. -6.1Y.8v. -6.1Y.8w. -6.1Y.8x. -6.1Y.8y. -6.1Y.8z. -6.1Y.8A. -6.1Y.8B. -6.1Y.8C. -6.1Y.8D. -6.1Y.8E. -6.1Y.8F. -6.1Y.8G. -4.1Y.8H. -4.1Y.8I. -6.1Y.8J. -6.1Y.8K. -6.1Y.8L. -6.1Y.8M. -6.1Y.8N. -6.1Y.8O. -6.1Y.8P. -0.1Y.2p. -0.1Y.2p. -8.1Y.3b. -1.1Y.46. -1.1Y.4X. -1.1Y.5x. -1.1Y.5J. -1.1Y.7n. -1.1Y.7q. +0.F.2n. +5.1J.2a. +7.2p.C. +c.28.1V. +0.1E.2d. +0.1H.2c. +0.1X.27. +7.2p.D. +c.28.1W. +0.1F.2d. +0.1I.2c. +0.1K.29. +7.2p.E. +0.1G.2d. +0.1J.2c. +0.1L.29. +0.G.2n. +5.1K.2a. +7.2p.F. +c.28.1X. +0.1H.2d. +0.1M.29. +0.H.2n. +5.1L.2a. +0.1I.2d. +0.1Z.1Y. +3.I.2n. +4.1N.29. +5.1M.2a. +0.1+.1Y. +0.1J.2d. +0.1K.2c. +0.1O.29. +3.J.2n. +5.1N.2a. +7.2p.G. +0.1/.1Y. +0.1L.2c. +0.1P.29. +0.K.2n. +5.1O.2a. +7.2p.H. +0.1M.2c. +0.1Q.29. +0.20.1Y. +0.L.2n. +5.1P.2a. +7.2p.I. +0.1K.2d. +0.1N.2c. +0.1Q.2a. +3.M.2n. +7.2p.J. +j.21.1Y. +0.1L.2d. +0.1O.2c. 0.1Z.1Z. -0.1Z.1+. -0.1Z.1/. -0.1Z.20. -0.1Z.21. -0.1Z.22. -0.1Z.23. -0.1Z.24. -0.1Z.25. -b.26.1Z. -0.1Z.27. -4.1Z.28. -0.1Z.2a. -0.1Z.2b. -0.1Z.2l. -6.2m.1Z. -4.2n.1Z. -0.1Z.2p. -2.2q.1Z. -2.2r.1Z. -7.2s.1Z. -7.2t.1Z. -2.2u.1Z. -0.1Z.2v. -0.1Z.2w. -e.2x.1Z. -9.2y.1Z. -0.1Z.2z. -d.1Z.2A. -9.2B.1Z. -0.1Z.2C. -0.1Z.2D. -d.1Z.2E. -0.1Z.2F. -0.1Z.2G. -9.2H.1Z. -0.1Z.2I. -d.1Z.2J. -0.1Z.2K. -c.2M.1Z. -9.2N.1Z. -2.2O.1Z. -9.2Q.1Z. -7.2S.1Z. -0.1Z.2T. -1.2U.1Z. -9.2V.1Z. -1.2W.1Z. -0.1Z.2X. -b.2+.1Z. -0.1Z.30. -0.1Z.35. -b.36.1Z. -0.1Z.37. -0.1Z.38. -5.39.1Z. -0.1Z.3d. -0.1Z.3h. -0.1Z.3i. -0.1Z.3m. -0.1Z.3p. -a.3v.1Z. -a.3y.1Z. -2.3z.1Z. -5.3A.1Z. -2.3B.1Z. -0.1Z.3C. -a.3F.1Z. -5.3G.1Z. -0.1Z.3M. -0.1Z.3Q. -5.40.1Z. -0.1Z.47. -0.1Z.4d. -0.1Z.4O. -0.1Z.4Q. -5.4T.1Z. -d.1Z.4V. -0.1Z.51. -0.1Z.52. -a.5a.1Z. -2.5b.1Z. -5.5d.1Z. -0.1Z.5B. -0.1Z.5C. -5.5F.1Z. -d.1Z.5H. -0.1Z.5I. -f.1Z.5K. -7.5M.1Z. -0.1Z.5O. -0.1Z.5R. -0.1Z.5U. -0.1Z.5W. -1.5Y.1Z. -0.1Z.5Z. -0.1Z.5/. -0.1Z.60. -2.62.1Z. -2.66.1Z. -a.6b.1Z. -c.6e.1Z. -0.1Z.6F. -0.1Z.6M. -0.1Z.6+. -0.1Z.71. -5.79.1Z. -0.1Z.7D. -0.1Z.2p. -0.1Z.2p. +0.22.1Y. +7.2p.K. +0.1+.1Z. +0.1M.2d. +0.1P.2c. +0.23.1Y. +7.2p.L. +0.1/.1Z. 0.1+.1+. +0.1Q.2c. +0.24.1Y. +0.N.2n. +4.1N.2d. +7.2p.M. +f.1R.29. 0.1+.1/. -0.1+.20. -0.1+.21. -0.1+.22. -0.1+.23. -0.1+.24. -0.1+.25. -b.26.1+. -0.1+.27. -0.1+.28. -0.1+.2a. -0.1+.2b. -0.1+.2l. -6.2m.1+. -4.2n.1+. -0.1+.2p. -2.2q.1+. -2.2r.1+. -7.2s.1+. -7.2t.1+. -2.2u.1+. -d.1+.2v. -d.1+.2w. -e.2x.1+. -9.2y.1+. -0.1+.2z. -d.1+.2A. -9.2B.1+. -0.1+.2C. -0.1+.2D. -d.1+.2E. -0.1+.2F. -0.1+.2G. -9.2H.1+. -0.1+.2I. -d.1+.2J. -0.1+.2K. -c.2M.1+. -9.2N.1+. -2.2O.1+. -9.2Q.1+. -7.2S.1+. -0.1+.2T. -1.2U.1+. -9.2V.1+. -1.2W.1+. -d.1+.2X. -b.2+.1+. -0.1+.30. -d.1+.35. -b.36.1+. -d.1+.37. -0.1+.38. -5.39.1+. -0.1+.3d. -d.1+.3h. -0.1+.3i. -0.1+.3m. -0.1+.3p. -a.3v.1+. -a.3y.1+. -2.3z.1+. -5.3A.1+. -2.3B.1+. -0.1+.3C. -a.3F.1+. -5.3G.1+. -0.1+.3M. -0.1+.3Q. -5.40.1+. -0.1+.47. -0.1+.4d. -0.1+.4O. -0.1+.4Q. -5.4T.1+. -0.1+.4V. -0.1+.51. -0.1+.52. -a.5a.1+. -2.5b.1+. -5.5d.1+. -0.1+.5B. -0.1+.5C. -5.5F.1+. -0.1+.5H. -0.1+.5I. -f.1+.5K. -7.5M.1+. -0.1+.5O. -0.1+.5R. -0.1+.5U. -0.1+.5W. -1.5Y.1+. -0.1+.5Z. -0.1+.5/. -0.1+.60. -2.62.1+. -2.66.1+. -a.6b.1+. -c.6e.1+. -0.1+.6F. -0.1+.6M. -0.1+.6+. -0.1+.71. -5.79.1+. -0.1+.7D. -0.1+.2p. -0.1+.2p. +0.1O.2d. +0.20.1Z. +0.O.2n. +4.1S.29. +5.1R.2a. +f.25.1Y. 0.1/.1/. +0.1+.20. +0.1P.2d. +0.1S.2a. +0.26.1Y. +0.P.2n. +e.21.1Z. 0.1/.20. +0.1+.21. +0.1Q.2d. +0.22.1Z. +0.27.1Y. +0.Q.2n. +7.2p.N. 0.1/.21. -0.1/.22. -0.1/.23. -0.1/.24. -0.1/.25. -b.26.1/. -0.1/.27. -0.1/.28. -0.1/.2a. -0.1/.2b. -0.1/.2l. -6.2m.1/. -4.2n.1/. -i.1/.2p. -2.2q.1/. -2.2r.1/. -7.2s.1/. -7.2t.1/. -2.2u.1/. -e.2x.1/. -9.2y.1/. -9.2B.1/. -9.2H.1/. -c.2M.1/. -9.2N.1/. -2.2O.1/. -9.2Q.1/. -7.2S.1/. -i.1/.2T. -1.2U.1/. -9.2V.1/. -1.2W.1/. -b.2+.1/. -3.36.1/. -5.39.1/. -0.1/.3d. -0.1/.3i. -a.3v.1/. -a.3y.1/. -2.3z.1/. -5.3A.1/. -2.3B.1/. -a.3F.1/. -5.3G.1/. -5.40.1/. -0.1/.47. -0.1/.4O. -5.4T.1/. -0.1/.51. -a.5a.1/. -2.5b.1/. -5.5d.1/. -0.1/.5B. -0.1/.5C. -5.5F.1/. -i.1/.5H. -0.1/.5I. -f.1/.5K. -7.5M.1/. -d.1/.5R. -1.5Y.1/. -d.1/.5/. -0.1/.60. -2.62.1/. -2.66.1/. -a.6b.1/. -c.6e.1/. -0.1/.6F. -0.1/.6M. -5.79.1/. -i.1/.2p. -i.1/.2p. +0.1+.22. +0.1R.2c. 0.20.20. +0.23.1Z. +0.R.2n. +7.2p.O. +f.1V.29. +j.S.2m. +0.1/.22. +0.1+.23. +0.1S.2c. +0.1W.29. 0.20.21. +0.24.1Z. +3.S.2n. +5.2q.0. +7.2p.P. +c.28.1Y. +f.1V.2a. +0.1/.23. +0.1+.24. +0.1Z.25. 0.20.22. +5.1W.2a. +5.2q.1. +7.2p.Q. +c.T.2n. +e.21.21. +0.1/.24. +0.1+.25. +0.1R.2d. +0.1X.29. +0.1Z.26. 0.20.23. +0.U.2n. +5.1V.2b. +5.2q.2. +7.2p.R. +e.21.22. +0.1/.25. +0.1+.26. +0.1S.2d. +0.1V.2c. +0.1X.2a. +0.1Z.27. 0.20.24. -0.20.25. -b.26.20. -0.20.27. -4.20.28. -6.20.29. -0.20.2a. -0.20.2b. -6.20.2c. -6.20.2d. -6.20.2f. -6.20.2g. -6.20.2h. -6.20.2i. -6.20.2j. -0.20.2l. -6.2m.20. -4.2n.20. -1.20.2o. -d.20.2p. -2.2q.20. -2.2r.20. -7.2s.20. -7.2t.20. -2.2u.20. -d.20.2v. -0.20.2w. -e.2x.20. -9.2y.20. -0.20.2z. -d.20.2A. -9.2B.20. -0.20.2C. -0.20.2D. -0.20.2E. -0.20.2F. -0.20.2G. -9.2H.20. -0.20.2I. -d.20.2J. -0.20.2K. -6.20.2L. -c.2M.20. -9.2N.20. -2.2O.20. -6.20.2P. -9.2Q.20. -8.20.2R. -7.2S.20. -0.20.2T. -1.2U.20. -9.2V.20. -1.2W.20. -0.20.2X. -8.20.2Y. -8.20.2Z. -b.2+.20. -8.20.2/. -0.20.30. -8.20.31. -8.20.32. -8.20.33. -8.20.34. -0.20.35. -b.36.20. -0.20.37. -0.20.38. -5.39.20. -6.20.3a. -6.20.3c. -0.20.3d. -1.20.3e. -6.20.3f. -8.20.3g. -d.20.3h. -0.20.3i. -1.20.3j. -8.20.3k. -6.20.3l. -0.20.3m. -8.20.3n. -8.20.3o. -0.20.3p. -8.20.3q. -6.20.3r. -4.20.3s. -8.20.3t. -8.20.3u. -6.20.3v. -6.20.3w. -6.20.3x. -a.3y.20. -2.3z.20. -5.3A.20. -2.3B.20. -0.20.3C. -6.20.3D. -6.20.3E. -a.3F.20. -5.3G.20. -6.20.3H. -6.20.3I. -6.20.3J. -6.20.3K. -6.20.3L. -d.20.3M. -6.20.3N. -6.20.3O. -6.20.3P. -0.20.3Q. -6.20.3R. -6.20.3S. -1.20.3T. -6.20.3U. -6.20.3V. -6.20.3W. -6.20.3X. -6.20.3Y. -6.20.3Z. -1.20.3+. -1.20.3/. -5.40.20. -6.20.41. -6.20.42. -6.20.43. -6.20.44. -6.20.45. -0.20.47. -6.20.48. -6.20.49. -1.20.4a. -6.20.4b. -6.20.4c. -0.20.4d. -6.20.4e. -6.20.4f. -6.20.4g. -6.20.4h. -6.20.4i. -6.20.4j. -8.20.4k. -6.20.4l. -6.20.4m. -6.20.4n. -6.20.4o. -6.20.4p. -6.20.4q. -6.20.4r. -6.20.4s. -6.20.4t. -6.20.4u. -6.20.4v. -6.20.4w. -6.20.4x. -6.20.4y. -6.20.4z. -6.20.4A. -6.20.4B. -6.20.4C. -6.20.4D. -6.20.4E. -1.20.4F. -8.20.4G. -8.20.4H. -1.20.4I. -8.20.4J. -8.20.4K. -8.20.4L. -6.20.4M. -6.20.4N. -0.20.4O. -6.20.4P. -d.20.4Q. -1.20.4R. -6.20.4S. -4.20.4T. -8.20.4U. -d.20.4V. -6.20.4W. -8.20.4Y. -1.20.4Z. -6.20.4+. -6.20.4/. -1.20.50. -d.20.51. -0.20.52. -8.20.53. -8.20.54. -8.20.55. -8.20.56. -1.20.57. -8.20.58. -8.20.59. -a.5a.20. -2.5b.20. -6.20.5c. -5.5d.20. -8.20.5e. -8.20.5f. -6.20.5g. -8.20.5h. -6.20.5i. -6.20.5j. -6.20.5k. -6.20.5l. -6.20.5m. -6.20.5n. -6.20.5o. -6.20.5p. -6.20.5q. -6.20.5r. -1.20.5s. -6.20.5t. -6.20.5u. -6.20.5v. -1.20.5w. -1.20.5y. -1.20.5z. -8.20.5A. -0.20.5B. -0.20.5C. -1.20.5D. -8.20.5E. -5.5F.20. -8.20.5G. -d.20.5H. -0.20.5I. -f.20.5K. -8.20.5L. -8.20.5M. -8.20.5N. -0.20.5O. -d.20.5P. -6.20.5Q. -0.20.5R. -6.20.5S. -6.20.5T. -d.20.5U. -8.20.5V. -0.20.5W. -8.20.5X. -8.20.5Y. -0.20.5Z. -0.20.5/. -0.20.60. -6.20.61. -2.62.20. -6.20.64. -6.20.65. -2.66.20. -6.20.67. -6.20.68. -6.20.69. -6.20.6a. -6.20.6b. -6.20.6c. -6.20.6d. -6.20.6e. -6.20.6f. -6.20.6g. -6.20.6h. -6.20.6i. -6.20.6j. -6.20.6k. -6.20.6l. -6.20.6m. -6.20.6n. -6.20.6o. -6.20.6p. -6.20.6q. -6.20.6r. -6.20.6s. -6.20.6t. -6.20.6u. -6.20.6v. -6.20.6w. -6.20.6x. -6.20.6y. -6.20.6z. -6.20.6A. -6.20.6B. -6.20.6C. -6.20.6D. -6.20.6E. -0.20.6F. -6.20.6G. -6.20.6H. -6.20.6I. -6.20.6J. -6.20.6K. -8.20.6L. -0.20.6M. -6.20.6N. -6.20.6O. -6.20.6P. -6.20.6Q. -6.20.6R. -6.20.6S. -6.20.6T. -6.20.6U. -6.20.6V. -6.20.6W. -1.20.6X. -6.20.6Y. -1.20.6Z. -0.20.6+. -1.20.6/. -1.20.70. -d.20.71. -6.20.72. -6.20.73. -1.20.74. -6.20.75. -1.20.76. -1.20.77. -6.20.78. -5.79.20. -1.20.7a. -6.20.7b. -6.20.7c. -6.20.7d. -6.20.7e. -6.20.7f. -6.20.7g. -6.20.7h. -6.20.7i. -6.20.7j. -6.20.7k. -6.20.7l. -1.20.7m. -6.20.7o. -6.20.7p. -1.20.7r. -1.20.7s. -8.20.7t. -6.20.7u. -6.20.7v. -6.20.7w. -6.20.7x. -6.20.7y. -1.20.7z. -6.20.7A. -8.20.7B. -6.20.7C. -d.20.7D. -6.20.7E. -6.20.7F. -d.20.7G. -6.20.7H. -6.20.7I. -6.20.7J. -1.20.7K. -1.20.7L. -1.20.7M. -1.20.7N. -1.20.7O. -1.20.7P. -6.20.7Q. -8.20.7R. -6.20.7S. -6.20.7T. -1.20.7U. -8.20.7V. -6.20.7W. -6.20.7X. -6.20.7Y. -6.20.7Z. -1.20.7+. -1.20.7/. -1.20.80. -1.20.81. -1.20.82. -6.20.83. -1.20.84. -1.20.85. -1.20.87. -1.20.88. -8.20.89. -6.20.8a. -8.20.8b. -6.20.8c. -8.20.8d. -6.20.8e. -6.20.8f. -6.20.8g. -6.20.8h. -6.20.8i. -6.20.8j. -6.20.8k. -6.20.8l. -6.20.8m. -6.20.8n. -6.20.8o. -6.20.8p. -6.20.8q. -6.20.8r. -6.20.8s. -6.20.8t. -6.20.8u. -6.20.8v. -6.20.8w. -6.20.8x. -6.20.8y. -6.20.8z. -6.20.8A. -6.20.8B. -6.20.8C. -6.20.8D. -6.20.8E. -6.20.8F. -6.20.8G. -4.20.8H. -4.20.8I. -6.20.8J. -6.20.8K. -6.20.8L. -6.20.8M. -6.20.8N. -6.20.8O. -6.20.8P. -d.20.2p. -d.20.2p. -8.20.3b. -1.20.46. -1.20.4X. -1.20.5x. -1.20.5J. -1.20.7n. -1.20.7q. -d.21.21. -0.21.22. -0.21.23. -0.21.24. -0.21.25. -b.26.21. -0.21.27. -0.21.28. -0.21.2a. -0.21.2b. -0.21.2l. -6.2m.21. -4.2n.21. -0.21.2p. -2.2q.21. -2.2r.21. -7.2s.21. -7.2t.21. -2.2u.21. -0.21.2v. -d.21.2w. -e.2x.21. -9.2y.21. -0.21.2z. -d.21.2A. -9.2B.21. -0.21.2C. -0.21.2D. -0.21.2E. -0.21.2F. -0.21.2G. -9.2H.21. -0.21.2I. -d.21.2J. -0.21.2K. -c.2M.21. -9.2N.21. -2.2O.21. -9.2Q.21. -7.2S.21. -0.21.2T. -1.2U.21. -9.2V.21. -1.2W.21. -d.21.2X. -d.2+.21. -0.21.30. -d.21.35. -b.36.21. -0.21.37. -0.21.38. -5.39.21. -0.21.3d. -d.21.3h. -0.21.3i. -0.21.3m. -0.21.3p. -a.3v.21. -a.3y.21. -2.3z.21. -5.3A.21. -2.3B.21. -0.21.3C. -a.3F.21. -5.3G.21. -0.21.3M. -0.21.3Q. -5.40.21. -0.21.47. -0.21.4d. -0.21.4O. -0.21.4Q. -5.4T.21. -0.21.4V. -d.21.51. -0.21.52. -a.5a.21. -2.5b.21. -5.5d.21. -0.21.5B. -0.21.5C. -5.5F.21. -0.21.5H. -0.21.5I. -f.21.5K. -7.5M.21. -d.21.5O. -0.21.5R. -0.21.5U. -0.21.5W. -1.5Y.21. -0.21.5Z. -0.21.5/. -0.21.60. -2.62.21. -2.66.21. -a.6b.21. -c.6e.21. -0.21.6F. -0.21.6M. -0.21.6+. -0.21.71. -5.79.21. -0.21.7D. -0.21.2p. -0.21.2p. 0.22.22. +0.V.2n. +5.2q.3. +7.2p.S. +e.21.23. +0.1/.26. +0.1+.27. +0.1W.2c. +0.20.25. 0.22.23. +0.W.2n. +7.2p.T. +e.21.24. +0.1/.27. +0.20.26. 0.22.24. +0.X.2n. +5.2q.5. +7.2p.U. +c.28.1Z. +e.21.25. +f.23.23. +0.1V.2d. +0.1X.2c. +0.20.27. 0.22.25. -b.26.22. -0.27.22. -0.22.28. -d.22.29. -0.22.2a. -0.22.2b. -d.22.2c. -6.22.2d. -6.22.2f. -d.22.2g. -6.22.2h. -6.22.2i. -6.22.2j. -0.22.2l. -6.2m.22. -1.22.2o. -0.22.2p. -2.2q.22. -2.2r.22. -7.2s.22. -7.2t.22. -2.2u.22. -0.22.2v. -0.22.2w. -e.2x.22. -9.2y.22. -0.22.2z. -0.22.2A. -9.2B.22. -0.22.2C. -0.22.2D. -0.22.2E. -0.22.2F. -0.22.2G. -9.2H.22. -0.22.2I. -0.22.2J. -0.22.2K. -d.22.2L. -c.2M.22. -9.2N.22. -2.2O.22. -6.22.2P. -9.2Q.22. -8.22.2R. -7.2S.22. -0.22.2T. -1.2U.22. -9.2V.22. -1.2W.22. -0.22.2X. -8.22.2Y. -8.22.2Z. -b.2+.22. -8.22.2/. -0.22.30. -8.22.31. -8.22.32. -8.22.33. -8.22.34. -0.22.35. -b.36.22. -0.22.37. -0.22.38. -5.39.22. -6.22.3a. -6.22.3c. -0.22.3d. -1.22.3e. -d.22.3f. -8.22.3g. -0.22.3h. -0.22.3i. -1.22.3j. -8.22.3k. -d.22.3l. -0.22.3m. -8.22.3n. -8.22.3o. -0.22.3p. -8.22.3q. -6.22.3r. -4.22.3s. -8.22.3t. -8.22.3u. -6.22.3v. -6.22.3w. -6.22.3x. -a.3y.22. -2.3z.22. -5.3A.22. -2.3B.22. -0.22.3C. -6.22.3D. -6.22.3E. -a.3F.22. -5.3G.22. -6.22.3H. -6.22.3I. -6.22.3J. -6.22.3K. -6.22.3L. -0.22.3M. -6.22.3N. -6.22.3O. -6.22.3P. -0.22.3Q. -6.22.3R. -6.22.3S. -1.22.3T. -6.22.3U. -6.22.3V. -6.22.3W. -6.22.3X. -6.22.3Y. -6.22.3Z. -1.22.3+. -1.22.3/. -5.40.22. -d.22.41. -6.22.42. -d.22.43. -6.22.44. -6.22.45. -0.22.47. -6.22.48. -6.22.49. -1.22.4a. -6.22.4b. -6.22.4c. -0.22.4d. -d.22.4e. -6.22.4f. -6.22.4g. -6.22.4h. -6.22.4i. -6.22.4j. -8.22.4k. -6.22.4l. -6.22.4m. -6.22.4n. -6.22.4o. -6.22.4p. -d.22.4q. -6.22.4r. -6.22.4s. -6.22.4t. -6.22.4u. -6.22.4v. -6.22.4w. -6.22.4x. -6.22.4y. -6.22.4z. -6.22.4A. -6.22.4B. -6.22.4C. -6.22.4D. -6.22.4E. -1.22.4F. -8.22.4G. -8.22.4H. -1.22.4I. -8.22.4J. -8.22.4K. -8.22.4L. -6.22.4M. -d.22.4N. -0.22.4O. -6.22.4P. -0.22.4Q. -1.22.4R. -6.22.4S. -4.22.4T. -8.22.4U. -0.22.4V. -6.22.4W. -8.22.4Y. -1.22.4Z. -6.22.4+. -6.22.4/. -1.22.50. -0.22.51. -0.22.52. -8.22.53. -8.22.54. -8.22.55. -8.22.56. -1.22.57. -8.22.58. -8.22.59. -a.5a.22. -2.5b.22. -6.22.5c. -5.5d.22. -8.22.5e. -8.22.5f. -6.22.5g. -8.22.5h. -6.22.5i. -6.22.5j. -6.22.5k. -6.22.5l. -6.22.5m. -6.22.5n. -6.22.5o. -6.22.5p. -6.22.5q. -6.22.5r. -1.22.5s. -6.22.5t. -6.22.5u. -6.22.5v. -1.22.5w. -1.22.5y. -1.22.5z. -8.22.5A. -0.22.5B. -0.22.5C. -1.22.5D. -8.22.5E. -5.5F.22. -8.22.5G. -0.22.5H. -0.22.5I. -f.22.5K. -8.22.5L. -8.22.5M. -8.22.5N. -0.22.5O. -8.22.5P. -6.22.5Q. -0.22.5R. -6.22.5S. -6.22.5T. -0.22.5U. -8.22.5V. -0.22.5W. -8.22.5X. -8.22.5Y. -0.22.5Z. -h.5+.22. -0.22.5/. -0.22.60. -d.22.61. -2.62.22. -d.22.64. -6.22.65. -2.66.22. -6.22.67. -6.22.68. -6.22.69. -6.22.6a. -6.22.6b. -6.22.6c. -6.22.6d. -6.22.6e. -6.22.6f. -6.22.6g. -6.22.6h. -6.22.6i. -d.22.6j. -6.22.6k. -6.22.6l. -6.22.6m. -6.22.6n. -6.22.6o. -6.22.6p. -d.22.6q. -6.22.6r. -6.22.6s. -6.22.6t. -6.22.6u. -6.22.6v. -6.22.6w. -6.22.6x. -6.22.6y. -6.22.6z. -6.22.6A. -6.22.6B. -6.22.6C. -6.22.6D. -6.22.6E. -0.22.6F. -6.22.6G. -6.22.6H. -6.22.6I. -6.22.6J. -6.22.6K. -8.22.6L. -0.6M.22. -6.22.6N. -d.22.6O. -6.22.6P. -6.22.6Q. -6.22.6R. -6.22.6S. -6.22.6T. -6.22.6U. -6.22.6V. -6.22.6W. -1.22.6X. -6.22.6Y. -1.22.6Z. -0.22.6+. -1.22.6/. -1.22.70. -0.22.71. -6.22.72. -6.22.73. -1.22.74. -d.22.75. -1.22.76. -1.22.77. -6.22.78. -5.79.22. -1.22.7a. -6.22.7b. -6.22.7c. -6.22.7d. -6.22.7e. -6.22.7f. -6.22.7g. -6.22.7h. -6.22.7i. -6.22.7j. -6.22.7k. -6.22.7l. -1.22.7m. -6.22.7o. -6.22.7p. -1.22.7r. -1.22.7s. -8.22.7t. -6.22.7u. -6.22.7v. -6.22.7w. -6.22.7x. -6.22.7y. -1.22.7z. -6.22.7A. -8.22.7B. -6.22.7C. -0.22.7D. -6.22.7E. -d.22.7F. -6.22.7G. -6.22.7H. -6.22.7I. -6.22.7J. -1.22.7K. -1.22.7L. -1.22.7M. -1.22.7N. -1.22.7O. -1.22.7P. -6.22.7Q. -8.22.7R. -6.22.7S. -d.22.7T. -1.22.7U. -8.22.7V. -6.22.7W. -6.22.7X. -6.22.7Y. -d.22.7Z. -1.22.7+. -1.22.7/. -1.22.80. -1.22.81. -1.22.82. -6.22.83. -1.22.84. -1.22.85. -h.86.22. -1.22.87. -1.22.88. -8.22.89. -d.22.8a. -8.22.8b. -6.22.8c. -8.22.8d. -6.22.8e. -d.22.8f. -6.22.8g. -6.22.8h. -6.22.8i. -6.22.8j. -6.22.8k. -6.22.8l. -6.22.8m. -6.22.8n. -6.22.8o. -6.22.8p. -6.22.8q. -6.22.8r. -6.22.8s. -6.22.8t. -6.22.8u. -6.22.8v. -6.22.8w. -6.22.8x. -6.22.8y. -6.22.8z. -6.22.8A. -6.22.8B. -d.22.8C. -6.22.8D. -6.22.8E. -6.22.8F. -6.22.8G. -4.22.8H. -4.22.8I. -6.22.8J. -6.22.8K. -6.22.8L. -6.22.8M. -6.22.8N. -d.22.8O. -d.22.8P. -0.22.2p. -0.22.2p. -8.22.3b. -1.22.46. -1.22.4X. -1.22.5x. -1.22.5J. -1.22.7n. -1.22.7q. -0.23.23. -3.24.23. +0.23.24. +5./.2h. +5.2q.6. +7.2p.V. +c.28.1+. +c.Y.2n. +e.21.26. +0.1W.2d. +0.22.26. 0.23.25. -b.26.23. -0.27.23. -0.23.28. -0.23.2a. -0.23.2b. -7.2e.23. -0.23.2l. -6.2m.23. -4.2n.23. -0.23.2p. -2.2q.23. -2.2r.23. -7.2s.23. -7.2t.23. -2.2u.23. -0.23.2v. -d.23.2w. -e.2x.23. -9.2y.23. -0.23.2z. -0.23.2A. -9.2B.23. -0.23.2C. -0.23.2D. -0.23.2E. -0.23.2F. -0.23.2G. -9.2H.23. -0.23.2I. -0.23.2J. -0.23.2K. -c.2M.23. -9.2N.23. -2.2O.23. -9.2Q.23. -7.2S.23. -0.23.2T. -1.2U.23. -9.2V.23. -1.2W.23. -0.23.2X. -d.2+.23. -0.23.30. -d.23.35. -b.36.23. -d.23.37. -0.23.38. -5.39.23. -0.23.3d. -d.23.3h. -5.3i.23. -0.23.3m. -0.23.3p. -a.3v.23. -a.3y.23. -2.3z.23. -5.3A.23. -2.3B.23. -0.23.3C. -a.3F.23. -5.3G.23. -0.23.3M. -d.23.3Q. -5.40.23. -0.23.47. -0.23.4d. -0.23.4O. -0.23.4Q. -5.4T.23. -0.23.4V. -0.23.51. -0.23.52. -a.5a.23. -2.5b.23. -5.5d.23. -0.23.5B. -0.23.5C. -5.5F.23. -0.23.5H. -0.23.5I. -0.23.5K. -7.5M.23. -0.23.5O. -0.23.5R. -0.23.5U. -0.23.5W. -1.5Y.23. -0.23.5Z. -0.23.5/. -0.23.60. -2.62.23. -d.23.63. -2.66.23. -a.6b.23. -c.6e.23. -d.23.6F. -0.6M.23. -0.23.6+. -0.23.71. -5.79.23. -0.23.7D. -0.23.2p. -0.23.2p. -3.24.24. -3.24.25. -b.26.24. -0.27.24. -0.24.28. -6.24.29. -0.24.2a. -0.24.2b. -6.24.2c. -6.24.2d. -7.2e.24. -6.24.2f. -6.24.2g. -6.24.2h. -6.24.2i. -6.24.2j. -3.24.2l. -6.2m.24. -4.2n.24. -1.24.2o. -0.24.2p. -2.2q.24. -2.2r.24. -7.2s.24. -7.2t.24. -2.2u.24. -0.24.2v. -0.24.2w. -e.2x.24. -9.2y.24. -0.24.2z. -0.24.2A. -9.2B.24. -0.24.2C. -0.24.2D. -0.24.2E. -0.24.2F. -0.24.2G. -9.2H.24. -0.24.2I. -3.24.2J. -0.24.2K. -6.24.2L. -c.2M.24. -9.2N.24. -2.2O.24. -6.24.2P. -9.2Q.24. -8.24.2R. -7.2S.24. -0.24.2T. -1.2U.24. -9.2V.24. -1.2W.24. -0.24.2X. -8.24.2Y. -8.24.2Z. -d.2+.24. -8.24.2/. -0.24.30. -8.24.31. -8.24.32. -8.24.33. -8.24.34. -0.24.35. -6.36.24. -0.24.37. -3.24.38. -5.39.24. -6.24.3a. -6.24.3c. -3.24.3d. -1.24.3e. -6.24.3f. -8.24.3g. -0.24.3h. -5.3i.24. -1.24.3j. -8.24.3k. -6.24.3l. -3.24.3m. -8.24.3n. -8.24.3o. -0.24.3p. -8.24.3q. -6.24.3r. -4.24.3s. -8.24.3t. -8.24.3u. -6.24.3v. -6.24.3w. -6.24.3x. -a.3y.24. -2.3z.24. -5.3A.24. -2.3B.24. -3.24.3C. -3.24.3D. -6.24.3E. -a.3F.24. -5.3G.24. -6.24.3H. -6.24.3I. -6.24.3J. -6.24.3K. -6.24.3L. -3.24.3M. -6.24.3N. -6.24.3O. -6.24.3P. -3.24.3Q. -6.24.3R. -6.24.3S. -1.24.3T. -6.24.3U. -6.24.3V. -6.24.3W. -6.24.3X. -6.24.3Y. -6.24.3Z. -1.24.3+. -1.24.3/. -5.40.24. -6.24.41. -6.24.42. -3.24.43. -6.24.44. -6.24.45. -3.24.47. -6.24.48. -6.24.49. -1.24.4a. -6.24.4b. -6.24.4c. -0.24.4d. -6.24.4e. -6.24.4f. -6.24.4g. -6.24.4h. -6.24.4i. -6.24.4j. -8.24.4k. -6.24.4l. -6.24.4m. -6.24.4n. -6.24.4o. -6.24.4p. -6.24.4q. -6.24.4r. -6.24.4s. -6.24.4t. -6.24.4u. -6.24.4v. -6.24.4w. -6.24.4x. -6.24.4y. -6.24.4z. -6.24.4A. -6.24.4B. -6.24.4C. -6.24.4D. -6.24.4E. -1.24.4F. -8.24.4G. -8.24.4H. -1.24.4I. -8.24.4J. -8.24.4K. -8.24.4L. -3.24.4M. -3.24.4N. -3.24.4O. -6.24.4P. -3.24.4Q. -1.24.4R. -6.24.4S. -4.24.4T. -8.24.4U. -3.24.4V. -6.24.4W. -8.24.4Y. -1.24.4Z. -6.24.4+. -6.24.4/. -1.24.50. -3.24.51. -3.24.52. -8.24.53. -8.24.54. -8.24.55. -8.24.56. -1.24.57. -8.24.58. -8.24.59. -a.5a.24. -2.5b.24. -6.24.5c. -5.5d.24. -8.24.5e. -8.24.5f. -6.24.5g. -8.24.5h. -6.24.5i. -6.24.5j. -6.24.5k. -6.24.5l. -6.24.5m. -6.24.5n. -6.24.5o. -6.24.5p. -6.24.5q. -6.24.5r. -1.24.5s. -6.24.5t. -3.24.5u. -3.24.5v. -1.24.5w. -1.24.5y. -1.24.5z. -8.24.5A. -0.24.5B. -0.24.5C. -1.24.5D. -8.24.5E. -5.5F.24. -8.24.5G. -3.24.5H. -0.24.5I. -f.24.5K. -8.24.5L. -8.24.5M. -8.24.5N. -0.24.5O. -8.24.5P. -6.24.5Q. -0.24.5R. -6.24.5S. -6.24.5T. -3.24.5U. -8.24.5V. -3.24.5W. -8.24.5X. -8.24.5Y. -3.24.5Z. -h.5+.24. -0.24.5/. -0.24.60. -6.24.61. -2.62.24. -6.24.64. -6.24.65. -2.66.24. -6.24.67. -6.24.68. -6.24.69. -6.24.6a. -6.24.6b. -6.24.6c. -6.24.6d. -6.24.6e. -6.24.6f. -6.24.6g. -6.24.6h. -6.24.6i. -6.24.6j. -6.24.6k. -6.24.6l. -6.24.6m. -6.24.6n. -6.24.6o. -6.24.6p. -6.24.6q. -6.24.6r. -6.24.6s. -6.24.6t. -6.24.6u. -6.24.6v. -6.24.6w. -6.24.6x. -6.24.6y. -6.24.6z. -6.24.6A. -3.24.6B. -6.24.6C. -6.24.6D. -6.24.6E. -0.24.6F. -6.24.6G. -6.24.6H. -6.24.6I. -6.24.6J. -6.24.6K. -8.24.6L. -0.6M.24. -6.24.6N. -6.24.6O. -6.24.6P. -6.24.6Q. -6.24.6R. -6.24.6S. -6.24.6T. -6.24.6U. -6.24.6V. -6.24.6W. -1.24.6X. -6.24.6Y. -1.24.6Z. -3.24.6+. -1.24.6/. -1.24.70. -0.24.71. -6.24.72. -6.24.73. -1.24.74. -6.24.75. -1.24.76. -1.24.77. -6.24.78. -5.79.24. -1.24.7a. -6.24.7b. -6.24.7c. -6.24.7d. -6.24.7e. -6.24.7f. -6.24.7g. -6.24.7h. -6.24.7i. -6.24.7j. -6.24.7k. -6.24.7l. -1.24.7m. -6.24.7o. -6.24.7p. -1.24.7r. -1.24.7s. -8.24.7t. -6.24.7u. -6.24.7v. -6.24.7w. -6.24.7x. -6.24.7y. -1.24.7z. -6.24.7A. -8.24.7B. -6.24.7C. -3.24.7D. -6.24.7E. -6.24.7F. -3.24.7G. -6.24.7H. -6.24.7I. -6.24.7J. -1.24.7K. -1.24.7L. -1.24.7M. -1.24.7N. -1.24.7O. -1.24.7P. -6.24.7Q. -8.24.7R. -6.24.7S. -6.24.7T. -1.24.7U. -8.24.7V. -6.24.7W. -6.24.7X. -6.24.7Y. -6.24.7Z. -1.24.7+. -1.24.7/. -1.24.80. -1.24.81. -1.24.82. -6.24.83. -1.24.84. -1.24.85. -h.86.24. -1.24.87. -1.24.88. -8.24.89. -6.24.8a. -8.24.8b. -6.24.8c. -8.24.8d. -6.24.8e. -6.24.8f. -6.24.8g. -6.24.8h. -6.24.8i. -6.24.8j. -6.24.8k. -6.24.8l. -6.24.8m. -6.24.8n. -6.24.8o. -6.24.8p. -6.24.8q. -6.24.8r. -6.24.8s. -6.24.8t. -6.24.8u. -6.24.8v. -6.24.8w. -6.24.8x. -6.24.8y. -6.24.8z. -6.24.8A. -6.24.8B. -6.24.8C. -6.24.8D. -6.24.8E. -6.24.8F. -6.24.8G. -4.24.8H. -4.24.8I. -6.24.8J. -6.24.8K. -6.24.8L. -6.24.8M. -6.24.8N. -6.24.8O. -6.24.8P. -0.24.2p. -0.24.2p. -8.24.3b. -1.24.46. -1.24.4X. -1.24.5x. -1.24.5J. -1.24.7n. -1.24.7q. +0.24.24. +0.Z.2n. +5./.2i. +5.1V.2e. +7.2p.W. +c.28.1/. +e.21.27. +0.22.27. +0.23.26. +0.24.25. +5./.2j. +5.2q.8. +7.2p.X. +c.28.20. +0.1X.2d. +0.23.27. +0.24.26. 0.25.25. -b.26.25. -0.27.25. -4.25.28. -0.25.2a. -0.25.2b. -0.25.2l. -6.2m.25. -4.2n.25. -0.25.2p. -2.2q.25. -2.2r.25. -7.2s.25. -7.2t.25. -2.2u.25. -0.25.2v. -0.25.2w. -e.2x.25. -9.2y.25. -0.25.2z. -d.25.2A. -9.2B.25. -0.25.2C. -0.25.2D. -d.25.2E. -0.25.2F. -0.25.2G. -9.2H.25. -0.25.2I. -d.25.2J. -0.25.2K. -c.2M.25. -9.2N.25. -2.2O.25. -9.2Q.25. -7.2S.25. -0.25.2T. -1.2U.25. -9.2V.25. -1.2W.25. -0.25.2X. -d.2+.25. -0.25.30. -0.25.35. -b.36.25. -0.25.37. -0.25.38. -5.39.25. -0.25.3d. -d.25.3h. -5.3i.25. -0.25.3m. -0.25.3p. -a.3v.25. -a.3y.25. -2.3z.25. -5.3A.25. -2.3B.25. -0.25.3C. -a.3F.25. -5.3G.25. -0.25.3M. -0.25.3Q. -5.40.25. -0.25.47. -0.25.4d. -0.25.4O. -d.25.4Q. -7.4T.25. -d.25.4V. -0.25.51. -0.25.52. -a.5a.25. -2.5b.25. -5.5d.25. -0.25.5B. -0.25.5C. -5.5F.25. -d.25.5H. -0.25.5I. -f.25.5K. -7.5M.25. -0.25.5O. -0.25.5R. -0.25.5U. -0.25.5W. -1.5Y.25. -0.25.5Z. -0.25.5/. -0.25.60. -2.62.25. -2.66.25. -a.6b.25. -c.6e.25. -0.25.6F. -0.6M.25. -0.25.6+. -0.25.71. -5.79.25. -0.25.7D. -0.25.2p. -0.25.2p. -b.26.26. -b.26.27. -b.26.28. -b.26.2a. -b.26.2b. -b.26.2l. -6.2m.26. -4.2n.26. -b.26.2p. -2.2q.26. -2.2r.26. -7.2s.26. -7.2t.26. -2.2u.26. -b.26.2v. -b.26.2w. -e.2x.26. -9.2y.26. -b.26.2z. -b.26.2A. -9.2B.26. -b.26.2C. -b.26.2D. -b.26.2E. -b.26.2F. -b.26.2G. -9.2H.26. -b.26.2I. -b.26.2J. -b.26.2K. -c.2M.26. -9.2N.26. -2.2O.26. -9.2Q.26. -7.2S.26. -b.26.2T. -1.2U.26. -9.2V.26. -1.2W.26. -b.26.2X. -d.2+.26. -b.26.30. -b.26.35. -3.36.26. -b.26.37. -b.26.38. -5.39.26. -b.26.3d. -b.26.3h. -b.26.3i. -b.26.3m. -b.26.3p. -a.3v.26. -a.3y.26. -2.3z.26. -5.3A.26. -2.3B.26. -b.26.3C. -a.3F.26. -5.3G.26. -b.26.3M. -b.26.3Q. -5.40.26. -b.26.47. -b.26.4d. -b.26.4O. -b.26.4Q. -7.4T.26. -b.26.4V. -b.26.51. -b.26.52. -a.5a.26. -2.5b.26. -5.5d.26. -b.26.5B. -b.26.5C. -b.26.5F. -b.26.5H. -b.26.5I. -f.26.5K. -7.5M.26. -b.26.5O. -b.26.5R. -b.26.5U. -b.26.5W. -1.5Y.26. -b.26.5Z. -b.26.5/. -b.26.60. -2.62.26. -2.66.26. -a.6b.26. -c.6e.26. -b.26.6F. -b.26.6M. -b.26.6+. -b.26.71. -5.79.26. -b.26.7D. -b.26.2p. -b.26.2p. +5./.2k. +7.2p.Y. +c.28.21. +0.24.27. +4.26.25. +5./.2l. +5.1V.2f. +5.2q.a. +7.2p.Z. +c.28.22. +0.+.2n. +0.25.27. +4.26.26. +5.2q.b. +c.28.23. +0./.2n. +4.26.27. +5.2q.c. +c.28.24. +0.10.2n. 0.27.27. -4.27.28. -4.27.29. -0.27.2a. -0.27.2b. -4.27.2c. -4.27.2d. -4.2e.27. -4.27.2f. -4.27.2g. -4.27.2h. -4.27.2i. -4.27.2j. -0.27.2l. -6.2m.27. -4.2n.27. -1.27.2o. -0.27.2p. -2.2q.27. -2.2r.27. -7.2s.27. -7.2t.27. -2.2u.27. -d.27.2v. +c.28.25. +0.11.2n. +7.2p.+. +c.28.26. +0.12.2n. +7.2p./. +c.28.27. +0.29.1Y. +7.2p.10. +5.2a.1Y. +7.2p.11. +c.28.28. +0.13.2n. +7.2p.12. +0.14.2n. +0.15.2n. +0.2c.1Y. +0.16.2n. +0.29.1Z. +7.2p.13. +0.1+.29. +0.17.2n. +0.1Z.2a. +7.2p.14. +0.1/.29. +0.1+.2a. +0.18.2n. +4.2d.1Y. +7.2p.15. +0.19.2n. +0.20.29. +5.1/.2a. +7.2p.16. +0.1a.2n. +0.1Z.2c. +0.20.2a. +7.1+.2b. +7.2p.17. +e.21.29. +0.1+.2c. +0.1b.2n. +0.22.29. +2.1d.2k. +7.2p.18. +e.21.2a. +0.1/.2c. +0.1c.2n. +0.23.29. +2.1d.2l. +5.22.2a. +7.2p.19. +0.1Z.2d. +0.20.2c. +0.23.2a. +0.29.24. +7.2p.1a. +0.1+.2d. +0.1d.2n. +0.24.2a. +0.29.25. +7.22.2b. +7.2p.1b. +e.21.2c. +0.1/.2d. +0.22.2c. +0.25.2a. +0.29.26. +7.1+.2e. +7.2p.1c. +0.20.2d. +0.23.2c. +0.26.2a. +0.29.27. +5.1h.2h. +f.24.2b. +0.1e.2n. +0.24.2c. +5.1h.2i. +5.27.2a. +7.2p.1d. +e.21.2d. +0.1f.2n. +0.22.2d. +0.25.2c. +5.1h.2j. +7.1+.2f. +7.26.2b. +c.28.29. +0.23.2d. +0.26.2c. +0.2n.1g. +5.1h.2k. +7.22.2e. +c.28.2a. +0.24.2d. +0.27.2c. +5.1h.2l. +7.2p.1e. +0.25.2d. +7.2p.1f. +f.24.2e. +0.1h.2n. +0.26.2d. +7.22.2f. +7.2p.1g. +c.28.2c. +0.27.2d. +4.1i.2n. +7.26.2e. +j.1j.2m. +4.1j.2n. +5.2g.1O. +7.24.2f. +7.2p.1h. +c.28.2d. +f.2g.1P. +7.26.2f. +7.2p.1i. +f.2g.1Q. +0.1k.2n. +7.2p.1j. +3.1l.2n. +0.1m.2n. +0.1n.2n. +5.1t.2h. +7.2p.1k. +f.2g.1R. +0.1o.2n. +5.1t.2i. +7.2p.1l. +0.1p.2n. +5.1t.2j. +7.2p.1m. +0.1q.2n. +5.1t.2k. +7.2p.1n. +0.1r.2n. +0.29.29. +5.1t.2l. +5.2g.1V. +7.2p.1o. +0.1s.2n. +5.29.2a. +7.2p.1p. +f.2g.1W. +0.1t.2n. +7.2p.1q. +f.2a.2a. +3.1u.2n. +5.29.2b. +7.2p.1r. +f.2g.1X. +0.1v.2n. +0.29.2c. +7.2p.1s. +0.1w.2n. +7.2c.2a. +7.2p.1t. +0.1x.2n. +7.2p.1u. +0.1y.2n. +0.29.2d. +7.2p.1v. +0.1z.2n. +0.2a.2d. +5.29.2e. +7.2p.1w. +f.2c.2c. +0.1A.2n. +7.2p.1x. +0.1B.2n. +7.2p.1y. +0.1C.2n. +5.29.2f. +7.2p.1z. +f.2c.2d. +0.1D.2n. +7.2p.1A. +j.2a.2f. +7.2p.1B. +0.1E.2n. +0.2d.2d. +1.2f.2b. +7.2p.1C. +0.1F.2n. +7.2p.1D. +0.1G.2n. +0.1H.2n. +7.2p.1E. +0.1I.2n. +7.2p.1F. +0.1J.2n. +1.2f.2e. +7.2p.1G. +7.2p.1H. +7.2p.1I. +0.1K.2n. +1.2f.2f. +7.2p.1J. +0.1L.2n. +0.1M.2n. +0.1N.2n. +7.2p.1K. +0.1O.2n. +7.2p.1L. +0.1P.2n. +7.2p.1M. +0.1Q.2n. +7.2p.1N. +7.2p.1O. +5.1V.2h. +7.2p.1P. +8.2g.25. +7.2p.1Q. +8.2g.26. +f.1R.2n. +0.1S.2n. +5.1V.2k. +5.1V.2l. +7.2p.1R. +0.1V.2n. +7.2p.1S. +0.1W.2n. +0.1X.2n. +7.2p.1V. +7.2p.1W. +7.2p.1X. +5.2g.29. +f.2g.2a. +0.2n.1Y. +7.1+.2h. +8.2g.2c. +f.1+.2i. +7.1+.2j. +7.1+.2k. +7.2p.1Y. +7.1+.2l. +7.22.2h. +7.22.2i. +f.1Z.2n. +0.1+.2n. +7.22.2j. +7.24.2h. +0.1/.2n. +7.22.2k. +f.24.2i. +0.20.2n. +7.22.2l. +7.24.2j. +7.26.2h. +7.2p.1Z. +7.24.2k. +7.26.2i. +7.2p.1+. +e.21.2n. +0.22.2n. +7.24.2l. +7.26.2j. +7.2p.1/. +0.23.2n. +7.26.2k. +7.2p.20. +0.24.2n. +7.26.2l. +7.2p.21. +0.25.2n. +7.2p.22. +4.26.2n. +7.2p.23. +0.27.2n. +7.2p.24. +7.2p.25. +7.2p.26. +c.28.2n. +7.2p.27. +7.2p.28. +5.29.2h. +5.29.2i. +5.29.2j. +j.2a.2i. +5.29.2k. +5.29.2l. +0.29.2n. +f.2a.2n. +1.2n.2b. +7.2p.29. +0.2c.2n. +7.2p.2a. +1.2f.2h. +5.2q.1O. +1.2f.2i. +5.2q.1P. +0.2d.2n. +1.2f.2j. +5.2q.1Q. +7.2p.2c. +1.2f.2k. +1.2f.2l. +7.2p.2d. +5.2q.1R. +5.2q.1S. +5.2q.1V. +5.2q.1W. +5.2q.1X. +5.2q.1Y. +5.2q.1Z. +5.2q.1+. +5.2q.1/. +5.2q.20. +5.2q.21. +5.2q.22. +5.2q.23. +5.2q.25. +5.2q.26. +5.2q.27. +5.2q.28. +5.2q.29. +5.2q.2a. +5.2q.2b. +1.2n.2h. +1.2n.2i. +1.2n.2j. +1.2n.2k. +1.2n.2l. +0.2n.2n. +7.2p.2n. +7.2p.2p. +2.0.2r. +2.0.2s. +2.1.2r. +2.0.2t. +2.1.2s. +2.2.2r. +2.1.2t. +2.2.2s. +2.3.2r. +2.2.2t. +2.3.2s. +2.4.2r. +2.3.2t. +2.4.2s. +2.5.2r. +2.4.2t. +2.6.2r. +e.5.2s. +2.5.2t. +2.6.2s. +2.7.2r. +2.6.2t. +2.7.2s. +2.8.2r. +2.7.2t. +2.8.2s. +2.9.2r. +2.8.2t. +2.9.2s. +2.a.2r. +2.9.2t. +2.a.2s. +2.b.2r. +2.a.2t. +2.b.2s. +2.b.2t. +1.c.2u. +5.2q.2h. +2.c.2v. +5.2q.2i. +5.2q.2j. +5.2q.2k. +5.2q.2l. +5.2q.2n. +3.2y.0. +3.2y.1. +5.2p.2q. +3.2y.2. +1.k.2u. +3.2y.3. +2.k.2v. +e.4.2y. +i.c.2w. +3.2y.5. +3.2y.6. +e.7.2y. +0.d.2w. +3.2x.c. +3.2y.8. +0.e.2w. +e.9.2y. +0.f.2w. +3.2y.a. +0.g.2w. +3.2y.b. +0.h.2w. +1.r.2u. +3.2x.d. +3.2y.c. +0.i.2w. +1.s.2u. +2.r.2v. +3.2x.e. +8.2z.c. +0.j.2w. +2.s.2v. +3.2x.f. +8.2A.c. +0.k.2w. +1.u.2u. +3.2B.c. +3.2x.g. +0.l.2w. +2.u.2v. +3.2x.h. +3.2y.d. +a.2F.0. +i.c.2C. +0.m.2w. +3.2x.i. +3.2y.e. +8.2z.d. +a.2F.1. +3.2x.j. +3.2y.f. +3.n.2w. +8.2A.d. +8.2z.e. +a.2F.2. +0.o.2w. +3.2B.d. +3.2x.k. +3.2y.g. +8.2A.e. +8.2z.f. +a.2F.3. +g.2E.b. +i.c.2D. +0.p.2w. +3.2B.e. +3.2x.l. +3.2y.h. +4.d.2C. +8.2A.f. +8.2z.g. +e.4.2F. +f.2E.c. +0.q.2w. +3.2B.f. +3.2x.m. +3.2y.i. +4.e.2C. +8.2A.g. +8.2z.h. +a.2F.5. +3.2B.g. +3.2y.j. +3.n.2x. +4.f.2C. +8.2A.h. +8.2z.i. +a.2F.6. +0.d.2D. +3.2B.h. +3.2x.o. +3.2y.k. +4.g.2C. +8.2A.i. +8.2z.j. +e.7.2F. +0.e.2D. +0.r.2w. +2.A.2v. +3.2B.i. +3.2x.p. +3.2y.l. +4.h.2C. +8.2A.j. +8.2z.k. +a.2F.8. +g.2E.d. +0.f.2D. +0.s.2w. +3.2B.j. +3.2x.q. +3.2y.m. +4.i.2C. +8.2A.k. +8.2z.l. +e.9.2F. +g.2E.e. +0.g.2D. +0.j.2C. +0.t.2w. +3.2B.k. +3.n.2y. +8.2A.l. +8.2z.m. +a.2F.a. +g.2E.f. +0.h.2D. +0.u.2w. +3.2B.l. +3.2y.o. +4.k.2C. +8.2A.m. +8.2z.n. +a.2F.b. +g.2E.g. +0.i.2D. +0.l.2C. +0.v.2w. +3.2B.m. +3.2x.r. +3.2y.p. +8.2A.n. +8.2z.o. +a.2F.c. +g.2E.h. +0.j.2D. +0.m.2C. +0.w.2w. +3.2B.n. +3.2x.s. +3.2y.q. +8.2A.o. +8.2z.p. +g.2E.i. +0.k.2D. +0.x.2w. +3.2B.o. +3.2x.t. +3.n.2C. +8.2A.p. +8.2z.q. +g.2E.j. +i.c.2G. +0.l.2D. +0.y.2w. +3.2B.p. +3.2x.u. +4.o.2C. +8.2A.q. +g.2E.k. +0.m.2D. +0.p.2C. +2.2H.c. +3.2B.q. +3.2x.v. +3.2y.r. +a.2F.d. +f.2w.z. +g.2E.l. +3.2x.w. +3.2y.s. +3.n.2D. +4.q.2C. +8.2z.r. +a.2F.e. +g.2E.m. +0.d.2G. +0.o.2D. +3.2x.x. +3.2y.t. +8.2A.r. +8.2z.s. +a.2F.f. +g.2E.n. +0.A.2w. +0.e.2G. +0.p.2D. +3.2B.r. +3.2x.y. +3.2y.u. +8.2A.s. +8.2z.t. +a.2F.g. +g.2E.o. +0.B.2w. +0.f.2G. +0.q.2D. +0.r.2C. +2.2H.d. +3.2B.s. +3.2x.z. +3.2y.v. +8.2A.t. +8.2z.u. +a.2F.h. +g.2E.p. +i.c.2I. +0.C.2w. +0.g.2G. +0.s.2C. +2.2H.e. +3.2B.t. +3.2y.w. +8.2A.u. +8.2z.v. +a.2F.i. +g.2E.q. +0.D.2w. +0.h.2G. +0.t.2C. +2.2H.f. +3.2B.u. +3.2y.x. +8.2A.v. +8.2z.w. +a.2F.j. +0.E.2w. +0.i.2G. +0.r.2D. +0.u.2C. +2.2H.g. +3.2B.v. +3.2x.A. +3.2y.y. +8.2A.w. +8.2z.x. +a.2F.k. +0.j.2G. +0.s.2D. +0.v.2C. +2.2H.h. +3.2B.w. +3.2x.B. +3.2y.z. +4.d.2I. +4.F.2w. +8.2A.x. +8.2z.y. +a.2F.l. +g.2E.r. +0.k.2G. +0.t.2D. +0.w.2C. +2.2H.i. +3.2B.x. +3.2x.C. +4.e.2I. +8.2A.y. +8.2z.z. +a.2F.m. +g.2E.s. +0.l.2G. +0.u.2D. +0.x.2C. +2.2H.j. +3.2B.y. +3.2x.D. +3.n.2F. +4.f.2I. +8.2A.z. +a.2J.c. +g.2E.t. +0.G.2w. +0.m.2G. +0.y.2C. +2.2H.k. +3.2B.z. +3.2x.E. +3.2y.A. +4.g.2I. +4.v.2D. +a.2F.o. +g.2E.u. +0.H.2w. +0.w.2D. +2.2H.l. +3.2x.F. +3.2y.B. +3.n.2G. +4.h.2I. +8.2z.A. +a.2F.p. +f.2C.z. +g.2E.v. +0.o.2G. +0.x.2D. +2.2H.m. +3.2y.C. +3.I.2w. +4.i.2I. +8.2A.A. +8.2z.B. +a.2F.q. +g.2E.w. +0.p.2G. +0.y.2D. +2.2H.n. +3.2B.A. +3.2y.D. +3.J.2w. +4.j.2I. +8.2A.B. +8.2z.C. +a.2J.d. +g.2E.x. +i.c.2K. +0.2D.z. +0.K.2w. +0.q.2G. +2.2H.o. +3.2B.B. +3.2x.G. +3.2y.E. +4.A.2C. +4.k.2I. +8.2A.C. +8.2z.D. +a.2J.e. +g.2E.y. +0.B.2C. +0.L.2w. +2.2H.p. +3.2B.C. +3.2x.H. +3.2y.F. +4.l.2I. +8.2A.D. +8.2z.E. +a.2F.r. +a.2J.f. +g.2E.z. +2.2H.q. +3.2B.D. +3.I.2x. +3.M.2w. +4.C.2C. +4.m.2I. +8.2A.E. +8.2z.F. +a.2F.s. +a.2J.g. +0.A.2D. +0.D.2C. +0.d.2K. +0.r.2G. +3.2B.E. +3.J.2x. +3.n.2I. +8.2A.F. +a.2F.t. +a.2J.h. +0.B.2D. +0.e.2K. +0.s.2G. +3.2B.F. +3.2x.K. +3.2y.G. +4.E.2C. +4.o.2I. +a.2F.u. +a.2J.i. +g.2E.A. +0.C.2D. +0.f.2K. +0.N.2w. +0.t.2G. +2.2H.r. +3.2x.L. +3.2y.H. +4.F.2C. +4.p.2I. +8.2z.G. +a.2F.v. +a.2J.j. +g.2E.B. +i.c.2L. +0.D.2D. +0.g.2K. +0.O.2w. +0.u.2G. +2.2H.s. +3.I.2y. +3.M.2x. +4.q.2I. +8.2A.G. +8.2z.H. +a.2F.w. +a.2J.k. +g.2E.C. +0.E.2D. +0.h.2K. +0.P.2w. +0.v.2G. +2.2H.t. +3.2B.G. +3.J.2y. +8.2A.H. +8.2z.I. +a.2F.x. +a.2J.l. +g.2E.D. +0.G.2C. +0.i.2K. +0.Q.2w. +0.w.2G. +2.2H.u. +3.2B.H. +3.2y.K. +4.F.2D. +8.2A.I. +8.2z.J. +a.2F.y. +a.2J.m. +g.2E.E. +0.d.2L. +0.H.2C. +0.j.2K. +0.r.2I. +0.R.2w. +0.x.2G. +2.2H.v. +3.2B.I. +3.2x.N. +3.2y.L. +3.n.2J. +8.2A.J. +8.2z.K. +a.2F.z. +g.2E.F. +i.c.2M. +0.e.2L. +0.k.2K. +0.y.2G. +1./.2u. +2.2H.w. +3.2B.J. +3.2x.O. +3.I.2C. +3.M.2y. +3.S.2w. +4.s.2I. +8.2A.K. +8.2z.L. +a.2J.o. +0.f.2L. +0.l.2K. +2./.2v. +2.2H.x. +3.2B.K. +3.2x.P. +3.J.2C. +4.2G.z. +4.G.2D. +4.t.2I. +8.2A.L. +8.2z.M. +a.2J.p. +c.T.2w. +0.g.2L. +0.H.2D. +0.K.2C. +0.m.2K. +0.U.2w. +2.2H.y. +3.2B.L. +3.2x.Q. +4.u.2I. +8.2A.M. +a.2F.A. +a.2J.q. +g.2E.G. +0.h.2L. +0.L.2C. +0.V.2w. +2.2H.z. +3.2B.M. +3.2x.R. +3.2y.N. +3.I.2D. +3.n.2K. +4.d.2M. +4.v.2I. +a.2F.B. +g.2E.H. +i.c.2N. +0.A.2G. +0.i.2L. +0.o.2K. +0.W.2w. +3.2y.O. +3.J.2D. +3.M.2C. +3.S.2x. +4.e.2M. +4.w.2I. +8.2z.N. +a.2F.C. +g.2E.I. +i.c.2O. +0.B.2G. +0.j.2L. +0.K.2D. +0.p.2K. +0.X.2w. +3.2x.T. +3.2y.P. +4.f.2M. +4.x.2I. +8.2A.N. +8.2z.O. +a.2F.D. +a.2J.r. +a.2P.c. +g.2E.J. +0.C.2G. +0.k.2L. +0.L.2D. +0.q.2K. +2.2H.A. +3.2B.N. +3.2x.U. +3.2y.Q. +4.g.2M. +4.y.2I. +8.2A.O. +8.2z.P. +a.2F.E. +a.2J.s. +c.Y.2w. +g.2E.K. +l.2q.2q. +0.D.2G. +0.d.2N. +0.h.2M. +0.l.2L. +0.N.2C. +0.Z.2w. +2.2H.B. +3.2B.O. +3.2x.V. +3.2y.R. +3.M.2D. +8.2A.P. +8.2z.Q. +a.2F.F. +a.2J.t. +f.2I.z. +g.2E.L. +i.c.2Q. +0.d.2O. +0.E.2G. +0.e.2N. +0.m.2L. +2.2H.C. +3.2B.P. +3.2x.W. +3.S.2y. +4.i.2M. +4.O.2C. +8.2A.Q. +8.2z.R. +a.2J.u. +g.2E.M. +i.c.2R. +0.e.2O. +0.F.2G. +0.f.2N. +0.P.2C. +0.r.2K. +2.2H.D. +3.2B.Q. +3.2x.X. +3.2y.T. +3.n.2L. +4.j.2M. +8.2A.R. +8.2z.S. +a.2J.v. +a.2P.d. +0.f.2O. +0.g.2N. +0.N.2D. +0.o.2L. +0.s.2K. +2.2H.E. +3.2B.R. +3.2x.Y. +3.2y.U. +4.A.2I. +4.k.2M. +4.Q.2C. +8.2A.S. +8.2z.T. +a.2F.G. +a.2J.w. +a.2P.e. +i.c.2S. +0.+.2w. +0.d.2Q. +0.g.2O. +0.h.2N. +0.O.2D. +0.p.2L. +0.R.2C. +0.t.2K. +2.2H.F. +3.2B.S. +3.2x.Z. +3.2y.V. +4.B.2I. +4.l.2M. +8.2A.T. +8.2z.U. +a.2F.H. +a.2J.x. +a.2P.f. +g.2E.N. +0./.2w. +0.e.2Q. +0.G.2G. +0.h.2O. +0.i.2N. +0.m.2M. +0.P.2D. +0.q.2L. +0.u.2K. +3.2B.T. +3.2y.W. +3.I.2F. +3.S.2C. +4.C.2I. +4.d.2R. +8.2A.U. +8.2z.V. +a.2J.y. +a.2P.g. +a.2V.0. +g.2E.O. +0.10.2w. +0.f.2Q. +0.H.2G. +0.i.2O. +0.Q.2D. +0.v.2K. +3.2B.U. +3.2y.X. +3.J.2F. +3.n.2M. +4.D.2I. +4.e.2R. +4.j.2N. +8.2A.V. +8.2z.W. +a.2J.z. +a.2P.h. +a.2V.1. +c.T.2C. +g.2E.P. +0.11.2w. +0.d.2S. +0.g.2Q. +0.j.2O. +0.R.2D. +0.w.2K. +2.2H.G. +3.2B.V. +3.2y.Y. +3.I.2G. +4.E.2I. +4.f.2R. +4.k.2N. +4.o.2M. +4.U.2C. +8.2A.W. +8.2z.X. +a.2F.K. +a.2P.i. +a.2V.2. +g.2E.Q. +0.e.2S. +0.h.2Q. +0.k.2O. +0.l.2N. +0.r.2L. +0.x.2K. +2.2H.H. +3.2B.W. +3.2x.+. +3.2y.Z. +3.J.2G. +3.S.2D. +4.12.2w. +4.F.2I. +4.g.2R. +4.p.2M. +4.V.2C. +5.c.2T. +8.2A.X. +8.2z.Y. +a.2F.L. +a.2P.j. +a.2V.3. +g.2E.R. +0.f.2S. +0.i.2Q. +0.K.2G. +0.l.2O. +0.m.2N. +0.s.2L. +0.W.2C. +0.y.2K. +2.2H.I. +3.2B.X. +3.2x./. +3.M.2F. +4.h.2R. +4.q.2M. +8.2A.Y. +8.2z.Z. +a.2J.A. +a.2P.k. +c.T.2D. +e.4.2V. +g.2E.S. +0.g.2S. +0.L.2G. +0.m.2O. +0.t.2L. +0.U.2D. +0.X.2C. +2.2H.J. +3.2B.Y. +3.2x.10. +3.n.2N. +4.2K.z. +4.i.2R. +4.j.2Q. +8.2A.Z. +a.2J.B. +a.2P.l. +a.2V.5. +g.2E.T. +0.13.2w. +0.u.2L. +0.V.2D. +2.1d.2v. +2.2H.K. +3.2B.Z. +3.2x.11. +3.M.2G. +3.n.2O. +4.G.2I. +4.h.2S. +4.j.2R. +4.k.2Q. +4.o.2N. +a.2J.C. +a.2P.m. +a.2V.6. +c.Y.2C. +g.2E.U. +0.i.2S. +0.l.2Q. +0.o.2O. +0.p.2N. +0.r.2M. +0.v.2L. +0.W.2D. +0.Z.2C. +2.2H.L. +3.2x.12. +3.2y.+. +3.n.2P. +4.14.2w. +4.H.2I. +4.k.2R. +a.2F.N. +a.2J.D. +d.2U.c. +e.7.2V. +g.2E.V. +0.A.2K. +0.j.2S. +0.m.2Q. +0.p.2O. +0.w.2L. +0.X.2D. +2.2H.M. +3.2y./. +3.I.2I. +4.15.2w. +4.l.2R. +4.q.2N. +4.s.2M. +8.2z.+. +a.2F.O. +a.2J.E. +a.2P.o. +a.2V.8. +g.2E.W. +0.16.2w. +0.B.2K. +0.k.2S. +0.N.2G. +0.q.2O. +0.t.2M. +0.x.2L. +3.2y.10. +3.J.2I. +3.n.2Q. +4.m.2R. +8.2A.+. +8.2z./. +a.2F.P. +a.2J.F. +a.2P.p. +c.Y.2D. +e.9.2V. +g.2E.X. +0.C.2K. +0.l.2S. +0.O.2G. +0.o.2Q. +0.u.2M. +0.y.2L. +3.2B.+. +3.2x.13. +3.2y.11. +3.n.2R. +4.17.2w. +4.K.2I. +4.Z.2D. +8.2A./. +8.2z.10. +a.2F.Q. +a.2P.q. +a.2V.a. +g.2E.Y. +0.+.2C. +0.D.2K. +0.L.2I. +0.m.2S. +0.P.2G. +0.p.2Q. +0.r.2N. +0.v.2M. +2.2H.N. +3.2B./. +3.2x.14. +3.2y.12. +4.18.2w. +4.o.2R. +8.2A.10. +8.2z.11. +a.2F.R. +a.2V.b. +d.2U.d. +f.2L.z. +g.2E.Z. +0.19.2w. +0.E.2K. +0.Q.2G. +0.q.2Q. +0.r.2O. +0.s.2N. +0.w.2M. +2.2H.O. +3.2B.10. +3.2x.15. +3.M.2I. +3.n.2S. +3.S.2F. +4./.2C. +4.p.2R. +8.2A.11. +8.2z.12. +a.2J.G. +a.2V.c. +d.2U.e. +0.F.2K. +0.o.2S. +0.R.2G. +0.s.2O. +0.t.2N. +0.x.2M. +1.1h.2u. +2.2H.P. +3.2B.11. +3.2W.c. +3.2x.16. +4.10.2C. +4.1a.2w. +4.q.2R. +8.2A.12. +a.2F.T. +a.2J.H. +a.2P.r. +d.2U.f. +0.+.2D. +0.1b.2w. +0.A.2L. +0.p.2S. +0.t.2O. +0.u.2N. +0.y.2M. +2.1h.2v. +2.2H.Q. +3.2B.12. +3.2x.17. +3.2y.13. +3.I.2J. +3.S.2G. +4.11.2C. +5.c.2X. +5.k.2T. +a.2F.U. +a.2P.s. +d.2U.g. +0./.2D. +0.B.2L. +0.q.2S. +0.r.2Q. +0.u.2O. +0.v.2N. +2.2H.R. +3.2x.18. +3.2y.14. +3.J.2J. +4.12.2C. +4.1c.2w. +4.N.2I. +8.2z.13. +a.2F.V. +a.2P.t. +c.T.2G. +d.2U.h. +f.2M.z. +g.2E.+. +0.10.2D. +0.C.2L. +0.G.2K. +0.r.2R. +0.s.2Q. +0.U.2G. +0.v.2O. +0.w.2N. +2.2H.S. +3.2x.19. +3.2y.15. +4.O.2I. +8.2A.13. +8.2z.14. +a.2F.W. +a.2J.K. +a.2P.u. +a.2V.d. +d.2U.i. +g.2E./. +0.11.2D. +0.D.2L. +0.H.2K. +0.t.2Q. +0.V.2G. +0.w.2O. +0.x.2N. +2.2H.T. +3.2B.13. +3.2W.d. +3.2x.1a. +3.2y.16. +4.1d.2w. +4.P.2I. +4.s.2R. +8.2A.14. +8.2z.15. +a.2F.X. +a.2J.L. +a.2P.v. +a.2V.e. +d.2U.j. +g.2E.10. +0.13.2C. +0.E.2L. +0.r.2S. +0.u.2Q. +0.W.2G. +0.x.2O. +0.y.2N. +2.2H.U. +3.2B.14. +3.2W.e. +3.2x.1b. +3.2y.17. +3.I.2K. +3.M.2J. +4.12.2D. +4.A.2M. +4.Q.2I. +4.t.2R. +8.2A.15. +8.2z.16. +a.2F.Y. +a.2P.w. +a.2V.f. +a.2Y.c. +d.2U.k. +g.2E.11. +0.2N.z. +0.F.2L. +0.s.2S. +0.v.2Q. +0.X.2G. +0.y.2O. +2.2H.V. +3.2B.15. +3.2W.f. +3.2x.1c. +3.2y.18. +3.J.2K. +4.14.2C. +4.B.2M. +4.R.2I. +4.u.2R. +8.2A.16. +8.2z.17. +a.2F.Z. +a.2P.x. +a.2V.g. +d.2U.l. +g.2E.12. +0.1e.2w. +0.2O.z. +0.K.2K. +0.t.2S. +0.w.2Q. +2.2H.W. +3.2B.16. +3.2W.g. +3.2y.19. +3.S.2I. +4.15.2C. +4.C.2M. +4.v.2R. +8.2A.17. +8.2z.18. +a.2P.y. +a.2V.h. +c.Y.2G. +d.2U.m. +0.13.2D. +0.1f.2w. +0.L.2K. +0.u.2S. +0.x.2Q. +0.Z.2G. +2.2H.X. +3.2B.17. +3.2W.h. +3.2x.1d. +3.2y.1a. +4.16.2C. +4.D.2M. +4.w.2R. +8.2A.18. +8.2z.19. +9.c.2Z. +a.2J.N. +a.2P.z. +a.2V.i. +c.T.2I. +d.2U.n. +0.14.2D. +0.A.2N. +0.G.2L. +0.v.2S. +0.y.2Q. +2.2H.Y. +3.2B.18. +3.2W.i. +3.2y.1b. +3.M.2K. +4.17.2C. +4.E.2M. +4.U.2I. +4.x.2R. +8.2A.19. +8.2z.1a. +a.2J.O. +a.2V.j. +a.2Y.d. +d.2U.o. +f.2w.1g. +g.2E.13. +0.15.2D. +0.A.2O. +0.B.2N. +0.H.2L. +0.w.2S. +2.2H.Z. +3.2B.19. +3.2W.j. +3.2y.1c. +4.18.2C. +4.F.2M. +4.r.2T. +4.V.2I. +4.y.2R. +8.2A.1a. +8.2z.1b. +a.2F.+. +a.2J.P. +a.2V.k. +a.2Y.e. +d.2U.p. +f.2Q.z. +g.2E.14. +0.16.2D. +0.19.2C. +0.B.2O. +0.x.2S. +3.2B.1a. +3.2W.k. +3.2x.1e. +3.I.2L. +4.C.2N. +4.W.2I. +7.s.2T. +8.2A.1b. +8.2z.1c. +a.2F./. +a.2J.Q. +a.2P.A. +a.2V.l. +a.2Y.f. +d.2U.q. +f.2R.z. +g.2E.15. +0.+.2G. +0.17.2D. +0.1h.2w. +0.C.2O. +0.N.2K. +0.y.2S. +3.2B.1b. +3.2W.l. +3.2x.1f. +3.2y.1d. +3.J.2L. +4.1a.2C. +4.D.2N. +4.X.2I. +5.k.2X. +8.2A.1c. +a.2F.10. +a.2J.R. +a.2P.B. +a.2V.m. +a.2Y.g. +g.2E.16. +0./.2G. +0.18.2D. +0.2S.z. +0.A.2Q. +0.D.2O. +0.K.2L. +0.O.2K. +3.2B.1c. +3.2W.m. +3.2x.1g. +3.n.2V. +3.S.2J. +4.1b.2C. +4.1i.2w. +4.E.2N. +4.G.2M. +4.u.2T. +8.2z.1d. +a.2F.11. +a.2P.C. +a.2Y.h. +c.Y.2I. +g.2E.17. +0.10.2G. +0.19.2D. +0.1j.2w. +0.B.2Q. +0.E.2O. +0.L.2L. +0.P.2K. +1.1t.2u. +2.2H.+. +3.n.2W. +4.1c.2C. +4.A.2R. +4.F.2N. +4.H.2M. +4.Z.2I. +8.2+.c. +8.2A.1d. +a.2F.12. +a.2J.T. +a.2P.D. +a.2V.o. +a.2Y.i. +d.2U.r. +g.2E.18. +0.11.2G. +0.C.2Q. +0.F.2O. +0.Q.2K. +2.1t.2v. +2.2H./. +3.2B.1d. +3.2W.o. +3.2y.1e. +3.I.2M. +3.M.2L. +4.1a.2D. +4.B.2R. +a.2J.U. +a.2P.E. +a.2V.p. +a.2Y.j. +d.2U.s. +g.2E.19. +0.1b.2D. +0.A.2S. +0.D.2Q. +0.R.2K. +2.2H.10. +3.2W.p. +3.2x.1h. +3.2y.1f. +3.J.2M. +4.12.2G. +4.1d.2C. +4.C.2R. +8.2z.1e. +a.2J.V. +a.2P.F. +a.2V.q. +a.2Y.k. +d.2U.t. +g.2E.1a. +0.1c.2D. +0.1k.2w. +0.B.2S. +0.E.2Q. +0.G.2N. +0.K.2M. +2.2/.c. +2.2H.11. +3.2W.q. +3.2x.1i. +3.2y.1g. +3.S.2K. +4.D.2R. +8.2A.1e. +8.2z.1f. +a.2F.13. +a.2J.W. +a.2Y.l. +d.2U.u. +g.2E.1b. +0.+.2I. +0.C.2S. +0.F.2Q. +0.G.2O. +0.H.2N. +0.L.2M. +0.N.2L. +2.2H.12. +3.1l.2w. +3.2B.1e. +3.2x.1j. +4.E.2R. +8.2+.d. +8.2A.1f. +8.2z.1g. +a.2F.14. +a.2J.X. +a.2Y.m. +c.T.2K. +d.2U.v. +g.2E.1c. +0.13.2G. +0.1d.2D. +0.1m.2w. +0.D.2S. +0.H.2O. +0.O.2L. +0.U.2K. +3.2B.1f. +3.I.2N. +3.M.2M. +3.n.2Y. +4./.2I. +4.1e.2C. +4.F.2R. +8.2+.e. +8.2A.1g. +9.k.2Z. +a.2F.15. +a.2J.Y. +a.2P.G. +a.2V.r. +d.2U.w. +0.14.2G. +0.1n.2w. +0.E.2S. +0.P.2L. +0.V.2K. +3.2B.1g. +3.2W.r. +3.2y.1h. +3.I.2O. +3.J.2N. +4.10.2I. +4.1f.2C. +8.2+.f. +a.2F.16. +a.2J.Z. +a.2P.H. +a.2V.s. +a.2Y.o. +d.2U.x. +g.2E.1d. +0.15.2G. +0.1o.2w. +0.F.2S. +0.G.2Q. +0.K.2N. +0.Q.2L. +0.W.2K. +2.2/.d. +2.2H.13. +3.2W.s. +3.2x.1k. +3.2y.1i. +3.I.2P. +3.J.2O. +4.11.2I. +4.r.2X. +8.2+.g. +8.2z.1h. +a.2F.17. +a.2V.t. +a.2Y.p. +d.2U.y. +f.2C.1g. +i.c.30. +0.16.2G. +0.1e.2D. +0.1p.2w. +0.H.2Q. +0.K.2O. +0.L.2N. +0.R.2L. +0.X.2K. +2.2/.e. +2.2H.14. +3.1l.2x. +3.2W.t. +3.2y.1j. +3.J.2P. +4.12.2I. +4.G.2R. +4.N.2M. +7.s.2X. +8.2+.h. +8.2A.1h. +8.2z.1i. +a.2F.18. +a.2V.u. +a.2Y.q. +d.2U.z. +0.17.2G. +0.1f.2D. +0.1q.2w. +0.L.2O. +2.2/.f. +2.2H.15. +3.2B.1h. +3.2W.u. +3.2x.1m. +3.I.2Q. +3.M.2N. +3.S.2L. +4.H.2R. +4.O.2M. +8.2+.i. +8.2A.1i. +8.2z.1j. +a.2F.19. +a.2P.K. +a.2V.v. +c.Y.2K. +g.2E.1e. +0.18.2G. +0.1r.2w. +0.2D.1g. +0.G.2S. +0.Z.2K. +2.2/.g. +2.2H.16. +3.2B.1i. +3.2W.v. +3.2x.1n. +3.I.2R. +3.J.2Q. +3.M.2O. +4.1h.2C. +4.P.2M. +4.T.2L. +4.u.2X. +8.2+.j. +8.2A.1j. +a.2F.1a. +a.2J.+. +a.2P.L. +a.2V.w. +g.2E.1f. +0.19.2G. +0.1s.2w. +0.d.30. +0.H.2S. +0.K.2Q. +0.U.2L. +2.2/.h. +2.2H.17. +3.2B.1j. +3.2W.w. +3.2x.1o. +3.2y.1k. +3.J.2R. +3.M.2P. +4.13.2I. +4.1i.2C. +4.Q.2M. +8.2+.k. +a.2F.1b. +a.2J./. +a.2V.x. +a.2Y.r. +d.2U.A. +g.2E.1g. +0.1a.2G. +0.1j.2C. +0.e.30. +0.L.2Q. +0.N.2N. +0.R.2M. +0.V.2L. +2.2/.i. +2.2H.18. +3.1l.2y. +3.2W.x. +3.2x.1p. +3.I.2S. +4.14.2I. +4.1t.2w. +4.K.2R. +8.2+.l. +8.2z.1k. +a.2F.1c. +a.2J.10. +a.2V.y. +a.2Y.s. +d.2U.B. +0.1b.2G. +0.1h.2D. +0.f.30. +0.L.2R. +0.N.2O. +0.W.2L. +2.2/.j. +2.2H.19. +3.1u.2w. +3.2W.y. +3.2x.1q. +3.2y.1m. +3.J.2S. +3.M.2Q. +3.S.2M. +4.15.2I. +4.O.2N. +8.2+.m. +8.2A.1k. +8.2z.1l. +a.2J.11. +a.2V.z. +a.2Y.t. +d.2U.C. +0.+.2K. +0.1c.2G. +0.1v.2w. +0.g.30. +0.K.2S. +0.O.2O. +0.P.2N. +0.X.2L. +2.2/.k. +2.2H.1a. +3.2B.1k. +3.2W.z. +3.2x.1r. +3.2y.1n. +3.M.2R. +4.16.2I. +4.1i.2D. +8.2+.n. +8.2A.1l. +8.2z.1m. +9.r.2Z. +a.2F.1d. +a.2J.12. +a.2P.N. +a.2Y.u. +c.T.2M. +d.2U.D. +g.2E.1h. +0./.2K. +0.1j.2D. +0.1k.2C. +0.1w.2w. +0.h.30. +0.L.2S. +0.P.2O. +0.Q.2N. +2.2/.l. +2.2H.1b. +3.2B.1l. +3.2x.1s. +3.2y.1o. +4.17.2I. +4.U.2M. +8.2+.o. +8.2A.1m. +8.2z.1n. +9.s.2Z. +a.2P.O. +a.2Y.v. +c.Y.2L. +d.2U.E. +g.2E.1i. +0.10.2K. +0.1d.2G. +0.1x.2w. +0.i.30. +0.N.2Q. +0.Q.2O. +0.R.2N. +0.Z.2L. +1.31.c. +2.2/.m. +2.2H.1c. +3.1l.2C. +3.2B.1m. +3.2x.1t. +3.2y.1p. +3.M.2S. +4.18.2I. +4.V.2M. +8.2+.p. +8.2A.1n. +8.2z.1o. +a.2P.P. +a.2V.A. +a.2Y.w. +d.2U.F. +g.2E.1j. +0.11.2K. +0.19.2I. +0.1y.2w. +0.j.30. +0.O.2Q. +0.R.2O. +2.2/.n. +3.1u.2x. +3.2B.1n. +3.2W.A. +3.2y.1q. +3.S.2N. +4.1m.2C. +4.N.2R. +4.W.2M. +8.2+.q. +8.2A.1o. +8.2z.1p. +9.u.2Z. +a.2F.1e. +a.2J.13. +a.2P.Q. +a.2V.B. +a.2Y.x. +0.1k.2D. +0.1n.2C. +0.1z.2w. +0.k.30. +0.P.2Q. +2.2/.o. +2.2H.1d. +3.2B.1o. +3.2W.B. +3.2x.1v. +3.2y.1r. +3.S.2O. +4.12.2K. +4.1a.2I. +4.O.2R. +4.X.2M. +8.2A.1p. +8.2z.1q. +a.2F.1f. +a.2J.14. +a.2P.R. +a.2V.C. +a.2Y.y. +c.T.2N. +0.1A.2w. +0.1e.2G. +0.1o.2C. +0.l.30. +0.N.2S. +0.Q.2Q. +2.2/.p. +3.1l.2D. +3.2B.1p. +3.2W.C. +3.2x.1w. +3.2y.1s. +3.S.2P. +4.1b.2I. +4.P.2R. +4.U.2N. +8.2A.1q. +8.2z.1r. +a.2F.1g. +a.2J.15. +a.2V.D. +a.2Y.z. +c.T.2O. +c.Y.2M. +d.2U.G. +g.2E.1k. +0.+.2L. +0.1B.2w. +0.1f.2G. +0.1m.2D. +0.1p.2C. +0.m.30. +0.O.2S. +0.R.2Q. +0.U.2O. +0.Z.2M. +1.31.d. +2.2/.q. +3.2B.1q. +3.2W.D. +3.2x.1x. +3.2y.1t. +4.1c.2I. +4.Q.2R. +4.V.2N. +8.2+.r. +8.2A.1r. +8.2z.1s. +a.2J.16. +a.2P.T. +a.2V.E. +a.32.c. +d.2U.H. +g.2E.1l. +0./.2L. +0.13.2K. +0.1C.2w. +0.1n.2D. +0.1q.2C. +0.P.2S. +0.V.2O. +1.31.e. +2.2H.1e. +3.1u.2y. +3.2B.1r. +3.2W.E. +3.2x.1y. +3.n.30. +3.S.2Q. +4.2G.1g. +4.R.2R. +4.W.2N. +8.2+.s. +8.2A.1s. +8.2z.1t. +a.2J.17. +a.2P.U. +a.2V.F. +d.2U.I. +g.2E.1m. +0.10.2L. +0.14.2K. +0.1D.2w. +0.1o.2D. +0.o.30. +0.Q.2S. +0.W.2O. +1.31.f. +2.2H.1f. +3.2B.1s. +3.2W.F. +3.2x.1z. +3.2y.1v. +3.S.2R. +4.1d.2I. +4.1r.2C. +4.X.2N. +8.2+.t. +8.2A.1t. +8.2z.1u. +a.2F.1h. +a.2J.18. +a.2P.V. +a.2Y.A. +c.T.2Q. +d.2U.J. +g.2E.1n. +0.11.2L. +0.15.2K. +0.1p.2D. +0.p.30. +0.R.2S. +0.U.2Q. +0.X.2O. +1.31.g. +1.33.c. +2.2/.r. +2.2H.1g. +3.2B.1t. +3.2x.1A. +3.2y.1w. +4.1s.2C. +8.2+.u. +8.2A.1u. +8.2z.1v. +a.2F.1i. +a.2J.19. +a.2P.W. +a.2Y.B. +c.T.2R. +c.Y.2N. +d.2U.K. +g.2E.1o. +0.+.2M. +0.16.2K. +0.1E.2w. +0.1h.2G. +0.1q.2D. +0.q.30. +0.V.2Q. +0.Z.2N. +1.31.h. +2.2/.s. +3.2B.1u. +3.2x.1B. +3.2y.1x. +3.S.2S. +4.12.2L. +4.1t.2C. +4.U.2R. +8.2+.v. +8.2A.1v. +8.2z.1w. +a.2F.1j. +a.2J.1a. +a.2P.X. +a.2V.G. +a.2Y.C. +a.32.d. +c.Y.2O. +d.2U.L. +g.2E.1p. +i.c.34. +0.17.2K. +0.1F.2w. +0.1i.2G. +0.1r.2D. +0.W.2Q. +0.Z.2O. +1.31.i. +2.2/.t. +3.1u.2C. +3.2B.1v. +3.2W.G. +3.2x.1C. +3.2y.1y. +4./.2M. +4.1e.2I. +4.V.2R. +8.2+.w. +8.2A.1w. +8.2z.1x. +9.c.35. +a.2J.1b. +a.2P.Y. +a.2V.H. +a.2Y.D. +a.32.e. +c.T.2S. +d.2U.M. +g.2E.1q. +0.18.2K. +0.1G.2w. +0.1j.2G. +0.1s.2D. +0.1v.2C. +0.U.2S. +0.X.2Q. +1.31.j. +2.2/.u. +2.2H.1h. +3.2B.1w. +3.2W.H. +3.2x.1D. +3.2y.1z. +3.I.2V. +4.10.2M. +4.1f.2I. +4.W.2R. +8.2+.x. +8.2A.1x. +8.2z.1y. +9.c.36. +a.2J.1c. +a.2P.Z. +a.2Y.E. +a.32.f. +g.2E.1r. +0.13.2L. +0.19.2K. +0.1H.2w. +0.r.30. +0.V.2S. +1.31.k. +1.33.d. +2.2/.v. +2.2H.1i. +3.2B.1x. +3.2y.1A. +3.I.2W. +3.J.2V. +4.11.2M. +4.1t.2D. +4.1w.2C. +4.X.2R. +8.2+.y. +8.2A.1y. +8.2z.1z. +a.2F.1k. +a.2Y.F. +a.32.g. +c.Y.2Q. +f.2I.1g. +g.2E.1s. +0.+.2N. +0.14.2L. +0.1a.2K. +0.1x.2C. +0.d.34. +0.s.30. +0.W.2S. +0.Z.2Q. +1.31.l. +1.33.e. +2.2/.w. +2.2H.1j. +3.1l.2F. +3.1u.2D. +3.2B.1y. +3.2x.1E. +3.2y.1B. +3.J.2W. +4.12.2M. +4.1I.2w. +8.2+.z. +8.2A.1z. +8.2z.1A. +a.2J.1d. +a.2V.K. +a.32.h. +c.Y.2R. +d.2U.N. +g.2E.1t. +0.+.2O. +0.15.2L. +0.1b.2K. +0.1J.2w. +0.1k.2G. +0.1v.2D. +0.1y.2C. +0.e.34. +0.t.30. +0.X.2S. +1.31.m. +1.33.f. +2.2/.x. +3.2B.1z. +3.2W.K. +3.2x.1F. +3.2y.1C. +4./.2N. +4.Z.2R. +8.2A.1A. +8.2z.1B. +a.2F.1m. +a.2V.L. +a.32.i. +d.2U.O. +g.2E.1u. +0./.2O. +0.16.2L. +0.1c.2K. +0.1w.2D. +0.f.34. +0.u.30. +1.31.n. +1.33.g. +2.2/.y. +3.1l.2G. +3.2B.1A. +3.2W.L. +3.2x.1G. +3.2y.1D. +3.M.2V. +4.10.2N. +4.1h.2I. +4.1z.2C. +8.2A.1B. +8.2z.1C. +a.2F.1n. +a.2P.+. +a.2Y.G. +a.32.j. +c.Y.2S. +d.2U.P. +g.2E.1v. +i.c.37. +0.10.2O. +0.13.2M. +0.17.2L. +0.1m.2G. +0.1x.2D. +0.g.34. +0.v.30. +0.Z.2S. +1.31.o. +1.33.h. +2.2/.z. +2.2H.1k. +3.2B.1B. +3.2x.1H. +3.M.2W. +4.11.2N. +4.1A.2C. +4.1i.2I. +8.2+.A. +8.2A.1C. +8.2z.1D. +9.c.38. +a.2F.1o. +a.2J.1e. +a.2P./. +a.2Y.H. +a.32.k. +d.2U.Q. +g.2E.1w. +0.+.2Q. +0.11.2O. +0.18.2L. +0.1d.2K. +0.1j.2I. +0.1n.2G. +0.1y.2D. +0.h.34. +0.w.30. +1.31.p. +1.33.i. +2.2H.1l. +3.2B.1C. +3.2x.1I. +3.2y.1E. +3.I.2Y. +4.12.2N. +4.14.2M. +4.1B.2C. +4.1K.2w. +8.2+.B. +8.2A.1D. +a.2F.1p. +a.2J.1f. +a.2P.10. +a.32.l. +d.2U.R. +g.2E.1x. +0./.2Q. +0.+.2R. +0.19.2L. +0.1C.2C. +0.1L.2w. +0.1o.2G. +0.1z.2D. +0.i.34. +0.x.30. +1.31.q. +1.33.j. +2.2H.1m. +3.2B.1D. +3.2x.1J. +3.2y.1F. +3.J.2Y. +4.12.2O. +4.15.2M. +8.2+.C. +8.2z.1E. +a.2F.1q. +a.2J.1g. +a.2P.11. +a.2V.N. +a.32.m. +d.2U.S. +g.2E.1y. +0.10.2Q. +0.16.2M. +0.1A.2D. +0.1M.2w. +0.1p.2G. +0.j.34. +0.y.30. +1.1V.2u. +1.33.k. +2.2/.A. +2.2H.1n. +3.2W.N. +3.2y.1G. +3.n.32. +4./.2R. +4.1a.2L. +4.1D.2C. +8.2+.D. +8.2A.1E. +8.2z.1F. +a.2F.1r. +a.2P.12. +a.2V.O. +a.2Y.K. +c.37.d. +d.2U.T. +g.2E.1z. +i.c.39. +0.+.2S. +0.11.2Q. +0.13.2N. +0.1b.2L. +0.1e.2K. +0.1q.2G. +0.k.34. +1.33.l. +2.1V.2v. +2.2/.B. +2.2H.1o. +3.2B.1E. +3.2W.O. +3.2y.1H. +4.10.2R. +4.17.2M. +4.1B.2D. +4.1k.2I. +4.1N.2w. +8.2+.E. +8.2A.1F. +8.2z.1G. +9.c.3a. +a.2F.1s. +a.2V.P. +a.2Y.L. +a.32.o. +c.37.e. +d.2U.U. +f.30.z. +g.2E.1A. +0./.2S. +0.13.2O. +0.1C.2D. +0.1c.2L. +0.1f.2K. +0.1O.2w. +0.1r.2G. +0.l.34. +1.31.r. +1.33.m. +2.2/.C. +2.2H.1p. +3.1l.2I. +3.2B.1F. +3.2W.P. +3.2x.1K. +3.2y.1I. +3.M.2Y. +4.11.2R. +4.12.2Q. +4.14.2N. +4.18.2M. +4.1E.2C. +4.c.3b. +8.2+.F. +8.2A.1G. +8.2z.1H. +9.k.35. +a.2F.1t. +a.2J.1h. +a.2V.Q. +a.32.p. +d.2U.V. +e.0.3h. +f.37.f. +g.2E.1B. +0.10.2S. +0.14.2O. +0.19.2M. +0.1D.2D. +0.1P.2w. +0.1s.2G. +0.m.34. +1.31.s. +1.33.n. +2.2/.D. +2.2H.1q. +3.1u.2F. +3.2B.1G. +3.2W.Q. +3.2x.1L. +3.2y.1J. +4.12.2R. +4.15.2N. +4.1F.2C. +4.1m.2I. +4.2K.1g. +6.3i.0. +8.2A.1H. +8.2z.1I. +9.c.3c. +9.k.36. +a.2J.1i. +a.2P.13. +a.2V.R. +a.32.q. +d.2U.W. +e.1.3h. +f.37.g. +g.2E.1C. +0.11.2S. +0.15.2O. +0.1d.2L. +0.1Q.2w. +0.1t.2G. +0.A.30. +0.d.39. +1.31.t. +1.33.o. +2.2/.E. +2.2H.1r. +3.2B.1H. +3.2W.R. +3.2x.1M. +3.n.34. +3.S.2V. +4.16.2N. +4.1a.2M. +4.1G.2C. +4.1n.2I. +6.3i.1. +8.2A.1I. +8.2z.1J. +9.c.3d. +a.2F.1v. +a.2J.1j. +a.2P.14. +c.37.h. +d.2U.X. +e.2.3h. +g.2E.1D. +0.13.2Q. +0.16.2O. +0.1E.2D. +0.1o.2I. +0.B.30. +0.e.39. +0.o.34. +1.31.u. +1.33.p. +2.2/.F. +2.2H.1s. +3.1u.2G. +3.2B.1I. +3.2x.1N. +3.S.2W. +4.12.2S. +4.17.2N. +4.1b.2M. +4.1H.2C. +6.3i.2. +8.2+.G. +8.2A.1J. +a.2F.1w. +a.2P.15. +a.2V.T. +a.2Y.N. +c.3f.a. +d.2U.Y. +e.3.3h. +f.37.i. +i.c.3e. +0.14.2Q. +0.17.2O. +0.1F.2D. +0.1h.2K. +0.1v.2G. +0.C.30. +0.f.39. +0.p.34. +1.31.v. +1.33.q. +2.2H.1t. +3.2B.1J. +3.2W.T. +3.2x.1O. +3.2y.1K. +4.13.2R. +4.18.2N. +4.1c.2M. +4.1I.2C. +4.1p.2I. +6.3i.3. +8.2+.H. +a.2F.1x. +a.2P.16. +a.2V.U. +a.2Y.O. +a.32.r. +d.2U.Z. +e.4.3h. +f.37.j. +g.2E.1E. +0.15.2Q. +0.18.2O. +0.19.2N. +0.1e.2L. +0.1G.2D. +0.1i.2K. +0.1J.2C. +0.1w.2G. +0.D.30. +0.g.39. +0.q.34. +1.31.w. +2.2H.1u. +3.2W.U. +3.2x.1P. +3.2y.1L. +4.14.2R. +4.1q.2I. +5./.2T. +8.2+.I. +8.2z.1K. +a.2F.1y. +a.2J.1k. +a.2P.17. +a.2V.V. +a.2Y.P. +a.32.s. +e.0.3m. +e.4.3i. +e.5.3h. +f.37.k. +g.2E.1F. +i.c.3f. +0.13.2S. +0.16.2Q. +0.19.2O. +0.1f.2L. +0.1H.2D. +0.1j.2K. +0.1x.2G. +0.E.30. +0.h.39. +1.31.x. +2.2/.G. +2.2H.1v. +3.1l.2J. +3.2W.V. +3.2x.1Q. +3.2y.1M. +4.15.2R. +4.1a.2N. +4.1d.2M. +4.1r.2I. +6.3i.5. +8.2+.J. +8.2A.1K. +8.2z.1L. +9.k.38. +a.2F.1z. +a.2P.18. +a.2V.W. +a.2Y.Q. +a.32.t. +e.1.3m. +e.6.3h. +f.1R.2w. +f.37.l. +g.2E.1G. +0.14.2S. +0.17.2Q. +0.1a.2O. +0.1I.2D. +0.1y.2G. +0.d.3e. +0.F.30. +0.i.39. +1.31.y. +1.33.r. +2.1.3n. +2.2/.H. +2.2H.1w. +3.2B.1K. +3.2W.W. +3.2y.1N. +4.16.2R. +4.1b.2N. +4.1s.2I. +4.1S.2w. +6.3i.6. +8.2+.K. +8.2A.1L. +8.2z.1M. +a.2F.1A. +a.2J.1m. +a.2P.19. +a.2V.X. +a.2Y.R. +a.32.u. +e.2.3m. +e.7.3h. +f.2L.1g. +f.37.m. +g.2E.1H. +0.15.2S. +0.18.2Q. +0.1b.2O. +0.1J.2D. +0.1z.2G. +0.e.3e. +0.j.39. +0.r.34. +1.31.z. +1.33.s. +2.2.3n. +2.2/.I. +2.2H.1x. +3.2B.1L. +3.2W.X. +3.2y.1O. +3.n.37. +3.S.2Y. +4.17.2R. +4.1c.2N. +4.1K.2C. +4.1t.2I. +8.2+.L. +8.2A.1M. +8.2z.1N. +a.2F.1B. +a.2J.1n. +a.2P.1a. +a.2V.Y. +a.32.v. +d.2U.+. +e.3.3m. +e.7.3i. +e.8.3h. +g.2E.1I. +0.16.2S. +0.19.2Q. +0.1A.2G. +0.1c.2O. +0.1k.2K. +0.1L.2C. +0.f.3e. +0.k.39. +0.s.34. +1.33.t. +2.2/.J. +2.2H.1y. +2.3.3n. +3.1u.2I. +3.2B.1M. +3.2W.Y. +3.2y.1P. +4.18.2R. +4.1e.2M. +4.r.35. +6.3i.8. +8.2+.M. +8.2A.1N. +8.2z.1O. +a.2F.1C. +a.2J.1o. +a.2P.1b. +a.2V.Z. +a.2Y.T. +a.32.w. +c.37.o. +c.3f.d. +d.2U./. +e.0.3q. +e.4.3m. +e.9.3h. +g.2E.1J. +i.c.3g. +0.17.2S. +0.1a.2Q. +0.1B.2G. +0.1h.2L. +0.1M.2C. +0.1V.2w. +0.G.30. +0.g.3e. +0.l.39. +0.t.34. +1.33.u. +2.2/.K. +2.2H.1z. +2.4.3n. +3.1l.2K. +3.2B.1N. +3.2W.Z. +3.2x.1R. +3.2y.1Q. +4.19.2R. +4.1d.2N. +4.1f.2M. +4.1v.2I. +4.k.3a. +8.2A.1O. +8.2z.1P. +9.r.36. +9.s.35. +a.2F.1D. +a.2J.1p. +a.2P.1c. +a.2Y.U. +a.32.x. +c.37.p. +c.3f.e. +d.2U.10. +e.0.3r. +e.1.3q. +e.5.3m. +e.9.3i. +e.a.3h. +0.18.2S. +0.1b.2Q. +0.1C.2G. +0.1d.2O. +0.1K.2D. +0.1m.2K. +0.H.30. +0.m.39. +0.u.34. +1.31.A. +1.33.v. +2.2/.L. +2.2H.1A. +2.5.3n. +3.2B.1O. +3.2x.1S. +4.1a.2R. +4.1i.2L. +4.1N.2C. +4.1w.2I. +4.h.3e. +6.3i.a. +8.2A.1P. +8.2z.1Q. +9.k.3b. +9.s.36. +a.2J.1q. +a.2Y.V. +a.32.y. +c.3f.f. +d.2U.11. +e.1.3r. +e.2.3q. +e.6.3m. +e.b.3h. +f.1W.2w. +f.2M.1g. +f.37.q. +0.19.2S. +0.1c.2Q. +0.1D.2G. +0.1j.2L. +0.1L.2D. +0.1n.2K. +0.1O.2C. +0.i.3e. +0.v.34. +1.31.B. +1.33.w. +2.2/.M. +2.2H.1B. +2.6.3n. +3.2B.1P. +3.I.30. +3.n.39. +4.1b.2R. +4.1x.2I. +4.u.35. +6.3i.b. +8.2+.N. +8.2A.1Q. +9.k.3c. +a.2F.1E. +a.2J.1r. +a.2P.1d. +a.2Y.W. +a.32.z. +c.3f.g. +d.2U.12. +e.2.3r. +e.3.3q. +e.7.3m. +g.2E.1K. +i.c.3h. +0.1a.2S. +0.1e.2N. +0.1M.2D. +0.1o.2K. +0.1P.2C. +0.d.3g. +0.j.3e. +0.o.39. +0.w.34. +1.31.C. +1.33.x. +2.2H.1C. +2.7.3n. +3.2B.1Q. +3.J.30. +4.1c.2R. +4.1X.2w. +4.1y.2I. +4.u.36. +8.2+.O. +9.k.3d. +a.2F.1F. +a.2J.1s. +a.2V.+. +a.2Y.X. +c.3f.h. +e.3.3r. +e.4.3q. +e.8.3m. +g.2E.1L. +i.c.3i. +0.1b.2S. +0.1d.2Q. +0.1E.2G. +0.1e.2O. +0.1p.2K. +0.e.3g. +0.k.3e. +0.p.39. +0.x.34. +1.31.D. +1.33.y. +2.2H.1D. +2.8.3n. +3.2W.+. +3.2x.1V. +3.2y.1R. +4.1f.2N. +4.1h.2M. +4.1N.2D. +4.1z.2I. +4.K.30. +5.c.3j. +8.2+.P. +a.2F.1G. +a.2J.1t. +a.2V./. +a.2Y.Y. +c.37.r. +c.3f.i. +e.4.3r. +e.5.3q. +e.9.3m. +f.1Q.2C. +g.2E.1M. +0.1c.2S. +0.1F.2G. +0.1f.2O. +0.1k.2L. +0.1O.2D. +0.1q.2K. +0.2N.1g. +0.f.3g. +0.L.30. +0.l.3e. +0.q.39. +0.y.34. +1.31.E. +1.33.z. +2.2/.N. +2.9.3n. +3.1u.2J. +3.2W./. +3.2x.1W. +3.2y.1S. +4.1A.2I. +4.1d.2R. +4.1i.2M. +4.3f.j. +8.2+.Q. +8.2z.1R. +9.c.3k. +9.r.38. +a.2F.1H. +a.2P.1e. +a.2V.10. +a.2Y.Z. +d.2U.13. +e.32.A. +e.5.3r. +e.6.3q. +e.a.3m. +f.37.s. +g.2E.1N. +0.1G.2G. +0.1j.2M. +0.1P.2D. +0.1r.2K. +0.2O.1g. +0.34.z. +0.g.3g. +0.m.3e. +1.31.F. +2.2/.O. +2.2H.1E. +2.a.3n. +3.1l.2L. +3.2W.10. +3.M.30. +4.1B.2I. +4.d.3h. +5./.2X. +5.c.3l. +8.2+.R. +8.2A.1R. +8.2z.1S. +9.s.38. +a.2F.1I. +a.2J.1v. +a.2P.1f. +a.2V.11. +a.32.B. +c.3f.k. +d.2U.14. +e.6.3r. +e.7.3q. +e.b.3m. +f.37.t. +g.2E.1O. +0.1d.2S. +0.1H.2G. +0.1m.2L. +0.1Q.2D. +0.1s.2K. +0.h.3g. +2.2/.P. +2.2H.1F. +2.b.3n. +3.2B.1R. +3.2W.11. +3.2x.1X. +3.n.3e. +4.1C.2I. +4.1e.2Q. +4.e.3h. +6.3i.d. +8.2+.S. +8.2A.1S. +a.2F.1J. +a.2J.1w. +a.2P.1g. +a.2V.12. +a.32.C. +c.3f.l. +d.2U.15. +e.7.3r. +e.8.3q. +f.37.u. +g.2E.1P. +i.c.3m. +0.1f.2Q. +0.1I.2G. +0.1n.2L. +0.1t.2K. +0.i.3g. +0.o.3e. +0.r.39. +1.33.A. +1.c.3n. +2.2/.Q. +2.2H.1G. +3.2B.1S. +3.2W.12. +3.2y.1V. +4.1D.2I. +4.1e.2R. +4.1h.2N. +4.f.3h. +6.3i.e. +8.2+.T. +9.u.38. +a.2J.1x. +a.32.D. +c.3f.m. +d.2U.16. +e.8.3r. +e.9.3q. +f.1R.2C. +f.37.v. +g.2E.1Q. +0.1h.2O. +0.1J.2G. +0.1o.2L. +0.A.34. +0.g.3h. +0.j.3g. +0.N.30. +0.p.3e. +0.s.39. +1.31.G. +1.33.B. +2.2/.R. +2.2H.1H. +3.1u.2K. +3.2y.1W. +3.n.3f. +4.1f.2R. +4.1i.2N. +4.1k.2M. +4.1S.2C. +4.r.3a. +5.c.3o. +6.3i.f. +8.2+.U. +8.2z.1V. +a.2J.1y. +a.2Y.+. +a.32.E. +c.37.w. +d.2U.17. +e.9.3r. +e.a.3q. +f.2Q.1g. +0.1e.2S. +0.1i.2O. +0.1j.2N. +0.1p.2L. +0.1v.2K. +0.B.34. +0.k.3g. +0.O.30. +0.q.3e. +0.t.39. +1.31.H. +1.33.C. +2.2/.S. +2.2H.1I. +2.A.35. +3.1l.2M. +4.1E.2I. +4.h.3h. +4.r.3b. +4.s.3a. +6.3i.g. +8.2+.V. +8.2A.1V. +8.2z.1W. +a.2F.1K. +a.2J.1z. +a.2P.1h. +a.2V.13. +a.2Y./. +a.32.F. +c.3f.o. +d.2U.18. +e.a.3r. +e.b.3q. +e.c.3p. +f.2R.1g. +f.37.x. +0.1f.2S. +0.1j.2O. +0.1q.2L. +0.1R.2D. +0.1w.2K. +0.C.34. +0.d.3m. +0.l.3g. +0.P.30. +0.u.39. +1.1+.2u. +1.31.I. +1.33.D. +2.2/.T. +2.2H.1J. +3.2B.1V. +3.2W.13. +3.2y.1X. +4.1F.2I. +4.1m.2M. +4.i.3h. +6.3i.h. +8.2+.W. +8.2A.1W. +9.r.3c. +9.s.3b. +a.2F.1L. +a.2J.1A. +a.2P.1i. +a.2V.14. +a.2Y.10. +c.3f.p. +d.2U.19. +e.b.3r. +f.37.y. +i.c.3q. +0.1h.2Q. +0.1K.2G. +0.1r.2L. +0.1x.2K. +0.2S.1g. +0.D.34. +0.e.3m. +0.m.3g. +0.Q.30. +0.v.39. +1.31.J. +1.33.E. +2.1+.2v. +2.2/.U. +3.2B.1W. +3.2W.14. +4.1G.2I. +4.1n.2M. +4.1S.2D. +4.j.3h. +4.r.3d. +4.u.3a. +6.3i.i. +8.2+.X. +8.2z.1X. +9.s.3c. +a.2F.1M. +a.2J.1B. +a.2P.1j. +a.2V.15. +a.2Y.11. +c.37.z. +c.3f.q. +d.2U.1a. +f.1V.2C. +g.2E.1R. +i.c.3r. +0.1k.2N. +0.1L.2G. +0.1o.2M. +0.1s.2L. +0.1W.2C. +0.1y.2K. +0.E.34. +0.f.3m. +0.R.30. +0.r.3e. +0.w.39. +1.31.K. +1.33.F. +1.c.3s. +2.2/.V. +3.2W.15. +3.n.3g. +4.1H.2I. +4.1h.2R. +4.1i.2Q. +4.k.3h. +4.s.3d. +4.u.3b. +6.3i.j. +8.2+.Y. +8.2A.1X. +9./.2Z. +a.2F.1N. +a.2J.1C. +a.2V.16. +a.2Y.12. +a.32.G. +d.2U.1b. +g.2E.1S. +0.1j.2Q. +0.1k.2O. +0.1M.2G. +0.1z.2K. +0.F.34. +0.g.3m. +0.l.3h. +0.o.3g. +0.s.3e. +0.x.39. +1.31.L. +2.2/.W. +2.2H.1K. +3.1l.2N. +3.2B.1X. +3.2W.16. +3.S.30. +4.1I.2I. +4.1i.2R. +4.1p.2M. +4.1t.2L. +6.3i.k. +8.2+.Z. +9.u.3c. +a.2F.1O. +a.2J.1D. +a.2V.17. +a.32.H. +d.2U.1c. +0.1A.2K. +0.1h.2S. +0.1j.2R. +0.1X.2C. +0.h.3m. +0.m.3h. +0.p.3g. +0.y.39. +1.22.2u. +1.31.M. +2.2/.X. +2.2H.1L. +3.1l.2O. +3.1u.2L. +3.2W.17. +3.I.32. +4.1J.2I. +4.1m.2N. +4.1N.2G. +4.1q.2M. +4.d.3q. +4.t.3e. +4.u.3d. +5.k.3j. +6.3i.l. +a.2F.1P. +a.2P.1k. +a.2V.18. +c.3f.r. +c.T.30. +f.1V.2D. +f.37.A. +0.1B.2K. +0.1m.2O. +0.1n.2N. +0.1O.2G. +0.1v.2L. +0.i.3m. +0.q.3g. +0.U.30. +1.33.G. +2.2/.Y. +2.22.2v. +2.2H.1M. +2.A.38. +3.1l.2P. +3.2W.18. +3.J.32. +3.n.3h. +4.1i.2S. +4.1r.2M. +4.d.3r. +4.e.3q. +4.u.3e. +6.3i.m. +9.c.3t. +9.k.3k. +a.2F.1Q. +a.2J.1E. +a.2V.19. +a.2Y.13. +c.37.B. +c.3f.s. +d.2U.1d. +e.39.z. +f.1W.2D. +g.2E.1V. +0.1C.2K. +0.1j.2S. +0.1k.2Q. +0.1n.2O. +0.1o.2N. +0.1P.2G. +0.1w.2L. +0.2w.1Y. +0.j.3m. +0.o.3h. +0.V.30. +1.24.2u. +1.33.H. +2.2/.Z. +2.2H.1N. +3.2W.19. +3.n.3i. +4.1s.2M. +4.e.3r. +4.f.3q. +4.G.34. +4.v.3e. +5.c.3u. +5.k.3l. +a.2J.1F. +a.2P.1m. +a.2V.1a. +a.2Y.14. +a.32.K. +c.37.C. +c.3f.t. +g.2E.1W. +0.1D.2K. +0.1o.2O. +0.1p.2N. +0.1Q.2G. +0.1X.2D. +0.1x.2L. +0.f.3r. +0.H.34. +0.k.3m. +0.p.3h. +0.W.30. +0.w.3e. +1.31.N. +1.33.I. +2.24.2v. +2.2H.1O. +3.1l.2Q. +3.2W.1a. +4.1K.2I. +4.1k.2R. +4.1t.2M. +4.g.3q. +6.3i.o. +8.2+.+. +a.2J.1G. +a.2P.1n. +a.2V.1b. +a.2Y.15. +a.32.L. +c.37.D. +c.3f.u. +i.c.3v. +0.1L.2I. +0.1m.2Q. +0.1p.2O. +0.1y.2L. +0.A.39. +0.g.3r. +0.l.3m. +0.q.3h. +0.r.3g. +0.X.30. +1.26.2u. +1.31.O. +1.33.J. +1.k.3n. +2.2H.1P. +3.1l.2R. +3.1u.2M. +3.2W.1b. +3.I.34. +3.M.32. +4.1q.2N. +4.h.3q. +4.x.3e. +6.3i.p. +8.2+./. +9.c.3w. +a.2J.1H. +a.2P.1o. +a.2V.1c. +a.2Y.16. +c.37.E. +c.3f.v. +d.2U.1e. +g.2E.1X. +0.1E.2K. +0.1k.2S. +0.1n.2Q. +0.1q.2O. +0.1z.2L. +0.B.39. +0.h.3r. +0.m.3m. +0.s.3g. +1.31.P. +1.33.K. +2.26.2v. +2.2H.1Q. +3.2W.1c. +3.J.34. +4.1M.2I. +4.1m.2R. +4.1r.2N. +4.1v.2M. +4.c.3x. +4.i.3q. +4.y.3e. +5.1h.2T. +5.k.3o. +6.3i.q. +8.2+.10. +a.2F.1R. +a.2J.1I. +a.2P.1p. +a.2Y.17. +c.3f.w. +c.Y.30. +d.2U.1f. +f.37.F. +0.1A.2L. +0.1F.2K. +0.1o.2Q. +0.1r.2O. +0.3e.z. +0.C.39. +0.K.34. +0.t.3g. +0.Z.30. +1.31.Q. +1.33.L. +2.2/.+. +3.1l.2S. +3.2x.1Y. +3.n.3m. +4.1N.2I. +4.1n.2R. +4.1s.2N. +4.1w.2M. +4.i.3r. +4.j.3q. +4.k.3p. +8.2+.11. +a.2F.1S. +a.2J.1J. +a.2P.1q. +a.2V.1d. +a.2Y.18. +c.3f.x. +d.2U.1g. +i.c.3y. +0.1B.2L. +0.1G.2K. +0.1m.2S. +0.1O.2I. +0.1o.2R. +0.1p.2Q. +0.1R.2G. +0.1s.2O. +0.1Z.2w. +0.D.39. +0.d.3v. +0.L.34. +0.o.3m. +0.r.3h. +0.u.3g. +1.31.R. +1.33.M. +2.2/./. +2.c.3z. +3.2W.1d. +4.1t.2N. +4.1x.2M. +4.j.3r. +4.k.3q. +8.2+.12. +a.2P.1r. +a.2Y.19. +a.32.N. +c.3f.y. +0.1+.2w. +0.1C.2L. +0.1H.2K. +0.1n.2S. +0.1P.2I. +0.1q.2Q. +0.1S.2G. +0.1t.2O. +0.E.39. +0.e.3v. +0.p.3m. +0.s.3h. +0.v.3g. +1.31.S. +2.2/.10. +3.1u.2N. +3.M.34. +4.1p.2R. +4.1y.2M. +4.k.3r. +4.l.3q. +6.3i.r. +9.c.3A. +a.2P.1s. +a.2Y.1a. +a.32.O. +c.37.G. +c.3f.z. +0.1/.2w. +0.1D.2L. +0.1I.2K. +0.1o.2S. +0.1r.2Q. +0.1v.2N. +0.A.3e. +0.F.39. +0.f.3v. +0.l.3r. +0.q.3m. +0.w.3g. +1.31.T. +1.k.3s. +2.2/.11. +2.2H.1R. +3.1u.2O. +4.1q.2R. +4.1z.2M. +4.m.3q. +4.t.3h. +6.3i.s. +7.r.3j. +a.2F.1V. +a.2J.1K. +a.2P.1t. +a.2V.1e. +a.2Y.1b. +a.32.P. +d.2U.1h. +f.1Q.2I. +f.37.H. +0.+.30. +0.1J.2K. +0.1p.2S. +0.1s.2Q. +0.1v.2O. +0.20.2w. +0.B.3e. +0.d.3y. +0.g.3v. +0.m.3r. +0.x.3g. +1.31.U. +1.33.N. +2.2/.12. +2.2H.1S. +3.1u.2P. +3.2W.1e. +3.2y.1Y. +3.I.37. +3.n.3q. +4.1A.2M. +4.1r.2R. +4.1w.2N. +4.u.3h. +5.c.3B. +6.3i.t. +7.s.3j. +8.2+.13. +9.r.3k. +a.2F.1W. +a.2J.1L. +a.2V.1f. +a.2Y.1c. +a.32.Q. +d.2U.1i. +0./.30. +0.1B.2M. +0.1E.2L. +0.1q.2S. +0.1V.2G. +0.1w.2O. +0.1x.2N. +0.C.3e. +0.e.3y. +0.h.3v. +0.N.34. +0.v.3h. +0.y.3g. +1.31.V. +1.33.O. +2.3.3H. +3.2W.1f. +3.2x.1Z. +3.J.37. +3.n.3r. +4.1s.2R. +4.1t.2Q. +4.o.3q. +5.c.3C. +6.3i.u. +7.r.3l. +8.2+.14. +8.2z.1Y. +9.s.3k. +a.2J.1M. +a.2P.1v. +a.2V.1g. +a.32.R. +c.3f.A. +d.2U.1j. +e.21.2w. +0.10.30. +0.1F.2L. +0.1r.2S. +0.1W.2G. +0.1x.2O. +0.1y.2N. +0.D.3e. +0.f.3y. +0.G.39. +0.i.3v. +0.O.34. +0.r.3m. +0.w.3h. +0.z.3g. +1.31.W. +1.33.P. +2.c.3D. +3.1u.2Q. +3.2W.1g. +3.2x.1+. +3.S.32. +4.1C.2M. +4.1t.2R. +4.k.3t. +4.o.3r. +4.p.3q. +6.3i.v. +7.s.3l. +7.u.3j. +8.2+.15. +8.2A.1Y. +a.2F.1X. +a.2J.1N. +a.2P.1w. +a.2Y.1d. +b.3G.8. +b.3J.2. +c.37.K. +c.3f.B. +f.22.2w. +0.11.30. +0.1G.2L. +0.1K.2K. +0.1s.2S. +0.1v.2Q. +0.1y.2O. +0.23.2w. +0.E.3e. +0.g.3y. +0.H.39. +0.j.3v. +0.P.34. +0.p.3r. +0.s.3m. +1.31.X. +1.33.Q. +1.r.3n. +2.2/.13. +2.2H.1V. +3.1u.2R. +3.2B.1Y. +3.2x.1/. +4.1D.2M. +4.1z.2N. +4.q.3q. +4.u.3k. +4.x.3h. +5.k.3u. +6.3i.w. +8.2+.16. +9.c.3E. +a.2J.1O. +a.2P.1x. +a.32.T. +c.37.L. +c.3f.C. +f.1R.2I. +0.1H.2L. +0.1L.2K. +0.1t.2S. +0.1w.2Q. +0.1X.2G. +0.1z.2O. +0.24.2w. +0.2C.1Y. +0.F.3e. +0.h.3y. +0.k.3v. +0.Q.34. +0.q.3r. +0.t.3m. +0.y.3h. +1.31.Y. +1.33.R. +1.s.3n. +2.2/.14. +2.2H.1W. +3.2x.20. +3.I.39. +3.M.37. +4.12.30. +4.1A.2N. +4.1S.2I. +4.1v.2R. +6.3i.x. +7.r.3o. +7.u.3l. +8.2+.17. +9.c.3F. +a.2J.1P. +a.2P.1y. +a.2V.1h. +a.32.U. +c.3f.D. +d.2U.1k. +0.1A.2O. +0.1B.2N. +0.1I.2L. +0.1M.2K. +0.1x.2Q. +0.25.2w. +0.A.3g. +0.i.3y. +0.l.3v. +0.R.34. +0.u.3m. +1.31.Z. +1.33.S. +2.2/.15. +3.1u.2S. +3.2W.1h. +3.2x.21. +3.2y.1Z. +3.J.39. +4.1E.2M. +4.1w.2R. +4.k.3w. +4.r.3p. +4.s.3o. +6.3i.y. +6.3L.3. +8.2+.18. +a.2J.1Q. +a.2P.1z. +a.2V.1i. +a.2Y.1e. +a.32.V. +c.3f.E. +d.2U.1l. +f.3h.z. +0.1B.2O. +0.1J.2L. +0.1v.2S. +0.1y.2Q. +0.26.2w. +0.B.3g. +0.j.3y. +0.K.39. +0.m.3v. +0.r.3q. +0.v.3m. +1.33.T. +1.u.3n. +2.2/.16. +2.2H.1X. +3.2W.1i. +3.2x.22. +3.2y.1+. +3.S.34. +4.1C.2N. +4.1F.2M. +4.1N.2K. +4.1x.2R. +4.k.3x. +5.1h.2X. +6.3i.z. +8.2+.19. +8.2z.1Z. +9.s.3p. +a.2P.1A. +a.2V.1j. +a.2Y.1f. +a.32.W. +b.3G.c. +c.3f.F. +d.2U.1m. +0.13.30. +0.1C.2O. +0.1O.2K. +0.1V.2I. +0.1w.2S. +0.1z.2Q. 0.27.2w. -e.2x.27. -9.2y.27. -0.27.2z. -d.27.2A. -9.2B.27. +0.2D.1Y. +0.C.3g. +0.k.3y. +0.L.39. +0.r.3r. +0.w.3m. +1.33.U. +2.2/.17. +3.2W.1j. +3.2x.23. +3.2y.1/. +3.n.3v. +4.1D.2N. +4.1G.2M. +4.1y.2R. +4.G.3e. +4.s.3q. +4.u.3o. +8.2+.1a. +8.2A.1Z. +8.2z.1+. +a.2P.1B. +a.2Y.1g. +a.32.X. +c.37.N. +c.T.34. +d.2U.1n. +0.14.30. +0.1A.2Q. +0.1D.2O. +0.1P.2K. +0.1x.2S. +0.D.3g. +0.H.3e. +0.l.3y. +0.o.3v. +0.s.3r. +0.U.34. +0.x.3m. +1.33.V. +1.r.3s. +2.2/.18. +2.k.3z. +3.2B.1Z. +3.2x.24. +3.2y.20. +3.M.39. +4.1H.2M. +4.1z.2R. +4.A.3h. +4.t.3q. +8.2+.1b. +8.2A.1+. +8.2z.1/. +9.u.3p. +a.2P.1C. +a.32.Y. +c.37.O. +d.2U.1o. +f.1W.2I. +g.2E.1Y. +0.15.30. +0.1B.2Q. +0.1Q.2K. +0.1y.2S. +0.B.3h. +0.E.3g. +0.m.3y. +0.p.3v. +0.t.3r. +0.V.34. +0.y.3m. +1.31.+. +1.33.W. +1.s.3s. +2.2/.19. +3.2B.1+. +3.2x.25. +3.2y.21. +3.I.3e. +4.1A.2R. +4.1E.2N. +4.1I.2M. +4.1K.2L. +4.k.3A. +4.u.3q. +5.1t.2T. +6.3i.A. +8.2+.1c. +8.2A.1/. +8.2z.20. +a.2J.1R. +a.2P.1D. +a.2V.1k. +a.32.Z. +c.28.2w. +c.37.P. +c.3f.G. +d.2U.1p. +f.1Z.2C. +0.16.30. +0.1C.2Q. +0.1E.2O. +0.1F.2N. +0.1L.2L. +0.1X.2I. +0.1z.2S. +0.F.3g. +0.q.3v. +0.u.3r. +0.W.34. +1.31./. +1.33.X. +2.2/.1a. +3.1l.2V. +3.2B.1/. +3.2W.1k. +3.2x.26. +3.2y.22. +3.J.3e. +3.n.3y. +4.1B.2R. +4.1J.2M. +4.3f.H. +4.C.3h. +4.v.3q. +5.c.3H. +6.3i.B. +6.3m.z. +8.2A.20. +8.2z.21. +a.2J.1S. +a.2Y.1h. +b.3G.d. +c.37.Q. +d.2U.1q. +f.1+.2C. +0.1/.2C. +0.17.30. +0.1A.2S. +0.1D.2Q. +0.1F.2O. +0.1G.2N. +0.1M.2L. +0.D.3h. +0.K.3e. +0.N.39. +0.o.3y. +0.v.3r. +0.X.34. +1.31.10. +1.33.Y. +1.u.3s. +2.2/.1b. +3.1l.2W. +3.2B.20. +3.2x.27. +3.2y.23. +3.I.3f. +4.1C.2R. +4.r.3t. +4.w.3q. +5.c.3I. +5.k.3B. +6.3i.C. +8.2+.1d. +8.2A.21. +8.2z.22. +a.2P.1E. +a.2V.1m. +a.2Y.1i. +b.3G.e. +c.37.R. +d.2U.1r. +0.18.30. +0.1B.2S. +0.1G.2O. +0.1Z.2D. +0.L.3e. +0.O.39. +0.p.3y. +0.w.3r. +1.31.11. +1.33.Z. +2.2/.1c. +3.2B.21. +3.2W.1m. +3.2y.24. +3.J.3f. +3.S.37. +4.1D.2R. +4.1H.2N. +4.1N.2L. +4.E.3h. +4.s.3t. +4.x.3q. +5.k.3C. +6.3i.D. +7.r.3u. +8.2A.22. +8.2z.23. +a.2P.1F. +a.2V.1n. +a.2Y.1j. +b.3G.f. +b.3J.c. +b.3R.2. +c.Y.34. +d.2U.1s. +f.20.2C. +0.1+.2D. +0.19.30. +0.1C.2S. +0.1E.2Q. +0.1H.2O. +0.1O.2L. +0.1R.2K. +0.A.3m. +0.G.3g. +0.P.39. +0.q.3y. +0.r.3v. +0.x.3r. +1.29.2u. +1.31.12. +2.k.3D. +3.2B.22. +3.2W.1n. +3.2x.28. +3.2y.25. +3.3K.c. +3.M.3e. +4.1I.2N. +4.1K.2M. +4.3f.K. +4.F.3h. +4.y.3q. +4.Z.34. +5.1h.2Z. +6.3i.E. +7.s.3u. +8.2A.23. +8.2z.24. +a.2J.1V. +a.2P.1G. +a.2V.1o. +a.32.+. +b.3G.g. +c.37.T. +d.2U.1t. +g.2E.1Z. +j.21.2C. +0.1/.2D. +0.1D.2S. +0.1F.2Q. +0.1I.2O. +0.1J.2N. +0.1P.2L. +0.1S.2K. +0.3q.z. +0.B.3m. +0.H.3g. +0.Q.39. +0.s.3v. +0.y.3r. +2.2/.1d. +2.29.2v. +3.2B.23. +3.2W.1o. +3.2y.26. +4.1a.30. +4.1E.2R. +4.1L.2M. +4.u.3t. +6.3i.F. +6.3L.c. +8.2+.1e. +8.2A.24. +8.2z.25. +9.k.3E. +9.r.3w. +a.2J.1W. +a.2P.1H. +a.2V.1p. +a.32./. +b.3G.h. +c.37.U. +c.3f.L. +d.2U.1u. +f.22.2C. +g.2E.1+. +0.1b.30. +0.1G.2Q. +0.1J.2O. +0.23.2C. +0.C.3m. +0.R.39. +0.t.3v. +3.2B.24. +3.2W.1p. +3.2y.27. +3.3M.c. +3.I.3g. +3.M.3f. +4.1F.2R. +4.1M.2M. +4.s.3w. +4.u.3u. +6.3r.z. +8.2+.1f. +8.2A.25. +8.2z.26. +9.k.3F. +9.r.3x. +a.2P.1I. +a.2V.1q. +a.2Y.1k. +a.32.10. +b.3G.i. +c.37.V. +d.2U.1v. +f.1Q.2L. +f.20.2D. +g.2E.1/. +0.1c.30. +0.1E.2S. +0.1H.2Q. +0.24.2C. +0.D.3m. +0.N.3e. +0.r.3y. +0.u.3v. +1.31.13. +1.33.+. +3.1l.2Y. +3.2B.25. +3.2W.1q. +3.J.3g. +3.S.39. +4.1G.2R. +4.1N.2M. +4.G.3h. +4.s.3x. +8.2+.1g. +8.2A.26. +8.2z.27. +a.2F.1Y. +a.2J.1X. +a.2P.1J. +a.2V.1r. +a.32.11. +b.3G.j. +b.3J.d. +c.37.W. +d.2U.1w. +g.2E.20. +i.c.3N. +j.21.2D. +0.+.34. +0.1F.2S. +0.22.2D. +0.25.2C. +0.E.3m. +0.H.3h. +0.K.3g. +0.O.3e. +0.s.3y. +0.v.3v. +1.31.14. +1.33./. +2.2/.1e. +2.r.3z. +3.2B.26. +3.2W.1r. +3.2y.28. +3.3K.d. +4.1H.2R. +4.1I.2Q. +4.1K.2N. +4.1O.2M. +4.A.3q. +4.u.3w. +5.c.3O. +6.3i.G. +8.2A.27. +a.2V.1s. +a.2Y.1m. +a.32.12. +b.3G.k. +b.3J.e. +c.37.X. +c.T.39. +d.2U.1x. +f.1V.2K. +g.2E.21. +0./.34. +0.1d.30. +0.1G.2S. +0.1J.2Q. +0.1K.2O. +0.1L.2N. +0.1W.2K. +0.26.2C. +0.2G.1Y. +0.A.3r. +0.F.3m. +0.L.3g. +0.P.3e. +0.t.3y. +0.U.39. +0.w.3v. +1.31.15. +1.33.10. +2.2/.1f. +2.c.3P. +2.s.3z. +3.2B.27. +3.2W.1s. +3.3K.e. +3.I.3h. +4.1I.2R. +4.B.3q. +4.r.3A. +4.u.3x. +6.3i.H. +6.3L.d. +8.2z.28. +a.2V.1t. +a.2Y.1n. +b.3G.l. +b.3J.f. +c.37.Y. +c.3f.N. +d.2U.1y. +f.1P.2M. +f.23.2D. +g.2E.22. +0.10.34. +0.1H.2S. +0.1L.2O. +0.24.2D. 0.27.2C. +0.B.3r. +0.Q.3e. +0.u.3y. +0.V.39. +0.x.3v. +1.31.16. +1.33.11. +2.2/.1g. +3.1u.2V. +3.2W.1t. +3.3K.f. +3.3M.d. +3.I.3i. +3.J.3h. +3.M.3g. +4.1J.2R. +4.1M.2N. +4.C.3q. +6.3L.e. +8.2+.1h. +8.2A.28. +9./.35. +9.s.3A. +a.2P.1K. +a.2Y.1o. +b.3G.m. +b.3J.g. +c.37.Z. +c.3f.O. +d.2U.1z. +f.1Q.2M. +f.1R.2L. +g.2E.23. +0.11.34. +0.1I.2S. +0.1M.2O. +0.1X.2K. +0.d.3N. +0.K.3h. +0.R.3e. +0.v.3y. +0.W.39. +0.y.3v. +1.31.17. +1.33.12. +2.2H.1Y. +2.u.3z. +3.1u.2W. +3.2B.28. +3.3K.g. +3.3M.e. +3.J.3i. +4.1N.2N. +4.1S.2L. +4.C.3r. +4.D.3q. +5.1t.2X. +5.c.3Q. +6.3L.f. +7.r.3B. +8.2+.1i. +9./.36. +a.2P.1L. +a.2V.1v. +a.2Y.1p. +a.32.13. +b.3G.n. +b.3J.h. +c.3f.P. +d.2U.1A. +f.25.2D. +g.2E.24. +0.1e.30. +0.1J.2S. +0.1K.2Q. +0.1O.2N. +0.26.2D. +0.e.3N. +0.G.3m. +0.L.3h. +0.w.3y. +0.X.39. +0.z.3v. +1.31.18. +3.2W.1v. +3.3K.h. +3.3M.f. +3.S.3e. +4.12.34. +4.1N.2O. +4.D.3r. +4.E.3q. +5.k.3H. +5.r.3C. +6.3i.K. +6.3L.g. +7.s.3B. +8.2+.1j. +9.u.3A. +a.2F.1Z. +a.2P.1M. +a.2V.1w. +a.2Y.1q. +a.32.14. +b.3G.o. +b.3J.i. +c.28.2C. +c.3f.Q. +d.2U.1B. +g.2E.25. +0.1f.30. +0.1L.2Q. +0.1O.2O. +0.1P.2N. 0.27.2D. -d.27.2E. -0.27.2F. +0.f.3N. +0.H.3m. +0.N.3g. +0.x.3y. +1.31.19. +2.2/.1h. +2.r.3D. +3.2W.1w. +3.3K.i. +3.3M.g. +3.M.3h. +4.1K.2R. +4.E.3r. +4.F.3q. +5.k.3I. +5.s.3C. +6.3i.L. +6.3L.h. +a.2F.1+. +a.2P.1N. +a.2V.1x. +a.2Y.1r. +a.32.15. +b.3G.p. +b.3J.j. +b.3R.c. +c.3f.R. +c.T.3e. +c.Y.39. +d.2U.1C. +g.2E.26. +0.1M.2Q. +0.1P.2O. +0.1Q.2N. +0.1V.2L. +0.1Z.2G. +0.g.3N. +0.O.3g. +0.U.3e. +0.y.3y. +0.Z.39. +1.31.1a. +1.33.13. +2.2/.1i. +2.s.3D. +3.2W.1x. +3.3K.j. +3.3M.h. +3.I.3m. +3.M.3i. +3.S.3f. +4.1L.2R. +4.F.3r. +6.3L.i. +6.3S.c. +7.u.3B. +9.r.3E. +a.2F.1/. +a.2P.1O. +a.2V.1y. +a.2Y.1s. +a.32.16. +b.3G.q. +b.3J.k. +c.37.+. +d.2U.1D. +f.1R.2M. +f.30.1g. +g.2E.27. +0.1+.2G. +0.13.34. +0.1K.2S. +0.1Q.2O. +0.1W.2L. +0.2I.1Y. +0.A.3v. +0.h.3N. +0.P.3g. +0.V.3e. +1.2f.2u. +1.31.1b. +1.33.14. +2.2.3+. +2.2/.1j. +3.2W.1y. +3.3K.k. +3.3M.i. +3.J.3m. +4.1M.2R. +4.1N.2Q. +4.1S.2M. +5.c.3T. +5.u.3C. +6.3L.j. +6.3y.z. +8.2+.1k. +9.r.3F. +9.s.3E. +a.2F.20. +a.2P.1P. +a.2V.1z. +a.2Y.1t. +a.32.17. +b.3J.l. +c.28.2D. +c.37./. +c.3f.T. +0.1/.2G. +0.14.34. +0.1L.2S. +0.1O.2Q. +0.29.2w. +0.B.3v. +0.i.3N. +0.K.3m. +0.Q.3g. +0.W.3e. +1.31.1c. +1.33.15. +2.0.41. +2.2f.2v. +2.2H.1Z. +2.A.3w. +2.u.3D. +3.1u.2Y. +3.2W.1z. +3.3K.l. +3.3M.j. +4.1N.2R. +4.G.3q. +4.N.3h. +5.c.3U. +6.3L.k. +8.2+.1l. +9./.38. +9.s.3F. +a.2F.21. +a.2P.1Q. +a.2V.1A. +a.32.18. +b.3J.m. +c.37.10. +c.3f.U. +d.2U.1E. +g.2E.28. +0.15.34. +0.1h.30. +0.1M.2S. +0.1O.2R. +0.1P.2Q. +0.1X.2L. +0.20.2G. +0.C.3v. +0.G.3r. +0.j.3N. +0.L.3m. +0.R.3g. +0.X.3e. +1.33.16. +2.1.41. +2.2H.1+. +2.A.3x. +3.2W.1A. +3.3K.m. +3.3M.k. +4.H.3q. +4.O.3h. +5.2a.2w. +5.c.3V. +6.3i.N. +6.3L.l. +8.2+.1m. +9.u.3E. +a.2F.22. +a.2V.1B. +a.2Y.1v. +a.32.19. +b.3G.r. +b.3J.n. +b.3R.d. +c.37.11. +c.3f.V. +d.2U.1F. +0.+.39. +0.16.34. +0.1i.30. +0.1P.2R. +0.1V.2M. +0.A.3y. +0.D.3v. +0.H.3r. +0.k.3N. +0.P.3h. +1.31.1d. +1.33.17. +2.2.41. +2.2/.1k. +2.2H.1/. +3.2W.1B. +3.3K.n. +3.3M.l. +3.I.3q. +3.M.3m. +3.S.3g. +4.1N.2S. +5.c.3W. +6.3i.O. +6.3L.m. +6.3S.d. +8.2+.1n. +9.1t.2Z. +9.u.3F. +a.2F.23. +a.2V.1C. +a.2Y.1w. +a.32.1a. +b.3G.s. +b.3J.o. +b.3R.e. +c.37.12. +c.3f.W. +c.Y.3e. +d.2U.1G. +f.1Q.2Q. +f.1R.2N. +j.21.2G. +0./.39. +0.17.34. +0.1j.30. +0.1O.2S. +0.1Q.2R. +0.1W.2M. +0.22.2G. +0.B.3y. +0.E.3v. +0.l.3N. +0.Q.3h. +1.33.18. +2.2/.1l. +2.2H.20. +2.6.3+. +2.A.3z. +3.2W.1C. +3.3K.o. +3.3M.m. +3.I.3r. +3.J.3q. +3.n.3L. +4.1S.2N. +4.Z.3e. +5.c.3X. +5.k.3O. +6.3i.P. +6.3S.e. +8.2+.1o. +a.2F.24. +a.2V.1D. +a.2Y.1x. +a.32.1b. +b.3G.t. +b.3J.p. +b.3R.f. +c.3f.X. +c.T.3g. +d.2U.1H. +f.1R.2O. +0.10.39. +0.18.34. +0.1P.2S. +0.1S.2O. +0.23.2G. +0.C.3y. +0.F.3v. +0.m.3N. +0.R.3h. +0.U.3g. +1.33.19. +2.2/.1m. +2.2H.21. +2.A.3A. +2.k.3P. +3.2W.1D. +3.2x.29. +3.3K.p. +3.3M.n. +3.J.3r. +4./.3a. +4.K.3q. +6.3i.Q. +6.3L.o. +6.3S.f. +8.2+.1p. +a.2F.25. +a.2P.1R. +a.2Y.1y. +a.32.1c. +b.3G.u. +b.3J.q. +b.3R.g. +c.3f.Y. +d.2U.1I. +f.1Z.2I. +f.2c.2w. +0.11.39. +0.19.34. +0.1Q.2S. +0.1X.2M. +0.24.2G. +0.D.3y. +0.K.3r. +0.L.3q. +0.N.3m. +0.V.3g. +1.31.1e. +1.33.1a. +2.2/.1n. +2.2H.22. +3.2x.2a. +3.3K.q. +3.3M.o. +3.n.3N. +3.S.3h. +4./.3b. +6.3i.R. +6.3L.p. +6.3S.g. +7.r.3H. +8.2+.1q. +a.2F.26. +a.2J.1Y. +a.2P.1S. +a.2V.1E. +a.2Y.1z. +b.3G.v. +b.3R.h. +c.37.13. +c.3f.Z. +d.2U.1J. +f.1+.2I. +0.1a.34. +0.1k.30. +0.1V.2N. +0.25.2G. +0.E.3y. +0.L.3r. +0.O.3m. +0.o.3N. +0.W.3g. +1.31.1f. +1.33.1b. +2.2/.1o. +2.2H.23. +3.2W.1E. +3.3M.p. +3.M.3q. +3.S.3i. +4.12.39. +4.s.3H. +5.k.3Q. +6.3L.q. +6.3S.h. +7.r.3I. +8.2+.1r. +9./.3c. +a.2F.27. +a.2V.1F. +a.2Y.1A. +a.32.1d. +b.3G.w. +b.3R.i. +c.37.14. +c.T.3h. +f.1/.2I. +f.1R.2Q. +i.c.3Y. +0.+.3e. +0.1b.34. +0.1V.2O. +0.26.2G. +0.2w.2d. +0.F.3y. +0.G.3v. +0.P.3m. +0.p.3N. +0.U.3h. +0.X.3g. +1.31.1g. +1.33.1c. +2.2/.1p. +2.2H.24. +3.1l.30. +3.2W.1F. +3.3M.q. +3.M.3r. +4./.3d. +4.1S.2Q. +6.3i.T. +6.3S.i. +7.s.3I. +8.2+.1s. +a.2V.1G. +a.2Y.1B. +b.3G.x. +b.3J.r. +b.3R.j. +c.37.15. +f.1R.2R. +f.1W.2N. +f.20.2I. +0./.3e. +0.1c.34. +0.1m.30. +0.1W.2O. 0.27.2G. -9.2H.27. -0.27.2I. -d.27.2J. +0.H.3v. +0.Q.3m. +0.q.3N. +0.V.3h. +2.2/.1q. +2.2H.25. +2.b.3+. +3.2W.1G. +3.2x.2c. +3.2y.29. +3.3K.r. +4.1S.2R. +5.c.3Z. +6.3i.U. +6.3S.j. +7.u.3H. +8.2+.1t. +a.2F.28. +a.2P.1V. +a.2V.1H. +a.2Y.1C. +b.3G.y. +b.3J.s. +b.3R.k. +c.37.16. +c.Y.3g. +d.2U.1K. +j.21.2I. +0.10.3e. +0.13.39. +0.1n.30. +0.1X.2N. +0.2K.1Y. +0.R.3m. +0.W.3h. +0.Z.3g. +1.33.1d. +2.2/.1r. +2.2H.26. +3.2W.1H. +3.2y.2a. +3.3K.s. +3.I.3v. +4.N.3q. +5.c.3+. +6.3i.V. +6.3L.r. +6.3S.k. +7.u.3I. +8.2+.1u. +8.2z.29. +a.2P.1W. +a.2V.1I. +a.2Y.1D. +a.32.1e. +b.3G.z. +b.3J.t. +b.3R.l. +c.37.17. +c.3f.+. +d.2U.1L. +f.1R.2S. +f.22.2I. +0.11.3e. +0.14.39. +0.1d.34. +0.1o.30. +0.1S.2S. +0.1V.2Q. +0.1X.2O. +0.d.3Y. +0.G.3y. +0.X.3h. +1.31.1h. +2.2/.1s. +2.2H.27. +3.2W.1I. +3.3K.t. +3.3M.r. +3.J.3v. +3.S.3m. +4.N.3r. +4.O.3q. +5.c.3/. +5.k.3T. +6.3i.W. +6.3L.s. +6.3S.l. +8.2+.1v. +8.2A.29. +8.2z.2a. +a.2J.1Z. +a.2V.1J. +a.32.1f. +b.3J.u. +b.3R.m. +c.28.2G. +c.37.18. +c.3f./. +d.2U.1M. +f.23.2I. +0.12.3e. +0.15.39. +0.1p.30. +0.1W.2Q. +0.24.2I. +0.e.3Y. +0.H.3y. +0.K.3v. +0.r.3N. +1.31.1i. +2.1d.35. +2.2/.1t. +3.2B.29. +3.2W.1J. +3.2x.2d. +3.3K.u. +3.3M.s. +4.O.3r. +4.P.3q. +5.k.3U. +6.3i.X. +6.3L.t. +6.3S.m. +8.2+.1w. +8.2A.2a. +a.2J.1+. +a.2P.1X. +a.2Y.1E. +a.32.1g. +b.3J.v. +b.3R.n. +c.37.19. +c.3f.10. +c.Y.3h. +d.2U.1N. +f.1V.2R. +i.c.40. +0.16.39. +0.1q.30. +0.1W.2R. +0.25.2I. +0.f.3Y. +0.L.3v. +0.P.3r. +0.s.3N. +0.U.3m. +1.31.1j. +1.33.1e. +2.2/.1u. +2.2H.28. +3.2B.2a. +3.2y.2c. +3.3K.v. +3.3M.t. +3.I.3y. +3.n.3S. +4.Q.3q. +4.Z.3h. +5.c.41. +5.k.3V. +6.3i.Y. +6.3L.u. +7.r.3O. +8.2+.1x. +a.2J.1/. +a.2Y.1F. +b.3G.A. +b.3J.w. +b.3R.o. +c.3f.11. +d.2U.1O. +f.29.2C. +f.37.1a. +0.+.3g. +0.17.39. +0.1e.34. +0.1r.30. +0.1V.2S. +0.1X.2Q. +0.26.2I. +0.g.3Y. +0.Q.3r. +0.t.3N. +0.V.3m. +1.33.1f. +2.2/.1v. +2.r.3P. +3.3K.w. +3.3M.u. +3.J.3y. +3.M.3v. +4.R.3q. +4.s.3O. +5.2a.2C. +5.c.42. +5.k.3W. +6.3i.Z. +6.3L.v. +6.3S.o. +8.2+.1y. +8.2z.2c. +a.2J.20. +a.2V.1K. +a.2Y.1G. +b.3G.B. +b.3J.x. +b.3R.p. +c.37.1b. +c.3f.12. +d.2U.1P. +0./.3g. +0.13.3e. +0.18.39. +0.1f.34. +0.1s.30. +0.1W.2S. +0.1X.2R. +0.1Z.2K. +0.h.3Y. +0.K.3y. +0.R.3r. +0.u.3N. +0.W.3m. +1.33.1g. +1.c.43. +2.2/.1w. +2.s.3P. +3.2W.1K. +3.3K.x. +3.3M.v. +3.S.3q. +5.k.3X. +6.3L.w. +6.3S.p. +8.2+.1z. +8.2A.2c. +a.2J.21. +a.2V.1L. +a.2Y.1H. +a.32.1h. +b.3G.C. +b.3J.y. +b.3R.q. +c.37.1c. +d.2U.1Q. +f.27.2I. +0.1+.2K. +0.10.3g. +0.14.3e. +0.19.39. +0.1t.30. +0.29.2D. +0.2L.1Y. +0.34.1g. +0.d.40. +0.i.3Y. +0.L.3y. +0.v.3N. +0.X.3m. +1.31.1k. +2.2/.1x. +3.2B.2c. +3.2W.1L. +3.2y.2d. +3.3K.y. +3.3M.w. +4.S.3r. +5.c.44. +6.3L.x. +6.3S.q. +7.r.3Q. +7.u.3O. +8.2+.1A. +a.2J.22. +a.2V.1M. +a.2Y.1I. +a.32.1i. +b.3G.D. +b.3J.z. +c.T.3q. +0.+.3h. +0.1/.2K. +0.11.3g. +0.15.3e. +0.1X.2S. +0.e.40. +0.j.3Y. +0.N.3v. +0.w.3N. +1.31.1l. +2.2/.1y. +2.u.3P. +3.1u.30. +3.2W.1M. +3.3K.z. +3.3M.x. +3.M.3y. +4.1a.39. +4.U.3q. +5.2a.2D. +5.c.45. +6.3L.y. +7.s.3Q. +8.2+.1B. +8.2z.2d. +a.2J.23. +a.2V.1N. +a.2Y.1J. +a.32.1j. +b.3G.E. +c.28.2I. +c.37.1d. +c.3f.13. +c.T.3r. +c.Y.3m. +f.2c.2C. +g.2E.29. +0./.3h. +0.12.3g. +0.16.3e. +0.1b.39. +0.1v.30. +0.20.2K. +0.f.40. +0.k.3Y. +0.O.3v. +0.x.3N. +0.Z.3m. +1.31.1m. +1.33.1h. +2.1d.38. +2.2/.1z. +2.A.3I. +3.2W.1N. +3.3M.y. +4.U.3r. +4.V.3q. +5.c.46. +6.3i.+. +6.3L.z. +8.2+.1C. +8.2A.2d. +a.2J.24. +a.2V.1O. +b.3G.F. +b.3R.r. +c.3f.14. +g.2E.2a. +0.17.3e. +0.1c.39. +0.1h.34. +0.1w.30. +0.g.40. +0.l.3Y. +0.P.3v. +0.V.3r. +0.y.3N. +1.31.1n. +1.33.1i. +2.2/.1A. +3.2B.2d. +3.2W.1O. +3.3M.z. +4.10.3h. +4.W.3q. +5.1V.2T. +5.c.47. +6.3i./. +6.3R.s. +6.3S.r. +7.u.3Q. +8.2+.1D. +a.2J.25. +a.2V.1P. +b.3J.A. +c.3f.15. +d.2U.1R. +j.21.2K. +0.18.3e. +0.1i.34. +0.1x.30. +0.22.2K. +0.2C.2d. +0.2M.1Y. +0.h.40. +0.m.3Y. +0.N.3y. +0.Q.3v. +0.W.3r. +1.31.1o. +1.33.1j. +2.2/.1B. +3.2W.1P. +3.3K.A. +4.11.3h. +4.X.3q. +5./.3j. +5.1h.35. +5.c.48. +5.k.3Z. +6.3i.10. +6.3N.z. +6.3R.t. +6.3S.s. +7.r.3T. +a.2J.26. +a.2V.1Q. +a.2Y.1K. +a.32.1k. +b.3J.B. +c.3f.16. +d.2U.1S. +f.2c.2D. +f.37.1e. +0.12.3h. +0.13.3g. +0.19.3e. +0.1d.39. +0.1j.34. +0.1y.30. +0.1Z.2L. +0.23.2K. +0.i.40. +0.O.3y. +0.R.3v. +0.X.3r. +1.31.1p. +2.2/.1C. +3.1l.32. +3.2W.1Q. +3.3K.B. +3.n.3Y. +4.s.3T. +5.1h.36. +5.c.49. +5.k.3+. +6.3i.11. +6.3L.A. +6.3S.t. +7.r.3U. +8.2+.1E. +9./.3k. +a.2J.27. +a.2Y.1L. +b.3G.G. +b.3J.C. +b.3R.u. +c.3f.17. +c.Y.3q. +f.37.1f. +g.2E.2c. +0.+.3m. +0.1+.2L. +0.14.3g. +0.1a.3e. +0.1z.30. +0.24.2K. +0.j.40. +0.o.3Y. +0.P.3y. +1.31.1q. +1.c.4a. +2.2/.1D. +3.3K.C. +3.3M.A. +3.S.3v. +4.r.3V. +4.s.3U. +4.Z.3q. +5./.3l. +5.k.3/. +6.3i.12. +6.3L.B. +6.3S.u. +8.2+.1F. +a.2Y.1M. +a.32.1m. +b.3G.H. +b.3J.D. +b.3R.v. +c.37.1g. +c.3f.18. +c.Y.3r. +0./.3m. +0.1/.2L. +0.15.3g. +0.1A.30. +0.1b.3e. +0.25.2K. +0.A.3N. +0.k.40. +0.p.3Y. +0.Q.3y. +0.Z.3r. +1.31.1r. +1.33.1k. +1.c.4b. +3.3K.D. +3.3M.B. +4.2d.2D. +4.3f.19. +4.r.3W. +4.s.3V. +4.u.3T. +6.3L.C. +6.3S.v. +8.2+.1G. +a.2J.28. +a.2Y.1N. +a.32.1n. +b.3G.I. +b.3J.E. +b.3R.w. +c.T.3v. +d.2U.1V. +0.10.3m. +0.13.3h. +0.16.3g. +0.1B.30. +0.1c.3e. +0.1e.39. +0.1k.34. +0.20.2L. +0.26.2K. +0.2N.1Y. +0.B.3N. +0.l.40. +0.q.3Y. +0.R.3y. +0.U.3v. +1./.3n. +1.31.1s. +1.33.1l. +2.2/.1E. +2.A.3O. +3.3K.E. +3.3M.C. +5.k.41. +6.3L.D. +6.3S.w. +7.r.3X. +7.s.3W. +7.u.3U. +8.2+.1H. +a.2V.1R. +a.2Y.1O. +a.32.1o. +b.3G.J. +b.3J.F. +b.3R.x. +c.3f.1a. +d.2U.1W. +g.2E.2d. +0.11.3m. +0.17.3g. +0.1C.30. +0.1f.39. 0.27.2K. -4.27.2L. -c.2M.27. -9.2N.27. -2.2O.27. -4.27.2P. -9.2Q.27. -4.27.2R. -7.2S.27. -0.27.2T. -1.2U.27. -9.2V.27. -1.2W.27. -0.27.2X. -4.27.2Y. -4.27.2Z. -d.2+.27. -4.27.2/. +0.2O.1Y. +0.C.3N. +0.m.40. +0.V.3v. +1.31.1t. +1.33.1m. +2.2/.1F. +2.A.3P. +3.1l.34. +3.2W.1R. +3.3K.F. +3.3M.D. +3.S.3y. +4.14.3h. +4.s.3X. +4.u.3V. +5./.3o. +5.k.42. +6.3i.13. +6.3L.E. +6.3S.x. +8.2+.1I. +a.2F.29. +a.2V.1S. +a.2Y.1P. +a.32.1p. +b.3G.K. +b.3R.y. +c.37.1h. +c.3f.1b. +f.1Z.2M. +j.21.2L. +0.+.3q. +0.1+.2M. +0.12.3m. +0.18.3g. +0.1D.30. +0.1d.3e. +0.1m.34. +0.22.2L. +0.D.3N. +0.W.3v. +1.31.1u. +1.33.1n. +1.k.43. +2.2/.1G. +3.2W.1S. +3.3M.E. +3.n.40. +4.15.3h. +4.u.3W. +5.1h.38. +6.3i.14. +6.3L.F. +6.3S.y. +6.4c.c. +8.2+.1J. +9./.3p. +a.2F.2a. +a.2P.1Y. +a.2Y.1Q. +a.32.1q. +b.3G.L. +b.3R.z. +c.37.1i. +c.3f.1c. +c.T.3y. +d.2U.1X. +e.39.1g. +0.+.3r. +0.16.3h. +0.19.3g. +0.1n.34. +0.23.2L. +0.29.2G. +0.E.3N. +0.o.40. +0.r.3Y. +0.U.3y. +0.X.3v. +1.31.1v. +1.33.1o. +2.2/.1H. +2.A.3Q. +3.3M.F. +4./.3q. +5.c.4d. +5.k.44. +6.3i.15. +6.3S.z. +7.u.3X. +a.32.1r. +b.3G.M. +b.3J.G. +c.28.2K. +f.1/.2M. +f.37.1j. +0./.3r. +0.1a.3g. +0.1E.30. +0.1o.34. +0.24.2L. +0.2Q.1Y. +0.F.3N. +0.p.40. +0.s.3Y. +0.V.3y. +1.31.1w. +1.33.1p. +2.2/.1I. +3.3K.G. +4.10.3q. +4.17.3h. +5.2a.2G. +5.k.45. +6.3i.16. +a.2V.1V. +a.32.1s. +b.3J.H. +c.3f.1d. +c.Y.3v. +f.20.2M. +0.10.3r. +0.13.3m. +0.1b.3g. +0.1e.3e. +0.1F.30. +0.1h.39. +0.1p.34. +0.25.2L. +0.q.40. +0.t.3Y. +0.W.3y. +0.Z.3v. +1./.3s. +1.31.1x. +1.33.1q. +2.2/.1J. +2.2H.29. +3.2W.1V. +3.3K.H. +4.11.3q. +4.18.3h. +5.c.4e. +5.k.46. +6.3i.17. +6.3L.G. +7.r.3Z. +8.2+.1K. +a.2F.2c. +a.2V.1W. +a.32.1t. +b.3J.I. +b.3R.A. +f.1Z.2N. +f.2R.1Y. +j.21.2M. +0.1+.2N. +0.11.3r. +0.14.3m. +0.19.3h. +0.1c.3g. +0.1f.3e. +0.1G.30. +0.1i.39. +0.1q.34. +0.1Z.2O. +0.22.2M. +0.26.2L. +0.u.3Y. +0.X.3y. +1.31.1y. +1.33.1r. +2.2H.2a. +3.1u.32. +3.2W.1W. +3.3K.I. +3.3M.G. +4.12.3q. +4.r.3+. +5.1h.3a. +5.1V.2X. +5.c.4f. +5.k.47. +6.3i.18. +6.3L.H. +6.3S.A. +6.4c.d. +7.s.3Z. +8.2+.1L. +a.2Y.1R. +b.3G.N. +b.3J.J. +b.3R.B. +c.37.1k. +0.1/.2N. +0.1+.2O. +0.15.3m. +0.1H.30. +0.1j.39. +0.1r.34. +0.23.2M. +0.27.2L. +0.2c.2G. +0.2S.1Y. +0.3e.1g. +0.G.3N. +0.v.3Y. +1.31.1z. +1.33.1s. +3.1l.37. +3.3K.J. +3.3M.H. +3.I.3L. +4.12.3r. +4.1a.3h. +5.1h.3b. +5.c.4g. +5.k.48. +6.3i.19. +6.3S.B. +6.4c.e. +7.r.3/. +7.s.3+. +8.2+.1M. +a.2P.1Z. +a.2V.1X. +a.2Y.1S. +a.32.1v. +b.3G.O. +b.3J.K. +b.3R.C. +c.3f.1e. +c.Y.3y. +0.1/.2O. +0.16.3m. +0.1d.3g. +0.1I.30. +0.1s.34. +0.20.2N. +0.24.2M. +0.H.3N. +0.r.40. +0.w.3Y. +0.Z.3y. +1.31.1A. +1.33.1t. +2.2/.1K. +3.2W.1X. +3.3K.K. +3.3M.I. +3.J.3L. +4./.3t. +4.1b.3h. +4.3f.1f. +5.1h.3c. +5.c.4h. +5.k.49. +6.3i.1a. +6.3S.C. +6.4c.f. +7.s.3/. +7.u.3Z. +8.2+.1N. +a.2F.2d. +a.2P.1+. +a.32.1w. +b.3G.P. +b.3J.L. +b.3R.D. +c.37.1m. +0.+.3v. +0.17.3m. +0.1J.30. +0.1t.34. +0.20.2O. +0.25.2M. +0.s.40. +0.x.3Y. +1.31.1B. +1.33.1u. +1.c.4i. +1.k.4a. +2.2/.1L. +2.2H.2c. +2.A.3V. +3.3K.L. +3.3M.J. +3.I.3N. +4.13.3q. +4.1c.3h. +4.u.3+. +5./.3u. +5.1h.3d. +6.3i.1b. +6.3L.K. +6.3S.D. +6.4c.g. +7.r.41. +8.2+.1O. +a.2P.1/. +a.32.1x. +b.3G.Q. +b.3J.M. +b.3R.E. +c.28.2L. +c.37.1n. +c.3f.1g. +f.1Z.2Q. +f.29.2I. +j.21.2N. +0./.3v. +0.1+.2Q. +0.18.3m. +0.1h.3e. +0.1k.39. +0.22.2N. +0.26.2M. +0.2G.2d. +0.t.40. +0.y.3Y. +1.31.1C. +1.33.1v. +1.k.4b. +2.2/.1M. +3.1u.34. +3.3K.M. +3.3M.K. +3.J.3N. +4.13.3r. +4.14.3q. +4.u.3/. +5.2a.2I. +6.3i.1c. +6.3L.L. +6.3S.E. +6.4c.h. +7.r.42. +7.s.41. +8.2+.1P. +9.1t.35. +a.2P.20. +a.2Y.1V. +a.32.1y. +b.3G.R. +b.3R.F. +c.37.1o. +f.1Z.2R. +i.c.4j. +j.21.2O. +0.1/.2Q. +0.10.3v. +0.19.3m. +0.1e.3g. +0.1i.3e. +0.1v.34. +0.22.2O. +0.23.2N. +0.K.3N. +0.u.40. +0.z.3Y. +1.31.1D. +1.33.1w. +1.r.43. +2.2/.1N. +2.A.3X. +3.1l.39. +3.3M.L. +3.M.3L. +4./.3w. +4.14.3r. +4.15.3q. +4.1d.3h. +4.1t.36. +4.s.42. +5.c.4k. +6.3S.F. +6.4c.i. +8.2+.1Q. +a.2P.21. +a.2Y.1W. +a.32.1z. +b.3G.S. +c.37.1p. +f.1+.2R. +f.27.2M. +0.+.3y. +0.11.3v. +0.15.3r. +0.1a.3m. +0.1f.3g. +0.1j.3e. +0.1K.30. +0.1m.39. +0.1w.34. +0.1Z.2S. +0.20.2Q. +0.23.2O. +0.24.2N. +0.L.3N. +0.v.40. +1.33.1x. +1.s.43. +2.2/.1O. +2.2H.2d. +2.c.4l. +3.3M.M. +4.16.3q. +6.3i.1d. +6.4c.j. +7.r.44. +7.u.41. +9./.3x. +a.2P.22. +a.32.1A. +b.3G.T. +b.3J.N. +c.37.1q. +c.3f.1h. +f.1/.2R. +0./.3y. +0.1+.2S. +0.12.3v. +0.1b.3m. +0.1L.30. +0.1n.39. +0.1x.34. +0.24.2O. +0.25.2N. +0.w.40. +1.31.1E. +1.33.1y. +2.2/.1P. +3.3K.N. +3.M.3N. +4.16.3r. +4.17.3q. +4.s.44. +4.u.42. +5.c.4m. +6.4c.k. +7.r.45. +a.2P.23. +a.2Y.1X. +a.32.1B. +b.3G.U. +b.3J.O. +b.3R.G. +c.28.2M. +c.37.1r. +c.3f.1i. +f.1V.2Z. +f.20.2R. +f.2c.2I. +h.1g.3g. +j.21.2Q. +0.1/.2S. +0.10.3y. +0.1c.3m. +0.1M.30. +0.1o.39. +0.1y.34. +0.22.2Q. +0.25.2O. +0.26.2N. +0.A.3Y. +0.d.4j. +0.x.40. +1.31.1F. +1.33.1z. +1.c.4n. +1.u.43. +2./.3z. +2.2/.1Q. +3.3K.O. +4.17.3r. +4.18.3q. +4.1e.3h. +4.s.45. +5.k.4d. +6.3L.N. +6.3S.G. +6.4c.l. +7.r.46. +a.2P.24. +a.32.1C. +b.3G.V. +b.3J.P. +b.3R.H. +c.37.1s. +c.3f.1j. +j.21.2R. +0.11.3y. +0.19.3q. +0.1k.3e. +0.1p.39. +0.1z.34. +0.20.2S. +0.23.2Q. +0.26.2O. +0.27.2N. +0.B.3Y. +0.e.4j. +0.y.40. +1.31.1G. +1.33.1A. +3.3K.P. +3.3M.N. +4./.3A. +4.18.3r. +4.1f.3h. +4.1N.30. +5.c.4o. +6.3i.1e. +6.3L.O. +6.3S.H. +6.4c.m. +7.r.47. +7.s.46. +7.u.44. +8.2+.1R. +a.2J.29. +a.2P.25. +a.32.1D. +b.3G.W. +b.3J.Q. +b.3R.I. +c.37.1t. +f.22.2R. +0.12.3y. +0.13.3v. +0.19.3r. +0.1A.34. +0.1d.3m. +0.1h.3g. +0.1O.30. +0.1q.39. +0.24.2Q. +0.27.2O. +0.2I.2d. +0.C.3Y. +0.f.4j. +0.N.3N. +0.z.40. +1.31.1H. +1.33.1B. +2.A.3Z. +3.1l.3e. +3.1u.37. +3.3K.Q. +3.3M.O. +3.I.3S. +3.n.4c. +4.1a.3q. +4.r.48. +4.u.45. +5.c.4p. +5.k.4e. +6.3i.1f. +6.3L.P. +7.s.47. +8.2+.1S. +9.1t.38. +a.2J.2a. +a.2P.26. +b.3G.X. +b.3J.R. +b.3R.J. +d.2U.1Y. +f.23.2R. +f.3h.1g. +j.21.2S. +0.14.3v. +0.1B.34. +0.1i.3g. +0.1m.3e. +0.1P.30. +0.1r.39. +0.22.2S. +0.24.2R. +0.25.2Q. +0.D.3Y. +0.g.4j. +0.O.3N. +1.31.1I. +1.33.1C. +2.A.3+. +3.3K.R. +3.3M.P. +3.J.3S. +4.1a.3r. +4.1b.3q. +4.s.48. +5./.3B. +5.k.4f. +6.3i.1g. +6.3L.Q. +6.4c.o. +7.r.49. +7.u.46. +a.2P.27. +a.32.1E. +b.3G.Y. +b.3J.S. +b.3R.K. +c.28.2N. +c.37.1v. +c.3f.1k. +0.15.3v. +0.1C.34. +0.1j.3g. +0.1n.3e. +0.1Q.30. +0.1s.39. +0.23.2S. +0.25.2R. +0.26.2Q. +0.E.3Y. +0.h.4j. +0.P.3N. +1.31.1J. +1.33.1D. +1.r.4a. +2.2/.1R. +2.A.3/. +3.1l.3f. +3.3K.S. +3.3M.Q. +4.1b.3r. +4.1c.3q. +5./.3C. +5.k.4g. +6.3L.R. +6.3S.K. +6.4c.p. +7.1+.2T. +7.s.49. +7.u.47. +a.32.1F. +b.3G.Z. +b.3J.T. +b.3R.L. +c.28.2O. +c.37.1w. +i.c.4q. +0.13.3y. +0.16.3v. +0.1D.34. +0.1e.3m. +0.1o.3e. +0.1t.39. +0.24.2S. +0.27.2Q. +0.29.2K. +0.A.40. +0.F.3Y. +0.i.4j. +0.Q.3N. +1.r.4b. +1.s.4a. +2./.3D. +2.2/.1S. +3.3K.T. +3.3M.R. +3.S.3L. +4.1c.3r. +4.1h.3h. +4.26.2R. +4.u.48. +5.c.4r. +5.k.4h. +6.3S.L. +6.4c.q. +8.2+.1V. +a.2J.2c. +a.2P.28. +a.32.1G. +b.3J.U. +b.3R.M. +c.37.1x. +c.3f.1m. +0.14.3y. +0.17.3v. +0.1f.3m. +0.1p.3e. +0.25.2S. +0.B.40. +0.j.4j. +0.R.3N. +1.33.1E. +1.k.4i. +1.s.4b. +3.1u.39. +3.3K.U. +3.3M.S. +3.M.3S. +4.1d.3q. +4.1i.3h. +4.1t.3a. +5.2a.2K. +5.c.4s. +6.3i.1h. +6.3L.T. +7.u.49. +8.2+.1W. +9./.3E. +a.32.1H. +b.3J.V. +c.37.1y. +c.3f.1n. +f.27.2R. +0.15.3y. +0.18.3v. +0.1E.34. +0.1j.3h. +0.1k.3g. +0.1q.3e. +0.1v.39. +0.26.2S. +0.C.40. +0.k.4j. +1.31.1K. +1.33.1F. +1.u.4a. +2.A.42. +3.3K.V. +3.3M.T. +3.S.3N. +4.1d.3r. +4.1t.3b. +5.1h.3j. +5.c.4t. +6.3i.1i. +6.3L.U. +6.3m.1g. +9./.3F. +a.2V.1Y. +a.32.1I. +b.3J.W. +c.28.2Q. +c.37.1z. +c.3f.1o. +d.2U.1Z. +0.16.3y. +0.19.3v. +0.1F.34. +0.1r.3e. +0.1w.39. +0.27.2S. +0.D.40. +0.d.4q. +0.G.3Y. +0.l.4j. +1.31.1L. +1.33.1G. +1.u.4b. +2.2/.1V. +2.A.43. +3.1l.3g. +3.2W.1Y. +3.3K.W. +3.3M.U. +4.1t.3c. +5.1h.3k. +5.k.4k. +6.3i.1j. +6.3L.V. +6.4c.r. +7.22.2T. +8.2+.1X. +a.2J.2d. +a.32.1J. +b.3G.+. +b.3J.X. +b.3R.N. +c.28.2R. +c.37.1A. +c.3f.1p. +c.T.3N. +d.2U.1+. +f.1R.30. +0.17.3y. +0.1a.3v. +0.1G.34. +0.1m.3g. +0.1s.3e. +0.1x.39. +0.E.40. +0.e.4q. +0.H.3Y. +0.m.4j. +0.U.3N. +1.31.1M. +1.33.1H. +2.2/.1W. +2.A.44. +2.k.4l. +3.3K.X. +3.3M.V. +4.1e.3q. +4.1S.30. +4.1t.3d. +5.1h.3l. +5.c.4u. +6.3L.W. +6.3S.N. +6.4c.s. +7.r.4d. +b.3G./. +b.3J.Y. +b.3R.O. +c.37.1B. +c.3f.1q. +d.2U.1/. +f.2c.2K. +0.18.3y. +0.1b.3v. +0.1e.3r. +0.1H.34. +0.1h.3m. +0.1k.3h. +0.1n.3g. +0.1t.3e. +0.1y.39. +0.F.40. +0.f.4q. +0.V.3N. +1.31.1N. +1.33.1I. +3.3K.Y. +3.3M.W. +3.I.3Y. +3.n.4j. +4.1f.3q. +5.c.4v. +5.k.4m. +6.3L.X. +6.3S.O. +6.4c.t. +7.s.4d. +b.3G.10. +b.3J.Z. +b.3R.P. +c.28.2S. +c.37.1C. +c.3f.1r. +d.2U.20. +f.24.2T. +0.19.3y. +0.1c.3v. +0.1f.3r. +0.1I.34. +0.1o.3g. +0.1z.39. +0.29.2L. +0.3q.1g. +0.g.4q. +0.o.4j. +0.W.3N. +1.1h.3n. +1.31.1O. +1.33.1J. +1.k.4n. +2.2/.1X. +2.A.46. +3.1l.3h. +3.1u.3e. +3.3K.Z. +3.3M.X. +3.J.3Y. +4.1i.3m. +5.c.4w. +6.3i.1k. +6.3L.Y. +6.3S.P. +6.4c.u. +7.r.4e. +a.32.1K. +b.3G.11. +b.3R.Q. +c.37.1D. +c.3f.1s. +d.2U.21. +0.1A.39. +0.1a.3y. +0.1J.34. +0.1j.3m. +0.1m.3h. +0.1p.3g. +0.1V.30. +0.1v.3e. +0.2K.2d. +0.h.4q. +0.K.3Y. +0.p.4j. +0.X.3N. +1.31.1P. +2.A.47. +3.1l.3i. +3.3M.Y. +5.1h.3o. +5.2a.2L. +5.k.4o. +6.3L.Z. +6.3r.1g. +6.3S.Q. +6.4c.v. +7.26.2T. +7.r.4f. +7.s.4e. +7.u.4d. +a.2V.1Z. +a.32.1L. +b.3G.12. +b.3R.R. +c.3f.1t. +d.2U.22. +0.1B.39. +0.1b.3y. +0.1d.3v. +0.1n.3h. +0.1q.3g. +0.1W.30. +0.1w.3e. +0.G.40. +0.i.4q. +0.L.3Y. +0.q.4j. +1.31.1Q. +3.1u.3f. +3.2W.1Z. +3.3M.Z. +5./.3H. +5.1h.3p. +5.2g.2w. +5.k.4p. +6.3i.1m. +6.3S.R. +6.4c.w. +7.r.4g. +7.s.4f. +9.c.4x. +a.2V.1+. +a.2Y.1Y. +a.32.1M. +b.3R.S. +c.37.1E. +c.Y.3N. +d.2U.23. +0.1C.39. +0.1c.3y. +0.1o.3h. +0.1r.3g. +0.1x.3e. +0.H.40. +0.j.4q. +0.Z.3N. +1.33.1K. +2.1d.3w. +3.2W.1+. +3.M.3Y. +3.S.3S. +4.1h.3q. +4.r.4h. +5./.3I. +5.c.4y. +6.3i.1n. +6.4c.x. +7.s.4g. +7.u.4e. +a.2V.1/. +a.32.1N. +b.3J.+. +b.3R.T. +c.37.1F. +c.3f.1v. +d.2U.24. +0.1D.39. +0.1K.34. +0.1k.3m. +0.1p.3h. +0.1s.3g. +0.1X.30. +0.1y.3e. +0.2c.2L. +0.k.4q. +1.33.1L. +1.r.4i. +2.1d.3x. +3.2W.1/. +3.3K.+. +3.I.40. +4.1h.3r. +4.1i.3q. +5.c.4z. +6.3i.1o. +6.3S.T. +6.4c.y. +7.1+.2X. +7.s.4h. +7.u.4f. +a.2V.20. +a.32.1O. +b.3G.13. +b.3J./. +b.3R.U. +c.37.1G. +c.3f.1w. +d.2U.25. +f.29.2M. +0.1d.3y. +0.1e.3v. +0.1j.3q. +0.1L.34. +0.1q.3h. +0.1t.3g. +0.1z.3e. +0.l.4q. +0.r.4j. +1.1h.3s. +1.33.1M. +1.s.4i. +3.1l.3m. +3.2W.20. +3.3K./. +3.J.40. +4.1i.3r. +5.2a.2M. +5.c.4A. +5.k.4r. +6.3i.1p. +6.3L.+. +6.3S.U. +6.4c.z. +7.u.4g. +a.2V.21. +a.32.1P. +b.3G.14. +b.3J.10. +b.3R.V. +c.37.1H. +c.3f.1x. +d.2U.26. +0.1A.3e. +0.1E.39. +0.1f.3v. +0.1j.3r. +0.1M.34. +0.1m.3m. +0.K.40. +0.m.4q. +0.N.3Y. +0.s.4j. +1.31.1R. +1.33.1N. +2.1d.3z. +3.1u.3g. +3.2W.21. +3.3K.10. +3.3M.+. +4.1r.3h. +4.u.4h. +5.2g.2x. +5.c.4B. +5.k.4s. +6.3i.1q. +6.3L./. +6.3S.V. +7.r.4k. +a.2V.22. +a.32.1Q. +b.3G.15. +b.3J.11. +b.3R.W. +c.37.1I. +c.3f.1y. +d.2U.27. +0.+.3N. +0.1F.39. +0.1n.3m. +0.1s.3h. +0.1v.3g. +0.2L.2d. +0.L.40. +0.O.3Y. +0.t.4j. +1.31.1S. +1.33.1O. +1.u.4i. +2.1d.3A. +2.r.4l. +3.2W.22. +3.3K.11. +3.3M./. +3.n.4q. +4.1B.3e. +4.1N.34. +5.c.4C. +5.k.4t. +6.3i.1r. +6.3L.10. +6.3S.W. +7.s.4k. +a.2V.23. +a.2Y.1Z. +b.3G.16. +b.3J.12. +b.3R.X. +c.3f.1z. +f.37.1J. +h.1g.3v. +0./.3N. +0.1C.3e. +0.1e.3y. +0.1G.39. +0.1O.34. +0.1o.3m. +0.1w.3g. +0.29.2N. +0.o.4q. +0.P.3Y. +0.u.4j. +1.33.1P. +2.s.4l. +3.2W.23. +3.3K.12. +3.3M.10. +3.M.40. +4.1k.3q. +4.1t.3h. +5.1h.3t. +5.c.4D. +6.3i.1s. +6.3L.11. +6.3S.X. +6.4c.A. +7.22.2X. +7.r.4m. +a.2V.24. +a.2Y.1+. +b.3G.17. +b.3R.Y. +c.3f.1A. +d.2U.28. +f.2c.2M. +0.10.3N. +0.1D.3e. +0.1f.3y. +0.1H.39. +0.1k.3r. +0.1P.34. +0.1p.3m. +0.1x.3g. +0.29.2O. +0.p.4q. +0.Q.3Y. +0.v.4j. +1.33.1Q. +1.r.4n. +2.A.4d. +2.c.4E. +3.1l.3q. +3.1u.3h. +3.2W.24. +3.3M.11. +4.s.4m. +5./.3O. +5.1h.3u. +5.2a.2N. +5.k.4u. +6.3i.1t. +6.3L.12. +6.3S.Y. +6.4c.B. +7.u.4k. +a.2V.25. +a.2Y.1/. +b.3G.18. +b.3R.Z. +c.3f.1B. +0.11.3N. +0.1h.3v. +0.1I.39. +0.1Q.34. +0.1q.3m. +0.1v.3h. +0.1y.3g. +0.q.4q. +0.R.3Y. +0.w.4j. +1.31.1V. +1.s.4n. +2./.3P. +2.u.4l. +3.1l.3r. +3.1u.3i. +3.2W.25. +3.3M.12. +4.1m.3q. +5.1t.3j. +5.2a.2O. +5.2g.2y. +5.c.4F. +5.k.4v. +6.3S.Z. +6.3y.1g. +6.4c.C. +7.24.2X. +7.r.4o. +a.2P.29. +a.2V.26. +a.2Y.20. +a.32.1R. +b.3G.19. +b.3J.13. +c.37.1K. +c.3f.1C. +0.1E.3e. +0.1i.3v. +0.1J.39. +0.1r.3m. +0.1z.3g. +0.2M.2d. +0.N.40. +0.x.4j. +1.31.1W. +3.2W.26. +3.3K.13. +3.S.3Y. +4.12.3N. +4.1m.3r. +4.1n.3q. +4.1w.3h. +5.1h.3w. +5.c.4G. +5.k.4w. +6.3i.1v. +6.4c.D. +7.r.4p. +7.s.4o. +7.u.4m. +8.2+.1Y. +8.2g.2z. +9.1+.2Z. +9.1t.3k. +a.2P.2a. +a.2V.27. +a.2Y.21. +a.32.1S. +b.3G.1a. +b.3J.14. +c.37.1L. +c.3f.1D. +0.1A.3g. +0.1F.3e. +0.1j.3v. +0.1n.3r. +0.1o.3q. +0.1s.3m. +0.1x.3h. +0.29.2Q. +0.O.40. +0.y.4j. +1.u.4n. +2.A.4f. +3.2W.27. +3.3K.14. +5./.3Q. +5.1h.3x. +5.1t.3l. +6.3i.1w. +6.3L.13. +6.4c.E. +7.26.2X. +7.s.4p. +8.2g.2A. +a.2Y.22. +b.3G.1b. +b.3J.15. +c.37.1M. +c.T.3Y. +f.2c.2N. +0.1B.3g. +0.1G.3e. +0.1h.3y. +0.1o.3r. +0.1y.3h. +0.2c.2O. +0.P.40. +0.r.4q. +0.U.3Y. +0.z.4j. +1.31.1X. +1.33.1R. +2.A.4g. +3.3K.15. +3.3M.13. +4.1p.3q. +4.1t.3m. +5.2a.2Q. +6.3i.1x. +6.3L.14. +6.4c.F. +7.u.4o. +9.k.4x. +a.2V.28. +a.2Y.23. +b.3G.1c. +b.3J.16. +b.3R.+. +c.3f.1E. +f.29.2R. +f.2g.2B. +f.37.1N. +0.13.3N. +0.1C.3g. +0.1H.3e. +0.1i.3y. +0.1K.39. +0.1p.3r. +0.Q.40. +0.s.4q. +0.V.3Y. +1.1t.3n. +1.33.1S. +2.1h.3z. +2.2/.1Y. +2.A.4h. +3.1u.3m. +3.2W.28. +3.3K.16. +3.3M.14. +4.1q.3q. +4.1z.3h. +5.2a.2R. +5.c.4H. +5.k.4y. +6.3i.1y. +6.3L.15. +6.3S.+. +7.r.4r. +7.u.4p. +a.2P.2c. +a.2Y.24. +a.32.1V. +b.3J.17. +b.3R./. +c.3f.1F. +f.1R.34. +f.2g.2C. +f.37.1O. +0.14.3N. +0.1D.3g. +0.1I.3e. +0.1j.3y. +0.1k.3v. +0.1L.39. +0.1q.3r. +0.1S.34. +0.1v.3m. +0.29.2S. +0.2N.2d. +0.R.40. +0.t.4q. +0.W.3Y. +3.3K.17. +3.3M.15. +4.1A.3h. +4.1r.3q. +5.1h.3A. +5.1t.3o. +5.c.4I. +5.k.4z. +6.3i.1z. +6.3L.16. +6.3S./. +7.r.4s. +7.s.4r. +9.22.2Z. +a.2Y.25. +a.32.1W. +b.3G.1d. +b.3J.18. +b.3R.10. +c.3f.1G. +f.37.1P. +0.15.3N. +0.1J.3e. +0.1M.39. +0.1w.3m. +0.2O.2d. +0.A.4j. +0.u.4q. +0.X.3Y. +3.1l.3v. +3.3K.18. +3.3M.16. +3.S.40. +4.1B.3h. +4.1r.3r. +4.1s.3q. +4.1t.3p. +5./.3T. +5.2a.2S. +5.c.4J. +5.k.4A. +6.3i.1A. +6.3L.17. +6.3S.10. +6.4c.G. +7.r.4t. +7.s.4s. +8.2+.1Z. +a.2Y.26. +b.3J.19. +b.3R.11. +c.37.1Q. +c.3f.1H. +f.2c.2Q. +0.16.3N. +0.1C.3h. +0.1E.3g. +0.1m.3v. +0.1s.3r. +0.1x.3m. +0.B.4j. +0.v.4q. +1.33.1V. +2.A.4k. +3.3K.19. +3.3M.17. +4.1N.39. +4.1t.3q. +4.s.4t. +5./.3U. +5.1h.3B. +5.c.4K. +5.k.4B. +6.3i.1B. +6.3L.18. +6.3S.11. +6.4c.H. +7.u.4r. +8.2+.1+. +9.24.2Z. +a.2P.2d. +a.2Y.27. +a.32.1X. +b.3J.1a. +b.3R.12. +c.3f.1I. +c.T.40. +c.Y.3Y. +f.2c.2R. +f.2g.2D. +0.17.3N. +0.1F.3g. +0.1k.3y. +0.1n.3v. +0.1O.39. +0.1V.34. +0.1y.3m. +0.30.1Y. +0.C.4j. +0.U.40. +0.w.4q. +0.Z.3Y. +1.33.1W. +2.A.4l. +3.1u.3q. +3.3M.18. +3.I.4c. +4.1D.3h. +4.1t.3r. +5./.3V. +5.1h.3C. +5.c.4L. +5.k.4C. +6.3i.1C. +6.3L.19. +6.3S.12. +7.r.4u. +7.u.4s. +8.2+.1/. +b.3G.1e. +b.3J.1b. +c.3f.1J. +e.3K.1a. +f.2g.2E. +0.18.3N. +0.1G.3g. +0.1K.3e. +0.1o.3v. +0.1P.39. +0.1W.34. +0.1z.3m. +0.2Q.2d. +0.D.4j. +0.V.40. +0.x.4q. +1.1t.3s. +2.1h.3D. +2.2/.1Z. +2.A.4m. +3.1l.3y. +3.1u.3r. +3.3K.1b. +3.3M.19. +3.J.4c. +4.1v.3q. +4.s.4u. +5./.3W. +5.c.4M. +5.k.4D. +6.3i.1D. +6.3L.1a. +7.r.4v. +7.u.4t. +8.2+.20. +9.26.2Z. +a.2Y.28. +b.3G.1f. +b.3J.1c. +f.1V.35. +f.2c.2S. +0.19.3N. +0.1A.3m. +0.1H.3g. +0.1L.3e. +0.1m.3y. +0.1p.3v. +0.1Q.39. +0.1v.3r. +0.2R.2d. +0.E.4j. +0.W.40. +0.y.4q. +1.33.1X. +2.1d.3I. +2.2/.1+. +2.A.4n. +2.k.4E. +3.3K.1c. +3.3M.1a. +4.1E.3h. +4.1w.3q. +5./.3X. +5.1h.3E. +5.29.2T. +5.c.4N. +6.3L.1b. +6.4c.K. +7.r.4w. +7.s.4v. +8.2+.21. +9.1V.36. +b.3G.1g. +b.3R.13. +c.37.1R. +0.1B.3m. +0.1F.3h. +0.1I.3g. +0.1M.3e. +0.1n.3y. +0.1q.3v. +0.1w.3r. +0.1X.34. +0.F.4j. +0.X.40. +2.2/.1/. +2.A.4o. +3.3M.1b. +4.1a.3N. +4.1x.3q. +4.u.4u. +5.1h.3F. +5.c.4O. +5.k.4F. +6.3i.1E. +6.3L.1c. +6.3S.13. +6.4c.L. +6.4q.z. +7.s.4w. +8.2+.22. +b.3J.1d. +b.3R.14. +c.3f.1K. +f.37.1S. +0.+.3Y. +0.1b.3N. +0.1C.3m. +0.1G.3h. +0.1J.3g. +0.1N.3e. +0.1o.3y. +0.1r.3v. +0.1x.3r. +0.2S.2d. +2.2/.20. +2.A.4p. +2.c.4P. +3.3K.1d. +3.3M.1c. +3.M.4c. +4.1t.3t. +4.1y.3q. +4.3f.1L. +5.k.4G. +6.3i.1F. +6.3S.14. +7.u.4v. +8.2+.23. +9.r.4x. +b.3R.15. +c.Y.40. +0./.3Y. +0.1c.3N. +0.1D.3m. +0.1O.3e. +0.1p.3y. +0.1s.3v. +0.1y.3r. +0.1Z.30. +0.Z.40. +2.2/.21. +4.1H.3h. +4.1z.3q. +5.1t.3u. +5.c.4Q. +6.3i.1G. +6.3L.1d. +6.3S.15. +7.r.4y. +7.u.4w. +8.2+.24. +9.s.4x. +b.3G.1h. +b.3R.16. +c.3f.1M. +0.1+.30. +0.10.3Y. +0.1P.3e. +0.1q.3y. +0.1t.3v. +0.A.4q. +0.G.4j. +2.2/.22. +3.3M.1d. +4.1A.3q. +4.1I.3h. +4.1z.3r. +4.3f.1N. +5.c.4R. +6.3i.1H. +6.3S.16. +7.r.4z. +7.s.4y. +8.2+.25. +b.3G.1i. +b.3J.1e. +b.3R.17. +c.37.1V. +d.2U.29. +f.1R.39. +0.1/.30. +0.11.3Y. +0.1E.3m. +0.1J.3h. +0.1K.3g. +0.1Q.3e. +0.1r.3y. +0.1S.39. +0.B.4q. +0.H.4j. +2.2/.23. +2.A.4r. +3.1u.3v. +3.3K.1e. +4.1A.3r. +4.1B.3q. +4.1d.3N. +4.1t.3w. +4.s.4z. +5./.3Z. +5.c.4S. +5.k.4H. +6.3i.1I. +6.3S.17. +6.4c.N. +7.r.4A. +8.2+.26. +9.1V.38. +9.u.4x. +b.3G.1j. +b.3J.1f. +b.3R.18. +c.3f.1O. +d.2U.2a. +f.37.1W. +0.12.3Y. +0.1B.3r. +0.1F.3m. +0.1L.3g. +0.1s.3y. +0.1v.3v. +0.20.30. +0.C.4q. +1.2n.2u. +2.1d.3O. +2.2/.24. +2.A.4s. +3.3K.1f. +3.I.4j. +4.1C.3q. +4.1t.3x. +5./.3+. +5.2g.2F. +5.k.4I. +6.3i.1J. +6.3L.1e. +6.3S.18. +6.4c.O. +7.r.4B. +7.s.4A. +7.u.4y. +8.2+.27. +b.3J.1g. +b.3R.19. +c.3f.1P. +0.+.40. +0.1G.3m. +0.1M.3g. +0.1t.3y. +0.1w.3v. +0.D.4q. +1.31.1Y. +2.1d.3P. +2.2/.25. +2.2n.2v. +2.A.4t. +2.c.4T. +3.3K.1g. +3.3M.1e. +3.J.4j. +4.1C.3r. +4.1D.3q. +4.3f.1Q. +4.u.4z. +5./.3/. +5.1h.3H. +5.k.4J. +6.3L.1f. +6.3S.19. +6.4c.P. +7.r.4C. +7.s.4B. +b.3R.1a. +c.37.1X. +e.21.30. +0./.40. +0.1e.3N. +0.1H.3m. +0.1N.3g. +0.1x.3v. +0.22.30. +0.E.4q. +0.K.4j. +2.0.4+. +2.1t.3z. +2.2/.26. +2.c.4U. +3.1u.3y. +3.3M.1f. +4.1D.3r. +4.1K.3h. +5.1h.3I. +5.k.4K. +6.3L.1g. +6.3S.1a. +6.4c.Q. +7.r.4D. +7.s.4C. +7.u.4A. +8.2+.28. +b.3G.1k. +b.3R.1b. +d.2U.2c. +f.1V.39. +f.2g.2G. +0.10.40. +0.13.3Y. +0.1f.3N. +0.1I.3m. +0.1L.3h. +0.1O.3g. +0.1v.3y. +0.1W.39. +0.1y.3v. +0.23.30. +0.F.4q. +0.L.4j. +2.1.4+. +2.1d.3Q. +2.2/.27. +2.r.4E. +3.3M.1g. +4.1E.3q. +4.1t.3A. +5./.41. +5.k.4L. +6.3i.1K. +6.3S.1b. +6.4c.R. +7.s.4D. +7.u.4B. +9.c.4V. +a.2V.29. +b.3G.1l. +b.3J.1h. +b.3R.1c. +f.1R.3e. +f.1V.3a. +0.11.40. +0.14.3Y. +0.1J.3m. +0.1M.3h. +0.1P.3g. +0.1S.3e. +0.1w.3y. +0.1z.3v. +0.24.30. +1.c.4W. +2.2.4+. +2.2H.2g. +2.A.4v. +2.s.4E. +3.2W.29. +3.3K.1h. +3.M.4j. +3.S.4c. +4.1E.3r. +4.1F.3q. +5./.42. +5.k.4M. +6.3i.1L. +6.3N.1g. +6.3S.1c. +7.r.4F. +7.u.4C. +a.2V.2a. +b.3G.1m. +b.3J.1i. +f.1V.3b. +0.12.40. +0.15.3Y. +0.1A.3v. +0.1F.3r. +0.1Q.3g. +0.1X.39. +0.1x.3y. +0.25.30. +1./.43. +1.2f.2T. +2.2/.28. +2.3.4+. +2.A.4w. +3.2W.2a. +3.3K.1i. +4.1G.3q. +4.1N.3h. +5.1t.3B. +5.29.2X. +5.k.4N. +6.3i.1M. +6.3L.1h. +6.4c.T. +7.r.4G. +7.s.4F. +7.u.4D. +9.1V.3c. +a.32.1Y. +b.3G.1n. +b.3J.1j. +b.3R.1d. +c.3f.1R. +d.2U.2d. +0.16.3Y. +0.1B.3v. +0.1G.3r. +0.1O.3h. +0.1y.3y. +0.26.30. +0.G.4q. +1.31.1Z. +2.4.4+. +2.u.4E. +3.3K.1j. +3.3M.1h. +4.1H.3q. +5./.44. +5.1t.3C. +5.k.4O. +6.3i.1N. +6.3L.1i. +6.3S.1d. +6.4c.U. +7.s.4G. +9.1V.3d. +b.3G.1o. +c.3f.1S. +0.17.3Y. +0.1C.3v. +0.1h.3N. +0.1H.3r. +0.1K.3m. +0.1P.3h. +0.1z.3y. 0.27.30. -4.27.31. -4.27.32. -4.27.33. -4.27.34. -0.27.35. -b.36.27. -0.27.37. -0.27.38. -5.39.27. -4.27.3a. -4.27.3c. -0.27.3d. -1.27.3e. -4.27.3f. -4.27.3g. +0.H.4q. +0.N.4j. +1.31.1+. +2.1t.3D. +2.5.4+. +2.A.4x. +2.k.4P. +3.3M.1i. +4.1I.3q. +5./.45. +6.3i.1O. +6.3L.1j. +6.4c.V. +7.u.4F. +9.c.4X. +a.2V.2c. +b.3G.1p. +e.0.53. +f.1V.3e. +0.13.40. +0.18.3Y. +0.1A.3y. +0.1D.3v. +0.1I.3r. +0.1L.3m. +0.1W.3e. +0.O.4j. +1.31.1/. +1.33.1Y. +2.6.4+. +2.A.4y. +3.2W.2c. +3.3M.1j. +3.I.4q. +4.1i.3N. +4.1J.3q. +5./.46. +5.1h.3O. +5.k.4Q. +6.3i.1P. +6.4c.W. +7.r.4H. +7.u.4G. +9.1t.3E. +9.c.4Y. +b.3G.1q. +b.3J.1k. +b.3R.1e. +e.1.53. +f.1Q.3h. +f.2g.2I. +0.14.40. +0.19.3Y. +0.1B.3y. +0.1j.3N. +0.1M.3m. +0.1R.3g. +0.34.1Y. +0.P.4j. +1.31.20. +2.1d.3V. +2.1h.3P. +2.7.4+. +2.A.4z. +3.3K.1k. +3.J.4q. +4.1J.3r. +5./.47. +5.k.4R. +6.3i.1Q. +6.3S.1e. +6.4c.X. +7.r.4I. +7.s.4H. +9.1t.3F. +a.2Y.29. +b.3G.1r. +b.3J.1l. +b.3R.1f. +c.28.30. +c.3f.1V. +e.0.55. +e.2.53. +0.15.40. +0.1a.3Y. +0.1C.3y. +0.1E.3v. +0.1S.3g. +0.1X.3e. +0.K.4q. +0.Q.4j. +1.31.21. +2.8.4+. +3.3K.1l. +4.1N.3m. +5./.48. +5.k.4S. +6.3L.1k. +6.3S.1f. +6.4c.Y. +7.r.4J. +7.s.4I. +a.2V.2d. +a.2Y.2a. +a.32.1Z. +b.3G.1s. +b.3J.1m. +b.3R.1g. +c.3f.1W. +e.1.55. +e.3.53. +0.16.40. +0.1b.3Y. +0.1D.3y. +0.1F.3v. +0.1O.3m. +0.L.4q. +0.R.4j. +1.31.22. +1.c.4Z. +2.1d.3X. +2.9.4+. +2.A.4B. +3.1l.3L. +3.2W.2d. +3.3K.1m. +3.3M.1k. +4.1K.3q. +5./.49. +5.1h.3Q. +6.3S.1g. +6.4c.Z. +7.r.4K. +7.s.4J. +7.u.4H. +a.32.1+. +b.3G.1t. +b.3J.1n. +e.2.55. +e.4.53. +0.17.40. +0.1c.3Y. +0.1G.3v. +0.1k.3N. +0.1L.3q. +0.1P.3m. +0.2w.2n. +1./.4a. +1.31.23. +2.a.4+. +2.A.4C. +2.k.4T. +3.3K.1n. +3.3M.1l. +3.M.4q. +3.S.4j. +4.1K.3r. +5.29.2Z. +6.3L.1m. +6.58.0. +7.r.4L. +7.s.4K. +7.u.4I. +a.32.1/. +b.3G.1u. +b.3J.1o. +c.3f.1X. +e.3.55. +e.5.53. +f.1R.3h. +0.18.40. +0.1E.3y. +0.1H.3v. +0.1L.3r. +0.1Q.3m. +1./.4b. +1.31.24. +1.33.1Z. +2.A.4D. +2.b.4+. +2.k.4U. +3.1l.3N. +3.3K.1o. +3.3M.1m. +4.1M.3q. +4.1S.3h. +4.r.4M. +6.3i.1R. +6.3L.1n. +6.58.1. +7.s.4L. +7.u.4J. +a.2Y.2c. +a.32.20. +b.3G.1v. +b.3J.1p. +b.3R.1h. +c.T.4j. +e.4.55. +e.6.53. +f.1V.3g. +0.19.40. +0.1d.3Y. +0.1F.3y. +0.1I.3v. +0.1m.3N. +0.1M.3r. +0.1W.3g. +0.1Z.34. +0.U.4j. +1.31.25. +1.33.1+. +3.3K.1p. +3.3M.1n. +4.1N.3q. +4.c.4+. +4.s.4M. +5.k.4V. +6.3i.1S. +6.3L.1o. +6.3S.1h. +6.58.2. +7.r.4N. +7.u.4K. +a.32.21. +b.3G.1w. +b.3J.1q. +b.3R.1i. +e.5.55. +e.7.53. +f.2g.2J. +0.1+.34. +0.1a.40. +0.1G.3y. +0.1J.3v. +0.1n.3N. +0.N.4q. +0.V.4j. +1.31.26. +1.33.1/. +1.k.4W. +3.3K.1q. +3.3M.1o. +4.1N.3r. +4.1O.3q. +4.c.4/. +5.1h.3T. +5.1t.3H. +6.3L.1p. +6.3S.1i. +6.4c.+. +6.58.3. +7.2p.2w. +7.r.4O. +7.s.4N. +7.u.4L. +a.32.22. +b.3G.1x. +b.3J.1r. +b.3R.1j. +c.37.1Y. +e.6.55. +e.8.53. +0.1/.34. +0.1b.40. +0.1H.3y. +0.1o.3N. +0.1O.3r. +0.1X.3g. +0.O.4q. +0.W.4j. +1.2f.2X. +1.31.27. +1.33.20. +2.1d.3Z. +2.r.4P. +3.2x.2n. +3.3K.1r. +3.3M.1p. +4.c.50. +4.s.4O. +4.u.4M. +5.1h.3U. +5.1t.3I. +6.3L.1q. +6.3S.1j. +6.4c./. +9.1+.35. +a.2Y.2d. +a.32.23. +b.3G.1y. +b.3J.1s. +e.4.58. +e.7.55. +e.9.53. +f.1P.3q. +f.1V.3h. +0.1c.40. +0.1e.3Y. +0.1I.3y. +0.1p.3N. +0.1P.3r. +0.1W.3h. +0.P.4q. +0.X.4j. +1.33.21. +2.1d.3+. +2.s.4P. +3.3K.1s. +3.3M.1q. +5./.4d. +5.1h.3V. +5.c.51. +6.3i.1V. +6.3L.1r. +6.4c.10. +6.58.5. +7.r.4Q. +7.u.4N. +9.1+.36. +a.32.24. +b.3G.1z. +b.3J.1t. +e.8.55. +e.a.53. +f.1Q.3q. +f.1R.3m. +f.20.34. +0.1f.3Y. +0.1J.3y. +0.1K.3v. +0.1q.3N. +0.1S.3m. +0.Q.4q. +1.31.28. +1.33.22. +2.1d.3/. +3.3K.1t. +3.3M.1r. +4.r.4R. +4.u.4O. +5.1h.3W. +5.1V.3j. +5.c.52. +6.3i.1W. +6.3L.1s. +6.4c.11. +6.58.6. +7.s.4Q. +8.2+.29. +9.k.4X. +a.32.25. +b.3G.1A. +b.3J.1u. +b.3R.1k. +c.Y.4j. +e.9.55. +e.b.53. +f.1Q.3r. +f.2g.2K. +j.21.34. +0.1d.40. +0.1L.3v. +0.1r.3N. +0.1X.3h. +0.22.34. +0.R.4q. +0.Z.4j. +1.33.23. +2.0.5d. +2.A.4H. +2.u.4P. +3.3K.1u. +3.3M.1s. +4.s.4R. +5./.4e. +5.1h.3X. +6.3L.1t. +6.3S.1k. +6.4c.12. +7.2p.2x. +7.r.4S. +8.2+.2a. +9.k.4Y. +a.32.26. +b.3G.1B. +b.3J.1v. +b.3R.1l. +e.7.58. +e.a.55. +f.1V.3k. +f.39.1Y. +h.1g.3Y. +i.c.53. +0.1M.3v. +0.1s.3N. +1.33.24. +2.1.5d. +3.1l.3S. +3.1u.3L. +3.2y.2n. +3.3K.1v. +3.3M.1t. +3.S.4q. +5./.4f. +5.1V.3l. +5.c.54. +6.3i.1X. +6.58.8. +7.s.4S. +7.u.4Q. +9.22.35. +a.32.27. +b.3G.1C. +b.3J.1w. +b.3R.1m. +e.b.55. +f.23.34. +f.37.1Z. +0.1K.3y. +0.1N.3v. +0.1V.3m. +0.24.34. +1.33.25. +2.1d.42. +2.2.5d. +2.2/.29. +2.A.4J. +2.r.4T. +3.3K.1w. +3.3M.1u. +4.1t.3N. +5./.4g. +6.3L.1v. +6.3S.1m. +7.u.4R. +8.2z.2n. +9.22.36. +b.3G.1D. +b.3J.1x. +b.3R.1n. +c.37.1+. +c.T.4q. +e.9.58. +f.1R.3q. +i.c.55. +0.1e.40. +0.1h.3Y. +0.1L.3y. +0.1O.3v. +0.1W.3m. +0.25.34. +0.U.4q. +1.1V.3n. +1.33.26. +1.c.56. +1.k.4Z. +2.1d.43. +2.2/.2a. +2.3.5d. +2.A.4K. +2.r.4U. +2.s.4T. +3.1u.3N. +3.3K.1x. +3.3M.1v. +4.1S.3q. +5./.4h. +5.1t.3O. +6.3L.1w. +6.3S.1n. +6.4c.13. +6.58.a. +7.u.4S. +8.2+.2c. +8.2A.2n. +9.1+.38. +9.24.35. +a.32.28. +b.3J.1y. +b.3R.1o. +c.37.1/. +e.1R.3r. +0.+.4j. +0.1f.40. +0.1i.3Y. +0.1M.3y. +0.1P.3v. +0.1v.3N. +0.26.34. +0.d.53. +0.V.4q. +1.2f.2Z. +1.33.27. +2.1d.44. +2.1t.3P. +2.4.5d. +2.A.4L. +2.s.4U. +3.2B.2n. +3.3K.1y. +3.3M.1w. +4.1S.3r. +5.1V.3o. +5.c.57. +6.3L.1x. +6.3S.1o. +6.4c.14. +6.58.b. +7.2p.2y. +7.r.4V. +9.24.36. +b.3G.1E. +b.3J.1z. +b.3R.1p. +c.37.20. +e.3.5e. +0./.4j. +0.1j.3Y. +0.1N.3y. +0.1Q.3v. +0.1w.3N. +0.1X.3m. +0.1Z.39. +0.27.34. +0.2C.2n. +0.3e.1Y. +0.e.53. +0.W.4q. +1.r.4W. +2.5.5d. +2.A.4M. +2.u.4T. +3.3K.1z. +3.3M.1x. +5.1h.3Z. +6.3L.1y. +6.3S.1p. +6.4c.15. +7.s.4V. +8.2z.2p. +8.58.c. +9.1V.3p. +9.26.35. +b.3G.1F. +b.3J.1A. +b.3R.1q. +c.37.21. +e.4.5e. +f.2g.2L. +h.1g.40. +0.1+.39. +0.10.4j. +0.1O.3y. +0.1x.3N. +0.29.30. +0.X.4q. +1.33.28. +1.s.4W. +2.1d.46. +2.2/.2c. +2.6.5d. +2.A.4N. +2.c.59. +2.u.4U. +3.3K.1A. +3.3M.1y. +4.d.55. +4.f.53. +5./.4k. +5.1h.3+. +5.1t.3Q. +6.3L.1z. +6.3S.1q. +6.4c.16. +8.2+.2d. +8.2A.2p. +9.26.36. +9.k.4+. +b.3G.1G. +b.3J.1B. +b.3R.1r. +c.37.22. +f.1V.3q. +0.1/.39. +0.11.4j. +0.1P.3y. +0.1V.3r. +0.1y.3N. +0.g.53. +2./.4l. +2.1.5j. +2.1d.47. +2.7.5d. +2.A.4O. +2.c.5a. +3.3K.1B. +3.3M.1z. +4.3f.1Y. +4.e.55. +5.1h.3/. +5.2a.30. +6.3L.1A. +6.3S.1r. +6.4c.17. +7.2p.2B. +7.u.4V. +9.1+.3a. +9.22.38. +9.k.4/. +b.3G.1H. +b.3J.1C. +b.3R.1s. +c.28.34. +c.Y.4q. +f.1W.3q. +f.37.23. +0.1h.40. +0.1k.3Y. +0.1Q.3y. +0.1z.3N. +0.20.39. +0.2n.2D. +0.h.53. +0.Z.4q. +1.1V.3s. +1.u.4W. +2.2.5j. +2.8.5d. +2.A.4P. +2.c.5b. +3.3K.1C. +3.3M.1A. +4.12.4j. +4.f.55. +5./.4m. +6.3L.1B. +6.3S.1s. +6.4c.18. +7.2p.2C. +9.1+.3b. +9.k.50. +9.r.4X. +b.3G.1I. +b.3J.1D. +b.3R.1t. +c.37.24. +e.7.5e. +f.1W.3r. +0.1A.3N. +0.1i.40. +1./.4n. +2.2/.2d. +2.3.5j. +2.9.5d. +2.A.4Q. +3.1l.3Y. +3.3K.1D. +3.3M.1B. +4.1X.3q. +4.g.55. +4.i.53. +5.1h.41. +5.k.51. +6.3L.1C. +6.3S.1t. +6.4c.19. +6.58.d. +9.1+.3c. +9.24.38. +9.r.4Y. +9.s.4X. +b.3G.1J. +b.3R.1u. +e.8.5e. +f.1R.3v. +f.2g.2M. +f.37.25. +g.2E.2n. +j.21.39. +0.1B.3N. +0.1j.40. +0.1m.3Y. +0.1S.3v. +0.1Z.3e. +0.22.39. +2.4.5j. +2.A.4R. +2.a.5d. +3.1u.3S. +3.3M.1C. +4.1X.3r. +4.h.55. +4.j.53. +5./.4o. +5.1h.42. +5.1t.3T. +5.k.52. +6.3L.1D. +6.4c.1a. +6.58.e. +9.1+.3d. +9.s.4Y. +b.3J.1E. +b.3R.1v. +e.9.5e. +f.2c.30. +f.37.26. +0.1+.3e. +0.13.4j. +0.1C.3N. +0.1n.3Y. +0.1Y.3g. +0.23.39. +1.1h.43. +2.5.5j. +2.A.4S. +3.3K.1E. +3.3M.1D. +4.i.55. +4.k.53. +5./.4p. +5.1t.3U. +6.3S.1v. +6.4c.1b. +6.58.f. +7.2p.2D. +9.1V.3t. +9.22.3a. +9.26.38. +9.c.5c. +9.u.4X. +b.3J.1F. +b.3R.1w. +e.a.5e. +f.37.27. +0.+.4q. +0.1/.3e. +0.14.4j. +0.1D.3N. +0.1o.3Y. +0.24.39. +0.l.53. +1.r.4Z. +2.6.5j. +2.c.5d. +3.3K.1F. +4.j.55. +4.u.4Y. +5.1h.44. +5.1t.3V. +5.1V.3u. +5.k.54. +6.3L.1E. +6.3S.1w. +6.4c.1c. +6.58.g. +7.2p.2E. +9.22.3b. +b.3G.1K. +b.3J.1G. +b.3R.1x. +c.3f.1Z. +f.1R.3y. +0./.4q. +0.15.4j. +0.1k.40. +0.1p.3Y. +0.1S.3y. +0.1V.3v. +0.25.39. +0.30.2d. +0.m.53. +1.s.4Z. +2.0.5m. +2.7.5j. +2.A.4T. +3.3K.1G. +3.3M.1E. +4.3f.1+. +4.k.55. +5.1h.45. +5.1t.3W. +6.3L.1F. +6.3S.1x. +6.58.h. +9.22.3c. +9.24.3a. +b.3G.1L. +b.3J.1H. +b.3R.1y. +f.20.3e. +f.2g.2N. +f.37.28. +i.c.5e. +0.10.4q. +0.16.4j. +0.1E.3N. +0.1q.3Y. +0.1W.3v. +0.26.39. +0.3h.1Y. +1.k.56. +2.1.5m. +2.8.5j. +2.A.4U. +2.c.5f. +3.1l.40. +3.3K.1H. +3.3M.1F. +3.n.53. +4.l.55. +5./.4r. +5.1h.46. +5.1t.3X. +5.1V.3w. +6.3L.1G. +6.3S.1y. +6.4c.1d. +6.58.i. +9.22.3d. +9.24.3b. +b.3G.1M. +b.3J.1I. +b.3R.1z. +c.3f.1/. +f.2g.2O. +j.21.3e. +0.11.4q. +0.17.4j. +0.1F.3N. +0.1m.40. +0.1r.3Y. +0.22.3e. +0.27.39. +1.31.29. +1.u.4Z. +2.1d.4d. +2.2.5m. +2.9.5j. +3.3K.1I. +3.3M.1G. +4.m.55. +4.o.53. +5./.4s. +5.1h.47. +5.2g.2P. +5.c.5g. +5.k.57. +6.3i.1Y. +6.3L.1H. +6.3S.1z. +6.58.j. +9.1V.3x. +9.24.3c. +9.26.3a. +9.r.4+. +b.3G.1N. +b.3J.1J. +b.3R.1A. +c.3f.20. +0.18.4j. +0.1G.3N. +0.1n.40. +0.1s.3Y. +0.1V.3y. +0.1X.3v. +0.1Z.3g. +0.p.53. +1.31.2a. +1.c.5h. +2.3.5m. +2.A.4W. +2.a.5j. +3.3K.1J. +3.3M.1H. +3.n.55. +4.12.4q. +4.3f.21. +5./.4t. +5.1h.48. +6.3L.1I. +6.3S.1A. +8.58.k. +9.24.3d. +9.26.3b. +9.r.4/. +9.s.4+. +b.3G.1O. +b.3R.1B. +f.23.3e. +0.1+.3g. +0.19.4j. +0.1H.3N. +0.1o.40. +0.1t.3Y. +0.1W.3y. +0.24.3e. +2.1V.3z. +2.4.5m. +2.b.5j. +2.c.5i. +2.k.59. +3.3M.1I. +4.d.5e. +4.o.55. +4.q.53. +5.1h.49. +6.3L.1J. +6.3S.1B. +6.4c.1e. +6.58.l. +9.26.3c. +9.r.50. +9.s.4/. +a.2F.2n. +b.3G.1P. +b.3R.1C. +c.28.39. +c.3f.22. +f.2g.2Q. +0.1/.3g. +0.1a.4j. +0.1I.3N. +0.1p.40. +1.1h.4a. +2.1d.4f. +2.5.5m. +2.c.5j. +2.k.5a. +3.1u.3Y. +3.3M.1J. +4.e.5e. +4.p.55. +5./.4u. +5.2g.2R. +6.3S.1C. +6.4c.1f. +6.58.m. +7.r.51. +9.1V.3A. +9.26.3d. +9.s.50. +9.u.4+. +b.3G.1Q. +b.3J.1K. +b.3R.1D. +c.3f.23. +f.25.3e. +0.13.4q. +0.1b.4j. +0.1J.3N. +0.1q.40. +0.1v.3Y. +0.1X.3y. +0.1Z.3h. +0.26.3e. +0.2G.2n. +1.1h.4b. +1.31.2c. +2.1d.4g. +2.6.5m. +2.A.4X. +2.k.5b. +3.3K.1K. +4.f.5e. +4.q.55. +5./.4v. +5.1t.3Z. +6.3m.1Y. +6.3S.1D. +6.4c.1g. +6.58.n. +7.r.52. +7.s.51. +9.u.4/. +a.32.29. +b.3J.1L. +c.3f.24. +f.20.3g. +0.1+.3h. +0.14.4q. +0.1c.4j. +0.1r.40. +0.1w.3Y. +0.27.3e. +0.r.53. +2.1d.4h. +2.7.5m. +2.A.4Y. +3.3K.1L. +4.g.5e. +5./.4w. +5.1t.3+. +5.1V.3B. +6.3i.1Z. +6.3L.1K. +6.58.o. +7.2p.2F. +7.s.52. +9.c.5k. +9.u.50. +a.32.2a. +b.3J.1M. +b.3R.1E. +c.3f.25. +f.2g.2S. +j.21.3g. +0.1/.3h. +0.15.4q. +0.1s.40. +0.1x.3Y. +0.22.3g. +2.2H.2n. +2.8.5m. +3.3K.1M. +3.3M.1K. +4.h.5e. +4.s.53. +5.1t.3/. +5.1V.3C. +6.3i.1+. +6.3L.1L. +6.3S.1E. +6.58.p. +7.3f.26. +7.r.54. +7.u.51. +b.3J.1N. +b.3R.1F. +0.16.4q. +0.1d.4j. +0.1K.3N. +0.1t.40. +0.1y.3Y. +0.20.3h. +0.23.3g. +0.r.55. +0.t.53. +1.31.2d. +1.33.29. +2.1V.3D. +2.9.5m. +3.3K.1N. +3.3M.1L. +4./.4x. +4.i.5e. +6.3i.1/. +6.3L.1M. +6.3S.1F. +6.4c.1h. +6.58.q. +7.1+.3j. +7.2p.2G. +7.s.54. +7.u.52. +9.k.5c. +b.3G.1R. +b.3J.1O. +b.3R.1G. +c.28.3e. +c.3f.27. +0.17.4q. +0.1L.3N. +0.1z.3Y. +0.24.3g. +0.29.34. +0.3q.1Y. +0.u.53. +1.33.2a. +1.r.56. +2.1d.4k. +2.A.4Z. +2.a.5m. +2.c.5l. +2.k.5d. +3.1u.40. +3.3K.1O. +3.3M.1M. +4.j.5e. +4.s.55. +5./.4y. +5.1h.4d. +5.1t.41. +6.3i.20. +6.3L.1N. +6.3S.1G. +6.4c.1i. +9.1+.3k. +9.1V.3E. +a.32.2c. +b.3G.1S. +b.3J.1P. +b.3R.1H. +j.21.3h. +0.18.4q. +0.1A.3Y. +0.1M.3N. +0.1v.40. +0.1Z.3m. +0.22.3h. +0.t.55. +0.v.53. +1.s.56. +2.1d.4l. +2.2H.2p. +2.b.5m. +3.3K.1P. +3.3M.1N. +4.3f.28. +4.k.5e. +5./.4z. +5.1t.42. +5.29.35. +5.2a.34. +6.3i.21. +6.3L.1O. +6.3r.1Y. +6.3S.1H. +6.4c.1j. +7.1+.3l. +7.r.57. +7.u.54. +b.3J.1Q. +b.3R.1I. +f.1V.3F. +f.25.3g. +0.1+.3m. +0.19.4q. +0.1B.3Y. +0.1e.4j. +0.1w.40. +0.23.3h. +0.26.3g. +0.w.53. +1.1t.43. +2.1d.4m. +2.2g.2T. +2.k.5f. +3.3K.1Q. +3.3M.1O. +4.1N.3N. +4.l.5e. +4.u.55. +5./.4A. +5.1h.4e. +5.29.36. +5.2a.35. +6.3i.22. +6.3L.1P. +6.3S.1I. +6.58.r. +7.s.57. +b.3R.1J. +f.2I.2n. +0.1/.3m. +0.1a.4q. +0.1C.3Y. +0.1f.4j. +0.1O.3N. +0.1x.40. +0.24.3h. +0.27.3g. +0.v.55. +0.x.53. +1.1+.3n. +1.33.2c. +1.c.5n. +1.u.56. +2.1d.4n. +2.A.4+. +2.r.59. +3.3M.1P. +4.m.5e. +5./.4B. +5.1h.4f. +5.1t.44. +5.k.5g. +6.3i.23. +6.3L.1Q. +6.3S.1J. +6.58.s. +7.22.3j. +a.32.2d. +b.3G.1V. +0.1b.4q. +0.1D.3Y. +0.1P.3N. +0.1y.40. +0.20.3m. +0.25.3h. +0.2c.34. +0.y.53. +1.k.5h. +2.1d.4o. +2.A.4/. +2.r.5a. +2.s.59. +3.3M.1Q. +3.n.5e. +4.w.55. +5./.4C. +5.1h.4g. +5.1t.45. +5.c.5o. +6.3i.24. +6.4c.1k. +6.58.t. +7.1+.3o. +7.u.57. +9.22.3k. +b.3G.1W. +h.1g.4j. +0.1c.4q. +0.1Q.3N. +0.1z.40. +0.z.53. +2.1d.4p. +2.A.50. +2.c.5p. +2.k.5i. +2.r.5b. +2.s.5a. +3.1l.4c. +4.26.3h. +4.o.5e. +4.x.55. +5./.4D. +5.1h.4h. +5.1t.46. +6.3i.25. +6.58.u. +7.22.3l. +7.24.3j. +7.2p.2I. +9.1+.3p. +b.3J.1R. +b.3R.1K. +c.28.3g. +e.21.3m. +f.1Z.3q. +0.1A.40. +0.1E.3Y. +0.22.3m. 0.27.3h. -0.27.3i. -1.27.3j. -4.27.3k. -4.27.3l. +0.y.55. +1.1h.4i. +1.33.2d. +2./.4E. +2.A.51. +2.k.5j. +2.s.5b. +2.u.59. +3.3K.1R. +4.p.5e. +5.1t.47. +5.c.5q. +6.3i.26. +6.3S.1K. +6.4c.1m. +6.58.v. +9.24.3k. +b.3G.1X. +b.3J.1S. +b.3R.1L. +d.2U.2g. +f.1+.3q. +f.1Z.3r. +f.37.29. +0.1/.3q. +0.1+.3r. +0.1d.4q. +0.1F.3Y. +0.1h.4j. +0.1Y.3v. +0.23.3m. +0.34.2d. +1.22.3n. +2.A.52. +2.u.5a. +3.3K.1S. +4.1B.40. +4.55.z. +4.q.5e. +5./.4F. +5.1t.48. +5.1V.3H. +5.29.38. +6.3i.27. +6.3L.1R. +6.3S.1L. +6.4c.1n. +6.58.w. +7.24.3l. +7.26.3j. +7.37.2a. +b.3R.1M. +0.1/.3r. +0.1C.40. +0.1G.3Y. +0.1i.4j. +0.24.3m. +1.1+.3s. +2.1d.4r. +2.u.5b. +3.3M.1R. +4.A.53. +4.k.5k. +5./.4G. +5.1h.4k. +5.1t.49. +5.1V.3I. +6.3L.1S. +6.3S.1M. +6.4c.1o. +6.58.x. +7.22.3o. +9.26.3k. +9.r.5c. +a.2J.2n. +b.3R.1N. +c.28.3h. +f.20.3q. +0.1D.40. +0.1H.3Y. +0.1j.4j. +0.1R.3N. +0.20.3r. +0.25.3m. +0.B.53. +1.1t.4a. +1.24.3n. +2.1d.4s. +2.1h.4l. +2.A.54. +2.r.5d. +3.3M.1S. +6.3i.28. +6.3S.1N. +6.4c.1p. +6.58.y. +7.26.3l. +9.22.3p. +9.s.5c. +b.3J.1V. +b.3R.1O. +j.21.3q. +0.1e.4q. +0.1I.3Y. +0.1S.3N. +0.29.39. +0.C.53. +0.r.5e. +1.1t.4b. +1.c.5r. +2.1d.4t. +2.s.5d. +3.3K.1V. +4.26.3m. +4.A.55. +5.1h.4m. +6.3S.1O. +6.3y.1Y. +6.4c.1q. +6.58.z. +b.3J.1W. +b.3R.1P. +c.37.2c. +e.21.3r. +f.22.3q. +f.24.3o. +0.1E.40. +0.1f.4q. +0.1J.3Y. +0.22.3r. 0.27.3m. -4.27.3n. -4.27.3o. -0.27.3p. -4.27.3q. -4.27.3r. -4.27.3s. -4.27.3t. -4.27.3u. -a.3v.27. -4.27.3w. -4.27.3x. -a.3y.27. -2.3z.27. -5.3A.27. -2.3B.27. -0.27.3C. -4.27.3D. -4.27.3E. -a.3F.27. -5.3G.27. -4.27.3H. -4.27.3I. -4.27.3J. -4.27.3K. -4.27.3L. -0.27.3M. -4.27.3N. -4.27.3O. -4.27.3P. -0.27.3Q. -4.27.3R. -4.27.3S. -1.27.3T. -4.27.3U. -4.27.3V. -4.27.3W. -4.27.3X. -4.27.3Y. -4.27.3Z. -1.27.3+. -1.27.3/. -5.40.27. -4.27.41. -4.27.42. -4.27.43. -4.27.44. -4.27.45. -0.27.47. -4.27.48. -4.27.49. -1.27.4a. -4.27.4b. -4.27.4c. -0.27.4d. -4.27.4e. -4.27.4f. -4.27.4g. -4.27.4h. -4.27.4i. -4.27.4j. -4.27.4k. -4.27.4l. -4.27.4m. -4.27.4n. -4.27.4o. -4.27.4p. -4.27.4q. -4.27.4r. -4.27.4s. -4.27.4t. -4.27.4u. -4.27.4v. -4.27.4w. -4.27.4x. -4.27.4y. -4.27.4z. -4.27.4A. -4.27.4B. -4.27.4C. -4.27.4D. -4.27.4E. -1.27.4F. -4.27.4G. -4.27.4H. -1.27.4I. -4.27.4J. -4.27.4K. -4.27.4L. -4.27.4M. -4.27.4N. -0.27.4O. -4.27.4P. -d.27.4Q. -1.27.4R. -4.27.4S. -4.27.4T. -4.27.4U. -d.27.4V. -4.27.4W. -4.27.4Y. -1.27.4Z. -4.27.4+. -4.27.4/. -1.27.50. -d.27.51. -0.27.52. -4.27.53. -4.27.54. -4.27.55. -4.27.56. -1.27.57. -4.27.58. -4.27.59. -a.5a.27. -2.5b.27. -4.27.5c. -5.5d.27. -4.27.5e. -4.27.5f. -4.27.5g. -4.27.5h. -4.27.5i. -4.27.5j. -4.27.5k. -4.27.5l. -4.27.5m. -4.27.5n. -4.27.5o. -4.27.5p. -4.27.5q. -4.27.5r. -1.27.5s. -4.27.5t. -4.27.5u. -4.27.5v. -1.27.5w. -1.27.5y. -1.27.5z. -4.27.5A. -0.27.5B. -0.27.5C. -1.27.5D. -4.27.5E. -5.5F.27. -4.27.5G. -d.27.5H. -0.27.5I. -f.27.5K. -4.27.5L. -4.27.5M. -4.27.5N. -0.27.5O. -4.27.5P. -4.27.5Q. -0.27.5R. -4.27.5S. -4.27.5T. -0.27.5U. -4.27.5V. -d.27.5W. -4.27.5X. -4.27.5Y. -0.27.5Z. -0.27.5/. -0.27.60. -4.27.61. -2.62.27. -4.27.64. -4.27.65. -2.66.27. -4.27.67. -4.27.68. -4.27.69. -4.27.6a. -a.6b.27. -4.27.6c. -4.27.6d. -4.27.6e. -4.27.6f. -4.27.6g. -4.27.6h. -4.27.6i. -4.27.6j. -4.27.6k. -4.27.6l. -4.27.6m. -4.27.6n. -4.27.6o. -4.27.6p. -4.27.6q. -4.27.6r. -4.27.6s. -4.27.6t. -4.27.6u. -4.27.6v. -4.27.6w. -4.27.6x. -4.27.6y. -4.27.6z. -4.27.6A. -4.27.6B. -4.27.6C. -4.27.6D. -4.27.6E. -0.27.6F. -4.27.6G. -4.27.6H. -4.27.6I. -4.27.6J. -4.27.6K. -4.27.6L. -0.6M.27. -4.27.6N. -4.27.6O. -4.27.6P. -4.27.6Q. -4.27.6R. -4.27.6S. -4.27.6T. -4.27.6U. -4.27.6V. -4.27.6W. -1.27.6X. -4.27.6Y. -0.27.6+. -1.27.6/. -1.27.70. -d.27.71. -4.27.72. -4.27.73. -1.27.74. -4.27.75. -1.27.76. -1.27.77. -4.27.78. -5.79.27. -1.27.7a. -4.27.7b. -4.27.7c. -4.27.7d. -4.27.7e. -4.27.7f. -4.27.7g. -4.27.7h. -4.27.7i. -4.27.7j. -4.27.7k. -4.27.7l. -1.27.7m. -4.27.7o. -4.27.7p. -1.27.7r. -1.27.7s. -4.27.7t. -4.27.7u. -4.27.7v. -4.27.7w. -4.27.7x. -4.27.7y. -1.27.7z. -4.27.7A. -4.27.7B. -4.27.7C. -d.27.7D. -4.27.7E. -4.27.7F. -4.27.7G. -4.27.7H. -4.27.7I. -4.27.7J. -1.27.7K. -1.27.7L. -1.27.7M. -1.27.7N. -1.27.7O. -1.27.7P. -4.27.7Q. -4.27.7R. -4.27.7S. -4.27.7T. -1.27.7U. -4.27.7V. -4.27.7W. -4.27.7X. -4.27.7Y. -4.27.7Z. -1.27.7+. -1.27.7/. -1.27.80. -1.27.81. -1.27.82. -4.27.83. -1.27.84. -1.27.85. -1.27.87. -1.27.88. -4.27.89. -4.27.8a. -4.27.8b. -4.27.8c. -4.27.8d. -4.27.8e. -4.27.8f. -4.27.8g. -4.27.8h. -4.27.8i. -4.27.8j. -4.27.8k. -4.27.8l. -4.27.8m. -4.27.8n. -4.27.8o. -4.27.8p. -4.27.8q. -4.27.8r. -4.27.8s. -4.27.8t. -4.27.8u. -4.27.8v. -4.27.8w. -4.27.8x. -4.27.8y. -4.27.8z. -4.27.8A. -4.27.8B. -4.27.8C. -4.27.8D. -4.27.8E. -4.27.8F. -4.27.8G. -4.27.8H. -4.27.8I. -4.27.8J. -4.27.8K. -4.27.8L. -4.27.8M. -4.27.8N. -4.27.8O. -4.27.8P. -0.27.2p. -0.27.2p. -4.27.3b. -1.27.46. -1.27.4X. -1.27.5x. -1.27.5J. -1.27.7n. -1.27.7q. -d.28.28. -6.2a.28. -0.28.2b. -d.2e.28. -d.28.2l. -6.2m.28. -4.2n.28. -4.28.2p. -2.2q.28. -2.2r.28. -7.2s.28. -7.2t.28. -2.2u.28. -4.28.2v. -4.28.2w. -e.2x.28. -9.2y.28. -4.28.2z. -4.28.2A. -9.2B.28. -4.28.2C. -4.28.2D. -4.28.2E. -4.28.2F. -4.28.2G. -9.2H.28. -4.28.2I. -4.28.2J. -4.28.2K. -c.2M.28. -9.2N.28. -2.2O.28. -9.2Q.28. -7.2S.28. -4.28.2T. -1.2U.28. -9.2V.28. -1.2W.28. -4.28.2X. -4.28.2Y. -6.2+.28. -4.28.30. -4.28.35. -b.36.28. -4.28.37. -4.28.38. -5.39.28. -d.28.3d. -4.28.3h. -4.3i.28. -4.28.3m. -4.28.3o. -0.28.3p. -4.28.3s. -a.3v.28. -a.3y.28. -2.3z.28. -5.3A.28. -2.3B.28. -4.28.3C. -a.3F.28. -5.3G.28. -4.28.3M. -4.28.3Q. -5.40.28. -0.28.47. -4.28.4d. -d.28.4O. -4.28.4Q. -4.28.4T. -4.28.4V. -d.28.51. -4.28.52. -a.5a.28. -2.5b.28. -5.5d.28. -0.28.5B. -0.28.5C. -4.5F.28. -4.28.5H. -4.28.5I. -4.28.5K. -7.5M.28. -4.28.5O. -d.28.5R. -4.28.5U. -4.28.5W. -1.5Y.28. -d.28.5Z. -d.28.5/. -4.28.60. -2.62.28. -2.66.28. -a.6b.28. -c.6e.28. -4.28.6F. -0.6M.28. -d.28.6+. -4.28.71. -5.79.28. -4.28.7D. -4.28.2p. -4.28.2p. -1.2d.29. -1.2l.29. -4.2n.29. -1.51.29. -5.5I.29. -6.5K.29. -7.5M.29. -1.5Y.29. -1.5+.29. -4.60.29. -5.62.29. -1.6q.29. -1.6t.29. -3.6F.29. -c.6H.29. -8.6M.29. -1.7B.29. -1.7R.29. -1.86.29. -1.8d.29. -d.2a.2a. -d.2a.2b. -7.2e.2a. -0.2a.2l. -6.2m.2a. -d.2a.2p. -2.2q.2a. -2.2r.2a. -7.2s.2a. -7.2t.2a. -2.2u.2a. -d.2a.2v. -d.2a.2w. -e.2x.2a. -9.2y.2a. -0.2a.2z. -d.2a.2A. -9.2B.2a. -d.2a.2C. -0.2a.2D. -d.2a.2E. -d.2a.2F. -0.2a.2G. -9.2H.2a. -d.2a.2I. -d.2a.2J. -d.2a.2K. -c.2M.2a. -9.2N.2a. -2.2O.2a. -9.2Q.2a. -7.2S.2a. -d.2a.2T. -1.2U.2a. -9.2V.2a. -1.2W.2a. -0.2a.2X. -b.2+.2a. -0.2a.30. -0.2a.35. -b.36.2a. -d.2a.37. -d.2a.38. -5.39.2a. -d.2a.3d. -d.2a.3h. -5.3i.2a. -d.2a.3m. -0.2a.3p. -a.3v.2a. -a.3y.2a. -5.3z.2a. -5.3A.2a. -2.3B.2a. -d.2a.3C. -a.3F.2a. -5.3G.2a. -d.2a.3M. -d.2a.3Q. -5.40.2a. -d.2a.47. -0.2a.4d. -d.2a.4O. -d.2a.4Q. -5.4T.2a. -d.2a.4V. -0.2a.51. -d.2a.52. -a.5a.2a. -2.5b.2a. -5.5d.2a. -0.2a.5B. -0.2a.5C. -5.5F.2a. -d.2a.5H. -0.2a.5I. -f.2a.5K. -7.5M.2a. -0.2a.5O. -d.2a.5R. -d.2a.5U. -d.2a.5W. -1.5Y.2a. -0.2a.5Z. -0.2a.5/. -0.2a.60. -2.62.2a. -2.66.2a. -a.6b.2a. -c.6e.2a. -0.2a.6F. -0.6M.2a. -0.2a.6+. -d.2a.71. -5.79.2a. -d.2a.7D. -d.2a.2p. -d.2a.2p. -0.2b.2b. -0.2b.2l. -6.2m.2b. -0.2p.2b. -2.2q.2b. -2.2r.2b. -7.2s.2b. -7.2t.2b. -2.2u.2b. -0.2v.2b. -3.2b.2w. -e.2x.2b. -9.2y.2b. -0.2z.2b. -0.2A.2b. -9.2B.2b. -0.2C.2b. -0.2D.2b. -0.2E.2b. -0.2F.2b. -0.2G.2b. -9.2H.2b. -0.2I.2b. -0.2J.2b. -0.2K.2b. -c.2M.2b. -9.2N.2b. -2.2O.2b. -9.2Q.2b. -7.2S.2b. -0.2T.2b. -1.2U.2b. -9.2V.2b. -1.2W.2b. -0.2X.2b. -b.2+.2b. -d.30.2b. -3.2b.35. -b.36.2b. -0.2b.37. -0.2b.38. -5.39.2b. -5.3d.2b. -0.2b.3h. -5.3i.2b. -0.2b.3m. -5.3p.2b. -a.3v.2b. -a.3y.2b. -2.3z.2b. -5.3A.2b. -2.3B.2b. -5.3C.2b. -a.3F.2b. -5.3G.2b. -0.2b.3M. -0.2b.3Q. -5.40.2b. -0.2b.47. -5.4d.2b. -0.2b.4O. -0.2b.4Q. -5.4T.2b. -0.2b.4V. -3.2b.51. -0.52.2b. -a.5a.2b. -2.5b.2b. -5.5d.2b. -5.5B.2b. -5.5C.2b. -5.5F.2b. -0.2b.5H. -0.5I.2b. -f.2b.5K. -7.5M.2b. -5.5O.2b. -0.2b.5R. -0.2b.5U. -0.2b.5W. -1.5Y.2b. -5.5Z.2b. -0.2b.5/. -0.60.2b. -2.62.2b. -2.66.2b. -a.6b.2b. -c.6e.2b. -0.2b.6F. -0.6M.2b. -0.6+.2b. -3.2b.71. -5.79.2b. -0.2b.7D. -0.2p.2b. -0.2p.2b. -1.2d.2c. -1.51.2c. -5.5I.2c. -6.5K.2c. -7.5M.2c. -1.5Y.2c. -1.5+.2c. -4.60.2c. -5.62.2c. -8.6F.2c. -c.6H.2c. -8.6M.2c. -1.7R.2c. -1.2d.2d. -1.2d.2f. -1.2d.2g. -1.2d.2h. -1.2d.2i. -1.2d.2j. -1.2d.2o. -1.2d.2L. -1.2d.2P. -1.2d.2R. -1.2d.2Y. -1.2d.2Z. -1.2d.2/. -1.2d.31. -1.2d.32. -1.2d.33. -1.2d.34. -1.2d.3a. -1.2d.3c. -1.2d.3e. -1.2d.3f. -1.2d.3g. -1.2d.3j. -1.2d.3k. -1.2d.3l. -1.2d.3n. -1.2d.3o. -1.2d.3q. -1.2d.3r. -1.2d.3s. -1.2d.3t. -1.2d.3u. -1.2d.3w. -1.2d.3x. -1.2d.3D. -1.2d.3E. -1.2d.3H. -1.2d.3I. -1.2d.3J. -1.2d.3K. -1.2d.3L. -1.2d.3N. -1.2d.3O. -1.2d.3P. -1.2d.3R. -1.2d.3S. -1.2d.3T. -1.2d.3U. -1.2d.3V. -1.2d.3W. -1.2d.3X. -1.2d.3Y. -1.2d.3Z. -1.2d.3+. -1.2d.3/. -1.2d.41. -1.2d.42. -1.2d.43. -1.2d.44. -1.2d.45. -1.2d.48. -1.2d.49. -1.2d.4a. -1.2d.4b. -1.2d.4c. -1.2d.4e. -1.2d.4f. -1.2d.4g. -1.2d.4h. -1.2d.4i. -1.2d.4j. -1.2d.4k. -1.2d.4l. -1.2d.4m. -1.2d.4n. -1.2d.4o. -1.2d.4p. -1.2d.4q. -1.2d.4r. -1.2d.4s. -1.2d.4t. -1.2d.4u. -1.2d.4v. -1.2d.4w. -1.2d.4x. -1.2d.4y. -1.2d.4z. -1.2d.4A. -1.2d.4B. -1.2d.4C. -1.2d.4D. -1.2d.4E. -1.2d.4F. -1.2d.4G. -1.2d.4H. -1.2d.4I. -1.2d.4J. -1.2d.4K. -1.2d.4L. -1.2d.4M. -1.2d.4N. -1.2d.4P. -1.2d.4R. -1.2d.4S. -1.2d.4T. -1.2d.4U. -1.2d.4W. -1.2d.4Y. -1.2d.4Z. -1.2d.4+. -1.2d.4/. -1.2d.50. -1.51.2d. -1.2d.53. -1.2d.54. -1.2d.55. -1.2d.56. -1.2d.57. -1.2d.58. -1.2d.59. -1.2d.5c. -1.2d.5e. -1.2d.5f. -1.2d.5g. -1.2d.5h. -1.2d.5i. -1.2d.5j. -1.2d.5k. -1.2d.5l. -1.2d.5m. -1.2d.5n. -1.2d.5o. -1.2d.5p. -1.2d.5q. -1.2d.5r. -1.2d.5s. -1.2d.5t. -1.2d.5u. -1.2d.5v. -1.2d.5w. -1.2d.5y. -1.2d.5z. -1.2d.5A. -1.2d.5D. -1.2d.5E. -1.2d.5G. -5.5I.2d. -6.5K.2d. -1.2d.5L. -7.5M.2d. -1.2d.5N. -1.2d.5P. -1.2d.5Q. -1.2d.5S. -1.2d.5T. -1.2d.5V. -1.2d.5X. -1.2d.5Y. -1.5+.2d. -4.60.2d. -1.2d.61. -5.62.2d. -1.2d.64. -1.2d.65. -1.2d.67. -1.2d.68. -1.2d.69. -1.2d.6a. -1.2d.6c. -1.2d.6d. -1.2d.6e. -1.2d.6f. -1.2d.6g. -1.2d.6h. -1.2d.6i. -1.2d.6j. -1.2d.6k. -1.2d.6l. -1.2d.6m. -1.2d.6n. -1.2d.6o. -1.2d.6p. -1.2d.6q. -1.2d.6r. -1.2d.6s. -1.2d.6t. -1.2d.6u. -1.2d.6v. -1.2d.6w. -1.2d.6x. -1.2d.6y. -1.2d.6z. -1.2d.6A. -1.2d.6B. -1.2d.6C. -1.2d.6D. -1.2d.6E. -8.6F.2d. -1.2d.6G. -1.2d.6H. -1.2d.6I. -1.2d.6J. -1.2d.6K. -1.2d.6L. -3.6M.2d. -1.2d.6N. -1.2d.6O. -1.2d.6P. -1.2d.6Q. -1.2d.6R. -1.2d.6S. -1.2d.6T. -1.2d.6U. -1.2d.6V. -1.2d.6W. -1.2d.6X. -1.2d.6Y. -1.2d.6Z. -1.2d.6/. -1.2d.70. -1.2d.72. -1.2d.73. -1.2d.74. -1.2d.75. -1.2d.76. -1.2d.77. -1.2d.78. -1.2d.7a. -1.2d.7b. -1.2d.7c. -1.2d.7d. -1.2d.7e. -1.2d.7f. -1.2d.7g. -1.2d.7h. -1.2d.7i. -1.2d.7j. -1.2d.7k. -1.2d.7l. -1.2d.7m. -1.2d.7o. -1.2d.7p. -1.2d.7r. -1.2d.7s. -1.2d.7t. -1.2d.7u. -1.2d.7v. -1.2d.7w. -1.2d.7x. -1.2d.7y. -1.2d.7z. -1.2d.7A. -1.2d.7B. -1.2d.7C. -1.2d.7E. -1.2d.7F. -1.2d.7G. -1.2d.7H. -1.2d.7I. -1.2d.7J. -1.2d.7K. -1.2d.7L. -1.2d.7M. -1.2d.7N. -1.2d.7O. -1.2d.7P. -1.2d.7Q. -1.2d.7S. -1.2d.7T. -1.2d.7V. -1.2d.7W. -1.2d.7X. -1.2d.7Y. -1.2d.7+. -1.2d.7/. -1.2d.80. -1.2d.81. -1.2d.82. -1.2d.83. -1.2d.84. -1.2d.85. -1.2d.87. -1.2d.88. -1.2d.89. -1.2d.8a. -1.2d.8b. -1.2d.8d. -1.2d.8e. -1.2d.8f. -1.2d.8g. -1.2d.8h. -1.2d.8i. -1.2d.8j. -1.2d.8k. -1.2d.8l. -1.2d.8m. -1.2d.8n. -1.2d.8o. -1.2d.8p. -1.2d.8q. -1.2d.8r. -1.2d.8s. -1.2d.8t. -1.2d.8u. -1.2d.8v. -1.2d.8w. -1.2d.8x. -1.2d.8y. -1.2d.8z. -1.2d.8A. -1.2d.8B. -1.2d.8C. -1.2d.8D. -1.2d.8E. -1.2d.8F. -1.2d.8G. -1.2d.8H. -1.2d.8I. -1.2d.8J. -1.2d.3b. -1.2d.46. -1.2d.4X. -1.2d.5x. -1.2d.5J. -1.2d.7n. -1.2d.7q. -4.2e.2p. -4.2e.2q. -4.2e.2r. -7.2e.2s. -7.2e.2t. -d.2e.2u. -d.2e.2v. -d.2e.2w. -d.2e.2x. -4.2e.2y. -d.2e.2z. -d.2e.2A. -d.2e.2B. -d.2e.2C. -d.2e.2D. -d.2e.2E. -d.2e.2F. -d.2e.2G. -4.2e.2H. -d.2e.2I. -4.2e.2J. -d.2e.2K. -c.2M.2e. -d.2e.2N. -d.2e.2O. -d.2e.2Q. -7.2e.2S. -j.2e.2T. -1.2U.2e. -d.2e.2V. -1.2W.2e. -4.2e.2X. -4.2e.2+. -d.2e.30. -d.2e.35. -d.2e.36. -d.2e.37. -7.2e.38. -4.2e.39. -4.2e.3a. -d.2e.3d. -4.2e.3h. -d.2e.3i. -d.2e.3l. -d.2e.3m. -d.2e.3p. -4.2e.3v. -d.2e.3w. -d.2e.3x. -4.2e.3y. -d.2e.3z. -d.2e.3A. -4.2e.3B. -4.2e.3C. -d.2e.3D. -d.2e.3E. -4.2e.3F. -d.2e.3G. -d.2e.3H. -4.2e.3I. -4.2e.3J. -4.2e.3K. -4.2e.3M. -d.2e.3O. -d.2e.3P. -d.2e.3Q. -d.2e.3R. -4.2e.3S. -d.2e.3U. -4.2e.3X. -d.2e.3Y. -4.2e.3Z. -d.2e.40. -d.2e.41. -d.2e.42. -d.2e.43. -d.2e.44. -d.2e.45. -d.2e.47. -d.2e.48. -d.2e.49. -d.2e.4b. -d.2e.4c. -d.2e.4d. -d.2e.4i. -4.2e.4j. -d.2e.4l. -d.2e.4n. -d.2e.4q. -d.2e.4r. -4.2e.4t. -d.2e.4u. -4.2e.4B. -d.2e.4C. -d.2e.4K. -4.2e.4M. -4.2e.4N. -4.2e.4O. -d.2e.4P. -d.2e.4Q. -d.2e.4T. -d.2e.4V. -d.2e.4+. -4.2e.4/. -d.2e.51. -d.2e.52. -d.2e.5a. -d.2e.5b. -d.2e.5d. -4.2e.5B. -4.2e.5C. -4.2e.5F. -4.2e.5G. -4.2e.5H. -7.2e.5I. -d.2e.5K. -7.2e.5M. -d.2e.5O. -4.2e.5R. -d.2e.5T. -4.2e.5U. -d.2e.5W. -d.2e.5X. -1.5Y.2e. -d.2e.5Z. -4.2e.5/. -7.2e.60. -7.2e.62. -d.2e.66. -d.2e.6b. -c.6e.2e. -d.2e.6F. -4.2e.6+. -4.2e.71. -d.2e.7B. -d.2e.7D. -4.2e.89. -4.2e.2p. -4.2e.2p. -1.2l.2f. -4.2n.2f. -1.51.2f. -1.5v.2f. -6.5I.2f. -6.5K.2f. -7.5M.2f. -1.5Y.2f. -1.5+.2f. -4.60.2f. -5.62.2f. -1.6q.2f. -1.6t.2f. -8.6F.2f. -c.6H.2f. -8.6M.2f. -1.7B.2f. -1.86.2f. -1.8d.2f. -1.2l.2g. -4.2n.2g. -1.4s.2g. -1.51.2g. -1.5v.2g. -5.5I.2g. -6.5K.2g. -7.5M.2g. -1.5Y.2g. -1.5+.2g. -4.60.2g. -5.62.2g. -1.6q.2g. -1.6t.2g. -3.6F.2g. -c.6H.2g. -8.6M.2g. -1.7B.2g. -1.86.2g. -1.8d.2g. -1.2l.2h. -4.2n.2h. -1.4s.2h. -1.51.2h. -1.5v.2h. -6.5I.2h. -6.5K.2h. -7.5M.2h. -1.5Y.2h. -1.5+.2h. -4.60.2h. -5.62.2h. -1.6q.2h. -1.6t.2h. -3.6F.2h. -c.6H.2h. -8.6M.2h. -1.7B.2h. -1.7R.2h. -1.86.2h. -1.8d.2h. -1.2l.2i. -4.2n.2i. -1.51.2i. -1.5v.2i. -6.5I.2i. -6.5K.2i. -7.5M.2i. -1.5Y.2i. -1.5+.2i. -4.60.2i. -5.62.2i. -1.6q.2i. -1.6t.2i. -8.6F.2i. -c.6H.2i. -8.6M.2i. -1.7B.2i. -1.86.2i. -1.8d.2i. -1.2l.2j. -4.2n.2j. -1.51.2j. -1.5v.2j. -5.5I.2j. -6.5K.2j. -7.5M.2j. -1.5Y.2j. -1.5+.2j. -4.60.2j. -5.62.2j. -1.6q.2j. -1.6t.2j. -8.6F.2j. -c.6H.2j. -8.6M.2j. -1.7B.2j. -1.86.2j. -1.8d.2j. -0.2l.2l. -6.2m.2l. -4.2n.2l. -1.2l.2o. -0.2p.2l. -2.2q.2l. -2.2r.2l. -7.2s.2l. -7.2t.2l. -2.2u.2l. -0.2v.2l. -0.2l.2w. -e.2x.2l. -9.2y.2l. -0.2z.2l. -d.2A.2l. -9.2B.2l. -0.2C.2l. -0.2D.2l. -0.2E.2l. -0.2F.2l. -0.2G.2l. -9.2H.2l. -0.2I.2l. -d.2J.2l. -0.2K.2l. -1.2l.2L. -c.2M.2l. -9.2N.2l. -2.2O.2l. -1.2l.2P. -9.2Q.2l. -1.2l.2R. -7.2S.2l. -0.2T.2l. -1.2U.2l. -9.2V.2l. -1.2W.2l. -0.2X.2l. -1.2l.2Y. -1.2l.2Z. -d.2+.2l. -1.2l.2/. -d.30.2l. -1.2l.31. -1.2l.33. -1.2l.34. -0.2l.35. -b.36.2l. -0.2l.37. -0.38.2l. -5.39.2l. -1.2l.3a. -1.2l.3c. -5.3d.2l. -1.2l.3e. -1.2l.3f. -1.2l.3g. -0.3h.2l. -5.3i.2l. -1.2l.3j. -1.2l.3k. -1.2l.3l. -0.2l.3m. -1.2l.3n. -1.2l.3o. -5.3p.2l. -1.2l.3q. -1.2l.3r. -1.2l.3s. -1.2l.3t. -1.2l.3u. -a.3v.2l. -1.2l.3w. -1.2l.3x. -a.3y.2l. -2.3z.2l. -5.3A.2l. -2.3B.2l. -5.3C.2l. -1.2l.3D. -1.2l.3E. -a.3F.2l. -5.3G.2l. -1.2l.3H. -1.2l.3I. -1.2l.3J. -1.2l.3K. -1.2l.3L. -0.2l.3M. -1.2l.3N. -1.2l.3O. -1.2l.3P. -0.2l.3Q. -1.2l.3R. -1.2l.3S. -1.2l.3T. -1.2l.3U. -1.2l.3V. -1.2l.3W. -1.2l.3X. -1.2l.3Y. -1.2l.3Z. -1.2l.3+. -1.2l.3/. -5.40.2l. -1.2l.41. -1.2l.42. -1.2l.43. -1.2l.44. -1.2l.45. -0.2l.47. -1.2l.48. -1.2l.49. -1.2l.4a. -1.2l.4b. -1.2l.4c. -5.4d.2l. -1.2l.4e. -1.2l.4f. -1.2l.4g. -1.2l.4h. -1.2l.4i. -1.2l.4j. -1.2l.4k. -1.2l.4l. -1.2l.4m. -1.2l.4n. -1.2l.4o. -1.2l.4p. -1.2l.4q. -1.2l.4r. -1.2l.4s. -1.2l.4t. -1.2l.4u. -1.2l.4v. -1.2l.4w. -1.2l.4x. -1.2l.4y. -1.2l.4z. -1.2l.4A. -1.2l.4B. -1.2l.4C. -1.2l.4D. -1.2l.4E. -1.2l.4F. -1.2l.4G. -1.2l.4H. -1.2l.4I. -1.2l.4J. -1.2l.4K. -1.2l.4L. -1.2l.4M. -1.2l.4N. -0.2l.4O. -1.2l.4P. -0.4Q.2l. -1.2l.4R. -1.2l.4S. -5.4T.2l. -1.2l.4U. -d.2l.4V. -1.2l.4W. -1.2l.4Y. -1.2l.4Z. -1.2l.4+. -1.2l.4/. -1.2l.50. -5.51.2l. -0.52.2l. -1.2l.53. -1.2l.54. -1.2l.55. -1.2l.56. -1.2l.58. -1.2l.59. -a.5a.2l. -2.5b.2l. -1.2l.5c. -5.5d.2l. -1.2l.5e. -1.2l.5f. -1.2l.5g. -1.2l.5h. -1.2l.5i. -1.2l.5j. -1.2l.5k. -1.2l.5l. -1.2l.5m. -1.2l.5n. -1.2l.5o. -1.2l.5p. -1.2l.5q. -1.2l.5r. -1.2l.5s. -1.2l.5t. -1.2l.5u. -1.2l.5w. -1.2l.5y. -1.2l.5z. -1.2l.5A. -5.5B.2l. -5.5C.2l. -1.2l.5D. -1.2l.5E. -5.5F.2l. -1.2l.5G. -0.5H.2l. -0.5I.2l. -f.2l.5K. -1.2l.5L. -7.5M.2l. -1.2l.5N. -5.5O.2l. -1.2l.5P. -1.2l.5Q. -5.5R.2l. -1.2l.5S. -1.2l.5T. -0.2l.5U. -1.2l.5V. -0.5W.2l. -1.2l.5X. -1.5Y.2l. -5.5Z.2l. -5.5/.2l. -0.60.2l. -1.2l.61. -2.62.2l. -1.2l.64. -1.2l.65. -2.66.2l. -1.2l.67. -1.2l.68. -1.2l.69. -1.2l.6a. -a.6b.2l. -1.2l.6c. -1.2l.6d. -c.6e.2l. -1.2l.6f. -1.2l.6g. -1.2l.6h. -1.2l.6i. -1.2l.6j. -1.2l.6l. -1.2l.6m. -1.2l.6n. -1.2l.6o. -1.2l.6p. -1.2l.6q. -1.2l.6r. -1.2l.6s. -1.2l.6t. -1.2l.6u. -1.2l.6v. -1.2l.6w. -1.2l.6x. -1.2l.6z. -1.2l.6A. -1.2l.6B. -1.2l.6C. -1.2l.6D. -0.2l.6F. -1.2l.6G. -1.2l.6J. -1.2l.6K. -1.2l.6L. -0.6M.2l. -1.2l.6N. -1.2l.6O. -1.2l.6P. -1.2l.6Q. -1.2l.6R. -1.2l.6S. -1.2l.6T. -1.2l.6U. -1.2l.6V. -1.2l.6W. -1.2l.6X. -1.2l.6Y. -1.2l.6Z. -0.6+.2l. -1.2l.6/. -1.2l.70. -0.71.2l. -1.2l.72. -1.2l.73. -1.2l.74. -1.2l.75. -1.2l.76. -1.2l.77. -1.2l.78. -5.79.2l. -1.2l.7a. -1.2l.7b. -1.2l.7c. -1.2l.7d. -1.2l.7e. -1.2l.7f. -1.2l.7g. -1.2l.7h. -1.2l.7i. -1.2l.7j. -1.2l.7k. -1.2l.7l. -1.2l.7o. -1.2l.7p. -1.2l.7r. -1.2l.7s. -1.2l.7t. -1.2l.7u. -1.2l.7v. -1.2l.7w. -1.2l.7x. -1.2l.7z. -1.2l.7B. -1.2l.7C. -0.2l.7D. -1.2l.7E. -1.2l.7F. -1.2l.7G. -1.2l.7H. -1.2l.7I. -1.2l.7J. -1.2l.7K. -1.2l.7L. -1.2l.7M. -1.2l.7N. -1.2l.7O. -1.2l.7P. -1.2l.7Q. -1.2l.7R. -1.2l.7S. -1.2l.7T. -1.2l.7U. -1.2l.7V. -1.2l.7W. -1.2l.7X. -1.2l.7Y. -1.2l.7Z. -1.2l.7+. -1.2l.7/. -1.2l.80. -1.2l.81. -1.2l.82. -1.2l.83. -1.2l.84. -1.2l.85. -1.2l.87. -1.2l.88. -1.2l.89. -1.2l.8a. -1.2l.8b. -1.2l.8d. -1.2l.8e. -1.2l.8f. -1.2l.8g. -1.2l.8h. -1.2l.8i. -1.2l.8j. -1.2l.8k. -1.2l.8l. -1.2l.8m. -1.2l.8n. -1.2l.8o. -1.2l.8p. -1.2l.8q. -1.2l.8r. -1.2l.8s. -1.2l.8t. -1.2l.8u. -1.2l.8v. -1.2l.8w. -1.2l.8x. -1.2l.8y. -1.2l.8z. -1.2l.8A. -1.2l.8B. -1.2l.8C. -1.2l.8D. -1.2l.8E. -1.2l.8F. -1.2l.8G. -1.2l.8H. -1.2l.8I. -1.2l.8J. -0.2p.2l. -0.2p.2l. -1.2l.3b. -1.2l.46. -1.2l.4X. -1.2l.5x. -1.2l.7n. -1.2l.7q. -6.2m.2m. -4.2m.2n. -6.2m.2p. -6.2m.2q. -6.2m.2r. -7.2s.2m. -7.2t.2m. -6.2m.2u. -6.2m.2v. -6.2m.2w. -6.2m.2x. -6.2m.2y. -6.2m.2z. -6.2m.2A. -6.2m.2B. -6.2m.2C. -6.2m.2D. -6.2m.2E. -6.2m.2F. -6.2m.2G. -6.2m.2H. -6.2m.2I. -6.2m.2J. -6.2m.2K. -c.2M.2m. -6.2m.2N. -6.2m.2O. -6.2m.2Q. -7.2S.2m. -6.2m.2T. -1.2U.2m. -6.2m.2V. -1.2W.2m. -6.2m.2X. -6.2m.2+. -6.2m.30. -6.2m.35. -6.2m.36. -6.2m.37. -6.2m.38. -6.2m.39. -6.2m.3h. -6.2m.3i. -6.2m.3m. -6.2m.3p. -a.3v.2m. -a.3y.2m. -6.2m.3z. -6.2m.3A. -6.2m.3B. -6.2m.3C. -a.3F.2m. -6.2m.3G. -6.2m.3M. -6.2m.3Q. -6.2m.40. -6.2m.47. -6.2m.4d. -6.2m.4O. -6.2m.4Q. -7.4T.2m. -6.2m.4V. -6.2m.51. -6.2m.52. -a.2m.5a. -6.2m.5b. -6.2m.5d. -6.2m.5B. -6.2m.5C. -6.2m.5F. -6.2m.5H. -6.2m.5I. -f.2m.5K. -7.5M.2m. -6.2m.5O. -6.2m.5R. -6.2m.5U. -6.2m.5W. -1.5Y.2m. -6.2m.5Z. -6.2m.5/. -6.2m.60. -6.2m.62. -6.2m.66. -a.6b.2m. -c.6e.2m. -6.2m.6F. -6.2m.6M. -6.2m.6+. -6.2m.71. -6.2m.79. -6.2m.7D. -6.2m.2p. -6.2m.2p. -j.2n.2n. -4.2n.2p. -4.2n.2q. -4.2n.2r. -7.2s.2n. -7.2t.2n. -3.2u.2n. -4.2n.2v. -4.2n.2w. -4.2x.2n. -4.2n.2y. -4.2n.2z. -4.2n.2A. -4.2n.2B. -4.2n.2C. -4.2n.2D. -4.2n.2E. -4.2n.2F. -4.2n.2G. -4.2H.2n. -4.2n.2I. -4.2n.2J. -4.2n.2K. -4.2n.2L. -c.2M.2n. -4.2n.2N. -d.2O.2n. -4.2n.2P. -4.2n.2Q. -4.2n.2R. -7.2S.2n. -4.2n.2T. -1.2U.2n. -4.2V.2n. -1.2W.2n. -4.2n.2X. -4.2n.2Y. -4.2n.2Z. -4.2n.2+. -4.2n.2/. -4.2n.30. -4.2n.31. -4.2n.32. -4.2n.33. -4.2n.34. -4.2n.35. -4.2n.36. -4.2n.37. -j.2n.38. -4.2n.39. -4.2n.3a. -4.2n.3c. -4.2n.3d. -4.2n.3f. -4.2n.3g. -4.2n.3h. -4.2n.3i. -4.2n.3k. -4.2n.3l. -4.2n.3m. -4.2n.3n. -4.2n.3o. -4.2n.3p. -4.2n.3q. -4.2n.3r. -4.2n.3s. -4.2n.3t. -4.2n.3u. -4.3v.2n. -4.2n.3w. -4.2n.3x. -4.3y.2n. -5.3z.2n. -4.2n.3A. -5.3B.2n. -4.2n.3C. -4.2n.3D. -4.2n.3E. -4.3F.2n. -4.2n.3G. -4.2n.3H. -4.2n.3I. -4.2n.3J. -4.2n.3K. -4.2n.3L. -4.2n.3M. -4.2n.3N. -4.2n.3O. -4.2n.3P. -4.2n.3Q. -4.2n.3R. -4.2n.3S. -4.2n.3U. -4.2n.3V. -4.2n.3W. -4.2n.3X. -4.2n.3Y. -4.2n.3Z. -4.40.2n. -4.2n.41. -4.2n.42. -4.2n.43. -4.2n.44. -4.2n.45. -4.2n.47. -4.2n.48. -4.2n.49. -4.2n.4b. -4.2n.4c. -4.2n.4d. -4.2n.4e. -4.2n.4f. -4.2n.4g. -4.2n.4h. -4.2n.4i. -4.2n.4j. -4.2n.4k. -4.2n.4l. -4.2n.4m. -4.2n.4n. -4.2n.4o. -4.2n.4p. -4.2n.4q. -4.2n.4r. -4.2n.4s. -4.2n.4t. -4.2n.4u. -4.2n.4v. -4.2n.4w. -4.2n.4x. -4.2n.4y. -4.2n.4z. -4.2n.4A. -4.2n.4B. -4.2n.4C. -4.2n.4D. -4.2n.4E. -4.2n.4G. -4.2n.4H. -4.2n.4J. -4.2n.4K. -4.2n.4L. -4.2n.4M. -4.2n.4N. -4.2n.4O. -4.2n.4P. -4.2n.4Q. -4.2n.4S. -4.2n.4T. -4.2n.4U. -4.2n.4V. -4.2n.4W. -4.2n.4Y. -4.2n.4+. -4.2n.4/. -4.2n.51. -4.2n.52. -4.2n.53. -4.2n.54. -4.2n.55. -4.2n.56. -4.2n.58. -4.2n.59. -4.5a.2n. -3.5b.2n. -4.2n.5c. -4.5d.2n. -4.2n.5e. -4.2n.5f. -4.2n.5g. -4.2n.5h. -4.2n.5i. -4.2n.5j. -4.2n.5k. -4.2n.5l. -4.2n.5m. -4.2n.5n. -4.2n.5o. -4.2n.5p. -4.2n.5q. -4.2n.5r. -4.2n.5t. -4.2n.5u. -4.2n.5v. -4.2n.5B. -4.2n.5C. -4.2n.5F. -4.2n.5G. -4.2n.5H. -4.2n.5I. -3.2n.5K. -4.2n.5L. -4.2n.5M. -4.2n.5N. -4.2n.5O. -4.2n.5Q. -4.2n.5R. -4.2n.5S. -4.2n.5T. -4.2n.5U. -4.2n.5V. -4.2n.5W. -4.2n.5X. -4.2n.5Y. -4.2n.5Z. -4.2n.5/. -4.2n.60. -4.2n.61. -5.62.2n. -4.2n.64. -4.2n.65. -5.66.2n. -4.2n.67. -4.2n.68. -4.2n.69. -4.2n.6a. -4.6b.2n. -4.2n.6c. -4.2n.6d. -4.2n.6e. -4.2n.6f. -4.2n.6g. -4.2n.6h. -4.2n.6i. -4.2n.6j. -4.2n.6k. -4.2n.6l. -4.2n.6m. -4.2n.6n. -4.2n.6o. -4.2n.6p. -4.2n.6q. -4.2n.6r. -4.2n.6t. -4.2n.6u. -4.2n.6v. -4.2n.6y. -4.2n.6z. -4.2n.6A. -4.2n.6B. -4.2n.6C. -4.2n.6D. -4.2n.6E. -4.2n.6F. -4.2n.6G. -4.2n.6H. -4.2n.6I. -4.2n.6J. -4.2n.6K. -4.2n.6L. -4.2n.6M. -4.2n.6N. -4.2n.6O. -4.2n.6P. -4.2n.6Q. -4.2n.6R. -4.2n.6S. -4.2n.6T. -4.2n.6U. -4.2n.6V. -4.2n.6W. -4.2n.6Y. -4.2n.6+. -4.2n.71. -4.2n.72. -4.2n.73. -4.2n.75. -4.2n.78. -4.2n.7b. -4.2n.7c. -4.2n.7d. -4.2n.7e. -4.2n.7f. -4.2n.7g. -4.2n.7h. -4.2n.7i. -4.2n.7j. -4.2n.7k. -4.2n.7l. -4.2n.7o. -4.2n.7p. -4.2n.7t. -4.2n.7u. -4.2n.7v. -4.2n.7w. -4.2n.7x. -4.2n.7y. -4.2n.7A. -4.2n.7B. -4.2n.7C. -4.2n.7D. -4.2n.7E. -4.2n.7F. -4.2n.7G. -4.2n.7H. -4.2n.7I. -4.2n.7J. -4.2n.7Q. -4.2n.7R. -4.2n.7S. -4.2n.7T. -4.2n.7V. -4.2n.7W. -4.2n.7X. -4.2n.7Y. -4.2n.7Z. -4.2n.83. -4.2n.89. -4.2n.8a. -4.2n.8b. -4.2n.8c. -4.2n.8d. -4.2n.8e. -4.2n.8f. -4.2n.8g. -4.2n.8h. -4.2n.8i. -4.2n.8j. -4.2n.8k. -4.2n.8l. -4.2n.8m. -4.2n.8n. -4.2n.8o. -4.2n.8p. -4.2n.8q. -4.2n.8r. -4.2n.8s. -4.2n.8t. -4.2n.8y. -4.2n.8z. -4.2n.8A. -4.2n.8B. -4.2n.8D. -4.2n.8E. -4.2n.8H. -4.2n.8I. -4.2n.8J. -4.2n.8K. -4.2n.8L. -4.2n.8M. -4.2n.8N. -4.2n.8O. -4.2n.8P. -4.2n.2p. -4.2n.2p. -4.2n.3b. -1.51.2o. -1.5I.2o. -1.5M.2o. -1.5Y.2o. -1.5+.2o. -1.60.2o. -1.62.2o. -1.6q.2o. -1.6t.2o. -1.6F.2o. -1.6H.2o. -1.6M.2o. -1.7B.2o. -1.7R.2o. -1.86.2o. -1.8d.2o. -0.2p.2p. -2.2q.2p. -2.2r.2p. -7.2s.2p. -7.2t.2p. -2.2u.2p. -0.2v.2p. -d.2p.2w. -e.2x.2p. -9.2y.2p. -0.2p.2z. -0.2A.2p. -9.2B.2p. -0.2p.2C. -0.2D.2p. -0.2E.2p. -0.2F.2p. -0.2p.2G. -9.2H.2p. -0.2I.2p. -0.2J.2p. -0.2p.2K. -c.2M.2p. -9.2N.2p. -2.2O.2p. -9.2Q.2p. -7.2S.2p. -0.2p.2T. -1.2U.2p. -9.2V.2p. -1.2W.2p. -0.2p.2X. -d.2+.2p. -d.30.2p. -d.2p.35. -3.36.2p. -0.2p.37. -0.2p.38. -5.39.2p. -5.3d.2p. -d.2p.3h. -5.3i.2p. -0.2p.3m. -0.2p.3p. -a.3v.2p. -a.3y.2p. -2.3z.2p. -5.3A.2p. -2.3B.2p. -0.2p.3C. -a.3F.2p. -5.3G.2p. -0.2p.3M. -0.2p.3Q. -5.40.2p. -0.2p.47. -0.2p.4d. -0.2p.4O. -0.2p.4Q. -5.4T.2p. -0.2p.4V. -d.2p.51. -0.2p.52. -a.5a.2p. -2.5b.2p. -5.5d.2p. -0.2p.5B. -0.2p.5C. -5.5F.2p. -0.2p.5H. -0.2p.5I. -f.2p.5K. -7.5M.2p. -d.2p.5O. -0.2p.5R. -0.2p.5U. -0.2p.5W. -1.5Y.2p. -0.2p.5Z. -0.2p.5/. -0.2p.60. -2.62.2p. -2.66.2p. -a.6b.2p. -c.6e.2p. -0.2p.6F. -0.6M.2p. -0.2p.6+. -0.2p.71. -5.79.2p. -0.2p.7D. -0.2p.2p. -0.2p.2p. -2.2q.2q. -2.2r.2q. -7.2s.2q. -7.2t.2q. -2.2u.2q. -2.2q.2v. -2.2q.2w. -e.2x.2q. -2.2q.2y. -2.2q.2z. -2.2q.2A. -2.2q.2B. -2.2q.2C. -2.2q.2D. -2.2q.2E. -2.2q.2F. -2.2q.2G. -9.2H.2q. -2.2q.2I. -2.2q.2J. -2.2q.2K. -c.2M.2q. -2.2q.2N. -2.2O.2q. -2.2q.2Q. -7.2S.2q. -2.2q.2T. -1.2U.2q. -9.2V.2q. -1.2W.2q. -2.2q.2X. -2.2q.2+. -2.2q.30. -2.2q.35. -2.2q.36. -2.2q.37. -2.2q.38. -2.2q.39. -2.2q.3d. -2.2q.3h. -2.2q.3i. -2.2q.3m. -2.2q.3p. -a.3v.2q. -a.3y.2q. -2.3z.2q. -2.2q.3A. -2.3B.2q. -2.2q.3C. -a.3F.2q. +0.D.53. +1.1h.4n. +1.26.3n. +2.A.56. +2.k.5l. +2.r.5f. +3.3K.1W. +4.B.55. +4.s.5e. +5./.4H. +5.29.3a. +5.2a.39. +6.3L.1V. +6.3S.1P. +6.4c.1r. +7.2p.2J. +9.1+.3t. +9.24.3p. +9.u.5c. +b.3R.1Q. +f.23.3q. +f.2g.2V. +i.c.5s. +0.1F.40. +0.1k.4j. +0.23.3r. +0.24.3q. +0.2K.2n. +1.22.3s. +1.2f.35. +2.A.57. +2.s.5f. +2.u.5d. +3.3M.1V. +4.C.55. +4.E.53. +4.t.5e. +5./.4I. +5.1h.4o. +5.29.3b. +6.3L.1W. +6.3S.1Q. +6.4c.1s. +6.4q.1g. +7.1+.3u. +7.26.3o. +7.r.5g. +b.3J.1X. +f.1Z.3v. +f.2g.2W. +0.1+.3v. +0.1G.40. +0.1V.3N. +0.24.3r. +1.2f.36. +1.r.5h. +2.1d.4v. +2.2g.2X. +3.1l.4j. +3.3K.1X. +3.3M.1W. +4.D.55. +4.F.53. +4.s.5g. +4.u.5e. +5./.4J. +5.1h.4p. +5.29.3c. +6.4c.1t. +6.58.A. +9.26.3p. +c.28.3m. +c.37.2d. +f.25.3q. +0.1/.3v. +0.1H.40. +0.1K.3Y. +0.1m.4j. +0.1W.3N. +0.26.3q. +0.2c.39. +1.24.3s. +1.k.5n. +1.s.5h. +2.1d.4w. +2.r.5i. +2.u.5f. +3.1u.4c. +4.E.55. +4.v.5e. +5./.4K. +5.1t.4d. +5.1V.3O. +5.29.3d. +6.3L.1X. +6.3r.25. +6.58.B. +9.1+.3w. +0.1h.4q. +0.1I.40. +0.1L.3Y. +0.1n.4j. +0.1Z.3y. +0.20.3v. +0.29.3e. +0.d.5s. +2.1V.3P. +2.A.5a. +2.r.5j. +2.s.5i. +3.3M.1X. +4.F.55. +4.u.5g. +4.w.5e. +5./.4L. +5.k.5o. +6.3r.26. +6.4c.1v. +6.58.C. +7.2p.2K. +9.1+.3x. +9.22.3t. +b.3R.1R. +f.27.3q. +0.1+.3y. +0.1i.4q. +0.1J.40. +0.1M.3Y. +0.1o.4j. +0.1X.3N. +0.e.5s. +0.G.53. +1.26.3s. +1.u.5h. +2.1d.4x. +2.A.5b. +2.c.5t. +2.k.5p. +2.s.5j. +4.x.5e. +5./.4M. +5.1h.4r. +5.1t.4e. +5.2a.3e. +6.3r.27. +6.3S.1R. +6.4c.1w. +6.58.D. +7.22.3u. +b.3R.1S. +j.21.3v. +0.1/.3y. +0.1j.4q. +0.1p.4j. +0.22.3v. +0.f.5s. +2.1+.3z. +2.1d.4y. +2.u.5i. +4.1N.3Y. +4.H.53. +4.r.5k. +4.y.5e. +5./.4N. +5.1h.4s. +5.1t.4f. +5.1V.3Q. +5.k.5q. +6.3S.1S. +6.4c.1x. +6.58.E. +9.24.3t. +c.28.3q. +c.3f.29. +f.2g.2Y. +f.39.2d. +0.1O.3Y. +0.1q.4j. +0.20.3y. +0.23.3v. +0.2L.2n. +0.g.5s. +1.2f.38. +2.1d.4z. +2.u.5j. +3.I.53. +4.G.55. +4.s.5k. +4.z.5e. +5./.4O. +5.1h.4t. +5.1t.4g. +6.4c.1y. +6.58.F. +9.1+.3A. +9.22.3w. +c.28.3r. +c.3f.2a. +f.24.3u. +0.1K.40. +0.1P.3Y. +0.1r.4j. +0.24.3v. +0.2c.3e. +2./.4P. +3.J.53. +4.H.55. +4.h.5s. +5.1t.4h. +6.4c.1z. +9.22.3x. +9.26.3t. +b.3R.1V. +j.21.3y. +0.1k.4q. +0.1L.40. +0.1Q.3Y. +0.1s.4j. +0.22.3y. +0.25.3v. +0.i.5s. +0.K.53. +1.1t.4i. +2.1d.4B. +2.2g.2Z. +2.A.5d. +2.r.5l. +3.I.55. +4.u.5k. +5./.4Q. +5.1h.4u. +6.3S.1V. +6.4c.1A. +7.1+.3B. +7.26.3u. +9.24.3w. +b.3G.1Y. +b.3R.1W. +i.c.5u. +0.1M.40. +0.1t.4j. +0.23.3y. +0.29.3g. +0.j.5s. +0.L.53. +1.k.5r. +2.1d.4C. +2.22.3z. +2.s.5l. +3.1l.4q. +3.J.55. +4.26.3v. +4.A.5e. +5./.4R. +5.1+.3C. +5.1h.4v. +5.1V.3T. +6.3S.1W. +6.4c.1B. +6.58.G. +7.2p.2L. +9.24.3x. +c.3f.2c. +0.1m.4q. +0.24.3y. +0.27.3v. +0.2M.2n. +0.k.5s. +1.2f.3a. +2.1+.3D. +2.1d.4D. +3.1u.4j. +3.M.53. +4.1N.40. +4.2d.3e. +4.B.5e. +4.K.55. +5./.4S. +5.1h.4w. +5.1t.4k. +5.1V.3U. +5.2a.3g. +6.4c.1C. +6.58.H. +9.22.3A. +9.26.3w. +b.3R.1X. +0.1n.4q. +0.1O.40. +0.1v.4j. +0.25.3y. +0.L.55. +0.l.5s. +1.2f.3b. +1.r.5n. +2.1t.4l. +2.24.3z. +2.A.5g. +2.u.5l. +4.C.5e. +5.1V.3V. +6.3S.1X. +6.4c.1D. +6.58.I. +9.1+.3E. +9.26.3x. +0.1o.4q. +0.1P.40. +0.1w.4j. +0.26.3y. +0.29.3h. +0.d.5u. +0.m.5s. +1.2f.3c. +1.s.5n. +2./.4T. +2.A.5h. +3.M.55. +4.D.5e. +5.1h.4x. +5.1t.4m. +5.1V.3W. +6.58.J. +7.22.3B. +7.r.5o. +9.1+.3F. +9.24.3A. +c.28.3v. +c.3f.2d. +f.1R.3Y. +0.1p.4q. +0.1Q.40. +0.1S.3Y. +0.1x.4j. +0.27.3y. +0.e.5u. +0.N.53. +1.1t.4n. +1.2f.3d. +2./.4U. +2.26.3z. +2.r.5p. +3.n.5s. +4.E.5e. +5.1h.4y. +5.1V.3X. +5.22.3C. +5.2a.3h. +6.3i.29. +6.4c.1E. +6.58.K. +7.2p.2M. +7.s.5o. +b.3G.1Z. +f.2c.3g. +0.1q.4q. +0.1y.4j. +0.2N.2n. +0.f.5u. +0.o.5s. +1.u.5n. +2.22.3D. +2.s.5p. +4.F.5e. +4.O.53. +5./.4V. +5.1h.4z. +5.1t.4o. +5.29.3j. +6.3i.2a. +6.4c.1F. +6.58.L. +7.24.3B. +7.r.5q. +8.2g.2+. +9.26.3A. +b.3G.1+. +b.3J.1Y. +0.1r.4q. +0.1z.4j. +0.2O.2n. +0.g.5u. +0.P.53. +0.p.5s. +1./.4W. +2.k.5t. +3.3K.1Y. +4.N.55. +4.u.5o. +5.1h.4A. +5.1t.4p. +5.24.3C. +5.29.3k. +6.4c.1G. +6.58.M. +7.s.5q. +9.22.3E. +b.3G.1/. +c.28.3y. +0.1A.4j. +0.1s.4q. +0.1V.3Y. +0.2d.3g. +0.h.5u. +0.Q.53. +0.q.5s. +2.1d.4H. +2.24.3D. +2.A.5k. +2.u.5p. +4.O.55. +5.1h.4B. +5.29.3l. +6.3L.1Y. +6.4c.1H. +7.26.3B. +9.22.3F. +a.2P.2n. +b.3G.20. +f.2c.3h. +0.1B.4j. +0.1t.4q. +0.1W.3Y. +0.29.3m. +0.i.5u. +0.R.53. +2.2/.2g. +2.c.5v. +3.3M.1Y. +4.G.5e. +4.P.55. +4.u.5q. +5.1h.4C. +5.26.3C. +6.3i.2c. +6.4c.1I. +7.2p.2N. +9.24.3E. +b.3G.21. +f.1R.40. +0.1C.4j. +0.1S.40. +0.2Q.2n. +0.j.5u. +1.29.3n. +1.r.5r. +2.1d.4J. +2.26.3D. +2.c.5w. +3.1u.4q. +3.S.53. +4.H.5e. +4.Q.55. +5.1h.4D. +5.1t.4r. +5.1V.3Z. +6.3N.1Y. +6.4c.1J. +6.58.N. +7.1+.3H. +7.2p.2O. +9./.4X. +9.24.3F. +b.3G.22. +f.2a.3m. +0.1D.4j. +0.1v.4q. +0.1X.3Y. +0.2d.3h. +0.k.5u. +0.r.5s. +1.s.5r. +2.1d.4K. +2.1h.4E. +3.I.5e. +4.R.55. +5.1t.4s. +5.1V.3+. +5.29.3o. +6.58.O. +7.1+.3I. +7.2p.2P. +9./.4Y. +9.26.3E. +9.c.5x. +b.3G.23. +b.3J.1Z. +c.T.53. +f.2R.2n. +0.1w.4q. +0.l.5u. +0.s.5s. +2.1d.4L. +3.3K.1Z. +3.J.5e. +3.S.55. +4.U.53. +5.1h.4F. +5.1t.4t. +5.1V.3/. +5.29.3p. +6.3i.2d. +6.58.P. +9.26.3F. +b.3G.24. +b.3J.1+. +0.1E.4j. +0.1V.40. +0.1x.4q. +0.29.3q. +0.2S.2n. +0.m.5u. +1.u.5r. +2.1d.4M. +3.3K.1+. +4.K.5e. +4.t.5s. +4.V.53. +5.1h.4G. +6.3L.1Z. +6.4c.1K. +6.58.Q. +7.2p.2Q. +9.c.5y. +b.3G.25. +b.3J.1/. +c.T.55. +f.2c.3m. +l.2g.30. +0.1F.4j. +0.1W.40. +0.1y.4q. +0.29.3r. +0.L.5e. +0.W.53. +1./.4Z. +2.1d.4N. +2.A.5n. +3.3K.1/. +3.3M.1Z. +3.n.5u. +4.U.55. +4.u.5s. +5.1t.4u. +5.1V.41. +5.2a.3q. +6.3L.1+. +6.4c.1L. +6.58.R. +7.22.3H. +7.2p.2R. +9.c.5z. +b.3G.26. +b.3J.20. +0.1G.4j. +0.1z.4q. +0.o.5u. +0.X.53. +1.29.3s. +2.1d.4O. +3.3K.20. +3.3M.1+. +3.M.5e. +4.V.55. +4.v.5s. +5.1t.4v. +5.1V.42. +5.2q.2w. +5.3r.2a. +6.3L.1/. +6.4c.1M. +6.58.S. +7.22.3I. +b.3G.27. +b.3J.21. +f.1Z.3N. +0.1+.3N. +0.1A.4q. +0.1H.4j. +0.1X.40. +0.p.5u. +1.1V.43. +2.1d.4P. +2.r.5t. +3.3K.21. +3.3M.1/. +4.W.55. +4.w.5s. +5.1h.4H. +5.1t.4w. +6.3L.20. +6.3m.2d. +6.4c.1N. +6.58.T. +7.24.3H. +7.2p.2S. +9.c.5A. +b.3J.22. +b.3R.1Y. +c.Y.53. +0.1/.3N. +0.1B.4q. +0.1I.4j. +0.q.5u. +0.Z.53. +1.2f.3j. +1.c.5B. +2.1d.4Q. +2.s.5t. +3.3K.22. +3.3M.20. +4.X.55. +4.x.5s. +5.1h.4I. +5.1V.44. +6.3L.21. +6.3S.1Y. +6.4c.1O. +6.58.U. +7.1+.3O. +7.24.3I. +9./.4+. +b.3G.28. +b.3J.23. +f.2c.3q. +0.1C.4q. +0.1J.4j. +0.20.3N. +1.2f.3k. +1.2n.2T. +2.1+.3P. +2.1d.4R. +3.3K.23. +3.3M.21. +4.1t.4x. +4.N.5e. +4.y.5s. +5.1h.4J. +5.1V.45. +5.29.3t. +6.3L.22. +6.3r.2c. +6.4c.1P. +6.58.V. +7.26.3H. +9./.4/. +b.3J.24. +c.Y.55. +0.1D.4q. +1.2f.3l. +2.1d.4S. +2.u.5t. +3.3K.24. +3.3M.22. +4.O.5e. +4.Z.55. +5.1h.4K. +5.1t.4y. +5.1V.46. +5.29.3u. +5.2q.2x. +6.3L.23. +6.4c.1Q. +6.58.W. +6.5s.z. +7.26.3I. +9./.50. +b.3J.25. +j.21.3N. +j.2a.3t. +0.22.3N. +0.29.3v. +0.2d.3q. +0.r.5u. +2.k.5v. +3.3K.25. +3.3M.23. +4.P.5e. +5./.51. +5.1h.4L. +5.1t.4z. +5.1V.47. +6.3L.24. +6.58.X. +7.1+.3Q. +9.c.5C. +b.3J.26. +0.+.53. +0.1E.4q. +0.1K.4j. +0.23.3N. +0.s.5u. +1.2f.3n. +2.1d.4T. +2.A.5r. +2.k.5w. +3.3K.26. +3.3M.24. +4.Q.5e. +5./.52. +5.1h.4M. +5.1t.4A. +5.1V.48. +5.29.3w. +5.2a.3v. +6.3L.25. +6.3r.2d. +6.58.Y. +7.22.3O. +9.c.5D. +b.3J.27. +b.3R.1Z. +0.1F.4q. +0.1L.4j. +0.24.3N. +0.A.5s. +0.t.5u. +1.2f.3o. +1.31.2g. +2.1d.4U. +2.22.3P. +3.3K.27. +3.3M.25. +4./.53. +4.R.5e. +5.1h.4N. +5.1t.4B. +5.1V.49. +5.29.3x. +6.3L.26. +6.3S.1Z. +6.58.Z. +b.3R.1+. +d.2U.2n. +e.k.5x. +j.2a.3w. +0.+.55. +0.1G.4q. +0.1M.4j. +0.25.3N. +0.29.3y. +0.B.5s. +0.u.5u. +1.1V.4a. +1.2f.3p. +2.c.5E. +3.3M.26. +3.S.5e. +4.10.53. +5./.54. +5.1h.4O. +5.1t.4C. +5.2a.3x. +5.2q.2y. +6.3L.27. +6.3S.1+. +6.4c.1R. +7.24.3O. +b.3J.28. +b.3R.1/. +0.1H.4q. +0.2a.3y. +0.C.5s. +0.v.5u. +1.1V.4b. +2.1d.4W. +2.1h.4P. +2.24.3P. +2.29.3z. +3.3K.28. +3.3M.27. +4./.55. +4.11.53. +4.1N.4j. +4.26.3N. +4.k.5y. +5.1t.4D. +6.3S.1/. +6.4c.1S. +7.1+.3T. +7.22.3Q. +8.2z.2q. +b.3R.20. +b.5F.c. +c.T.5e. +f.2c.3v. +0.1I.4q. +0.1O.4j. +0.1Y.3Y. +0.27.3N. +0.D.5s. +0.w.5u. +1./.56. +2.1t.4E. +3.5G.c. +4.10.55. +4.12.53. +4.26.3O. +4.U.5e. +5.1h.4Q. +5.29.3A. +6.3L.28. +6.3S.20. +7.1+.3U. +8.2A.2q. +9.k.5z. +b.3R.21. +d.2U.2p. +j.2a.3z. +0.1J.4q. +0.1P.4j. +0.E.5s. +0.x.5u. +1.2f.3s. +2.26.3P. +3.3M.28. +4.11.55. +4.2B.2q. +4.V.5e. +5./.57. +5.1h.4R. +5.1t.4F. +6.3S.21. +6.58.+. +7.1+.3V. +7.24.3Q. +b.3R.22. +f.2g.32. +j.2a.3A. +0.1Q.4j. +0.2c.3y. +0.2d.3v. +0.F.5s. +0.y.5u. +2.1d.4X. +2.A.5t. +4.12.55. +4.W.5e. +5.1h.4S. +5.1t.4G. +5.29.3B. +5.2q.2C. +6.3S.22. +6.4c.1V. +7.1+.3W. +8.58./. +9.k.5A. +a.2V.2n. +b.3R.23. +c.28.3N. +0.13.53. +0.5u.z. +1.k.5B. +2./.59. +2.1d.4Y. +2.c.5H. +3.2W.2n. +4.X.5e. +5.1V.4d. +5.29.3C. +6.3S.23. +6.4c.1W. +6.58.10. +7.1+.3X. +7.22.3T. +7.26.3Q. +b.3R.24. +b.5F.d. +0.1K.4q. +1.2f.3t. +1.2n.2X. +1.33.2g. +2./.5a. +2.1h.4T. +2.29.3D. +2.r.5v. +3.5G.d. +4.14.53. +5.2a.3C. +5.c.5I. +6.3S.24. +6.58.11. +7.22.3U. +b.3R.25. +b.5F.e. +c.Y.5e. +0.1L.4q. +0.1Y.40. +0.G.5s. +1.2f.3u. +2./.5b. +2.1h.4U. +2.r.5w. +2.s.5v. +3.5G.e. +4.13.55. +4.15.53. +4.Z.5e. +5.1t.4H. +5.1V.4e. +5.29.3E. +5.2g.34. +5.2q.2D. +6.3S.25. +6.3y.2d. +6.4c.1X. +6.58.12. +7.22.3V. +7.24.3T. +7.2p.2V. +b.3R.26. +b.5F.f. +f.1Z.3Y. +0.1+.3Y. +0.16.53. +0.1M.4q. +0.A.5u. +0.H.5s. +2.1d.4Z. +2.2g.35. +2.s.5w. +3.5G.f. +4.14.55. +4.k.5C. +5.1h.4V. +5.1t.4I. +5.1V.4f. +5.29.3F. +5.2E.2q. +6.3S.26. +7.22.3W. +7.24.3U. +7.2p.2W. +9.r.5x. +b.3R.27. +b.5F.g. +f.1R.4j. +0.1/.3Y. +0.1S.4j. +0.B.5u. +1.1h.4W. +1.2f.3w. +2.2g.36. +2.u.5v. +3.5G.g. +3.I.5s. +4.15.55. +4.17.53. +4.1N.4q. +5.1t.4J. +5.1V.4g. +6.3S.27. +7.22.3X. +7.24.3V. +7.26.3T. +9.k.5D. +9.s.5x. +b.5F.h. +j.2a.3F. +0.1O.4q. +0.20.3Y. +0.C.5u. +1.2f.3x. +2.u.5w. +3.5G.h. +3.J.5s. +4.16.55. +4.18.53. +4.r.5y. +5.1t.4K. +5.1V.4h. +6.58.13. +7.1+.3Z. +7.24.3W. +7.26.3U. +9./.5c. +a.2Y.2n. +b.3G.29. +b.3R.28. +b.5F.i. +0.+.5e. +0.19.53. +0.1P.4q. +0.D.5u. +0.K.5s. +1.1V.4i. +2./.5d. +2.1d.4+. +2.k.5E. +3.5G.i. +4.17.55. +4.s.5y. +5.1t.4L. +6.3S.28. +6.58.14. +7.24.3X. +7.26.3V. +9.r.5z. +9.u.5x. +b.3G.2a. +b.5F.j. +f.1+.3+. +j.21.3Y. +0.1Q.4q. +0.1V.4j. +0.1Z.40. +0.E.5u. +0.L.5s. +2.1d.4/. +2.2f.3z. +3.5G.j. +4./.5e. +4.18.55. +4.1a.53. +5.1h.4X. +5.1t.4M. +6.58.15. +7.1+.3/. +7.26.3W. +9.s.5z. +b.5F.k. +f.22.3Y. +0.1+.40. +0.19.55. +0.1W.4j. +0.23.3Y. +0.F.5u. +1.2f.3A. +1.2n.2Z. +2./.5f. +2.1d.50. +3.5G.k. +3.M.5s. +4.10.5e. +4.1b.53. +4.u.5y. +5.1h.4Y. +5.1t.4N. +5.1V.4k. +5.2g.37. +6.58.16. +7.26.3X. +7.2p.2Y. +9.r.5A. +b.5F.l. +0.1/.40. +0.24.3Y. +1.r.5B. +2.1d.51. +2.1V.4l. +2.2g.38. +3.5G.l. +4.11.5e. +4.1a.55. +4.1c.53. +4.u.5z. +5./.5g. +5.1t.4O. +5.29.3H. +6.58.17. +7.1+.41. +7.22.3Z. +9.s.5A. +b.3G.2c. +b.5F.m. +0.1X.4j. +0.20.40. +0.25.3Y. +1./.5h. +1.2f.3B. +1.s.5B. +2.1d.52. +2.1t.4P. +3.5G.m. +4.12.5e. +4.1b.55. +5.1V.4m. +5.29.3I. +6.58.18. +7.1+.42. +7.22.3+. +b.5F.n. +0.1d.53. +0.1R.4q. +0.G.5u. +0.N.5s. +1.1+.43. +1.1h.4Z. +1.1V.4n. +1.2f.3C. +2./.5i. +2.k.5H. +3.5G.n. +4.1c.55. +4.26.3Y. +4.u.5A. +5.1t.4Q. +5.2q.2F. +6.58.19. +7.22.3/. +7.24.3Z. +b.3J.29. +b.5F.o. +j.21.40. +0.1S.4q. +0.22.40. +0.27.3Y. +0.H.5u. +0.O.5s. +1.u.5B. +2./.5j. +2.1d.54. +2.2f.3D. +3.3K.29. +3.5G.o. +4.r.5C. +5.1t.4R. +5.1V.4o. +5.k.5I. +6.58.1a. +7.1+.44. +7.24.3+. +b.3G.2d. +b.3J.2a. +b.5F.p. +f.2g.39. +0.23.40. +0.P.5s. +1.2f.3E. +2.2g.3a. +2.A.5w. +3.3K.2a. +3.5G.p. +3.I.5u. +4.13.5e. +4.1d.55. +4.s.5C. +5.1t.4S. +5.1V.4p. +5.2q.2G. +6.3L.29. +6.58.1b. +7.1+.45. +7.22.41. +7.24.3/. +7.26.3Z. +9.r.5D. +b.5F.q. +0.1e.53. +0.24.40. +0.Q.5s. +1.2f.3F. +2.1d.56. +2.2g.3b. +2.A.5x. +3.3M.29. +3.5G.q. +3.J.5u. +4./.5k. +4.14.5e. +5.1h.4+. +6.3L.2a. +6.58.1c. +7.1+.46. +7.22.42. +7.26.3+. +8.2+.2n. +9.s.5D. +c.28.3Y. +0.1V.4q. +0.29.3N. +0.K.5u. +0.R.5s. +1.22.43. +2.1d.57. +2.1t.4T. +2.2g.3c. +2.2H.2q. +2.r.5E. +3.3M.2a. +4.15.5e. +4.1f.53. +4.u.5C. +5.1h.4/. +7.1+.47. +7.24.41. +7.26.3/. +b.3J.2c. +f.25.40. +0.1e.55. +0.1W.4q. +0.L.5u. +2.1t.4U. +2.2g.3d. +2.A.5y. +2.c.5J. +2.s.5E. +3.S.5s. +4.16.5e. +4.26.40. +5.1h.50. +5.1V.4r. +5.29.3O. +5.2a.3N. +6.3K.2c. +6.4c.1Y. +6.58.1d. +7.1+.48. +7.22.44. +7.24.42. +9.u.5D. +b.5F.r. +h.1g.53. +0.27.40. +1.24.43. +2./.5l. +2.2/.2n. +2.29.3P. +2.A.5z. +3.5G.r. +3.M.5u. +4.17.5e. +4.1f.55. +5.1h.51. +5.1t.4V. +5.1V.4s. +6.3L.2c. +7.1+.49. +7.22.45. +7.26.41. +8.2+.2p. +b.5F.s. +c.T.5s. +f.2g.3e. +0.1X.4q. +0.U.5s. +1.1+.4a. +1.1t.4W. +2.1d.5a. +2.c.5K. +2.u.5E. +3.3M.2c. +3.5G.s. +4.18.5e. +4.55.1g. +5.1h.52. +5.1V.4t. +7.22.46. +7.24.44. +7.26.42. +b.3J.2d. +b.5F.t. +0.19.5e. +0.V.5s. +1.1+.4b. +1.26.43. +2.1d.5b. +3.3K.2d. +3.5G.t. +4.1h.53. +5.29.3Q. +5.2q.2I. +6.58.1e. +7.22.47. +7.24.45. +b.5F.u. +c.28.40. +f.2c.3N. +f.2g.3f. +0.N.5u. +0.W.5s. +1./.5n. +1.2f.3H. +2.2/.2p. +2.A.5B. +2.r.5H. +3.5G.u. +4.1a.5e. +4.1i.53. +5.1h.54. +5.1V.4u. +6.3L.2d. +6.58.1f. +7.22.48. +7.24.46. +7.26.44. +b.5F.v. +0.1j.53. +0.30.2n. +0.O.5u. +0.X.5s. +1.2f.3I. +2.s.5H. +3.3M.2d. +3.5G.v. +4.1b.5e. +4.1h.55. +5./.5o. +5.1V.4v. +6.4c.1Z. +6.58.1g. +7.22.49. +7.24.47. +7.26.45. +7.r.5I. +9.1t.4X. +b.3R.29. +b.5F.w. +0.P.5u. +1.1h.56. +1.22.4a. +2./.5p. +3.5G.w. +4.1c.5e. +4.1i.55. +4.1t.4Y. +5.1V.4w. +6.3N.2d. +6.3S.29. +6.4c.1+. +7.24.48. +7.26.46. +7.s.5I. +b.3R.2a. +b.5F.x. +c.Y.5s. +0.1j.55. +0.Q.5u. +1.22.4b. +2.1d.5d. +2.u.5H. +3.5G.x. +4.Z.5s. +5./.5q. +5.1h.57. +5.29.3T. +6.3S.2a. +6.4c.1/. +7.1+.4d. +7.24.49. +7.26.47. +b.5F.y. +f.2g.3g. +0.1k.53. +0.1Y.4j. +0.R.5u. +1.24.4a. +2.A.5D. +3.5G.y. +4.1d.5e. +5.29.3U. +6.4c.20. +7.26.48. +7.2p.30. +7.u.5I. +8.58.1h. +9.1V.4x. +b.5F.z. +1.1t.4Z. +1.24.4b. +2.1h.59. +3.1l.53. +3.5G.z. +3.S.5u. +5.1V.4y. +5.29.3V. +5.2q.2J. +6.4c.21. +6.58.1i. +7.1+.4e. +7.26.49. +b.3R.2c. +1.26.4a. +2.1d.5g. +2.1h.5a. +2.A.5E. +4.1k.55. +4.1m.53. +5.1V.4z. +5.29.3W. +6.3S.2c. +6.4c.22. +6.58.1j. +7.1+.4f. +8.2g.3h. +c.T.5u. +j.2a.3V. +0.+.5s. +0.1n.53. +0.U.5u. +1./.5r. +1.26.4b. +1.2f.3O. +2.1d.5h. +2.1h.5b. +2.k.5J. +3.1l.55. +4.1e.5e. +5.1V.4A. +5.29.3X. +5.2g.3i. +6.4c.23. +7.1+.4g. +7.22.4d. +b.5F.A. +0./.5s. +0.1o.53. +0.V.5u. +2.2f.3P. +3.5G.A. +4.1f.5e. +4.1m.55. +5.1V.4B. +5.2g.3j. +6.4c.24. +7.1+.4h. +9.1t.4+. +b.3R.2d. +b.5F.B. +0.10.5s. +0.1p.53. +0.1Z.4j. +0.W.5u. +1.1+.4i. +2.2g.3k. +2.k.5K. +3.5G.B. +4.1n.55. +5.1V.4C. +5.2q.2K. +6.3S.2d. +6.4c.25. +6.58.1k. +7.22.4e. +9.1t.4/. +b.5F.C. +f.24.4d. +h.1g.5e. +0.1+.4j. +0.11.5s. +0.1o.55. +0.29.3Y. +0.X.5u. +1.2f.3Q. +1.31.2n. +3.5G.C. +4.1q.53. +4.1t.50. +5.1h.5c. +5.1V.4D. +6.4c.26. +6.58.1l. +7.22.4f. +b.5F.D. +0.1/.4j. +2.1d.5k. +2.1h.5d. +2.1V.4E. +3.5G.D. +4.12.5s. +4.1p.55. +4.1r.53. +5.1t.51. +5.2a.3Y. +6.4c.27. +6.58.1m. +7.1+.4k. +7.22.4g. +7.24.4e. +7.26.4d. +b.5F.E. +c.Y.5u. +f.2g.3m. +0.20.4j. +0.Z.5u. +2.1+.4l. +2.A.5I. +3.5G.E. +4.1h.5e. +4.1q.55. +4.1s.53. +5.1t.52. +5.1V.4F. +5.29.3Z. +6.4q.1Y. +6.58.1n. +7.22.4h. +b.5F.F. +f.24.4f. +1.22.4i. +1.31.2p. +2./.5t. +2.1h.5f. +2.2g.3o. +3.5G.F. +4.1i.5e. +4.1r.55. +4.1t.53. +5.1V.4G. +5.29.3+. +6.4c.28. +6.58.1o. +7.1+.4m. +7.24.4g. +7.26.4e. +e.21.4j. +0.13.5s. +0.1j.5e. +0.22.4j. +1.1+.4n. +1.2f.3T. +2.2g.3p. +3.1u.53. +4.1s.55. +4.26.4f. +5.1h.5g. +5.29.3/. +6.58.1p. +7.24.4h. +a.32.2n. +e.1t.54. +f.2c.3Y. +0.14.5s. +0.1v.53. +0.23.4j. +0.29.40. +1.1h.5h. +1.24.4i. +1.2f.3U. +4.1t.55. +5.2g.3q. +5.2q.2L. +6.58.1q. +7.1+.4o. +7.22.4k. +7.26.4g. +b.5F.G. +0.+.5u. +0.15.5s. +0.24.4j. +1.1t.56. +1.2f.3V. +2.1h.5i. +2.22.4l. +2.r.5J. +3.1u.55. +3.5G.G. +4.1w.53. +5.1V.4H. +5.29.41. +5.2a.40. +6.58.1r. +7.1+.4p. +7.26.4h. +b.5F.H. +f.2g.3r. +0./.5u. +0.16.5s. +0.1x.53. +0.1Z.4q. +0.25.4j. +0.2d.3Y. +1.26.4i. +1.2f.3W. +1.33.2n. +2.1d.5n. +2.1h.5j. +2.s.5J. +3.5G.H. +4.1k.5e. +4.1v.55. +5.1t.57. +5.1V.4I. +5.29.42. +6.58.1s. +7.22.4m. +7.24.4k. +7.2p.32. +b.5F.I. +0.1+.4q. +0.10.5u. +0.17.5s. +0.1y.53. +0.34.2n. +1.22.4n. +1.29.43. +1.2f.3X. +2.24.4l. +2.r.5K. +3.1l.5e. +3.5G.I. +4.1w.55. +4.26.4j. +5.1V.4J. +6.5L.c. +8.58.1t. +b.5F.J. +0.1/.4q. +0.11.5u. +0.18.5s. +0.27.4j. +1.2n.35. +2.1t.59. +2.s.5K. +2.u.5J. +3.5G.J. +4.1m.5e. +4.1x.55. +4.1z.53. +5.1h.5k. +5.1V.4K. +5.29.44. +5.2q.2M. +6.58.1u. +7.1+.4r. +7.22.4o. +7.24.4m. +7.26.4k. +b.5F.K. +f.2c.40. +0.19.5s. +0.20.4q. +1.24.4n. +1.2n.36. +1.33.2p. +2.1t.5a. +2.26.4l. +2.2g.3t. +2.c.5M. +3.5G.K. +4.12.5u. +4.1A.53. +4.1n.5e. +4.1y.55. +5.1V.4L. +5.29.45. +6.58.1v. +7.1+.4s. +7.22.4p. +b.5F.L. +0.1a.5s. +0.1B.53. +0.1o.5e. +2.1t.5b. +2.c.5N. +2.u.5K. +3.5G.L. +4.1z.55. +5.1V.4M. +5.29.46. +6.58.1w. +7.1+.4t. +7.24.4o. +7.26.4m. +7.2p.34. +b.5F.M. +c.28.4j. +f.2g.3u. +j.21.4q. +0.1b.5s. +0.1C.53. +0.22.4q. +0.2d.40. +1.26.4n. +2.1h.5l. +3.5G.M. +4.1A.55. +4.1p.5e. +5.1V.4N. +5.29.47. +6.58.1x. +6.5L.d. +7.24.4p. +f.2g.3v. +0.13.5u. +0.1B.55. +0.1c.5s. +0.23.4q. +1.2f.3Z. +2.2g.3w. +4.1D.53. +4.1q.5e. +5.1V.4O. +5.29.48. +5.2q.2N. +6.58.1y. +6.5L.e. +7.1+.4u. +7.22.4r. +7.26.4o. +9.c.5O. +0.14.5u. +0.1C.55. +0.24.4q. +1.2f.3+. +2.1d.5r. +2.1V.4P. +2.2g.3x. +4.1r.5e. +5.29.49. +5.2q.2O. +6.58.1z. +6.5L.f. +7.1+.4v. +7.22.4s. +7.26.4p. +9.1t.5c. +9.c.5P. +b.5F.N. +f.37.2n. +0.15.5u. +0.25.4q. +1.1h.5n. +1.29.4a. +1.2f.3/. +1.2n.38. +2.1t.5d. +3.5G.N. +4.1D.55. +4.1d.5s. +4.1E.53. +4.1s.5e. +5.1V.4Q. +5.2P.2q. +6.58.1A. +6.5L.g. +7.1+.4w. +7.22.4t. +b.5F.O. +f.24.4r. +f.2g.3y. +0.16.5u. +0.1F.53. +0.26.4q. +1.29.4b. +2./.5v. +3.5G.O. +4.1t.5e. +5.1h.5o. +5.1V.4R. +5.c.5Q. +6.58.1B. +6.5L.h. +7.24.4s. +b.5F.P. +0.17.5u. +0.1G.53. +0.27.4q. +1.2f.41. +2./.5w. +2.1h.5p. +2.1t.5f. +2.2g.3A. +2.A.5J. +3.1u.5e. +3.5G.P. +4.1E.55. +5.1V.4S. +5.2q.2Q. +6.58.1C. +6.5L.i. +7.22.4u. +7.24.4t. +7.26.4r. +7.2p.37. +9.1+.4x. +b.5F.Q. +0.18.5u. +0.1e.5s. +1.2f.42. +3.5G.Q. +4.1F.55. +4.1H.53. +4.1v.5e. +5.1h.5q. +5.1t.5g. +5.2q.2R. +6.58.1D. +6.5L.j. +7.1+.4y. +7.22.4v. +7.26.4s. +9./.5x. +b.5F.R. +f.39.2n. +0.19.5u. +0.1f.5s. +1.1t.5h. +1.2f.43. +1.2n.3a. +2.1V.4T. +2.A.5K. +3.5G.R. +4.1G.55. +4.1I.53. +4.1w.5e. +6.4c.29. +6.5L.k. +7.1+.4z. +7.22.4w. +7.24.4u. +7.26.4t. +9.c.5R. +b.5F.S. +c.28.4q. +0.1a.5u. +1.2f.44. +2.1d.5t. +2.1t.5i. +2.1V.4U. +3.5G.S. +4./.5y. +4.1H.55. +4.1J.53. +4.1x.5e. +5.29.4d. +5.2q.2S. +6.4c.2a. +6.58.1E. +6.5L.l. +6.5s.1g. +7.1+.4A. +7.24.4v. +b.5F.T. +0.1b.5u. +1.2f.45. +1.2n.3c. +2.1t.5j. +2.k.5M. +3.5G.T. +4.1I.55. +4.1y.5e. +5.1V.4V. +6.58.1F. +6.5L.m. +7.1+.4B. +7.24.4w. +7.26.4u. +7.2p.39. +9./.5z. +9.22.4x. +b.5F.U. +0.1c.5u. +1.1h.5r. +1.1V.4W. +1.2f.46. +1.2n.3d. +2.2g.3E. +2.k.5N. +3.5G.U. +3.n.5L. +4.1J.55. +4.1z.5e. +5.29.4e. +6.58.1G. +7.1+.4C. +7.22.4y. +7.26.4v. +b.5F.V. +0.1h.5s. +0.2n.3e. +1.2f.47. +2.2g.3F. +3.5G.V. +4.1A.5e. +4.1K.53. +4.1t.5k. +5.29.4f. +6.4c.2c. +6.58.1H. +6.5L.o. +7.1+.4D. +7.22.4z. +7.26.4w. +9./.5A. +9.24.4x. +b.5F.W. +0.1d.5u. +0.1L.53. +1./.5B. +1.2f.48. +2.1+.4E. +2.c.5S. +3.5G.W. +4.1B.5e. +4.1i.5s. +4.k.5O. +5.29.4g. +6.58.1I. +6.5L.p. +7.22.4A. +7.24.4y. +b.5F.X. +0.1j.5s. +0.1M.53. +1.2f.49. +3.5G.X. +4.1C.5e. +4.1K.55. +5.29.4h. +5.2g.3G. +5.2q.2T. +6.58.1J. +6.5L.q. +7.1+.4F. +7.22.4B. +7.24.4z. +9.26.4x. +9.k.5P. +b.5F.Y. +c.3f.2n. +f.1V.4X. +1.29.4i. +1.2f.4a. +2.1t.5l. +3.5G.Y. +4.1D.5e. +4.1L.55. +4.1N.53. +6.4c.2d. +7.1+.4G. +7.22.4C. +7.24.4A. +7.26.4y. +7.2p.3e. +b.5F.Z. +f.1V.4Y. +0.1e.5u. +0.1O.53. +0.29.4j. +1.2f.4b. +3.5G.Z. +4./.5C. +4.1M.55. +5.k.5Q. +7.22.4D. +7.24.4B. +7.26.4z. +0.1f.5u. +0.1k.5s. +0.1P.53. +0.2a.4j. +2.1h.5t. +2.22.4E. +4.1E.5e. +4.1N.55. +5.29.4k. +6.58.1K. +6.5L.r. +7.24.4C. +7.26.4A. +7.2p.3f. +9./.5D. +0.1O.55. +0.1Q.53. +0.2n.3g. +0.5u.1g. +1.1t.5n. +1.1V.4Z. +2.29.4l. +3.1l.5s. +4.1F.5e. +6.58.1L. +6.5L.s. +7.1+.4H. +7.22.4F. +7.26.4B. +d.2U.2q. +f.24.4D. +f.2g.3H. +0.1m.5s. +0.1P.55. +2./.5E. +2.24.4E. +2.r.5M. +4.1G.5e. +4.k.5R. +5.1t.5o. +5.29.4m. +6.58.1M. +6.5L.t. +7.1+.4I. +7.22.4G. +7.26.4C. +b.5F.+. +f.2g.3I. +0.1n.5s. +0.1Q.55. +1.29.4n. +1.2f.4d. +2.1t.5p. +2.r.5N. +2.s.5M. +3.5G.+. +4.1H.5e. +5.2g.3J. +5.c.5T. +6.58.1N. +6.5L.u. +7.1+.4J. +7.24.4F. +7.26.4D. +b.5F./. +f.2c.4j. +0.1h.5u. +0.1o.5s. +0.3h.2n. +2.26.4E. +2.s.5N. +3.5G./. +4.1I.5e. +5.1t.5q. +5.29.4o. +6.58.1O. +6.5L.v. +7.1+.4K. +7.24.4G. +7.2p.3g. +b.5F.10. +f.1V.4+. +f.2g.3K. +0.1i.5u. +0.1p.5s. +1.2f.4e. +2.u.5M. +3.5G.10. +4.1J.5e. +4.r.5O. +5.29.4p. +6.3i.2n. +6.58.1P. +6.5L.w. +7.1+.4L. +7.22.4H. +7.26.4F. +9.1V.4/. +b.5F.11. +f.1R.53. +f.2g.3L. +0.1j.5u. +0.1q.5s. +0.2d.4j. +1.2f.4f. +1.2n.3j. +2.1d.5w. +2.u.5N. +3.5G.11. +4.1S.53. +5.2g.3M. +5.2q.2V. +6.58.1Q. +6.5L.x. +7.1+.4M. +7.22.4I. +7.26.4G. +9.r.5P. +9.s.5O. +b.5F.12. +f.1V.50. +0.1r.5s. +0.29.4q. +1.2f.4g. +1.2n.3k. +2./.5H. +2.1d.5x. +2.k.5S. +3.5G.12. +5.1V.51. +5.2g.3N. +6.5L.y. +7.1+.4N. +7.22.4J. +7.24.4H. +7.2p.3h. +9.s.5P. +f.1R.55. +f.2W.2q. +0.1s.5s. +1.1t.5r. +1.2f.4h. +1.2n.3l. +4.1K.5e. +4.1S.55. +4.u.5O. +5./.5I. +5.1V.52. +5.29.4r. +5.2a.4q. +5.2q.2X. +6.5L.z. +7.1+.4O. +7.22.4K. +7.24.4I. +7.2p.3i. +7.r.5Q. +f.2g.3O. +0.1k.5u. +0.1L.5e. +0.1t.5s. +0.1V.53. +1.2f.4i. +2.1+.4P. +2.1d.5y. +5.29.4s. +6.3m.2n. +7.22.4L. +7.24.4J. +7.26.4H. +7.s.5Q. +9.u.5P. +b.5F.13. +0.1W.53. +1.2n.3n. +2.1d.5z. +3.1l.5u. +3.1u.5s. +3.5G.13. +4.1M.5e. +5.1V.54. +5.29.4t. +6.58.1R. +7.1+.4Q. +7.22.4M. +7.24.4K. +7.26.4I. +b.5F.14. +0.1m.5u. +0.1V.55. +0.1v.5s. +0.2c.4q. +1.2f.4k. +1.2n.3o. +3.5G.14. +4.1N.5e. +6.58.1S. +6.5L.A. +7.1+.4R. +7.22.4N. +7.24.4L. +7.26.4J. +7.u.5Q. +9.r.5R. +b.5F.15. +f.2g.3Q. +0.1n.5u. +0.1O.5e. +0.1w.5s. +0.1X.53. +1.1V.56. +1.2n.3p. +2.2f.4l. +3.5G.15. +5.29.4u. +5.2q.2Y. +5.c.5U. +6.5L.B. +7.1+.4S. +7.22.4O. +7.24.4M. +7.26.4K. +9.s.5R. +b.5F.16. +f.1W.55. +0.1o.5u. +0.1P.5e. +0.1x.5s. +0.3q.2n. +1.2f.4m. +2.1d.5B. +2.1h.5v. +2.22.4P. +2.A.5M. +3.5G.16. +5.1V.57. +5.29.4v. +5.2g.3R. +6.5L.C. +7.24.4N. +7.26.4L. +b.5F.17. +0.1p.5u. +0.1X.55. +0.1y.5s. +1.2f.4n. +2.1+.4T. +2.1h.5w. +2.1t.5t. +2.A.5N. +3.5G.17. +5.29.4w. +5.c.5V. +5.k.5T. +6.3r.2n. +6.4q.2d. +6.5L.D. +7.22.4Q. +7.24.4O. +7.26.4M. +8.58.1V. +9.u.5R. +b.5F.18. +f.1Q.5e. +f.2g.3S. +0.1q.5u. +0.1z.5s. +1.2f.4o. +1.2n.3s. +2.1+.4U. +2.1V.59. +2.24.4P. +3.5G.18. +5.1h.5x. +5.2q.2Z. +6.58.1W. +6.5L.E. +7.22.4R. +7.26.4N. +b.5F.19. +f.2g.3T. +0.1A.5s. +0.1r.5u. +1.2f.4p. +2.1V.5a. +2.A.5O. +2.r.5S. +3.5G.19. +5.29.4x. +5.2g.3U. +6.5L.F. +7.1+.4V. +7.22.4S. +7.24.4Q. +7.26.4O. +7.2p.3q. +b.5F.1a. +0.1s.5u. +1.1+.4W. +2.1d.5D. +2.1V.5b. +2.26.4P. +2.A.5P. +2.s.5S. +3.5G.1a. +4.1B.5s. +5.1h.5y. +5.29.4y. +5.2g.3V. +6.58.1X. +7.24.4R. +7.2p.3r. +b.5F.1b. +0.1C.5s. +0.1R.5e. +0.1t.5u. +1.2n.3t. +2.22.4T. +2.c.5W. +3.5G.1b. +5.1h.5z. +5.29.4z. +5.2g.3W. +7.24.4S. +7.26.4Q. +b.5F.1c. +0.1D.5s. +1.2f.4r. +1.2n.3u. +2.1d.5E. +2.22.4U. +2.A.5Q. +2.c.5X. +2.u.5S. +3.1u.5u. +3.5G.1c. +4.1S.5e. +5.29.4A. +6.5L.G. +7.26.4R. +0.1v.5u. +0.2n.3v. +1.2f.4s. +2.24.4T. +2.c.5Y. +5.1h.5A. +5.1V.5c. +5.29.4B. +6.5L.H. +7.22.4V. +7.26.4S. +9.1+.4X. +b.5F.1d. +0.1E.5s. +0.1w.5u. +1.1h.5B. +1.22.4W. +1.2f.4t. +1.2n.3w. +2.1V.5d. +2.24.4U. +3.5G.1d. +3.I.5L. +5.29.4C. +5.c.5Z. +8.2+.2q. +9.1+.4Y. +0.1F.5s. +0.1V.5e. +0.1x.5u. +1.2n.3x. +2.26.4T. +2.c.5+. +3.J.5L. +5.29.4D. +5.2g.3Y. +7.24.4V. +0.1G.5s. +0.1W.5e. +0.1y.5u. +1.24.4W. +1.2f.4u. +2./.5J. +2.1V.5f. +2.26.4U. +2.29.4E. +5.k.5U. +6.3y.2n. +6.5L.K. +7.2p.3v. +7.r.5T. +b.5F.1e. +0.1H.5s. +0.1z.5u. +1.1+.4Z. +1.2f.4v. +2.2/.2q. +2.2n.3z. +3.5G.1e. +4.s.5T. +5.1h.5C. +5.1V.5g. +5.29.4F. +6.5L.L. +7.26.4V. +9.22.4X. +b.5F.1f. +0.1A.5u. +0.1I.5s. +0.1X.5e. +1.1V.5h. +1.26.4W. +1.2f.4w. +1.2n.3A. +2./.5K. +2.1d.5I. +3.5G.1f. +3.M.5L. +5.1h.5D. +5.29.4G. +5.k.5V. +9.22.4Y. +b.5F.1g. +f.2g.3+. +0.1B.5u. +0.1J.5s. +0.1Y.53. +2.1V.5i. +3.5G.1g. +4.u.5T. +7.2p.3y. +9.24.4X. +f.2g.3/. +j.2a.4G. +0.1C.5u. +1.2f.4x. +1.2n.3B. +2.1.65. +2.1h.5E. +2.1t.5v. +2.1V.5j. +2.c.5/. +9.1+.4+. +9.24.4Y. +f.2g.40. +0.1D.5u. +0.55.1Y. +1.22.4Z. +1.2f.4y. +1.2n.3C. +2.1t.5w. +2.2.65. +2.c.60. +5.29.4H. +5.2q.30. +6.5L.N. +9.1+.4/. +9.26.4X. +b.5F.1h. +f.2g.41. +0.1K.5s. +1.2f.4z. +2.2n.3D. +2.3.65. +2.k.5W. +3.5G.1h. +5.29.4I. +5.2g.42. +5.c.61. +6.5L.O. +9.1+.50. +9.1t.5x. +9.26.4Y. +b.5F.1i. +f.1V.5k. +0.1E.5u. +0.1L.5s. +1.24.4Z. +1.2f.4A. +1.2n.3E. +2.4.65. +2.k.5X. +3.5G.1i. +5.29.4J. +5.c.62. +6.5L.P. +7.1+.51. +b.5F.1j. +0.1F.5u. +0.1M.5s. +0.1Z.53. +1.2f.4B. +1.2n.3F. +2.k.5Y. +3.5G.1j. +4.1t.5y. +5.29.4K. +6.5L.Q. +7.1+.52. +8.58.1Y. +9.22.4+. +e.5.65. +f.2g.44. +0.1+.53. +0.1G.5u. +0.1N.5s. +1.26.4Z. +1.2f.4C. +2.1h.5H. +2.1V.5l. +2.6.65. +4.r.5U. +5.29.4L. +5.k.5Z. +6.5L.R. +9.1t.5z. +9.22.4/. +0.1/.53. +0.1H.5u. +0.1O.5s. +1.2f.4D. +2.7.65. +2.c.63. +2.k.5+. +3.S.5L. +4.s.5U. +5.1h.5I. +5.29.4M. +7.1+.54. +9.22.50. +9.24.4+. +b.3G.2n. +b.5F.1k. +f.1Z.55. +0.1I.5u. +0.1P.5s. +0.20.53. +2.2f.4E. +2.8.65. +3.5G.1k. +5.29.4N. +5.2g.47. +6.5L.T. +7.22.51. +7.r.5V. +9.1t.5A. +9.24.4/. +b.5F.1l. +f.1+.55. +0.1/.55. +0.1J.5u. +0.1Q.5s. +1.1+.56. +1.1t.5B. +1.1V.5n. +1.2f.4F. +2.9.65. +3.5G.1l. +4.s.5V. +4.u.5U. +5.29.4O. +6.5L.U. +7.22.52. +9.24.50. +9.26.4+. +b.5F.1m. +e.21.53. +f.2g.48. +0.20.55. +0.22.53. +1.2f.4G. +2.29.4P. +2.a.65. +2.c.64. +3.5G.1m. +5.1V.5o. +5.2g.49. +6.58.1Z. +6.5L.V. +7.1+.57. +7.24.51. +9.26.4/. +b.3G.2p. +b.5F.1n. +0.23.53. +1.2n.3H. +1.31.2q. +2.1V.5p. +2.b.65. +3.5G.1n. +4.u.5V. +5.29.4Q. +6.5L.W. +7.22.54. +8.58.1+. +9.26.50. +b.5F.1o. +f.24.52. +j.21.55. +0.1K.5u. +0.24.53. +1.2n.3I. +2.1+.59. +2.k.5/. +2.r.5W. +3.5G.1o. +4.1t.5C. +4.26.51. +5.1V.5q. +5.29.4R. +5.c.65. +6.58.1/. +6.5L.X. +b.5F.1p. +f.22.55. +0.1L.5u. +0.23.55. +0.25.53. +1.22.56. +1.2f.4H. +2.1+.5a. +2.k.60. +2.r.5X. +2.s.5W. +3.5G.1p. +4.1Y.5e. +4.26.52. +5.29.4S. +6.58.20. +6.5L.Y. +7.24.54. +9.1t.5D. +b.3J.2n. +b.5F.1q. +f.1R.5s. +0.1M.5u. +0.1S.5s. +0.24.55. +1.2f.4I. +2.1+.5b. +2.1d.5J. +2.c.66. +2.r.5Y. +2.s.5X. +3.3K.2n. +3.5G.1q. +4.26.53. +5.k.61. +6.58.21. +6.5L.Z. +7.22.57. +b.5F.1r. +0.1N.5u. +0.25.55. +0.27.53. +1.24.56. +1.2f.4J. +2.1t.5E. +2.29.4T. +2.s.5Y. +2.u.5W. +3.5G.1r. +4.r.5Z. +5.32.2q. +5.c.67. +5.k.62. +6.3L.2n. +7.26.54. +8.58.22. +b.5F.1s. +f.2g.4c. +0.1O.5u. +1.1V.5r. +1.2f.4K. +2.1d.5K. +2.22.59. +2.29.4U. +2.c.68. +2.r.5+. +2.u.5X. +3.3M.2n. +3.5G.1s. +4.26.55. +4.s.5Z. +6.58.23. +7.24.57. +b.3J.2p. +b.5F.1t. +f.2g.4d. +0.1P.5u. +1.26.56. +1.2f.4L. +2.22.5a. +2.s.5+. +2.u.5Y. +3.5G.1t. +5.29.4V. +6.3N.2n. +7.2p.3K. +8.58.24. +9.1+.5c. +b.5F.1u. +c.28.53. +f.1V.5s. +f.27.55. +0.1Q.5u. +0.1W.5s. +1.29.4W. +1.2f.4M. +1.2n.3O. +1.33.2q. +2.1+.5d. +2.22.5b. +2.24.59. +2.k.63. +3.5G.1u. +4.u.5Z. +6.58.25. +6.5L.+. +7.26.57. +7.2p.3L. +b.5F.1v. +f.1Z.5e. +f.2g.4e. +1.2f.4N. +2.24.5a. +2.2n.3P. +2.A.5V. +2.u.5+. +3.5G.1v. +5.2q.34. +5.c.69. +6.5L./. +7.2p.3M. +8.58.26. +b.5F.1w. +c.28.55. +f.1+.5e. +f.2g.4f. +0.1X.5s. +1.2f.4O. +2.1+.5f. +2.1t.5H. +2.24.5b. +2.26.59. +3.5G.1w. +5.2q.35. +5.c.6a. +6.5L.10. +7.2p.3N. +8.58.27. +b.5F.1x. +f.1/.5e. +f.2g.4g. +0.20.5e. +1.2n.3Q. +2./.5M. +2.26.5a. +2.2f.4P. +2.k.64. +2.r.5/. +3.5G.1x. +5.1t.5I. +5.29.4X. +5.2q.36. +6.5L.11. +7.1+.5g. +9.22.5c. +b.5F.1y. +f.2g.4h. +0.1R.5u. +1.1+.5h. +1.2f.4Q. +1.c.6b. +2./.5N. +2.1h.5J. +2.1V.5t. +2.22.5d. +2.26.5b. +2.r.60. +2.s.5/. +3.5G.1y. +5.29.4Y. +6.5L.12. +8.58.28. +b.5F.1z. +j.21.5e. +0.1S.5u. +1.2f.4R. +2.1+.5i. +2.A.5W. +2.s.60. +3.5G.1z. +5.k.65. +7.r.61. +9.24.5c. +b.3R.2n. +b.5F.1A. +f.22.5e. +f.2g.4j. +0.23.5e. +1.2f.4S. +2.1+.5j. +2.1h.5K. +2.22.5f. +2.24.5d. +2.u.5/. +3.5G.1A. +4.s.61. +6.3S.2n. +7.r.62. +9./.5O. +b.5F.1B. +f.2g.4k. +0.24.5e. +1.29.4Z. +1.2n.3T. +2.A.5Y. +2.k.66. +2.u.60. +3.5G.1B. +5.2q.37. +6.5L.13. +7.22.5g. +7.s.62. +9./.5P. +9.26.5c. +b.5F.1C. +0.1V.5u. +0.25.5e. +1.22.5h. +1.2n.3U. +2.24.5f. +2.26.5d. +2.2f.4T. +2.A.5Z. +3.5G.1C. +4.u.61. +5.2q.38. +5.k.67. +6.5L.14. +9.1+.5k. +b.3R.2p. +b.5F.1D. +f.2g.4m. +0.1W.5u. +1.2n.3V. +2.22.5i. +2.2f.4U. +2.k.68. +2.r.63. +3.5G.1D. +4.26.5e. +4.u.62. +5./.5Q. +6.5L.15. +7.24.5g. +7.2p.3S. +1.24.5h. +1.2f.4V. +1.2n.3W. +2.22.5j. +2.26.5f. +2.s.63. +5.29.4+. +6.5L.16. +b.5F.1E. +f.27.5e. +f.2g.4o. +0.1X.5u. +1.2f.4W. +1.2n.3X. +2.1+.5l. +2.24.5i. +3.5G.1E. +5.29.4/. +5.2q.39. +6.5L.17. +7.26.5g. +b.5F.1F. +f.2g.4p. +1.26.5h. +2.24.5j. +2.r.64. +2.u.63. +3.5G.1F. +5.29.50. +5.2q.3a. +5.c.6c. +5.k.69. +6.5L.18. +9./.5R. +9.22.5k. +b.5F.1G. +c.28.5e. +j.2a.4/. +2.26.5i. +2.s.64. +3.5G.1G. +5.29.51. +5.2q.3b. +5.k.6a. +6.5L.19. +b.5F.1H. +f.2g.4q. +0.2n.3Y. +1.1+.5n. +1.2f.4X. +2.26.5j. +2.A.5/. +3.5G.1H. +5.29.52. +5.2q.3c. +6.5L.1a. +7.r.65. +9.24.5k. +b.5F.1I. +0.29.53. +1.2f.4Y. +1.k.6b. +2.22.5l. +2.A.60. +2.u.64. +3.5G.1I. +5.2q.3d. +6.5L.1b. +7.1+.5o. +7.s.65. +b.5F.1J. +1.2n.3Z. +2.1+.5p. +2.A.61. +2.r.66. +3.5G.1J. +5.29.54. +5.2q.3e. +6.5L.1c. +6.5s.1Y. +9.26.5k. +f.2a.53. +1.2n.3+. +2./.5S. +2.1V.5v. +2.24.5l. +2.s.66. +4.r.67. +7.1+.5q. +7.2p.3Y. +7.u.65. +f.29.55. +1.22.5n. +1.29.56. +1.2f.4Z. +1.2n.3/. +2.1t.5J. +2.1V.5w. +2.r.68. +4.s.67. +5.2a.55. +5.2q.3f. +6.5L.1d. +b.5F.1K. +0.2n.40. +2.26.5l. +2.s.68. +2.u.66. +3.5G.1K. +5.29.57. +7.22.5o. +9.1V.5x. +b.5F.1L. +f.2c.53. +f.2g.4v. +1.24.5n. +1.2n.41. +2.1d.5M. +2.1t.5K. +2.22.5p. +2.c.6d. +3.5G.1L. +4.u.67. +5.2g.4w. +8.58.29. +b.5F.1M. +1.1+.5r. +1.2f.4+. +1.2n.42. +2.1d.5N. +2.29.59. +2.c.6e. +2.u.68. +3.5G.1M. +6.5L.1e. +7.22.5q. +7.24.5o. +7.r.69. +8.58.2a. +b.5F.1N. +f.1V.5y. +f.1Z.5s. +f.2c.55. +0.1+.5s. +0.2d.53. +1.26.5n. +1.2f.4/. +1.2n.43. +2.24.5p. +2.29.5a. +2.2g.4x. +3.5G.1N. +5.2q.3g. +6.5L.1f. +7.2p.40. +7.r.6a. +7.s.69. +9.1V.5z. +b.5F.1O. +0.1/.5s. +1.2f.50. +1.2n.44. +2.1d.5O. +2.29.5b. +3.5G.1O. +5.c.6f. +5.k.6c. +6.5L.1g. +7.24.5q. +7.26.5o. +7.s.6a. +b.5F.1P. +f.2g.4y. +0.20.5s. +0.2d.55. +1.2f.51. +1.2n.45. +1.r.6b. +2.1d.5P. +2.26.5p. +3.5G.1P. +5./.5T. +5.c.6g. +6.58.2c. +7.u.69. +9.1V.5A. +b.5F.1Q. +0.5u.1Y. +1.1V.5B. +1.22.5r. +1.2f.52. +1.2n.46. +1.s.6b. +3.5G.1Q. +7.26.5q. +7.u.6a. +e.21.5s. +f.2g.4A. +l.2q.3h. +1.2n.47. +2.1d.5Q. +5.29.5c. +5.2q.3i. +6.5L.1h. +f.22.5s. +1.24.5r. +1.2f.54. +1.2n.48. +1.u.6b. +2.1+.5t. +2.29.5d. +5.2q.3j. +6.58.2d. +6.5L.1i. +f.23.5s. +j.2a.5c. +0.24.5s. +1.2n.49. +1.c.6h. +2.1h.5M. +5.2q.3k. +6.5L.1j. +b.5F.1R. +f.1V.5C. +f.29.5e. +f.2g.4D. +0.25.5s. +1.26.5r. +1.2f.56. +1.2n.4a. +2.1h.5N. +2.29.5f. +3.5G.1R. +5.2a.5e. +5.2q.3l. +9.1V.5D. +b.5F.1S. +0.1Z.5u. +1.2f.57. +1.2n.4b. +3.5G.1S. +4.26.5s. +5.29.5g. +5.2q.3m. +f.2g.4F. +0.1+.5u. +0.27.5s. +1.29.5h. +1.2f.58. +1.c.6i. +2.1V.5E. +2.22.5t. +2.k.6d. +5.1h.5O. +6.5L.1k. +0.1/.5u. +1.c.6j. +2.29.5i. +2.2f.59. +2.k.6e. +3.1l.5L. +5.1h.5P. +5.2q.3o. +7.r.6c. +b.5F.1V. +f.2c.5e. +0.20.5u. +2.24.5t. +2.29.5j. +2.2f.5a. +2.c.6k. +3.5G.1V. +4.s.6c. +5./.5U. +5.2q.3p. +6.4c.2n. +6.5L.1m. +b.5F.1W. +c.28.5s. +1.2n.4d. +1.c.6l. +2.2f.5b. +3.5G.1W. +5.1h.5Q. +5.2g.4H. +5.2q.3q. +5.k.6f. +6.5L.1n. +j.21.5u. +0.22.5u. +0.2d.5e. +2.26.5t. +2.A.6b. +4.u.6c. +5./.5V. +5.29.5k. +5.2q.3r. +5.k.6g. +6.5L.1o. +b.5F.1X. +f.2g.4I. +0.23.5u. +1.2n.4e. +2.1V.5H. +3.5G.1X. +6.5L.1p. +7.2p.4c. +j.2a.5k. +0.24.5u. +1.2f.5c. +1.2n.4f. +2.c.6m. +5.1h.5R. +5.1V.5I. +6.5L.1q. +0.25.5u. +1.2n.4g. +2.29.5l. +2.2f.5d. +6.5L.1r. +1.2n.4h. +1.k.6h. +2./.5W. +4.26.5u. +5.2q.3t. +6.5L.1s. +0.27.5u. +1.2n.4i. +2./.5X. +2.1+.5v. +2.2f.5f. +2.r.6d. +5.2q.3u. +6.5L.1t. +0.2n.4j. +1.29.5n. +1.2f.5g. +2./.5Y. +2.1+.5w. +2.r.6e. +2.s.6d. +3.1u.5L. +5.2q.3v. +1.2f.5h. +1.2n.4k. +1.k.6i. +2.1h.5S. +2.1t.5M. +2.s.6e. +5./.5Z. +5.29.5o. +5.2q.3w. +6.5L.1v. +9.1+.5x. +c.28.5u. +1.k.6j. +2./.5+. +2.1t.5N. +2.29.5p. +2.2f.5i. +2.2n.4l. +2.u.6d. +4.r.6f. +5.2g.4Q. +5.2q.3x. +6.5L.1w. +1.2n.4m. +2.22.5v. +2.2f.5j. +2.k.6k. +2.u.6e. +4.s.6f. +5.29.5q. +5.2q.3y. +6.5L.1x. +7.2p.4j. +7.r.6g. +9.1+.5y. +f.2g.4R. +1.2n.4n. +1.k.6l. +2.22.5w. +6.5L.1y. +7.s.6g. +9.1+.5z. +9.1t.5O. +1.2f.5k. +1.2n.4o. +2.24.5v. +4.u.6f. +5.2q.3A. +6.5L.1z. +9.1t.5P. +9.22.5x. +1.2n.4p. +2.24.5w. +4.u.6g. +6.5L.1A. +9.1+.5A. +1.1+.5B. +1.29.5r. +1.r.6h. +2./.5/. +2.26.5v. +2.k.6m. +5.1t.5Q. +5.2q.3B. +6.5L.1B. +9.22.5y. +9.24.5x. +1.s.6h. +2./.60. +2.26.5w. +2.2f.5l. +5.1h.5T. +5.2q.3C. +6.4q.2n. +6.5L.1C. +9.22.5z. +b.5F.1Y. +f.29.5s. +1.2n.4r. +3.5G.1Y. +5./.61. +6.5L.1D. +9.24.5y. +9.26.5x. +f.2a.5s. +1.2n.4s. +1.r.6i. +1.u.6h. +5./.62. +5.2q.3E. +9.1+.5C. +9.1t.5R. +9.22.5A. +9.24.5z. +1.22.5B. +1.2f.5n. +1.2n.4t. +1.5s.2b. +1.r.6j. +1.s.6i. +2.1d.5V. +2.1V.5J. +5.2q.3F. +6.5L.1E. +7.2p.4q. +9.1+.5D. +9.26.5y. +0.2c.5s. +1.2f.5o. +1.s.6j. +2.2g.4X. +2.r.6k. +6.5L.1F. +9.24.5A. +9.26.5z. +1.24.5B. +1.2n.4u. +1.r.6l. +1.u.6i. +2./.63. +2.1+.5E. +2.1V.5K. +2.29.5t. +2.2f.5p. +2.2g.4Y. +2.s.6k. 5.3G.2q. -2.2q.3M. -2.2q.3Q. -5.40.2q. -2.2q.47. -2.2q.4d. -2.2q.4O. -2.2q.4Q. -7.4T.2q. -2.2q.4V. -2.2q.51. -2.2q.52. -a.5a.2q. -2.5b.2q. -5.5d.2q. -2.2q.5B. -2.2q.5C. -2.2q.5F. -2.2q.5H. -2.2q.5I. -2.2q.5K. -7.5M.2q. -2.2q.5O. -2.2q.5R. -2.2q.5U. -2.2q.5W. -1.5Y.2q. -2.2q.5Z. -2.2q.5/. -2.2q.60. -2.62.2q. -2.66.2q. -a.6b.2q. -c.6e.2q. -2.2q.6F. -2.2q.6M. -2.2q.6+. -2.2q.71. -2.2q.79. -2.2q.7D. -2.2q.2p. -2.2q.2p. -2.2r.2r. -7.2s.2r. -7.2t.2r. -2.2u.2r. -2.2r.2v. -2.2r.2w. -e.2x.2r. -2.2r.2y. -2.2r.2z. -2.2r.2A. -2.2r.2B. -2.2r.2C. -2.2r.2D. -2.2r.2E. -2.2r.2F. -2.2r.2G. -9.2H.2r. -2.2r.2I. -2.2r.2J. -2.2r.2K. -c.2M.2r. -2.2r.2N. -2.2O.2r. -2.2r.2Q. -7.2S.2r. -2.2r.2T. -1.2U.2r. -9.2V.2r. -1.2W.2r. -2.2r.2X. -2.2r.2+. -2.2r.30. -2.2r.35. -2.2r.36. -2.2r.37. -2.2r.38. -2.2r.39. -2.2r.3d. -2.2r.3h. -2.2r.3i. -2.2r.3m. -2.2r.3p. -a.3v.2r. -a.3y.2r. -2.3z.2r. -2.2r.3A. -2.3B.2r. -2.2r.3C. -a.3F.2r. -5.3G.2r. -2.2r.3M. -2.2r.3Q. -5.40.2r. -2.2r.47. -2.2r.4d. -2.2r.4O. -2.2r.4Q. -7.4T.2r. -2.2r.4V. -2.2r.51. -2.2r.52. -a.5a.2r. -2.5b.2r. -5.5d.2r. -2.2r.5B. -2.2r.5C. -2.2r.5F. -2.2r.5H. -2.2r.5I. -2.2r.5K. -7.5M.2r. -2.2r.5O. -2.2r.5R. -2.2r.5U. -2.2r.5W. -1.5Y.2r. -2.2r.5Z. -2.2r.5/. -2.2r.60. -2.62.2r. -2.66.2r. -a.6b.2r. -c.6e.2r. -2.2r.6F. -2.2r.6M. -2.2r.6+. -2.2r.71. -2.2r.79. -2.2r.7D. -2.2r.2p. -2.2r.2p. -7.2s.2s. -7.2t.2s. -7.2s.2u. -7.2s.2v. -7.2s.2w. -7.2s.2x. -7.2s.2y. -7.2s.2z. -7.2s.2A. -7.2s.2B. -7.2s.2C. -7.2s.2D. -7.2s.2E. -7.2s.2F. -7.2s.2G. -7.2s.2H. -7.2s.2I. -7.2s.2J. -7.2s.2K. -c.2M.2s. -7.2s.2N. -7.2s.2O. -7.2s.2Q. -7.2S.2s. -7.2s.2T. -1.2U.2s. -7.2s.2V. -1.2W.2s. -7.2s.2X. -7.2s.2+. -7.2s.30. -7.2s.35. -7.2s.36. -7.2s.37. -7.2s.38. -7.2s.39. -7.2s.3d. -7.2s.3h. -7.2s.3i. -7.2s.3m. -7.2s.3p. -7.2s.3y. -7.2s.3z. -7.2s.3A. -7.2s.3B. -7.2s.3C. -7.2s.3F. -7.2s.3G. -7.2s.3M. -7.2s.3Q. -7.2s.40. -7.2s.47. -7.2s.4d. -7.2s.4O. -7.2s.4Q. -7.4T.2s. -7.2s.4V. -7.2s.51. -7.2s.52. -7.2s.5a. -7.2s.5b. -7.2s.5d. -7.2s.5B. -7.2s.5C. -7.2s.5F. -7.2s.5H. -7.2s.5I. -7.2s.5K. -7.5M.2s. -7.2s.5O. -7.2s.5R. -7.2s.5U. -7.2s.5W. -1.5Y.2s. -7.2s.5Z. -7.2s.5/. -7.2s.60. -7.2s.62. -7.2s.66. -7.2s.6b. -c.6e.2s. -7.2s.6F. -7.2s.6M. -7.2s.6+. -7.2s.71. -7.2s.79. -7.2s.7D. -7.2s.2p. -7.2s.2p. -7.2t.2t. -7.2t.2u. -7.2t.2v. -7.2t.2w. -7.2t.2x. -7.2t.2y. -7.2t.2z. -7.2t.2A. -7.2t.2B. -7.2t.2C. -7.2t.2D. -7.2t.2E. -7.2t.2F. -7.2t.2G. -7.2t.2H. -7.2t.2I. -7.2t.2J. -7.2t.2K. -c.2M.2t. -7.2t.2N. -7.2t.2O. -7.2t.2Q. -7.2S.2t. -7.2t.2T. -1.2U.2t. -7.2t.2V. -1.2W.2t. -7.2t.2X. -7.2t.2+. -7.2t.30. -7.2t.35. -7.2t.36. -7.2t.37. -7.2t.38. -7.2t.39. -7.2t.3d. -7.2t.3h. -7.2t.3i. -7.2t.3m. -7.2t.3p. -7.2t.3y. -7.2t.3z. -7.2t.3A. -7.2t.3B. -7.2t.3C. -7.2t.3F. -7.2t.3G. -7.2t.3M. -7.2t.3Q. -7.2t.40. -7.2t.47. -7.2t.4d. -7.2t.4O. -7.2t.4Q. -7.4T.2t. -7.2t.4V. -7.2t.51. -7.2t.52. -7.2t.5a. -7.2t.5b. -7.2t.5d. -7.2t.5B. -7.2t.5C. -7.2t.5F. -7.2t.5H. -7.2t.5I. -7.2t.5K. -7.5M.2t. -7.2t.5O. -7.2t.5R. -7.2t.5U. -7.2t.5W. -1.5Y.2t. -7.2t.5Z. -7.2t.5/. -7.2t.60. -7.2t.62. -7.2t.66. -7.2t.6b. -c.6e.2t. -7.2t.6F. -7.2t.6M. -7.2t.6+. -7.2t.71. -7.2t.79. -7.2t.7D. -7.2t.2p. -7.2t.2p. -2.2u.2u. -2.2u.2v. -2.2u.2w. -2.2u.2y. -2.2u.2z. -2.2u.2A. -2.2u.2B. -2.2u.2C. -2.2u.2D. -2.2u.2E. -2.2u.2F. -2.2u.2G. -2.2u.2H. -2.2u.2I. -2.2u.2J. -2.2u.2K. -c.2M.2u. -2.2u.2N. -2.2u.2O. -2.2u.2Q. -7.2S.2u. -2.2u.2T. -1.2U.2u. -2.2u.2V. -1.2W.2u. -2.2u.2X. -2.2u.2+. -2.2u.30. -2.2u.35. -2.2u.36. -2.2u.37. -2.2u.38. -2.2u.39. -2.2u.3d. -2.2u.3h. -2.2u.3i. -2.2u.3m. -2.2u.3p. -a.3v.2u. -a.3y.2u. -2.2u.3z. -2.2u.3A. -2.2u.3B. -2.2u.3C. -a.3F.2u. -2.2u.3G. -2.2u.3M. -2.2u.3Q. -2.2u.40. -2.2u.47. -2.2u.4d. -2.2u.4O. -2.2u.4Q. -7.4T.2u. -2.2u.4V. -2.2u.51. -2.2u.52. -2.2u.5a. -2.2u.5b. -2.2u.5d. -2.2u.5B. -2.2u.5C. -2.2u.5F. -2.2u.5H. -2.2u.5I. -f.2u.5K. -7.5M.2u. -2.2u.5O. -2.2u.5R. -2.2u.5U. -2.2u.5W. -1.5Y.2u. -2.2u.5Z. -2.2u.5/. -2.2u.60. -2.2u.62. -2.2u.66. -a.6b.2u. -c.6e.2u. -2.2u.6F. -2.2u.6M. -2.2u.6+. -2.2u.71. -2.2u.79. -2.2u.7D. -2.2u.2p. -2.2u.2p. -0.2v.2v. -d.2v.2w. -e.2x.2v. -9.2y.2v. -0.2v.2z. -d.2v.2A. -9.2B.2v. -0.2v.2C. -0.2v.2D. -0.2v.2E. -0.2v.2F. -0.2v.2G. -9.2H.2v. -0.2v.2I. -d.2v.2J. -0.2v.2K. -c.2M.2v. -9.2N.2v. -2.2O.2v. -9.2Q.2v. -7.2S.2v. -0.2v.2T. -1.2U.2v. -9.2V.2v. -1.2W.2v. -0.2v.2X. -d.2+.2v. -d.2v.30. -d.2v.35. -b.36.2v. -0.2v.37. -0.2v.38. -5.39.2v. -5.3d.2v. -d.2v.3h. -5.3i.2v. -0.2v.3m. -0.2v.3p. -a.3v.2v. -a.3y.2v. -2.3z.2v. -5.3A.2v. -2.3B.2v. -0.2v.3C. -a.3F.2v. -5.3G.2v. -0.2v.3M. -d.2v.3Q. -5.40.2v. -0.2v.47. -0.2v.4d. -0.2v.4O. -d.2v.4Q. -5.4T.2v. -0.2v.4V. -d.2v.51. -0.2v.52. -a.5a.2v. -2.5b.2v. -5.5d.2v. -0.2v.5B. -0.2v.5C. -5.5F.2v. -0.2v.5H. -0.2v.5I. -0.2v.5K. -7.5M.2v. -d.2v.5O. -0.2v.5R. -0.2v.5U. -0.2v.5W. -1.5Y.2v. -0.2v.5Z. -0.2v.5/. -0.2v.60. -2.62.2v. -2.66.2v. -a.6b.2v. -c.6e.2v. -0.2v.6F. -0.6M.2v. -d.2v.6+. -d.2v.71. -5.79.2v. -0.2v.7D. -0.2v.2p. -0.2v.2p. +6.5L.1G. +b.5F.1Z. +1.2f.5q. +1.2n.4v. +1.s.6l. +1.u.6j. +3.5G.1Z. +6.5L.1H. +9.22.5C. +9.26.5A. +b.5F.1+. +1.26.5B. +1.2n.4w. +2.1d.5W. +2.1t.5S. +2.u.6k. +3.5G.1+. +4.2d.5s. +6.5L.1I. +9.22.5D. +b.5F.1/. +1.5s.2e. +1.u.6l. +2./.64. +2.r.6m. +3.5G.1/. +6.5L.1J. +9.24.5C. +b.5F.20. +0.29.5u. +1.2n.4x. +2.1d.5Y. +2.22.5E. +2.s.6m. +3.5G.20. +5.1h.5U. +5.2q.3H. +9.24.5D. +b.5F.21. +1.2f.5r. +1.2n.4y. +2.1+.5H. +2.1d.5Z. +3.5G.21. +5./.65. +5.2a.5u. +5.2q.3I. +9.26.5C. +b.5F.22. +1.2n.4z. +1.5s.2f. +2.24.5E. +2.2g.4+. +2.u.6m. +3.5G.22. +5.1h.5V. +5.3J.2q. +6.5L.1K. +7.1+.5I. +9.26.5D. +b.5F.23. +1.2n.4A. +2./.66. +3.5G.23. +6.3K.2q. +6.5L.1L. +b.5F.24. +e.2g.4/. +1.2n.4B. +1.4G.2i. +2.26.5E. +2.2g.50. +3.5G.24. +5./.67. +5.2q.3L. +6.5L.1M. +b.5F.25. +f.2c.5u. +1.2n.4C. +1.4G.2j. +2./.68. +2.22.5H. +3.5G.25. +5.1t.5T. +5.2g.51. +6.3M.2q. +6.5L.1N. +b.5F.26. +e.0.6o. +1.2n.4D. +2.1h.5W. +2.A.6k. +3.5G.26. +5.2g.52. +5.2q.3N. +6.5L.1O. +7.22.5I. +b.5F.27. +e.0.6p. +e.1.6o. +0.5u.2d. +2.1d.5/. +2.1h.5X. +2.24.5H. +2.2f.5t. +2.2n.4E. +3.5G.27. +5.2g.53. +5.2q.3O. +6.5L.1P. +e.1.6p. +e.2.6o. +1.2n.4F. +2.1d.60. +2.1h.5Y. +5./.69. +6.5L.1Q. +7.24.5I. +b.5F.28. +e.2.6p. +e.3.6o. +f.2g.54. +1.2n.4G. +2.1d.61. +2.26.5H. +2.29.5v. +3.5G.28. +5./.6a. +5.1h.5Z. +e.3.6p. +e.4.6o. +f.2g.55. +2.1h.5+. +2.29.5w. +2.A.6m. +5.2q.3Q. +7.26.5I. +e.4.6p. +e.5.6o. +1./.6b. +5.29.5x. +e.5.6p. +e.6.6o. +1.2n.4H. +5.3R.2q. +6.5L.1R. +e.0.6u. +e.6.6p. +e.7.6o. +f.2g.58. +j.2a.5x. +1.2n.4I. +5.29.5y. +5.2q.3S. +6.5L.1S. +e.0.6v. +e.1.6u. +e.7.6p. +e.8.6o. +1.2n.4J. +5.1t.5U. +5.29.5z. +5.2q.3T. +e.1.6v. +e.2.6u. +e.8.6p. +e.9.6o. +j.2a.5y. +1.2n.4K. +2.1h.5/. +5.2q.3U. +e.2.6v. +e.3.6u. +e.9.6p. +e.a.6o. +j.2a.5z. +1.2n.4L. +2.1h.60. +5.1t.5V. +5.29.5A. +5.2q.3V. +6.5L.1V. +9.c.6n. +e.3.6v. +e.4.6u. +e.a.6p. +e.b.6o. +1.29.5B. +1.2n.4M. +2.1+.5J. +5.1h.61. +5.2q.3W. +6.5L.1W. +e.4.6v. +e.5.6u. +e.b.6p. +i.c.6o. +j.2a.5A. +1.2n.4N. +2.1V.5M. +2.2g.5c. +5.1h.62. +5.2q.3X. +e.5.6v. +e.6.6u. +i.c.6p. +1.2n.4O. +1.c.6q. +2.1+.5K. +2.1V.5N. +5./.6c. +6.5L.1X. +e.6.6v. +e.7.6u. +2.1t.5W. +2.2f.5v. +2.2n.4P. +5.29.5C. +e.7.6v. +e.8.6u. +f.2g.5e. +0.d.6o. +1.2n.4Q. +2.1h.63. +2.1t.5X. +2.22.5J. +2.2f.5w. +5.1V.5O. +5.29.5D. +5.2q.3Y. +9.c.6r. +e.8.6v. +e.9.6u. +0.d.6p. +0.e.6o. +1.2f.5x. +1.2n.4R. +2.1t.5Y. +9.1V.5P. +e.9.6v. +e.a.6u. +i.c.6s. +0.e.6p. +0.f.6o. +1.2n.4S. +2.22.5K. +2.24.5J. +2.29.5E. +4.b.6u. +5.1t.5Z. +5.2q.3Z. +9.c.6t. +e.a.6v. +0.f.6p. +0.g.6o. +1.2f.5y. +2.1h.64. +2.1t.5+. +5.1V.5Q. +5.2q.3+. +b.5F.29. +e.b.6v. +i.c.6u. +0.g.6p. +0.h.6o. +1.2f.5z. +2.24.5K. +2.26.5J. +2.2n.4T. +3.5G.29. +5.2q.3/. +b.5F.2a. +i.c.6v. +0.d.6s. +0.h.6p. +0.i.6o. +2.2n.4U. +3.5G.2a. +5.1h.65. +5.2q.40. +0.e.6s. +0.i.6p. +0.j.6o. +1.2f.5A. +1.2n.4V. +2./.6d. +2.26.5K. +2.2g.5k. +5.1V.5R. +5.2q.41. +9.k.6n. +0.f.6s. +0.j.6p. +0.k.6o. +1.2f.5B. +1.2n.4W. +1.c.6w. +2./.6e. +2.1d.6b. +2.1h.66. +2.29.5H. +4.d.6u. +5.2q.42. +b.5F.2c. +0.d.6v. +0.g.6s. +0.k.6p. +0.l.6o. +2.1t.5/. +3.5G.2c. +4.e.6u. +5.1h.67. +5.29.5I. +h.c.6x. +0.e.6v. +0.h.6s. +0.l.6p. +0.m.6o. +1.k.6q. +2.1h.68. +2.1t.60. +4.f.6u. +5./.6f. +5.2q.44. +e.0.6E. +e.c.6y. +0.f.6v. +0.i.6s. +0.m.6p. +1.2f.5C. +1.2n.4X. +3.n.6o. +4.g.6u. +5./.6g. +5.1t.61. +5.2q.45. +b.5F.2d. +e.1.6E. +0.g.6v. +0.j.6s. +0.o.6o. +1.2f.5D. +1.2n.4Y. +2.1V.5S. +3.5G.2d. +3.n.6p. +4.h.6u. +5.1t.62. +5.2q.46. +9.k.6r. +e.2.6E. +0.h.6v. +0.k.6s. +0.o.6p. +0.p.6o. +4.i.6u. +5.1h.69. +5.2q.47. +e.3.6E. +e.5L.1Y. +h.d.6x. +0.i.6v. +0.l.6s. +0.p.6p. +0.q.6o. +2.2f.5E. +4.j.6u. +5.1h.6a. +5.2q.48. +9.k.6t. +e.4.6E. +f.2g.5o. +h.e.6x. +0.j.6v. +0.m.6s. +0.q.6p. +1./.6h. +1.2n.4Z. +2.1t.63. +4.k.6u. +5.2q.49. +8.6z.c. +e.5.6E. +h.f.6x. +0.k.6v. +1.1h.6b. +3.n.6s. +4.l.6u. +5.2g.5q. +9.r.6n. +e.6.6E. +h.g.6x. +0.l.6v. +0.o.6s. +0.r.6o. +4.m.6u. +9.c.6A. +9.s.6n. +e.7.6E. +h.h.6x. +0.m.6v. +0.p.6s. +0.r.6p. +0.s.6o. +1./.6i. +1.2n.4+. +2.1t.64. +6.5L.1Z. +e.8.6E. +e.n.6u. +h.i.6x. +i.c.6B. +0.s.6p. +0.t.6o. +1./.6j. +1.2n.4/. +1.k.6w. +1.r.6q. +2.2f.5H. +3.n.6v. +4.o.6u. +6.5L.1+. +6.6s.q. +8.6z.d. +9.u.6n. +e.9.6E. +h.j.6x. +0.o.6v. +0.t.6p. +0.u.6o. +1.2f.5I. +1.2n.50. +1.s.6q. +2./.6k. +4.p.6u. +5.1t.65. +5.4c.2q. +6.5L.1/. +8.6z.e. +9.c.6C. +e.a.6E. +h.k.6x. +0.p.6v. +0.u.6p. +0.v.6o. +1./.6l. +1.2n.51. +2.1+.5M. +4.b.6E. +4.q.6u. +4.r.6r. +5.2q.4d. +5.c.6D. +6.5L.20. +8.6z.f. +9.k.6y. +e.0.6J. +f.2g.5s. +h.l.6x. +0.d.6B. +0.q.6v. +0.r.6s. +0.v.6p. +0.w.6o. +1.2n.52. +1.u.6q. +2.0.6K. +2.1+.5N. +2.1t.66. +6.5L.21. +8.6z.g. +9.s.6r. +e.1.6J. +h.m.6x. +i.c.6E. +0.2n.53. +0.e.6B. +0.s.6s. +0.w.6p. +0.x.6o. +1.6L.0. +2.1.6K. +5.1t.67. +5.2q.4e. +6.5L.22. +8.6z.h. +9.r.6t. +e.2.6J. +h.n.6x. +0.f.6B. +0.r.6u. +0.t.6s. +0.x.6p. +0.y.6o. +1.2n.54. +1.6L.1. +2./.6m. +2.1t.68. +2.2.6K. +2.29.5J. +5.1h.6c. +5.2q.4f. +5.c.6F. +6.5L.23. +8.6z.i. +9.1+.5O. +9.s.6t. +9.u.6r. +e.3.6J. +h.o.6x. +0.55.2n. +0.g.6B. +0.r.6v. +0.u.6s. +0.y.6p. +1.6L.2. +2.22.5M. +2.3.6K. +4.s.6u. +5.2q.4g. +6.5L.24. +6.6o.z. +8.6z.j. +9.1+.5P. +e.4.6J. +h.p.6x. +0.s.6v. +0.t.6u. +0.v.6s. +1.2n.56. +1.6L.3. +2.22.5N. +2.29.5K. +4.d.6E. +4.h.6B. +4.u.6t. +5.2q.4h. +6.5L.25. +6.6p.z. +7.2p.53. +8.6z.k. +e.5.6J. +h.q.6x. +0.i.6B. +0.t.6v. +0.u.6u. +0.w.6s. +1.2n.57. +1.6L.4. +2.24.5M. +4.e.6E. +5.1t.69. +5.c.6G. +6.5L.26. +7.1+.5Q. +8.6z.l. +e.6.6J. +0.A.6o. +0.j.6B. +0.u.6v. +0.v.6u. +0.x.6s. +1.6L.5. +1.r.6w. +2.24.5N. +2.6.6K. +4.f.6E. +5.1t.6a. +5.2q.4j. +6.58.2n. +6.5L.27. +7.2p.55. +8.6z.m. +9.22.5O. +9.k.6A. +e.7.6J. +0.A.6p. +0.B.6o. +0.g.6E. +0.k.6B. +0.v.6v. +0.y.6s. +1.6L.6. +1.s.6w. +2.26.5M. +2.2n.59. +2.7.6K. +4.w.6u. +5.2q.4k. +8.6z.n. +9.22.5P. +e.8.6J. +h.r.6x. +k.6N.0. +0.B.6p. +0.C.6o. +0.h.6E. +0.l.6B. +0.w.6v. +0.x.6u. +1.1t.6b. +1.6L.7. +2.26.5N. +2.2n.5a. +2.8.6K. +4.r.6y. +6.5L.28. +6.6s.z. +8.6z.o. +9.1+.5R. +9.24.5O. +e.9.6J. +f.2g.5u. +h.s.6x. +0.C.6p. +0.D.6o. +0.m.6B. +0.x.6v. +0.y.6u. +1.6L.8. +1.u.6w. +2.2n.5b. +2.9.6K. +4.i.6E. +5.2q.4m. +7.22.5Q. +8.58.2p. +8.6z.p. +9.24.5P. +9.k.6C. +9.s.6y. +e.a.6J. +h.t.6x. +i.c.6H. +0.6u.z. +0.D.6p. +0.E.6o. +0.y.6v. +1.6L.9. +2.1h.6d. +2.a.6K. +3.n.6B. +4.j.6E. +5.k.6D. +8.6z.q. +9.26.5O. +9.c.6I. +e.b.6J. +h.u.6x. +0.6v.z. +0.A.6s. +0.E.6p. +0.F.6o. +0.o.6B. +1.6L.a. +2.1h.6e. +2.b.6K. +4.k.6E. +5.2q.4o. +7.24.5Q. +9.26.5P. +9.u.6y. +e.0.6O. +h.v.6x. +i.c.6J. +0.B.6s. +0.F.6p. +0.p.6B. +1.2n.5c. +1.6L.b. +2.1V.5W. +4.l.6E. +5.2q.4p. +9.22.5R. +9.c.6K. +e.1.6O. +h.w.6x. +0.C.6s. +0.d.6H. +0.q.6B. +0.x.6x. +1.6L.c. +2.1+.5S. +2.1V.5X. +2.2f.5J. +2.2n.5d. +4.A.6u. +4.m.6E. +5.1h.6f. +5.k.6F. +7.26.5Q. +8.6z.r. +e.0.6P. +e.2.6O. +0.A.6v. +0.D.6s. +0.e.6H. +0.G.6o. +2.1V.5Y. +3.n.6E. +4.B.6u. +5.1h.6g. +5.2q.4q. +8.6z.s. +9.24.5R. +e.1.6P. +e.3.6O. +f.2n.5e. +h.y.6x. +0.B.6v. +0.d.6J. +0.E.6s. +0.f.6H. +0.G.6p. +0.H.6o. +2.1d.6k. +2.2f.5K. +2.2n.5f. +4.C.6u. +4.o.6E. +4.r.6A. +5.1V.5Z. +5.2q.4r. +8.6z.t. +e.2.6P. +e.4.6O. +h.z.6x. +0.C.6v. +0.e.6J. +0.g.6H. +0.H.6p. +0.p.6E. +0.r.6B. +1.2n.5g. +2.1V.5+. +3.I.6o. +4.D.6u. +5.1t.6c. +5.2q.4s. +5.k.6G. +6.6s.F. +8.6z.u. +9.26.5R. +9.s.6A. +e.3.6P. +e.5.6O. +0.D.6v. +0.f.6J. +0.h.6H. +0.s.6B. +1.2n.5h. +1.6L.d. +2.22.5S. +3.I.6p. +3.J.6o. +4.E.6u. +4.q.6E. +5.2q.4t. +7.2p.5e. +8.6z.v. +e.4.6P. +e.6.6O. +i.c.6M. +0.E.6v. +0.g.6J. +0.i.6H. +0.K.6o. +1.1h.6h. +1.6L.e. +2.2n.5i. +3.J.6p. +4.F.6u. +4.t.6B. +8.6z.w. +9.r.6C. +9.u.6A. +e.5.6P. +e.7.6O. +h.A.6x. +0.F.6v. +0.G.6s. +0.h.6J. +0.j.6H. +0.K.6p. +0.L.6o. +1.6L.f. +2.1d.6m. +2.24.5S. +2.2g.5x. +2.2n.5j. +4.u.6B. +5.2q.4u. +7.r.6D. +8.6z.x. +9.s.6C. +e.6.6P. +e.8.6O. +h.B.6x. +k.6N.c. +0.H.6s. +0.i.6J. +0.k.6H. +0.L.6p. +0.r.6E. +1.6L.g. +3.M.6o. +4.v.6B. +5.2q.4v. +7.1+.5T. +7.s.6D. +8.6z.y. +e.7.6P. +e.9.6O. +h.C.6x. +0.d.6M. +0.G.6u. +0.l.6H. +0.s.6E. +0.w.6B. +1.1h.6i. +1.2n.5k. +1.6L.h. +2.0.6Q. +2.1V.5/. +2.26.5S. +2.2g.5y. +3.I.6s. +3.M.6p. +4.j.6J. +5.2q.4w. +8.6z.z. +9.k.6I. +9.u.6C. +e.8.6P. +e.a.6O. +h.D.6x. +0.e.6M. +0.G.6v. +0.k.6J. +0.m.6H. +0.t.6E. +1.1h.6j. +1.6L.i. +2.1.6Q. +2.1V.60. +2.2g.5z. +3.6R.0. +3.J.6s. +4.H.6u. +4.x.6B. +7.r.6F. +7.u.6D. +e.9.6P. +e.b.6O. +h.E.6x. +0.f.6M. +0.H.6v. +0.K.6s. +0.l.6J. +0.N.6o. +0.u.6E. +1.6L.j. +2.1h.6k. +2.2.6Q. +3.6R.1. +3.I.6u. +3.n.6H. +4.y.6B. +5.1V.61. +5.2q.4x. +7.s.6F. +9.k.6K. +e.0.6S. +e.a.6P. +h.F.6x. +i.c.6O. +0.g.6M. +0.L.6s. +0.m.6J. +0.N.6p. +0.o.6H. +0.O.6o. +0.v.6E. +1.1h.6l. +1.6L.k. +2.1t.6d. +2.2g.5A. +2.2n.5l. +2.3.6Q. +3.6R.2. +3.I.6v. +3.J.6u. +5.1V.62. +5.2q.4y. +6.5L.29. +6.6B.z. +7.22.5T. +8.6z.A. +e.1.6S. +e.b.6P. +0.h.6M. +0.O.6p. +0.p.6H. +0.P.6o. +0.w.6E. +1.6L.l. +2.1t.6e. +2.4.6Q. +3.6R.3. +3.J.6v. +3.M.6s. +4.K.6u. +4.r.6G. +4.u.6F. +5.2q.4z. +6.5L.2a. +8.6z.B. +e.2.6S. +e.n.6J. +i.c.6P. +0.i.6M. +0.K.6v. +0.L.6u. +0.o.6J. +0.P.6p. +0.q.6H. +0.Q.6o. +0.x.6E. +1.6L.m. +2.29.5M. +2.5.6Q. +4.s.6G. +5.2q.4A. +7.24.5T. +8.6z.C. +e.3.6S. +e.4.6R. +h.G.6x. +0.A.6B. +0.d.6O. +0.j.6M. +0.L.6v. +0.p.6J. +0.Q.6p. +0.R.6o. +0.y.6E. +1.2n.5n. +1.6L.n. +2.1h.6m. +2.1V.63. +2.29.5N. +2.6.6Q. +3.6R.5. +3.M.6u. +5.1t.6f. +5.2q.4B. +8.6z.D. +e.4.6S. +h.H.6x. +0.B.6B. +0.e.6O. +0.k.6M. +0.N.6s. +0.R.6p. +1.2n.5o. +1.6L.o. +2.2g.5C. +2.7.6Q. +3.6R.6. +3.M.6v. +3.S.6o. +4.q.6J. +5.1t.6g. +5.2q.4C. +6.5L.2c. +6.6E.z. +7.26.5T. +7.u.6G. +8.6z.E. +e.5.6S. +h.I.6x. +0.C.6B. +0.d.6P. +0.f.6O. +0.l.6M. +0.O.6s. +0.r.6H. +1.5s.2h. +1.6L.p. +2.2g.5D. +2.2n.5p. +2.8.6Q. +3.S.6p. +5.29.5O. +5.2q.4D. +7.1+.5U. +8.6z.F. +c.T.6o. +e.6.6S. +e.7.6R. +h.J.6x. +0.D.6B. +0.e.6P. +0.g.6O. +0.m.6M. +0.P.6s. +0.s.6H. +0.U.6o. +1.2n.5q. +1.5s.2i. +1.6L.q. +2.1V.64. +2.9.6Q. +2.k.6N. +3.6R.8. +4.N.6u. +5.29.5P. +9.r.6I. +c.T.6p. +e.7.6S. +h.K.6x. +0.E.6B. +0.f.6P. +0.h.6O. +0.N.6v. +0.Q.6s. +0.r.6J. +0.t.6H. +0.U.6p. +0.V.6o. +1.5s.2j. +2.a.6Q. +3.n.6M. +4.A.6E. +4.O.6u. +5.2q.4F. +6.5L.2d. +7.1+.5V. +9.s.6I. +e.8.6S. +e.9.6R. +h.L.6x. +j.2a.5P. +0.F.6B. +0.g.6P. +0.i.6O. +0.o.6M. +0.O.6v. +0.R.6s. +0.s.6J. +0.u.6H. +0.V.6p. +0.W.6o. +1.1t.6h. +1.5s.2k. +3.6R.a. +3.M.6x. +4.B.6E. +4.P.6u. +5.1V.65. +5.29.5Q. +5.2q.4G. +8.6z.G. +9.r.6K. +e.9.6S. +f.2g.5F. +0.h.6P. +0.j.6O. +0.p.6M. +0.P.6v. +0.t.6J. +0.v.6H. +0.W.6p. +0.X.6o. +1.5s.2l. +1.6L.r. +3.6R.b. +4.C.6E. +4.Q.6u. +4.S.6s. +4.u.6I. +5.c.6Q. +7.22.5U. +8.6z.H. +9.s.6K. +e.a.6S. +f.2g.5G. +0.i.6P. +0.k.6O. +0.q.6M. +0.Q.6v. +0.u.6J. +0.w.6H. +0.X.6p. +1.2n.5r. +1.6L.s. +2.1V.66. +3.6R.c. +4.D.6E. +4.R.6u. +8.6z.I. +c.T.6s. +c.Y.6o. +e.b.6S. +0.j.6P. +0.l.6O. +0.R.6v. +0.U.6s. +0.v.6J. +0.x.6H. +0.Z.6o. +1.1t.6i. +1.6L.t. +2.1+.5W. +3.S.6u. +4.E.6E. +4.G.6B. +5.1V.67. +5.29.5R. +5.2q.4H. +6.5s.2n. +7.22.5V. +7.24.5U. +8.6z.J. +9.u.6K. +c.Y.6p. +h.N.6x. +0.H.6B. +0.k.6P. +0.m.6O. +0.V.6s. +0.w.6J. +0.y.6H. +0.Z.6p. +1.1t.6j. +1.6L.u. +2.1+.5X. +2.1V.68. +3.S.6v. +4.F.6E. +5.2q.4I. +5.c.6T. +8.6z.K. +c.T.6u. +h.O.6x. +0.l.6P. +0.r.6M. +0.W.6s. +0.x.6J. +0.z.6H. +1.6L.v. +2.1+.5Y. +2.1t.6k. +2.2f.5M. +3.I.6B. +3.n.6O. +4.U.6u. +5.2q.4J. +7.24.5V. +7.26.5U. +8.6z.L. +c.T.6v. +h.P.6x. +0.m.6P. +0.o.6O. +0.s.6M. +0.U.6v. +0.X.6s. +0.y.6J. +1.1t.6l. +1.6L.w. +2.2f.5N. +3.6R.d. +3.6V.8. +3.J.6B. +4.V.6u. +5.2q.4K. +7.1+.5Z. +7.2p.5s. +8.6z.M. +h.Q.6x. +0.+.6o. +0.G.6E. +0.K.6B. +0.p.6O. +0.t.6M. +0.V.6v. +1.6L.x. +2.1+.5+. +2.22.5W. +3.6R.e. +3.n.6P. +4.6J.z. +4.W.6u. +5.1V.69. +5.2q.4L. +7.26.5V. +9./.6n. +c.Y.6s. +h.R.6x. +k.6N.r. +0./.6o. +0.+.6p. +0.A.6H. +0.H.6E. +0.L.6B. +0.o.6P. +0.q.6O. +0.u.6M. +0.W.6v. +1.2f.5O. +1.6L.y. +2.22.5X. +2.29.5S. +2.2n.5t. +3.6R.f. +3.6V.a. +3.S.6x. +4.X.6u. +5.1V.6a. +5.2q.4M. +6.6s.Z. +k.6N.s. +0./.6p. +0.10.6o. +0.B.6H. +0.p.6P. +0.v.6M. +0.X.6v. +1.2f.5P. +1.6L.z. +2.1t.6m. +2.22.5Y. +2.24.5W. +3.6R.g. +3.6V.b. +3.I.6E. +3.M.6B. +5.2q.4N. +5.c.6U. +8.6z.N. +c.T.6x. +c.Y.6u. +0.10.6p. +0.11.6o. +0.A.6J. +0.C.6H. +0.q.6P. +0.w.6M. +0.Z.6u. +1./.6q. +1.1V.6b. +2.24.5X. +2.u.6N. +3.6R.h. +3.6V.c. +3.J.6E. +5.2q.4O. +7.22.5Z. +8.6z.O. +c.Y.6v. +h.U.6x. +0.11.6p. +0.B.6J. +0.D.6H. +0.K.6E. +0.r.6O. +0.x.6M. +0.Z.6v. +1.2f.5Q. +2.22.5+. +2.24.5Y. +2.26.5W. +3.6R.i. +4.12.6o. +5.c.6W. +8.6z.P. +h.V.6x. +0.+.6s. +0.5u.2n. +0.C.6J. +0.E.6H. +0.L.6E. +0.N.6B. +0.s.6O. +0.y.6M. +1.6L.A. +2.1+.5/. +2.26.5X. +3.6R.j. +4.12.6p. +5.2q.4Q. +5.c.6X. +5.k.6Q. +7.24.5Z. +8.6z.Q. +9./.6r. +h.W.6x. +0./.6s. +0.D.6J. +0.F.6H. +0.O.6B. +0.r.6P. +0.t.6O. +1.6L.B. +2.1+.60. +2.24.5+. +2.26.5Y. +3.6R.k. +3.M.6E. +5.2q.4R. +5.c.6Y. +6.6M.z. +8.6z.R. +h.X.6x. +0.+.6u. +0.10.6s. +0.13.6o. +0.E.6J. +0.P.6B. +0.s.6P. +0.u.6O. +1.2f.5R. +1.6L.C. +3.6R.l. +3.6V.d. +5.2q.4S. +5.c.6Z. +7.1+.61. +7.26.5Z. +8.6z.S. +9./.6t. +c.Y.6x. +0.+.6v. +0.11.6s. +0.13.6p. +0.14.6o. +0.F.6J. +0.Q.6B. +0.t.6P. +0.v.6O. +1.6L.D. +2.26.5+. +3.6R.m. +3.6V.e. +4./.6u. +5.29.5T. +5.k.6T. +7.1+.62. +7.2p.5u. +8.6z.T. +h.Z.6x. +0./.6v. +0.14.6p. +0.15.6o. +0.A.6M. +0.G.6H. +0.R.6B. +0.u.6P. +0.w.6O. +1.6L.E. +2.22.5/. +3.6V.f. +3.n.6R. +4.10.6u. +4.12.6s. +4.N.6E. +8.6z.U. +0.10.6v. +0.15.6p. +0.16.6o. +0.B.6M. +0.H.6H. +0.v.6P. +0.x.6O. +1.6L.F. +2.22.60. +3.6R.o. +3.6V.g. +3.S.6B. +4.11.6u. +4.O.6E. +8.6z.V. +b.6+.c. +0.11.6v. +0.16.6p. +0.C.6M. +0.G.6J. +0.w.6P. +0.y.6O. +2.1+.63. +2.24.5/. +2.A.6N. +3.6R.p. +3.6V.h. +3.I.6H. +4.12.6u. +4.P.6E. +5.1V.6c. +5.2q.4V. +5.c.6/. +7.22.61. +8.6z.W. +c.T.6B. +n.17.6o. +0.12.6v. +0.13.6s. +0.18.6o. +0.D.6M. +0.H.6J. +0.U.6B. +0.x.6P. +1./.6w. +2.24.60. +2.2f.5S. +3.6R.q. +3.6V.i. +3.J.6H. +4.Q.6E. +5.c.70. +6.6O.z. +7.22.62. +8.6z.X. +h.+.6x. +n.17.6p. +0.14.6s. +0.18.6p. +0.19.6o. +0.E.6M. +0.K.6H. +0.R.6E. +0.V.6B. +0.y.6P. +1.6L.G. +2.26.5/. +3.6V.j. +3.I.6J. +5.c.71. +5.k.6U. +7.24.61. +8.6z.Y. +h./.6x. +0.15.6s. +0.19.6p. +0.1a.6o. +0.6P.z. +0.F.6M. +0.L.6H. +0.W.6B. +1.6L.H. +2.1+.64. +2.26.60. +2.2n.5v. +3.6V.k. +3.J.6J. +3.S.6E. +4./.6y. +4.13.6u. +5.c.72. +7.24.62. +7.r.6Q. +8.6z.Z. +b.6+.d. +h.10.6x. +0.13.6v. +0.16.6s. +0.1a.6p. +0.1b.6o. +0.A.6O. +0.K.6J. +0.X.6B. +1.6L.I. +2.22.63. +2.2n.5w. +3.6R.r. +3.6V.l. +3.M.6H. +4.14.6u. +5.2q.4X. +5.c.73. +5.k.6W. +7.26.61. +7.s.6Q. +b.6+.e. +c.T.6E. +h.11.6x. +0.14.6v. +0.17.6s. +0.1b.6p. +0.1c.6o. +0.B.6O. +0.L.6J. +1.2n.5x. +1.6L.J. +3.6R.s. +3.6V.m. +4.15.6u. +4.U.6E. +5.2q.4Y. +5.c.74. +5.k.6X. +7.1+.65. +7.26.62. +b.6+.f. +c.Y.6B. +h.12.6x. +0.15.6v. +0.18.6s. +0.1c.6p. +0.A.6P. +0.C.6O. +0.G.6M. +1.6L.K. +2.24.63. +3.6R.t. +3.M.6J. +3.n.6V. +4.16.6u. +4.V.6E. +4.Z.6B. +5.29.5U. +5.c.75. +5.k.6Y. +7.r.6T. +7.u.6Q. +b.6+.g. +d.71.d. +0.16.6v. +0.19.6s. +0.1d.6o. +0.B.6P. +0.D.6O. +0.H.6M. +0.N.6H. +0.W.6E. +1.2n.5y. +1.6L.L. +2.1+.66. +2.22.64. +3.6R.u. +3.6V.o. +4.17.6u. +5.c.76. +5.k.6Z. +7.s.6T. +8.6z.+. +b.6+.h. +d.71.e. +0.17.6v. +0.1a.6s. +0.1d.6p. +0.C.6P. +0.E.6O. +0.O.6H. +0.X.6E. +1.2f.5T. +1.2n.5z. +1.6L.M. +2.1V.6d. +2.26.63. +3.6R.v. +3.6V.p. +3.I.6M. +4.18.6u. +5.29.5V. +5.c.77. +7.1+.67. +8.6z./. +b.6+.i. +d.71.f. +h.13.6x. +0.18.6v. +0.19.6u. +0.1b.6s. +0.D.6P. +0.F.6O. +0.N.6J. +0.P.6H. +2.1+.68. +2.1V.6e. +2.24.64. +3.6R.w. +3.6V.q. +3.J.6M. +5.c.78. +7.22.65. +7.u.6T. +8.6z.10. +b.6+.j. +c.Y.6E. +d.71.g. +h.14.6x. +0.+.6B. +0.19.6v. +0.1c.6s. +0.1e.6o. +0.E.6P. +0.K.6M. +0.O.6J. +0.Q.6H. +0.Z.6E. +1.2n.5A. +3.6R.x. +4./.6A. +4.1a.6u. +5.c.79. +8.6z.11. +b.6+.k. +d.71.h. +h.15.6x. +0./.6B. +0.1a.6v. +0.1e.6p. +0.1f.6o. +0.F.6P. +0.L.6M. +0.P.6J. +0.R.6H. +1.6L.N. +2.22.66. +2.26.64. +3.6R.y. +4.1b.6u. +5.1V.6f. +5.2q.4+. +5.c.7a. +5.k.6/. +7.24.65. +7.r.6U. +8.6z.12. +b.6+.l. +d.71.i. +h.16.6x. +0.10.6B. +0.1b.6v. +0.1d.6s. +0.1f.6p. +0.G.6O. +0.Q.6J. +1.6L.O. +2.29.5W. +3.6R.z. +3.6V.r. +3.M.6M. +3.S.6H. +4.1c.6u. +4.s.6U. +5.1V.6g. +5.2q.4/. +5.c.7b. +5.k.70. +6.6o.1g. +7.1+.69. +7.22.67. +b.6+.m. +d.71.j. +h.17.6x. +0.11.6B. +0.1c.6v. +0.H.6O. +0.R.6J. +1.6L.P. +2.22.68. +2.24.66. +2.29.5X. +3.6V.s. +5.2q.50. +5.c.7c. +6.6p.1g. +7.1+.6a. +7.26.65. +7.r.6W. +9./.6C. +b.6+.n. +c.T.6H. +d.71.k. +h.18.6x. +0.+.6E. +0.G.6P. +0.U.6H. +1.2n.5C. +1.6L.Q. +2.29.5Y. +3.6V.t. +3.I.6O. +3.S.6J. +4.12.6B. +4.1d.6u. +4.r.6X. +5./.6D. +5.1h.6n. +5.2q.51. +5.c.7d. +5.k.72. +7.24.67. +7.s.6W. +7.u.6U. +8.6z.13. +b.6+.o. +d.71.l. +h.19.6x. +0.1d.6v. +0.1h.6o. 0.2w.2w. -e.2x.2w. -9.2y.2w. -3.2z.2w. -d.2A.2w. -9.2B.2w. +0.H.6P. +0.N.6M. +0.V.6H. +1.1+.6b. +1.2n.5D. +1.6L.R. +2.24.68. +2.26.66. +3.6R.A. +3.6V.u. +3.J.6O. +4./.6E. +5.29.5Z. +5.2q.52. +5.c.7e. +5.k.73. +6.6s.1e. +7.r.6Y. +7.s.6X. +8.6z.14. +b.6+.p. +c.T.6J. +d.71.m. +h.1a.6x. +0.1h.6p. +0.K.6O. +0.O.6M. +0.U.6J. +0.W.6H. +1.1V.6h. +1.6L.S. +2.29.5+. +3.6R.B. +3.6V.v. +3.I.6P. +4.10.6E. +4.1i.6o. +4.s.6Y. +4.u.6W. +5.2q.53. +5.c.7f. +5.k.74. +6.6s.1f. +7.22.69. +7.26.67. +7.r.6Z. +8.6z.15. +b.6+.q. +d.71.n. +h.1b.6x. +0.13.6B. +0.1i.6p. +0.1j.6o. +0.L.6O. +0.P.6M. +0.V.6J. +0.X.6H. +1.1h.6q. +1.2f.5U. +1.6L.T. +2.26.68. +2.2n.5E. +3.6R.C. +3.6V.w. +3.J.6P. +4.11.6E. +4.1e.6u. +4.u.6X. +5./.6F. +5.2q.54. +5.c.7g. +5.k.75. +6.6s.1g. +7.22.6a. +7.s.6Z. +8.6z.16. +d.71.o. +h.1c.6x. +0.14.6B. +0.1e.6v. +0.1j.6p. +0.K.6P. +0.Q.6M. +0.W.6J. +1.6L.U. +3.6R.D. +3.6V.x. +3.M.6O. +4.12.6E. +4.1f.6u. +4.u.6Y. +5.2q.55. +5.c.7h. +5.k.76. +7.24.69. +8.6z.17. +b.5F.2n. +c.Y.6H. +d.71.p. +0.15.6B. +0.1f.6v. +0.6u.1g. +0.L.6P. +0.R.6M. +0.X.6J. +0.Z.6H. +1.1V.6i. +1.22.6b. +1.2f.5V. +1.6L.V. +3.2x.2w. +3.5G.2n. +3.6R.E. +3.6V.y. +4.u.6Z. +5.1h.6r. +5.k.77. +7.24.6a. +8.6z.18. +b.6+.r. +d.71.q. +h.1d.6x. +0.16.6B. +0.1k.6o. +0.6v.1g. +1.1V.6j. +1.6L.W. +3.6R.F. +3.6V.z. +3.M.6P. +3.S.6M. +5./.6G. +5.2q.57. +5.c.7i. +5.k.78. +6.6s.1h. +7.26.69. +7.r.6/. +8.6z.19. +b.6+.s. +c.Y.6J. +0.13.6E. +0.17.6B. +0.1k.6p. +0.N.6O. +0.Z.6J. +1.24.6b. +1.6L.X. +2.1V.6k. +2.29.5/. +2.c.7j. +3.1l.6o. +5.1h.6t. +5.k.79. +6.6s.1i. +7.26.6a. +7.r.70. +7.s.6/. +8.58.2q. +8.6z.1a. +b.2p.5F. +b.6+.t. +c.T.6M. +0.18.6B. +0.1j.6s. +0.1m.6o. +0.O.6O. +0.U.6M. +1.1V.6l. +1.6L.Y. +2.29.60. +2.2n.5H. +3.1l.6p. +4.14.6E. +4.1h.6u. +4.s.70. +5.c.7k. +5.k.7a. +7.2p.5G. +8.6z.1b. +b.6+.u. +d.71.r. +h.1e.6x. +0.+.6H. +0.19.6B. +0.1h.6v. +0.1m.6p. +0.1n.6o. +0.N.6P. +0.P.6O. +0.V.6M. +1.26.6b. +1.2n.5I. +1.6L.Z. +2.2f.5W. +3.2x.2x. +3.2y.2w. +3.6R.G. +3.6V.A. +4.15.6E. +4.1i.6u. +5.29.61. +5.c.7l. +5.k.7b. +7.1+.6c. +7.r.72. +7.u.6/. +8.6z.1c. +b.6+.v. +d.71.s. +h.1f.6x. +j.2a.60. +0./.6H. +0.1a.6B. +0.1i.6v. +0.1j.6u. +0.1n.6p. +0.1o.6o. +0.O.6P. +0.Q.6O. +0.W.6M. +2.2f.5X. +3.6R.H. +3.6V.B. +4.16.6E. +5.29.62. +5.c.7m. +5.k.7c. +7.r.73. +7.s.72. +7.u.70. +8.2z.2w. +b.6+.w. +d.71.t. +f.2g.5L. +h.1g.6x. +0.+.6J. +0.10.6H. +0.1b.6B. +0.1j.6v. +0.1k.6s. +0.1o.6p. +0.1p.6o. +0.P.6P. +0.R.6O. +0.X.6M. +2.1V.6m. +2.2f.5Y. +3.6V.C. +3.I.6R. +4.17.6E. +5.c.7n. +5.k.7d. +7.r.74. +7.s.73. +8.2A.2w. +8.6z.1d. +9./.6I. +b.6+.x. +d.71.u. +0./.6J. +0.11.6H. +0.1c.6B. +0.1p.6p. +0.1q.6o. +0.Q.6P. +1.1h.6w. +1.2f.5Z. +3.1l.6s. +3.2B.2w. +3.6V.D. +3.J.6R. +3.S.6O. +4.18.6E. +4.s.74. +5.c.7o. +5.k.7e. +7.r.75. +7.u.72. +b.6+.y. +c.Y.6M. +d.71.v. +0.10.6J. +0.19.6E. +0.1m.6s. +0.1q.6p. +0.1r.6o. 0.2C.2w. -d.2D.2w. -d.2E.2w. -0.2F.2w. -0.2G.2w. -9.2H.2w. -d.2I.2w. -d.2J.2w. -0.2K.2w. -c.2M.2w. -9.2N.2w. -2.2O.2w. -9.2Q.2w. -7.2S.2w. -d.2T.2w. -1.2U.2w. -9.2V.2w. -1.2W.2w. -0.2X.2w. -b.2+.2w. -d.30.2w. -0.2w.35. -b.36.2w. -0.2w.37. -0.38.2w. -5.39.2w. -5.3d.2w. -0.3h.2w. -5.3i.2w. -0.2w.3m. -5.3p.2w. -a.3v.2w. -a.3y.2w. -2.3z.2w. -5.3A.2w. -2.3B.2w. -5.3C.2w. -a.3F.2w. -5.3G.2w. -0.2w.3M. -0.2w.3Q. -5.40.2w. -0.2w.47. -5.4d.2w. -0.2w.4O. -3.4Q.2w. -7.4T.2w. -0.2w.4V. -5.51.2w. -0.52.2w. -a.5a.2w. -2.5b.2w. -5.5d.2w. -5.5B.2w. -5.5C.2w. -5.5F.2w. -0.5H.2w. -0.5I.2w. -f.2w.5K. -7.5M.2w. -5.5O.2w. -5.5R.2w. -0.2w.5U. -3.5W.2w. -1.5Y.2w. -5.5Z.2w. -5.5/.2w. -0.60.2w. -2.62.2w. -2.66.2w. -k.6b.2w. -c.6e.2w. -0.2w.6F. -0.6M.2w. -0.6+.2w. -d.71.2w. -5.79.2w. -0.2w.7D. -d.2p.2w. -d.2p.2w. -e.2x.2x. -e.2x.2y. -e.2x.2z. -e.2x.2A. -e.2x.2B. -e.2x.2C. -e.2x.2D. -e.2x.2E. -e.2x.2F. -e.2x.2G. -e.2x.2H. -e.2x.2I. -e.2x.2J. -e.2x.2K. -c.2M.2x. -e.2x.2N. -e.2x.2O. -e.2x.2Q. -7.2S.2x. -e.2x.2T. -1.2U.2x. -e.2x.2V. -1.2W.2x. -e.2x.2X. -e.2x.2+. -e.2x.30. -e.2x.35. -e.2x.36. -e.2x.37. -e.2x.38. -e.2x.39. -e.2x.3d. -e.2x.3h. -e.2x.3i. -e.2x.3m. -e.2x.3p. -a.3v.2x. -a.3y.2x. -e.2x.3z. -e.2x.3A. -e.2x.3B. -e.2x.3C. -a.3F.2x. -e.2x.3G. -e.2x.3M. -e.2x.3Q. -e.2x.40. -e.2x.47. -e.2x.4d. -e.2x.4O. -e.2x.4Q. -7.4T.2x. -e.2x.4V. -e.2x.51. -e.2x.52. -e.2x.5b. -e.2x.5d. -e.2x.5B. -e.2x.5C. -e.2x.5F. -e.2x.5H. -e.2x.5I. -f.2x.5K. -7.5M.2x. -e.2x.5O. -e.2x.5R. -e.2x.5U. -e.2x.5W. -1.5Y.2x. -e.2x.5Z. -e.2x.5/. -e.2x.60. -e.2x.62. -e.2x.66. -a.6b.2x. -c.6e.2x. -e.2x.6F. -e.2x.6M. -e.2x.6+. -e.2x.71. -e.2x.79. -e.2x.7D. -e.2x.2p. -e.2x.2p. -9.2y.2y. -9.2y.2z. -9.2y.2A. -9.2y.2B. -9.2y.2C. -9.2y.2D. -9.2y.2E. -9.2y.2F. -9.2y.2G. -9.2H.2y. -9.2y.2I. -9.2y.2J. -9.2y.2K. -c.2M.2y. -9.2N.2y. -2.2O.2y. -9.2Q.2y. -7.2S.2y. -9.2y.2T. -1.2U.2y. -9.2V.2y. -1.2W.2y. -9.2y.2X. -9.2y.2+. -9.2y.30. -9.2y.35. -9.2y.36. -9.2y.37. -9.2y.38. -9.2y.39. -9.2y.3d. -9.2y.3h. -9.2y.3i. -9.2y.3m. -9.2y.3p. -a.3v.2y. -a.3y.2y. -2.3z.2y. -9.2y.3A. -2.3B.2y. -9.2y.3C. -a.3F.2y. -5.3G.2y. -9.2y.3M. -9.2y.3Q. -5.40.2y. -9.2y.47. -9.2y.4d. -9.2y.4O. -9.2y.4Q. -7.4T.2y. -9.2y.4V. -9.2y.51. -9.2y.52. -a.5a.2y. -2.5b.2y. -5.5d.2y. -9.2y.5B. -9.2y.5C. -9.2y.5F. -9.2y.5H. -9.2y.5I. -9.2y.5K. -7.5M.2y. -9.2y.5O. -9.2y.5R. -9.2y.5U. -9.2y.5W. -1.5Y.2y. -9.2y.5Z. -9.2y.5/. -9.2y.60. -2.62.2y. -2.66.2y. -a.6b.2y. -c.6e.2y. -9.2y.6F. -9.2y.6M. -9.2y.6+. -9.2y.71. -9.2y.79. -9.2y.7D. -9.2y.2p. -9.2y.2p. -0.2z.2z. -0.2A.2z. -9.2B.2z. -0.2z.2C. -0.2D.2z. -0.2E.2z. -0.2F.2z. -0.2G.2z. -9.2H.2z. -0.2I.2z. -0.2J.2z. -0.2z.2K. -c.2M.2z. -9.2N.2z. -2.2O.2z. -9.2Q.2z. -7.2S.2z. -0.2T.2z. -1.2U.2z. -9.2V.2z. -1.2W.2z. -0.2z.2X. -b.2+.2z. -d.30.2z. -3.2z.35. -6.36.2z. -0.2z.37. -0.2z.38. -5.39.2z. -5.3d.2z. -3.2z.3h. -5.3i.2z. -0.2z.3m. -5.3p.2z. -a.3v.2z. -a.3y.2z. -2.3z.2z. -5.3A.2z. -2.3B.2z. -0.2z.3C. -a.3F.2z. -5.3G.2z. -0.2z.3M. -3.2z.3Q. -5.40.2z. -0.2z.47. -0.2z.4d. -0.2z.4O. -0.2z.4Q. -5.4T.2z. -0.2z.4V. -3.2z.51. -0.2z.52. -a.5a.2z. -2.5b.2z. -5.5d.2z. -0.2z.5B. -0.2z.5C. -5.5F.2z. -0.2z.5H. -0.2z.5I. -f.2z.5K. -7.5M.2z. -3.2z.5O. -3.2z.5R. -0.2z.5U. -0.2z.5W. -1.5Y.2z. -0.2z.5Z. -0.2z.5/. -0.2z.60. -2.62.2z. -2.66.2z. -a.6b.2z. -c.6e.2z. -0.2z.6F. -0.6M.2z. -0.2z.6+. -3.2z.71. -5.79.2z. -0.2z.7D. -0.2p.2z. -0.2p.2z. -d.2A.2A. -9.2B.2A. -0.2A.2C. -0.2D.2A. -0.2A.2E. -0.2F.2A. -0.2A.2G. -9.2H.2A. -0.2I.2A. -d.2A.2J. -0.2A.2K. -c.2M.2A. -9.2N.2A. -2.2O.2A. -9.2Q.2A. -7.2S.2A. -0.2A.2T. -1.2U.2A. -9.2V.2A. -1.2W.2A. -0.2A.2X. -d.2+.2A. -0.2A.30. -d.2A.35. -b.36.2A. -0.2A.37. -0.2A.38. -5.39.2A. -5.3d.2A. -d.2A.3h. -5.3i.2A. -0.2A.3m. -0.2A.3p. -a.3v.2A. -a.3y.2A. -2.3z.2A. -5.3A.2A. -2.3B.2A. -0.2A.3C. -a.3F.2A. -5.3G.2A. -0.2A.3M. -d.2A.3Q. -5.40.2A. -0.2A.47. -0.2A.4d. -0.2A.4O. -0.2A.4Q. -5.4T.2A. -0.2A.4V. -d.2A.51. -0.2A.52. -a.5a.2A. -2.5b.2A. -5.5d.2A. -0.2A.5B. -0.2A.5C. -5.5F.2A. -0.2A.5H. -0.2A.5I. -f.2A.5K. -7.5M.2A. -d.2A.5O. -0.2A.5R. -0.2A.5U. -0.2A.5W. -1.5Y.2A. -0.2A.5Z. -d.2A.5/. -0.2A.60. -2.62.2A. -2.66.2A. -a.6b.2A. -c.6e.2A. -0.2A.6F. -0.6M.2A. -d.2A.6+. -d.2A.71. -5.79.2A. -0.2A.7D. -0.2A.2p. -0.2A.2p. -9.2B.2B. -9.2B.2C. -9.2B.2D. -9.2B.2E. -9.2B.2F. -9.2B.2G. -9.2H.2B. -9.2B.2I. -9.2B.2J. -9.2B.2K. -c.2M.2B. -9.2N.2B. -2.2O.2B. -9.2Q.2B. -7.2S.2B. -9.2B.2T. -1.2U.2B. -9.2V.2B. -1.2W.2B. -9.2B.2X. -9.2B.2+. -9.2B.30. -9.2B.35. -9.2B.36. -9.2B.37. -9.2B.38. -9.2B.39. -9.2B.3d. -9.2B.3h. -9.2B.3i. -9.2B.3m. -9.2B.3p. -a.3v.2B. -a.3y.2B. -2.3z.2B. -9.2B.3A. -2.3B.2B. -9.2B.3C. -a.3F.2B. -5.3G.2B. -9.2B.3M. -9.2B.3Q. -5.40.2B. -9.2B.47. -9.2B.4d. -9.2B.4O. -9.2B.4Q. -7.4T.2B. -9.2B.4V. -9.2B.51. -9.2B.52. -a.5a.2B. -2.5b.2B. -5.5d.2B. -9.2B.5B. -9.2B.5C. -9.2B.5F. -9.2B.5H. -9.2B.5I. -9.2B.5K. -7.5M.2B. -9.2B.5O. -9.2B.5R. -9.2B.5U. -9.2B.5W. -1.5Y.2B. -9.2B.5Z. -9.2B.5/. -9.2B.60. -2.62.2B. -2.66.2B. -a.6b.2B. -c.6e.2B. -9.2B.6F. -9.2B.6M. -9.2B.6+. -9.2B.71. -9.2B.79. -9.2B.7D. -9.2B.2p. -9.2B.2p. +0.R.6P. +0.Z.6M. +1.6L.+. +2.29.63. +2.2f.5+. +3.2y.2x. +3.6R.K. +3.6V.E. +4.12.6H. +4.1k.6u. +5.2q.5c. +5.k.7f. +7.22.6c. +7.r.76. +7.s.75. +7.u.73. +9./.6K. +b.6+.z. +c.T.6O. +d.71.w. +h.1h.6x. +0.11.6J. +0.1d.6B. +0.1k.6v. +0.1n.6s. +0.1r.6p. +0.1s.6o. +0.U.6O. +1.6L./. +3.1l.6u. +3.6R.L. +3.6V.F. +3.S.6P. +4.1a.6E. +4.1t.6n. +4.s.76. +5.1h.6y. +5.k.7g. +7.r.77. +7.u.74. +8.2z.2x. +8.6z.1e. +d.71.x. +h.1i.6x. +0.1o.6s. +0.1s.6p. +0.1t.6o. +0.V.6O. +1.6L.10. +2.2g.5O. +3.1l.6v. +3.M.6R. +4.12.6J. +4.1b.6E. +4.1m.6u. +4.r.78. +4.s.77. +4.T.6P. +4.u.75. +5.2q.5e. +5.c.7p. +5.k.7h. +7.24.6c. +8.2A.2x. +8.6z.1f. +d.71.y. +h.1j.6x. +0.13.6H. +0.1m.6v. +0.1p.6s. +0.1t.6p. +0.U.6P. +0.W.6O. +1.6L.11. +2.29.64. +2.2g.5P. +3.1u.6o. +3.2B.2x. +4.1c.6E. +4.1n.6u. +4.r.79. +5.c.7q. +7.s.78. +7.u.76. +8.6z.1g. +b.6+.A. +d.71.z. +f.2w.2D. +0.+.6M. +0.14.6H. +0.1e.6B. +0.1n.6v. +0.1o.6u. +0.1q.6s. +0.1v.6o. +0.V.6P. +0.X.6O. +1.1t.6q. +1.6L.12. +2.1+.6d. +2.A.6/. +3.1u.6p. +3.2x.2C. +3.2y.2y. +3.6V.G. +4.r.7a. +4.s.79. +4.u.77. +5.2q.5g. +5.c.7r. +5.k.7i. +7.26.6c. +b.6+.B. +g.2E.2w. +0./.6M. +0.13.6J. +0.15.6H. +0.1f.6B. +0.1o.6v. +0.1r.6s. +0.1v.6p. +0.1w.6o. +0.W.6P. +2.1+.6e. +2.2f.5/. +2.k.7j. +3.6R.N. +3.6V.H. +4.1d.6E. +4.1p.6u. +4.r.7b. +4.s.7a. +5.29.65. +7.u.78. +8.2z.2y. +b.6+.C. +c.Y.6O. +h.1k.6x. +0.10.6M. +0.16.6H. +0.1p.6v. +0.1s.6s. +0.1w.6p. +0.1x.6o. +0.X.6P. +0.Z.6O. +2.2f.60. +3.6R.O. +3.I.6V. +4.14.6J. +4.1q.6u. +4.1t.6r. +4.r.7c. +4.s.7b. +4.u.79. +5.c.7s. +5.k.7k. +6.6B.1g. +8.2A.2y. +8.2z.2z. +8.6z.1h. +b.6+.D. +d.71.A. +h.1l.6x. +0.11.6M. +0.17.6H. +0.1q.6v. +0.1t.6s. +0.1x.6p. +0.1y.6o. +1.2f.61. +1.6L.13. +2./.6N. +2.29.66. +2.A.72. +3.2B.2y. +3.2x.2D. +3.6R.P. +3.J.6V. +4.15.6J. +4.1r.6u. +4.r.7d. +4.s.7c. +4.u.7a. +5.k.7l. +8.2A.2z. +8.6z.1i. +b.6+.E. +c.Y.6P. +d.71.B. +e.0.7A. +f.1+.6f. +h.1m.6x. +i.c.7t. +0.16.6J. +0.18.6H. +0.1r.6v. +0.1y.6p. +0.1z.6o. +0.Z.6P. +1.2f.62. +1.6L.14. +2.22.6d. +2.2g.5R. +3.1u.6s. +3.2y.2C. +3.6R.Q. +3.6V.K. +4.12.6M. +4.1e.6E. +4.1s.6u. +4.s.7d. +4.u.7b. +5.1h.6A. +5.29.67. +5.k.7m. +7.r.7e. +8.2A.2A. +8.2z.2B. +8.6z.1j. +9.1t.6t. +b.6+.F. +d.71.C. +e.1.7A. +f.1+.6g. +g.2E.2x. +h.1n.6x. +0.17.6J. +0.19.6H. +0.1A.6o. +0.1h.6B. +0.1s.6v. +0.1v.6s. +0.1z.6p. +1.6L.15. +2.22.6e. +2.29.68. +3.6R.R. +3.6V.L. +4.1f.6E. +4.1t.6u. +4.s.7e. +4.u.7c. +5.2q.5k. +5.c.7u. +5.k.7n. +7.r.7f. +8.2A.2B. +8.2z.2C. +d.71.D. +e.2.7A. +h.1o.6x. +0.+.6O. +0.18.6J. +0.1A.6p. +0.1B.6o. +0.1i.6B. +0.1t.6v. +0.1w.6s. +1.6L.16. +2.24.6d. +2.A.75. +3.1u.6u. +3.2B.2B. +3.M.6V. +3.S.6R. +4.1a.6H. +4.u.7d. +5.k.7o. +6.6E.1g. +7.r.7g. +7.s.7f. +8.2A.2C. +d.71.E. +e.3.7A. +h.1p.6x. +0./.6O. +0.13.6M. +0.19.6J. +0.1b.6H. +0.1B.6p. +0.1C.6o. +0.1j.6B. +0.1x.6s. +0.d.7t. +1.6L.17. +2.24.6e. +2.2f.63. +2.2n.5J. +2.A.76. +3.1u.6v. +3.2B.2C. +3.2y.2D. +3.6R.T. +4.1v.6u. +5.1h.6C. +5.c.7v. +7.22.6f. +7.r.7h. +7.s.7g. +7.u.7e. +8.6z.1k. +b.6+.G. +d.71.F. +e.4.7A. +h.1q.6x. +0.+.6P. +0.10.6O. +0.14.6M. +0.1a.6J. +0.1c.6H. +0.1C.6p. +0.1D.6o. +0.1v.6v. +0.1y.6s. 0.2C.2C. -0.2D.2C. -0.2E.2C. -0.2F.2C. -0.2G.2C. -9.2H.2C. -0.2I.2C. -0.2J.2C. -0.2C.2K. -c.2M.2C. -9.2N.2C. -2.2O.2C. -9.2Q.2C. -7.2S.2C. -0.2T.2C. -1.2U.2C. -9.2V.2C. -1.2W.2C. -0.2C.2X. -b.2+.2C. -0.30.2C. -3.2C.35. -b.36.2C. -3.2C.37. -3.2C.38. -5.39.2C. -5.3d.2C. -3.2C.3h. -5.3i.2C. -0.2C.3m. -5.3p.2C. -a.3v.2C. -a.3y.2C. -2.3z.2C. -5.3A.2C. -2.3B.2C. -0.2C.3C. -a.3F.2C. -5.3G.2C. -0.2C.3M. -0.2C.3Q. -5.40.2C. -0.2C.47. -0.2C.4d. -0.2C.4O. -0.2C.4Q. -5.4T.2C. -3.2C.4V. -3.2C.51. -0.2C.52. -a.5a.2C. -2.5b.2C. -5.5d.2C. -0.2C.5B. -0.2C.5C. -5.5F.2C. -0.2C.5H. -0.2C.5I. -f.2C.5K. -7.5M.2C. -3.2C.5O. -0.2C.5R. -0.2C.5U. -0.2C.5W. -1.5Y.2C. -0.2C.5Z. -0.2C.5/. -0.2C.60. -2.62.2C. -2.66.2C. -a.6b.2C. -c.6e.2C. -0.2C.6F. -0.6M.2C. -0.2C.6+. -0.2C.71. -5.79.2C. -0.2C.7D. -0.2p.2C. -0.2p.2C. +0.e.7t. +1.1+.6h. +1.6L.18. +2.26.6d. +2.A.77. +3.6R.U. +4.1w.6u. +4.s.7h. +5.1h.6D. +5.29.69. +5.c.7w. +7.22.6g. +7.u.7f. +8.2z.2D. +8.6z.1l. +a.2F.2w. +b.6+.H. +e.5.7A. +g.2E.2y. +h.1r.6x. +0./.6P. +0.11.6O. +0.15.6M. +0.1D.6p. +0.1w.6v. +0.1z.6s. +0.f.7t. +1.1t.6w. +1.6L.19. +2.26.6e. +2.2n.5K. +2.A.78. +3.6R.V. +3.6V.N. +4.1b.6J. +4.1h.6E. +4.1x.6u. +5.29.6a. +5.c.7x. +5.k.7p. +7.24.6f. +7.r.7i. +7.u.7g. +8.2A.2D. +8.2z.2E. +8.6z.1m. +b.6+.I. +e.6.7A. +h.1s.6x. +0.10.6P. +0.16.6M. +0.1A.6s. +0.1c.6J. +0.1d.6H. +0.1E.6o. +0.1k.6B. +0.1x.6v. +0.2w.2G. +0.g.7t. +1.6L.1a. +2.2f.64. +2.r.7j. +3.2B.2D. +3.6R.W. +3.6V.O. +4.12.6O. +4.1i.6E. +4.1y.6u. +5.k.7q. +7.24.6g. +7.s.7i. +7.u.7h. +8.2A.2E. +8.6z.1n. +b.6+.J. +d.71.G. +e.7.7A. +h.1t.6x. +0.11.6P. +0.17.6M. +0.1B.6s. +0.1E.6p. +0.1F.6o. +0.1j.6E. +0.1y.6v. +0.h.7t. +1.1+.6i. +1.29.6b. +1.6L.1b. +2.A.7a. +2.s.7j. +3.1l.6B. +3.6R.X. +3.6V.P. +4.1z.6u. +4.26.6f. +5.1h.6F. +5.k.7r. +7.r.7k. +8.6z.1o. +9.1t.6y. +b.6+.K. +d.71.H. +e.8.7A. +f.2C.2D. +h.1u.6x. +0.18.6M. +0.1C.6s. +0.1d.6J. +0.1F.6p. +0.1G.6o. +0.1m.6B. +0.1z.6v. +0.i.7t. +1.1+.6j. +1.22.6h. +1.2f.65. +1.6L.1c. +2.2H.2w. +3.2x.2F. +3.6R.Y. +3.6V.Q. +4.12.6P. +4.1A.6u. +4.26.6g. +4.u.7i. +5.2q.5o. +5.c.7y. +7.r.7l. +7.s.7k. +8.6z.1p. +b.6+.L. +d.71.I. +e.9.7A. +g.2E.2C. +h.1v.6x. +0.13.6O. +0.19.6M. +0.1A.6v. +0.1B.6u. +0.1D.6s. +0.1e.6H. +0.1G.6p. +0.1H.6o. +0.1n.6B. +0.j.7t. +2.1+.6k. +2.A.7c. +2.u.7j. +3.6R.Z. +3.6V.R. +4.r.7m. +5.k.7s. +7.s.7l. +8.6z.1q. +b.6+.M. +d.71.J. +e.a.7A. +h.1w.6x. +0.14.6O. +0.1a.6M. +0.1B.6v. +0.1f.6H. +0.1H.6p. +0.1I.6o. +0.1o.6B. 0.2D.2D. -0.2D.2E. -0.2F.2D. -0.2D.2G. -9.2H.2D. -0.2I.2D. -d.2D.2J. -0.2D.2K. -c.2M.2D. -9.2N.2D. -2.2O.2D. -9.2Q.2D. -7.2S.2D. -0.2D.2T. -1.2U.2D. -9.2V.2D. -1.2W.2D. -0.2D.2X. -d.2+.2D. -0.2D.30. -d.2D.35. -b.36.2D. -0.2D.37. -0.2D.38. -5.39.2D. -5.3d.2D. -0.2D.3h. -5.3i.2D. -0.2D.3m. -0.2D.3p. -a.3v.2D. -a.3y.2D. -2.3z.2D. -5.3A.2D. -2.3B.2D. -0.2D.3C. -a.3F.2D. -5.3G.2D. -0.2D.3M. -d.2D.3Q. -5.40.2D. -0.2D.47. -0.2D.4d. -0.2D.4O. -0.2D.4Q. -5.4T.2D. -0.2D.4V. -d.2D.51. -0.2D.52. -a.5a.2D. -2.5b.2D. -5.5d.2D. -0.2D.5B. -0.2D.5C. -5.5F.2D. -0.2D.5H. -0.2D.5I. -f.2D.5K. -7.5M.2D. -d.2D.5O. -0.2D.5R. -0.2D.5U. -0.2D.5W. -1.5Y.2D. -0.2D.5Z. -0.2D.5/. -0.2D.60. -2.62.2D. -2.66.2D. -a.6b.2D. -c.6e.2D. -0.2D.6F. -0.6M.2D. -0.2D.6+. -d.2D.71. -5.79.2D. -0.2D.7D. -0.2D.2p. -0.2D.2p. -d.2E.2E. -0.2F.2E. -0.2E.2G. -9.2H.2E. -0.2I.2E. -d.2E.2J. -0.2E.2K. -c.2M.2E. -9.2N.2E. -2.2O.2E. -9.2Q.2E. -7.2S.2E. -0.2E.2T. -1.2U.2E. -9.2V.2E. -1.2W.2E. -0.2E.2X. -d.2+.2E. -0.2E.30. -d.2E.35. -3.36.2E. -0.2E.37. -d.2E.38. -5.39.2E. -5.3d.2E. -d.2E.3h. -5.3i.2E. -0.2E.3m. -0.2E.3p. -a.3v.2E. -a.3y.2E. -2.3z.2E. -5.3A.2E. -2.3B.2E. -0.2E.3C. -a.3F.2E. -5.3G.2E. -0.2E.3M. -d.2E.3Q. -5.40.2E. -0.2E.47. -0.2E.4d. -0.2E.4O. -0.2E.4Q. -5.4T.2E. -0.2E.4V. -d.2E.51. -0.2E.52. -a.5a.2E. -2.5b.2E. -5.5d.2E. -0.2E.5B. -0.2E.5C. -5.5F.2E. -0.2E.5H. -0.2E.5I. -f.2E.5K. -7.5M.2E. -0.2E.5O. -0.2E.5R. -0.2E.5U. -0.2E.5W. -1.5Y.2E. -0.2E.5Z. -d.2E.5/. -0.2E.60. -2.62.2E. -2.66.2E. -a.6b.2E. -c.6e.2E. -0.2E.6F. -0.6M.2E. -d.2E.6+. -d.2E.71. -5.79.2E. -0.2E.7D. -0.2E.2p. -0.2E.2p. -0.2F.2F. -0.2F.2G. -9.2H.2F. -0.2I.2F. -0.2F.2J. -0.2F.2K. -c.2M.2F. -9.2N.2F. -2.2O.2F. -9.2Q.2F. -7.2S.2F. -0.2F.2T. -1.2U.2F. -9.2V.2F. -1.2W.2F. -0.2F.2X. -d.2+.2F. -0.2F.30. -0.2F.35. -b.36.2F. -0.2F.37. -0.2F.38. -5.39.2F. -5.3d.2F. -0.2F.3h. -5.3i.2F. -0.2F.3m. -0.2F.3p. -a.3v.2F. -a.3y.2F. -2.3z.2F. -5.3A.2F. -2.3B.2F. -0.2F.3C. -a.3F.2F. -5.3G.2F. -0.2F.3M. -0.2F.3Q. -5.40.2F. -0.2F.47. -0.2F.4d. -0.2F.4O. -0.2F.4Q. -5.4T.2F. -0.2F.4V. -0.2F.51. -0.2F.52. -a.5a.2F. -2.5b.2F. -5.5d.2F. -0.2F.5B. -0.2F.5C. -5.5F.2F. -0.2F.5H. -0.2F.5I. -f.2F.5K. -7.5M.2F. -0.2F.5O. -0.2F.5R. -0.2F.5U. -0.2F.5W. -1.5Y.2F. -0.2F.5Z. -0.2F.5/. -0.2F.60. -2.62.2F. -2.66.2F. -a.6b.2F. -c.6e.2F. -0.2F.6F. -0.6M.2F. -0.2F.6+. -0.2F.71. -5.79.2F. -0.2F.7D. -0.2F.2p. -0.2F.2p. -3.2G.2G. -9.2H.2G. +0.k.7t. +1.1+.6l. +1.24.6h. +1.6L.1d. +2.2f.66. +2.A.7d. +3.2x.2G. +3.S.6V. +4.1C.6u. +4.1k.6E. +4.s.7m. +4.u.7k. +5.1h.6G. +5.2q.5q. +7.r.7n. +8.6z.1r. +9.c.7z. +d.71.K. +e.b.7A. +h.1x.6x. +0.13.6P. +0.15.6O. +0.1b.6M. +0.1C.6v. +0.1e.6J. +0.1E.6s. +0.1I.6p. +0.1J.6o. +0.1p.6B. +0.l.7t. +1.22.6i. +1.2f.67. +2.A.7e. +3.1l.6E. +3.6V.T. +4.1D.6u. +7.r.7o. +7.s.7n. +7.u.7l. +8.6z.1s. +d.71.L. +g.2E.2D. +h.1g.6H. +h.1y.6x. +i.c.7A. +0.14.6P. +0.16.6O. +0.1c.6M. +0.1D.6v. +0.1f.6J. +0.1F.6s. +0.1J.6p. +0.1q.6B. +0.2I.2w. +0.m.7t. +1.22.6j. +1.26.6h. +2.2f.68. +2.2H.2x. +2.A.7f. +3.2y.2F. +3.6V.U. +4.1m.6E. +4.u.7m. +5.k.7u. +7.s.7o. +8.6z.1t. +b.6+.N. +d.71.M. +g.2E.2E. +h.1z.6x. +0.15.6P. +0.17.6O. +0.1G.6s. +0.1r.6B. +1.24.6i. +1.6L.1e. +2.1+.6m. +2.22.6k. +3.6R.+. +3.6V.V. +3.n.7t. +4.1E.6u. +4.1n.6E. +4.6J.1g. +4.u.7n. +5./.6Q. +8.2z.2F. +8.6z.1u. +b.6+.O. +h.1A.6x. +0.16.6P. +0.18.6O. +0.1d.6M. +0.1E.6v. +0.1h.6H. +0.1H.6s. +0.1K.6o. +0.1o.6E. +0.1s.6B. +0.o.7t. +1.22.6l. +1.24.6j. +1.6L.1f. +2.A.7h. +3.2y.2G. +3.6R./. +3.6V.W. +4.1F.6u. +4.1t.6A. +5.k.7v. +7.r.7p. +7.u.7o. +8.2A.2F. +8.6z.1v. +b.6+.P. +h.1B.6x. +0.17.6P. +0.19.6O. +0.1F.6v. +0.1I.6s. +0.1K.6p. +0.1L.6o. +0.1p.6E. +0.1t.6B. +0.d.7A. +0.p.7t. +1.26.6i. +1.2f.69. +1.6L.1g. +2.24.6k. +3.2B.2F. +3.6R.10. +3.6V.X. +4.1G.6u. +4.1i.6H. +4.s.7p. +5.1h.6I. +5.29.6c. +5.2q.5s. +5.c.7B. +5.k.7w. +7.r.7q. +8.2z.2G. +8.6z.1w. +b.6+.Q. +d.71.N. +h.1C.6x. +0.18.6P. +0.1G.6v. +0.1j.6H. +0.1J.6s. +0.1L.6p. +0.1M.6o. +0.e.7A. +0.q.7t. +1.24.6l. +1.26.6j. +1.2f.6a. +2.1d.6N. +2.2H.2y. +2.A.7i. +3.1u.6B. +3.2x.2I. +3.6R.11. +3.6V.Y. +4.1a.6O. +4.1h.6J. +4.1H.6u. +4.1q.6E. +5./.6T. +5.c.7C. +5.k.7x. +7.r.7r. +7.s.7q. +8.2A.2G. +8.6z.1x. +a.2F.2C. +b.6+.R. +d.71.O. +h.1D.6x. +0.19.6P. +0.1b.6O. +0.1e.6M. +0.1H.6v. +0.1M.6p. +0.1v.6B. +0.f.7A. +2.22.6m. +2.26.6k. +2.2H.2z. +2.A.7j. +3.2B.2G. +3.6R.12. +3.6V.Z. +4.1i.6J. +4.1I.6u. +4.1N.6o. +4.1r.6E. +5.1h.6K. +5.c.7D. +7.s.7r. +7.u.7p. +8.6z.1y. +9.1t.6C. +b.6+.S. +d.71.P. +0.1c.6O. +0.1f.6M. +0.1I.6v. +0.1j.6J. +0.1O.6o. +0.1w.6B. +0.2C.2G. +0.g.7A. +1.26.6l. +1.2f.6b. +1.6L.1h. +2.2H.2A. +2.A.7k. +4.1a.6P. +4.1J.6u. +4.1N.6p. +4.1s.6E. +5.1t.6D. +5.c.7E. +7.r.7s. +7.u.7q. +8.6z.1z. +a.2J.2w. +b.6+.T. +d.71.Q. +h.1E.6x. +0.1b.6P. +0.1J.6v. +0.1k.6H. +0.1K.6s. +0.1O.6p. +0.1P.6o. +0.1x.6B. +0.h.7A. +0.r.7t. +1.6L.1i. +2.24.6m. +2.2H.2B. +4.1t.6E. +5.k.7y. +6.6M.1g. +7.s.7s. +7.u.7r. +8.6z.1A. +a.2F.2D. +b.6+.U. +d.71.R. +h.1F.6x. +0.1c.6P. +0.1d.6O. +0.1L.6s. +0.1P.6p. +0.1Q.6o. +0.1y.6B. +0.i.7A. +0.s.7t. +1.6L.1j. +2.2H.2C. +2.A.7m. +3.1l.6H. +3.1u.6E. +3.2y.2I. +3.6R.13. +8.6z.1B. +b.6+.V. +d.71.S. +g.2E.2F. +h.1G.6x. +0.1k.6J. +0.1m.6H. +0.1M.6s. +0.1Q.6p. +0.1v.6E. +0.1z.6B. +0.j.7A. +0.t.7t. +2.26.6m. +2.A.7n. +3.6R.14. +3.6V.+. +4.1K.6u. +4.2G.2D. +5./.6U. +5.1t.6F. +5.c.7F. +7.r.7u. +7.u.7s. +8.2z.2I. +8.6z.1C. +9.k.7z. +b.6+.W. +d.71.T. +h.1H.6x. +0.1A.6B. +0.1d.6P. +0.1h.6M. +0.1K.6v. +0.1L.6u. +0.1n.6H. +0.2w.2K. +0.k.7A. +0.u.7t. +2.A.7o. +3.1l.6J. +3.2x.2J. +3.6R.15. +3.6V./. +4.1N.6s. +4.1w.6E. +7.s.7u. +8.2A.2I. +8.6z.1D. +b.6+.X. +d.71.U. +g.2E.2G. +h.1I.6x. +0.1e.6O. +0.1i.6M. +0.1L.6v. +0.1m.6J. +0.1o.6H. +0.1O.6s. +0.l.7A. +0.v.7t. +1.6L.1k. +2.29.6d. +2.2H.2D. +3.2B.2I. +3.6R.16. +3.6V.10. +4.1B.6B. +4.1M.6u. +4.1x.6E. +4.r.7v. +5./.6W. +5.c.7G. +b.6+.Y. +d.71.V. +h.1J.6x. +0.1C.6B. +0.1f.6O. +0.1j.6M. +0.1M.6v. +0.1n.6J. +0.1p.6H. +0.1P.6s. +0.m.7A. +0.w.7t. +1.6L.1l. +2.1h.6N. +2.29.6e. +2.2H.2E. +3.6R.17. +3.6V.11. +4.1N.6u. +4.1y.6E. +4.s.7v. +5./.6X. +5.1t.6G. +5.2q.5u. +5.c.7H. +7.r.7w. +7.u.7u. +8.6z.1E. +b.6+.Z. +d.71.W. +f.1R.6o. +f.2C.2I. +0.1D.6B. +0.1e.6P. +0.1N.6v. +0.1o.6J. +0.1O.6u. +0.1q.6H. +0.1S.6o. +0.x.7t. +1.6L.1m. +2.A.7p. +3.6R.18. +3.6V.12. +3.n.7A. +4.1z.6E. +5./.6Y. +5.c.7I. +6.5L.2n. +6.6O.1g. +6.6s.1Q. +7.r.7x. +7.s.7w. +8.6z.1F. +d.71.X. +f.1R.6p. +0.1f.6P. +0.1O.6v. +0.1p.6J. +0.1P.6u. +0.1r.6H. +0.1S.6p. +0.o.7A. +0.y.7t. +1.2f.6c. +1.6L.1n. +3.2x.2K. +3.2y.2J. +3.6R.19. +4.1A.6E. +4.u.7v. +5./.6Z. +5.29.6f. +5.c.7J. +5.k.7B. +7.s.7x. +8.6z.1G. +d.71.Y. +h.1K.6x. +0.1B.6E. +0.1E.6B. +0.1k.6M. +0.1P.6v. +0.1q.6J. +0.1s.6H. +0.6P.1g. +0.p.7A. +0.z.7t. +1.6L.1o. +2.2n.5M. +3.6R.1a. +5.1V.6n. +5.29.6g. +5.k.7C. +7.u.7w. +8.2z.2J. +8.6z.1H. +d.71.Z. +f.1Q.6u. +f.2I.2D. +h.1L.6x. +0.1C.6E. +0.1F.6B. +0.1h.6O. +0.1Q.6v. +0.1r.6J. +0.1t.6H. +0.1V.6o. +0.2L.2w. +0.q.7A. +1.6L.1p. +2.2n.5N. +3.1l.6M. +3.6R.1b. +3.6V.13. +5.k.7D. +7.2p.5L. +7.r.7y. +7.u.7x. +8.2A.2J. +8.6z.1I. +a.2F.2F. +b.6+.+. +g.2E.2I. +h.1M.6x. +j.2a.6g. +0.1G.6B. +0.1i.6O. +0.1m.6M. +0.1s.6J. +0.1V.6p. +0.1W.6o. +1.6L.1q. +2.A.7s. +3.1u.6H. +3.2B.2J. +3.6R.1c. +3.6V.14. +4.1D.6E. +5.k.7E. +6.6s.1R. +7.s.7y. +8.6z.1J. +9.1t.6I. +b.6+./. +h.1N.6x. +0.1H.6B. +0.1h.6P. +0.1j.6O. +0.1n.6M. +0.1v.6H. +0.1W.6p. +0.A.7t. +1.1V.6q. +1.2n.5O. +1.6L.1r. +3.2y.2K. +3.6V.15. +4.1t.6J. +5./.6/. +6.6s.1S. +9.r.7z. +a.2F.2G. +a.2J.2C. +b.6+.10. +h.1O.6x. +0.1I.6B. +0.1i.6P. +0.1o.6M. +0.1w.6H. +0.1X.6o. +0.B.7t. +0.r.7A. +1.29.6h. +1.2n.5P. +1.6L.1s. +3.1u.6J. +3.6R.1d. +3.6V.16. +4.1E.6E. +4.s.7z. +5./.70. +7.u.7y. +8.2z.2K. +9.1t.6K. +b.6+.11. +d.71.+. +f.1R.6u. +h.1P.6x. +0.1J.6B. +0.1j.6P. +0.1p.6M. +0.1v.6J. +0.1x.6H. +0.1X.6p. +0.2G.2G. +0.2M.2w. +0.C.7t. +0.s.7A. +2.2H.2F. +2.A.7u. +3.2x.2L. +3.6V.17. +4.1F.6E. +4.1S.6u. +5./.71. +5.1V.6r. +5.k.7F. +8.2A.2K. +8.6z.1K. +9.1t.6L. +b.6+.12. +f.1R.6v. +h.1Q.6x. +0.1k.6O. +0.1q.6M. +0.1S.6v. +0.1w.6J. +0.1y.6H. +0.D.7t. +0.t.7A. +1.2n.5Q. +1.6L.1u. +3.2B.2K. +3.6V.18. +4.1G.6E. +5./.72. +6.6s.1V. +8.6z.1L. +9.u.7z. +a.2J.2D. +d.71.10. +0.1r.6M. +0.1x.6J. +0.1z.6H. +0.2C.2K. +0.E.7t. +0.u.7A. +1.29.6i. +1.6L.1v. +2.2f.6d. +2.2H.2G. +2.A.7v. +3.1l.6O. +3.6R.1e. +3.6V.19. +4.1H.6E. +5./.73. +5.k.7G. +6.6s.1W. +8.6z.1M. +9.1V.6t. +d.71.11. +g.2E.2J. +0.1A.6H. +0.1K.6B. +0.1k.6P. +0.1m.6O. +0.1s.6M. +0.1y.6J. +0.F.7t. +0.v.7A. +1.29.6j. +1.6L.1w. +2.2f.6e. +3.6R.1f. +3.6V.1a. +4.1I.6E. +5./.74. +5.2q.5x. +5.k.7H. +7.r.7B. +8.6z.1N. +b.6+.13. +d.71.12. +f.1V.6u. +0.1L.6B. +0.1n.6O. +0.1t.6M. +0.1V.6v. +0.1z.6J. +0.2N.2w. +0.w.7A. +1.2n.5R. +1.6L.1x. +2.29.6k. +2.2H.2H. +2.A.7x. +3.1l.6P. +3.2x.2M. +3.2y.2L. +3.6R.1g. +3.6V.1b. +4.1B.6H. +4.1J.6E. +4.r.7C. +5./.75. +5.k.7I. +6.6s.1X. +7.s.7B. +8.6z.1O. +a.2F.2I. +b.6+.14. +f.1R.6x. +f.1W.6u. +0.1A.6J. +0.1C.6H. +0.1M.6B. +0.1m.6P. +0.1o.6O. +0.1W.6v. +0.2K.2D. +0.2w.2O. +0.x.7A. +1.29.6l. +1.2f.6f. +1.6L.1y. +2.c.7K. +3.1u.6M. +3.6V.1c. +4.s.7C. +5./.76. +5.2q.5y. +5.k.7J. +7.r.7D. +8.2z.2L. +8.6z.1P. +b.6+.15. +h.1S.6x. +0.1B.6J. +0.1D.6H. +0.1N.6B. +0.1n.6P. +0.1p.6O. +0.1v.6M. +0.1X.6u. 0.2I.2G. -d.2J.2G. +0.G.7t. +0.y.7A. +1.2f.6g. +1.6L.1z. +2.1t.6N. +5./.77. +5.1h.6Q. +5.2q.5z. +7.r.7E. +7.s.7D. +7.u.7B. +8.2A.2L. +8.6z.1Q. +a.2P.2w. +b.6+.16. +d.71.13. +g.2E.2K. +0.1C.6J. +0.1O.6B. +0.1o.6P. +0.1q.6O. +0.1w.6M. +0.1X.6v. +0.7A.z. +0.H.7t. +1.1V.6w. +1.6L.1A. +3.2B.2L. +3.6R.1h. +3.6V.1d. +4.1K.6E. +4.s.7E. +5./.78. +7.u.7C. +b.6+.17. +d.71.14. +0.1E.6H. +0.1L.6E. +0.1P.6B. +0.1p.6P. +0.1r.6O. +0.1V.6x. +0.1x.6M. +0.2C.2L. +0.2Q.2w. +1.6L.1B. +2.29.6m. +2.2H.2I. +3.2x.2N. +3.2y.2M. +3.6R.1i. +3.I.7t. +4.1D.6J. +5./.79. +5.2q.5A. +7.u.7D. +b.6+.18. +d.71.15. +j.0.7N. +0.1F.6H. +0.1Q.6B. +0.1q.6P. +0.1s.6O. +0.1y.6M. +0.2R.2w. +1.6L.1C. +2.1.7N. +2.2n.5S. +3.2x.2O. +3.6R.1j. +3.J.7t. +4.1M.6E. +4.r.7F. +5./.7a. +5.1h.6T. +7.u.7E. +8.2z.2M. +9.1V.6y. +b.6+.19. +d.71.16. +f.1W.6x. +0.1G.6H. +0.1r.6P. +0.1t.6O. +0.1z.6M. +0.A.7A. +0.K.7t. +1.2f.6h. +1.6L.1D. +2.2.7N. +3.6V.1e. +4.1E.6J. +4.1N.6E. +5./.7b. +5.c.7L. +7.s.7F. +8.2A.2M. +8.6z.1R. +a.2F.2J. +a.2P.2x. +b.6+.1a. +d.71.17. +0.1A.6M. +0.1F.6J. +0.1H.6H. +0.1O.6E. +0.1s.6P. +0.2w.2S. +0.B.7A. +0.L.7t. +2.3.7N. +2.c.7M. +3.1u.6O. +3.2B.2M. +3.6V.1f. +4.r.7G. +5./.7c. +8.6z.1S. +b.6+.1b. +d.71.18. +f.2L.2D. +h.1X.6x. +0.1B.6M. +0.1G.6J. +0.1I.6H. +0.1P.6E. +0.1t.6P. +0.1v.6O. +0.2C.2M. +0.C.7A. +1.6L.1E. +2.4.7N. +3.2x.2Q. +3.2y.2N. +3.6R.1k. +3.6V.1g. +3.M.7t. +4.s.7G. +4.u.7F. +5./.7d. +5.2q.5C. +7.r.7H. +a.2J.2G. +b.6+.1c. +d.71.19. +f.2I.2I. +g.2E.2L. +0.1C.6M. +0.1H.6J. +0.1J.6H. +0.1Q.6E. +0.1R.6B. +0.1w.6O. +0.D.7A. +1.2f.6i. +1.6L.1F. +2.5.7N. +3.1l.6R. +3.1u.6P. +3.2x.2R. +3.2y.2O. +5./.7e. +5.2q.5D. +6.6o.1Y. +7.r.7I. +7.s.7H. +8.2z.2N. +d.71.1a. +0.1D.6M. +0.1I.6J. +0.1S.6B. +0.1v.6P. +0.1x.6O. +0.E.7A. +1.2f.6j. +1.6L.1G. +2.2H.2J. +2.6.7N. +2.A.7B. +3.6R.1m. +4.r.7J. +4.s.7I. +4.u.7G. +5./.7f. +5.1h.6U. +6.6p.1Y. +8.2A.2N. +8.2z.2O. +8.6z.1V. +a.2F.2K. +a.2P.2y. +b.6+.1d. +d.71.1b. +0.1J.6J. +0.1w.6P. +0.1y.6O. +0.F.7A. +0.N.7t. +1.6L.1H. +2.1d.6/. +2.2f.6k. +2.2g.65. +2.7.7N. +2.A.7C. +3.2B.2N. +3.2x.2S. +3.6R.1n. +3.6V.1h. +4.s.7J. +5./.7g. +7.u.7H. +8.2A.2O. +8.2z.2P. +8.6z.1W. +d.71.1c. +f.2M.2D. +0.1E.6M. +0.1K.6H. +0.1x.6P. +0.1z.6O. +0.2C.2N. 0.2G.2K. -c.2M.2G. -9.2N.2G. -2.2O.2G. -9.2Q.2G. -7.2S.2G. -0.2G.2T. -1.2U.2G. -9.2V.2G. -1.2W.2G. -0.2G.2X. -b.2+.2G. -0.30.2G. -0.2G.35. -3.36.2G. -0.2G.37. -0.2G.38. -5.39.2G. -5.3d.2G. -3.2G.3h. -5.3i.2G. -0.2G.3m. -0.2G.3p. -a.3v.2G. -a.3y.2G. -2.3z.2G. -5.3A.2G. -2.3B.2G. -0.2G.3C. -a.3F.2G. -5.3G.2G. -3.2G.3M. -3.2G.3Q. -5.40.2G. -3.2G.47. -0.2G.4d. -0.2G.4O. -0.2G.4Q. -5.4T.2G. -3.2G.4V. -0.2G.51. -0.2G.52. -a.5a.2G. -2.5b.2G. -5.5d.2G. -0.2G.5B. -0.2G.5C. -5.5F.2G. -3.2G.5H. -0.2G.5I. -f.2G.5K. -7.5M.2G. -0.2G.5O. -0.2G.5R. -3.2G.5U. -0.2G.5W. -1.5Y.2G. -0.2G.5Z. -0.2G.5/. -0.2G.60. -2.62.2G. -2.66.2G. -a.6b.2G. -c.6e.2G. -0.2G.6F. -0.6M.2G. -0.2G.6+. -0.2G.71. -5.79.2G. -0.2G.7D. -0.2p.2G. -0.2p.2G. -9.2H.2H. -9.2H.2I. -9.2H.2J. -9.2H.2K. -c.2M.2H. -9.2H.2N. -2.2O.2H. -9.2H.2Q. -7.2S.2H. -9.2H.2T. -1.2U.2H. -9.2H.2V. -1.2W.2H. -9.2H.2X. -9.2H.2+. -9.2H.30. -9.2H.35. -9.2H.36. -9.2H.37. -9.2H.38. -9.2H.39. -9.2H.3d. -9.2H.3h. -9.2H.3i. -9.2H.3m. -9.2H.3p. -a.3v.2H. -a.3y.2H. -2.3z.2H. -9.2H.3A. -2.3B.2H. -9.2H.3C. -a.3F.2H. -9.2H.3G. -9.2H.3M. -9.2H.3Q. -5.40.2H. -9.2H.47. -9.2H.4d. -9.2H.4O. -9.2H.4Q. -7.4T.2H. -9.2H.4V. -9.2H.51. -9.2H.52. -a.5a.2H. -2.5b.2H. -9.2H.5d. -9.2H.5B. -9.2H.5C. -9.2H.5F. -9.2H.5H. -9.2H.5I. -9.2H.5K. -7.5M.2H. -9.2H.5O. -9.2H.5R. -9.2H.5U. -9.2H.5W. -1.5Y.2H. -9.2H.5Z. -9.2H.5/. -9.2H.60. -2.62.2H. -2.66.2H. -a.6b.2H. -c.6e.2H. -9.2H.6F. -9.2H.6M. -9.2H.6+. -9.2H.71. -9.2H.79. -9.2H.7D. -9.2H.2p. -9.2H.2p. -0.2I.2I. -d.2I.2J. +0.O.7t. +1.2f.6l. +1.2n.5T. +1.6L.1I. +2.8.7N. +2.A.7D. +2.k.7K. +3.2B.2O. +3.2y.2Q. +3.6R.1o. +3.6V.1i. +5./.7h. +5.1h.6W. +5.5F.2q. +7.u.7I. +8.2A.2P. +f.1V.6A. +g.2E.2M. +0.1A.6O. +0.1F.6M. +0.1L.6H. +0.1R.6E. +0.1y.6P. +0.2C.2O. +0.P.7t. +1.6L.1J. +2.9.7N. +3.2B.2P. +3.2y.2R. +3.6R.1p. +3.6V.1j. +4.5G.2q. +4.u.7J. +5.1h.6X. +8.2z.2Q. +8.6z.1X. +b.6+.1e. +d.71.1d. +f.1V.6B. +0.1B.6O. +0.1G.6M. +0.1M.6H. +0.1W.6B. +0.1Z.6o. +0.1z.6P. +0.G.7A. +0.Q.7t. +2.0.7R. +2.1d.72. +2.2g.67. +2.2H.2K. +2.a.7N. +3.6R.1q. +4.1K.6J. +4.1S.6E. +5./.7i. +5.1h.6Y. +6.6s.1Y. +8.2A.2Q. +8.2z.2R. +9.1+.6n. +a.2J.2I. +a.2P.2C. +b.6+.1f. +0.1+.6o. +0.1A.6P. +0.1C.6O. +0.1H.6M. +0.1L.6J. +0.1Z.6p. +0.2N.2D. +0.H.7A. +0.R.7t. +2./.7j. +2.1.7R. +2.2f.6m. +2.b.7N. +3.2B.2Q. +3.2y.2S. +3.6R.1r. +4.1N.6H. +5.1h.6Z. +8.2A.2R. +9.1V.6C. +b.6+.1g. +0.1/.6o. +0.1+.6p. +0.1B.6P. +0.1D.6O. +0.1I.6M. +0.1M.6J. +0.1O.6H. +0.1X.6B. +0.2C.2Q. +0.2O.2D. +0.6u.1Y. +1.6L.1K. +2.1.7S. +2.2.7R. +2.A.7F. +3.2B.2R. +3.6R.1s. +3.6V.1k. +3.I.7A. +3.S.7t. +5./.7k. +5.1t.6Q. +5.1V.6D. +8.2z.2S. +a.2F.2L. +d.2U.2w. +d.71.1e. +g.2E.2N. +0.1/.6p. +0.1C.6P. +0.1J.6M. +0.1P.6H. +0.1V.6E. +0.20.6o. +0.6v.1Y. +1.1+.6q. +1.6L.1L. +2.1d.75. +2.2.7S. +2.3.7R. +3.1l.6V. +3.6R.1t. +3.J.7A. +4.1N.6J. +5./.7l. +5.2q.5I. +5.k.7L. +8.2A.2S. +a.2P.2D. +c.T.7t. +d.71.1f. +f.2C.2R. +g.2E.2O. +0.1D.6P. +0.1E.6O. +0.1O.6J. +0.1Q.6H. +0.20.6p. 0.2I.2K. -c.2M.2I. -9.2N.2I. -2.2O.2I. -9.2Q.2I. -7.2S.2I. -0.2I.2T. -1.2U.2I. -9.2V.2I. -1.2W.2I. -0.2I.2X. -d.2+.2I. -0.2I.30. -d.2I.35. -b.36.2I. -0.2I.37. -0.2I.38. -5.39.2I. -5.3d.2I. -d.2I.3h. -5.3i.2I. -0.2I.3m. -0.2I.3p. -a.3v.2I. -a.3y.2I. -2.3z.2I. -5.3A.2I. -2.3B.2I. -0.2I.3C. -a.3F.2I. -5.3G.2I. -0.2I.3M. -d.2I.3Q. -5.40.2I. -0.2I.47. -0.2I.4d. -0.2I.4O. -0.2I.4Q. -5.4T.2I. -0.2I.4V. -d.2I.51. -0.2I.52. -a.5a.2I. -2.5b.2I. -5.5d.2I. -0.2I.5B. -0.2I.5C. -5.5F.2I. -0.2I.5H. -0.2I.5I. -f.2I.5K. -7.5M.2I. -d.2I.5O. -0.2I.5R. -0.2I.5U. -0.2I.5W. -1.5Y.2I. -0.2I.5Z. -0.2I.5/. -0.2I.60. -2.62.2I. -2.66.2I. -a.6b.2I. -c.6e.2I. -0.2I.6F. -0.6M.2I. -0.2I.6+. -d.2I.71. -5.79.2I. -0.2I.7D. -0.2I.2p. -0.2I.2p. -d.2J.2J. -d.2J.2K. -c.2M.2J. -9.2N.2J. -2.2O.2J. -9.2Q.2J. -7.2S.2J. -0.2J.2T. -1.2U.2J. -9.2V.2J. -1.2W.2J. -0.2J.2X. -d.2+.2J. -d.30.2J. -d.2J.35. -3.36.2J. -0.2J.37. -d.2J.38. -5.39.2J. -5.3d.2J. -d.2J.3h. -5.3i.2J. -0.2J.3m. -0.2J.3p. -a.3v.2J. -a.3y.2J. -2.3z.2J. -5.3A.2J. -2.3B.2J. -0.2J.3C. -a.3F.2J. -5.3G.2J. -0.2J.3M. -d.2J.3Q. -5.40.2J. -0.2J.47. -0.2J.4d. -d.2J.4O. -d.2J.4Q. -5.4T.2J. -d.2J.4V. -d.2J.51. -0.2J.52. -a.5a.2J. -2.5b.2J. -5.5d.2J. -0.2J.5B. -0.2J.5C. -5.5F.2J. -d.2J.5H. -d.2J.5I. -f.2J.5K. -7.5M.2J. -d.2J.5O. -0.2J.5R. -0.2J.5U. -d.2J.5W. -1.5Y.2J. -0.2J.5Z. -d.2J.5/. -0.2J.60. -2.62.2J. -2.66.2J. -a.6b.2J. -c.6e.2J. -0.2J.6F. -0.6M.2J. -d.2J.6+. -d.2J.71. -5.79.2J. -0.2J.7D. -0.2J.2p. -0.2J.2p. +0.2L.2G. +0.K.7A. +0.U.7t. +1.6L.1M. +2.1d.76. +2.3.7S. +2.4.7R. +2.A.7G. +2.c.7O. +2.k.7M. +3.1u.6R. +3.2B.2S. +3.6V.1m. +5./.7m. +9.22.6n. +b.6+.1h. +d.71.1g. +e.21.6o. +f.1W.6E. +g.2E.2P. +0.1F.6O. +0.1P.6J. +0.22.6o. +0.2C.2S. +0.L.7A. +0.V.7t. +1.6L.1N. +2.1d.77. +2.2g.6a. +2.4.7S. +2.5.7R. +2.A.7H. +3.6R.1v. +3.6V.1n. +5./.7n. +5.1h.6/. +5.1t.6T. +5.1V.6F. +6.6s.1Z. +9.1+.6r. +b.6+.1i. +e.21.6p. +f.2Q.2D. +0.1E.6P. +0.1G.6O. +0.1K.6M. +0.1Q.6J. +0.22.6p. +0.23.6o. +0.W.7t. +1.6L.1O. +2.1d.78. +2.2H.2L. +2.5.7S. +2.c.7P. +3.6R.1w. +3.6V.1o. +3.M.7A. +4.1X.6E. +5./.7o. +5.1h.70. +6.6s.1+. +9.24.6n. +a.2F.2M. +a.2J.2J. +b.6+.1j. +d.2U.2x. +e.0.7V. +e.6.7R. +f.2R.2D. +g.2E.2Q. +0.1F.6P. +0.1H.6O. +0.1L.6M. +0.23.6p. +0.24.6o. +0.X.7t. +1.22.6q. +1.2n.5U. +1.6L.1P. +2.0.7W. +2.6.7S. +2.7.7R. +2.r.7K. +3.6R.1x. +3.6V.1p. +5.1h.71. +6.6s.1/. +9.1+.6t. +a.2V.2w. +e.1.7V. +f.1Z.6u. +g.2E.2R. +h.1Y.6x. +0.1+.6u. +0.1G.6P. +0.1I.6O. +0.1M.6M. +0.1Z.6v. +0.24.6p. +0.25.6o. +0.2M.2G. +0.2S.2D. +1.6L.1Q. +2.1.7W. +2.1d.7a. +2.7.7S. +2.8.7R. +2.s.7K. +3.2W.2w. +3.6R.1y. +3.6V.1q. +5.1h.72. +5.1V.6G. +6.6s.20. +9.26.6n. +c.Y.7t. +d.71.1i. +e.2.7V. +f.1R.6H. +0.1+.6v. +0.1H.6P. +0.1J.6O. +0.1S.6H. +0.25.6p. +0.26.6o. +0.N.7A. +0.Z.7t. +1.24.6q. +1.2n.5V. +2.2.7W. +2.8.7S. +2.9.7R. +3.6R.1z. +3.6V.1r. +4.1N.6M. +5./.7p. +5.1h.73. +6.6s.21. +9.22.6r. +b.6+.1k. +d.71.1j. +e.3.7V. +f.1/.6u. +g.2E.2S. +0.1/.6v. +0.1I.6P. +0.1O.6M. +0.20.6u. +0.26.6p. +0.27.6o. +0.2L.2I. +0.O.7A. +2.1d.7c. +2.2H.2M. +2.3.7W. +2.9.7S. +2.a.7R. +2.c.7Q. +2.u.7K. +3.6R.1A. +3.6V.1s. +5./.7q. +5.1h.74. +5.1t.6U. +6.6s.22. +a.2F.2N. +a.2J.2K. +b.6+.1l. +d.2U.2y. +e.4.7V. +f.1R.6J. +0.1J.6P. +0.1P.6M. +0.1S.6J. +0.20.6v. +0.27.6p. +0.P.7A. +1.26.6q. +2.1d.7d. +2.4.7W. +2.a.7S. +2.b.7R. +3.2x.2V. +3.6R.1B. +5./.7r. +5.1h.75. +6.6s.23. +6.6V.1t. +9.22.6t. +9.24.6r. +a.2F.2O. +b.6+.1m. +d.2U.2z. +e.21.6u. +e.5.7V. +0.1K.6O. +0.1Q.6M. +0.1V.6H. +0.2N.2G. +0.Q.7A. +1.1+.6w. +1.6L.1R. +2.1d.7e. +2.5.7W. +2.b.7S. +3.1u.6V. +3.2W.2x. +3.6R.1C. +5.1h.76. +5.1t.6W. +6.6s.24. +7.r.7L. +a.2P.2F. +b.6+.1n. +c.28.6o. +d.2U.2A. +d.71.1k. +e.21.6v. +e.6.7V. +f.22.6u. +h.1Z.6x. +0.+.7t. +0.1L.6O. +0.1W.6H. +0.22.6v. +0.23.6u. +0.2O.2G. +0.R.7A. +1.6L.1S. +1.c.7S. +2.1d.7f. +2.2n.5W. +2.6.7W. +2.r.7M. +3.6R.1D. +3.6V.1v. +5./.7s. +5.1h.77. +5.1t.6X. +6.6s.25. +7.s.7L. +8.6z.1Y. +9.1V.6I. +9.24.6t. +9.26.6r. +a.2Y.2w. +b.6+.1o. +c.28.6p. +d.2U.2B. +d.71.1l. +e.7.7V. +h.1+.6x. +0./.7t. +0.1K.6P. +0.1M.6O. +0.1V.6J. +0.23.6v. +0.24.6u. +0.2I.2M. 0.2K.2K. -c.2M.2K. -9.2N.2K. -2.2O.2K. -9.2Q.2K. -7.2S.2K. -0.2T.2K. -1.2U.2K. -9.2V.2K. -1.2W.2K. -0.2X.2K. -b.2+.2K. +2.2H.2N. +2.2n.5X. +2.7.7W. +2.s.7M. +3.6V.1w. +3.S.7A. +5.1h.78. +5.1t.6Y. +6.6s.26. +9.1+.6y. +a.2F.2Q. +a.2P.2G. +b.6+.1p. +d.2U.2C. +d.71.1m. +e.8.7V. +h.1/.6x. +0.10.7t. +0.1L.6P. +0.1W.6J. +0.1X.6H. +0.24.6v. +0.25.6u. +2.1d.7h. +2.2H.2O. +2.2n.5Y. +2.8.7W. +2.k.7O. +3.2y.2V. +3.6R.1E. +3.6V.1x. +4.1N.6O. +5.1h.79. +5.1t.6Z. +5.c.7T. +6.6s.27. +7.u.7L. +9.1V.6K. +9.26.6t. +a.2F.2R. +b.6+.1q. +c.T.7A. +d.71.1n. +e.9.7V. +h.20.6x. +0.11.7t. +0.1M.6P. +0.1O.6O. +0.25.6v. +0.2Q.2G. +0.U.7A. +1.22.6w. +1.2n.5Z. +1.6L.1V. +2.0.7Y. +2.2H.2P. +2.9.7W. +2.u.7M. +3.2W.2y. +3.6R.1F. +3.6V.1y. +4.26.6u. +5./.7u. +5.1h.7a. +6.6B.1Y. +8.2z.2V. +a.2J.2L. +b.6+.1r. +d.71.1o. +e.21.6x. +e.a.7V. +f.1R.6M. +0.1P.6O. +0.1S.6M. +0.1X.6J. +0.26.6v. +0.2R.2G. +0.V.7A. +1.6L.1W. +1.c.7U. +2.1.7Y. +2.1d.7i. +2.2n.5+. +2.a.7W. +2.k.7P. +3.2x.2Y. +3.6R.1G. +3.6V.1z. +4.12.7t. +4.1N.6P. +5.1h.7b. +8.2A.2V. +8.2z.2W. +a.2F.2S. +b.6+.1s. +c.28.6s. +d.2U.2D. +d.71.1p. +e.0.7Z. +e.b.7V. +f.27.6u. +h.22.6x. +0.1O.6P. +0.1Q.6O. +0.27.6v. +0.2N.2I. +0.W.7A. +1.24.6w. +2.1d.7j. +2.2.7Y. +2.2H.2Q. +2.A.7K. +2.b.7W. +3.2B.2V. +3.6R.1H. +3.6V.1A. +5./.7v. +5.1h.7c. +8.2A.2W. +8.6z.1Z. +9.22.6y. +b.6+.1t. +d.2U.2E. +d.71.1q. +e.1.7Z. +h.23.6x. +i.c.7V. +0.1P.6P. +0.2G.2S. +0.2I.2O. +0.X.7A. +1.6L.1X. +2.1d.7k. +2.2H.2R. +2.3.7Y. +3.2B.2W. +3.6R.1I. +3.6V.1B. +5./.7w. +5.1h.7d. +5.1t.6/. +8.6z.1+. +a.2V.2C. +b.6+.1u. +c.28.6u. +d.71.1r. +e.2.7Z. +h.24.6x. +0.13.7t. +0.1V.6M. +0.25.6x. +0.2L.2K. +1.26.6w. +2.4.7Y. +3.2W.2C. +3.6R.1J. +3.6V.1C. +5./.7x. +5.1h.7e. +5.1t.70. +8.6z.1/. +9.24.6y. +a.2J.2M. +a.2P.2I. +b.6+.1v. +c.28.6v. +c.Y.7A. +d.71.1s. +e.3.7Z. +f.1Q.6P. +0.14.7t. +0.1W.6M. +0.1Z.6B. +0.Z.7A. +1.c.7X. +2.1d.7m. +2.2H.2S. +2.k.7Q. +3.2y.2Y. +3.6V.1D. +5.1h.7f. +5.1t.71. +8.6z.20. +9.1+.6A. +b.6+.1w. +e.4.7Z. +h.26.6x. +0.1+.6B. +0.15.7t. +0.2Q.2I. +0.d.7V. +2.1d.7n. +2.1V.6N. +2.2n.5/. +2.6.7Y. +5.1h.7g. +5.1t.72. +8.2+.2w. +8.2z.2Y. +8.6z.21. +9.26.6y. +a.2V.2D. +b.6+.1x. +d.71.1u. +e.5.7Z. +f.1R.6O. +h.27.6x. +0.1/.6B. +0.16.7t. +0.1X.6M. +0.e.7V. +2.1d.7o. +2.2n.60. +2.7.7Y. +3.2W.2D. +3.6R.1K. +3.6V.1E. +4.1S.6O. +5./.7y. +5.1h.7h. +5.1t.73. +8.2A.2Y. +8.6z.22. +b.6+.1y. +d.71.1v. +e.6.7Z. +f.2I.2R. +g.2E.2V. +0.17.7t. +0.20.6B. +0.2M.2K. +0.f.7V. +1.2n.61. +1.k.7S. +2.8.7Y. +2.A.7M. +2.r.7O. +3.2B.2Y. +3.6R.1L. +3.6V.1F. +5.1t.74. +8.6z.23. +9.1+.6C. +a.2J.2N. +b.6+.1z. +d.71.1w. +e.7.7Z. +f.1R.6P. +g.2E.2W. +h.28.6x. +0.+.7A. +0.18.7t. +0.1S.6P. +0.2I.2S. +0.g.7V. +1.2n.62. +2.2/.2w. +2.9.7Y. +2.s.7O. +3.6R.1M. +3.6V.1G. +4./.7z. +5.1h.7i. +5.1t.75. +7.1+.6D. +9.22.6A. +9.24.6z. +a.2J.2O. +a.2Y.2C. +b.6+.1A. +d.71.1x. +e.8.7Z. +f.1Z.6E. +j.21.6B. +0./.7A. +0.1+.6E. +0.19.7t. +0.22.6B. +0.2L.2L. +0.h.7V. +2.1d.7p. +2.1h.7j. +2.a.7Y. +2.r.7P. +3.6R.1N. +3.6V.1H. +5.1t.76. +5.k.7T. +8.2+.2x. +8.6z.25. +a.2P.2J. +b.6+.1B. +d.2U.2F. +d.71.1y. +e.9.7Z. +f.1V.6O. +0.1/.6E. +0.10.7A. +0.1a.7t. +0.1W.6O. +0.i.7V. +2.b.7Y. +2.s.7P. +2.u.7O. +3.6R.1O. +3.6V.1I. +5.1h.7k. +5.1t.77. +5.29.6n. +8.6z.26. +9.24.6A. +b.6+.1C. +d.71.1z. +e.a.7Z. +f.23.6B. +0.11.7A. +0.1b.7t. +0.1V.6P. +0.20.6E. +0.24.6B. +0.29.6o. +0.2N.2K. +0.j.7V. +1.c.7Y. +1.k.7U. +2.2n.63. +3.6R.1P. +3.6V.1J. +5.1h.7l. +5.1t.78. +7.1+.6F. +8.6z.27. +a.2J.2Q. +a.2Y.2D. +b.6+.1D. +d.2U.2G. +d.71.1A. +e.b.7Z. +f.22.6C. +j.2a.6n. +0.1c.7t. +0.1W.6P. +0.1X.6O. +0.1Y.6H. +0.25.6B. +0.29.6p. +0.2a.6o. +0.2O.2K. +0.2w.30. +0.k.7V. +2.2/.2x. +2.u.7P. +3.6R.1Q. +4.12.7A. +5.1h.7m. +5.1t.79. +7.22.6D. +9.26.6A. +a.2J.2R. +d.71.1B. +e.21.6E. +g.2E.2Y. +i.c.7Z. +0.22.6E. +0.26.6B. +0.2a.6p. +0.2L.2M. +0.l.7V. +1.29.6q. +2.1d.7s. +2.2H.2U. +2.r.7Q. +5./.7B. +5.1h.7n. +5.1t.7a. +8.2+.2y. +8.6z.28. +9.24.6C. +a.2P.2K. +b.6+.1E. +d.71.1C. +0.1d.7t. +0.23.6E. +0.27.6B. +0.m.7V. +2.2n.64. +2.s.7Q. +3.6V.1K. +4.1X.6P. +4.6J.1Y. +5./.7C. +5.1h.7o. +5.1t.7b. +7.24.6D. +8.2+.2z. +a.2J.2S. +a.2V.2F. +b.6+.1F. +d.71.1D. +f.1+.6G. +0.13.7A. +0.24.6E. +0.2c.6o. +0.2Q.2K. +1.k.7X. +3.2W.2F. +3.6V.1L. +3.n.7V. +5./.7D. +5.1t.7c. +5.29.6r. +7.22.6F. +8.2+.2A. +9.26.6C. +b.6+.1G. +0.14.7A. +0.25.6E. +0.2c.6p. +0.2R.2K. +0.o.7V. +1.2n.65. +1.6L.1Y. +1.r.7S. +2.1d.7u. +2.2/.2y. +2.u.7Q. +3.2x.30. +3.6R.1R. +3.6V.1M. +4.d.7Z. +5./.7E. +5.1t.7d. +5.c.7+. +6.6s.29. +7.26.6D. +8.2+.2B. +a.2V.2G. +b.6+.1H. +c.28.6B. +d.71.1E. +j.2a.6r. +0.15.7A. +0.1e.7t. +0.1Z.6H. +0.26.6E. +0.2N.2L. +0.p.7V. +1.s.7S. +2.2/.2z. +3.2W.2G. +3.6R.1S. +3.6V.1N. +4.e.7Z. +5.1h.7p. +5.1t.7e. +5.29.6t. +5.6s.2a. +7.24.6F. +8.2+.2C. +b.6+.1I. +d.2U.2I. +d.71.1F. +f.2M.2M. +0.1+.6H. +0.16.7A. +0.1f.7t. +0.27.6E. +0.2K.2S. +0.2L.2O. +0.q.7V. +2.1d.7v. +2.2/.2A. +2.2H.2V. +2.2n.66. +2.A.7O. +3.6V.1O. +4.f.7Z. +4.r.7T. +5.1h.7q. +5.1t.7f. +6.6o.2d. +7.22.6G. +b.6+.1J. +d.71.1G. +f.29.6u. +j.2a.6t. +0.1/.6H. +0.17.7A. +0.29.6v. +1.2n.67. +1.u.7S. +2.2/.2B. +2.2H.2W. +3.6V.1P. +4.g.7Z. +4.s.7T. +5./.7F. +5.1h.7r. +5.1t.7g. +5.1V.6Q. +5.2a.6u. +6.6p.2d. +7.26.6F. +9.1+.6I. +a.2P.2L. +d.71.1H. +f.1Z.6J. +h.1g.7t. +0.1+.6J. +0.18.7A. +0.20.6H. +1.r.7U. +2.1d.7x. +2.2/.2C. +2.2n.68. +2.A.7P. +3.2y.30. +3.6R.1V. +3.6V.1Q. +4.h.7Z. +5.1t.7h. +5.2a.6v. +5.c.7/. +6.6M.1Y. +6.6s.2c. +7.24.6G. +8.2+.2D. +a.2Y.2F. +c.28.6E. +d.71.1I. +0.1/.6J. +0.19.7A. +0.2N.2M. +0.2Q.2L. +0.r.7V. +1.2f.6n. +1.31.2w. +1.6L.1Z. +1.s.7U. +3.6R.1W. +4.i.7Z. +5./.7G. +5.1h.7s. +7.u.7T. +8.2+.2E. +8.2z.30. +9.1+.6K. +b.6+.1K. +d.71.1J. +j.21.6H. +0.1a.7A. +0.1h.7t. +0.20.6J. +0.2M.2O. +0.s.7V. +1.29.6w. +1.6L.1+. +1.c.80. +1.k.7Y. +4.j.7Z. +5./.7H. +5.1t.7i. +5.1V.6T. +6.6v.2b. +7.26.6G. +8.2A.30. +a.2V.2I. +a.2Y.2G. +b.6+.1L. +f.22.6H. +f.2c.6u. +f.2L.2R. +0.1b.7A. +0.1i.7t. +0.23.6H. +0.2c.6v. +0.t.7V. +1.2n.69. +1.6L.1/. +1.u.7U. +2.1t.7j. +2.2/.2D. +3.2B.30. +3.2W.2I. +3.6R.1X. +4.k.7Z. +5./.7I. +5.5L.2q. +6.6s.2d. +9.22.6I. +a.2P.2M. +b.6+.1M. +d.2U.2J. +h.29.6x. +j.21.6J. +0.1c.7A. +0.1j.7t. +0.22.6J. +0.24.6H. +0.2C.30. +0.2L.2S. +0.u.7V. +1.2f.6q. +1.2n.6a. +1.6L.20. +1.r.7X. +2.2/.2E. +2.2H.2Y. +2.A.7Q. +3.6V.1R. +4.l.7Z. +5./.7J. +5.1h.7u. +5.1t.7k. +5.29.6y. +5.2a.6x. +b.6+.1N. +d.71.1K. +0.1Z.6M. +0.23.6J. +0.25.6H. +0.2d.6u. +0.2N.2N. +0.2Q.2M. +0.v.7V. +1.31.2x. +1.6L.21. +1.s.7X. +3.6V.1S. +4.m.7Z. +5.1t.7l. +9.22.6K. +9.24.6I. +a.32.2w. +b.6+.1O. +d.71.1L. +0.1+.6M. +0.1d.7A. +0.24.6J. +0.2N.2O. +0.6v.2d. +0.w.7V. +1.2f.6r. +1.2n.6b. +1.6L.22. +3.n.7Z. +4.26.6H. +5.1h.7v. +5.1t.7m. +6.6O.1Y. +7.6x.2b. +b.6+.1P. +d.71.1M. +f.2M.2R. +0.1/.6M. +0.1k.7t. +0.25.6J. +0.27.6H. +0.x.7V. +1.6L.23. +1.u.7X. +4.2O.2O. +4.o.7Z. +5.1h.7w. +5.1t.7n. +5.1V.6U. +5.k.7+. +6.6v.2e. +9.24.6K. +9.26.6I. +a.2P.2N. +b.6+.1Q. +d.2U.2K. +d.71.1N. +f.30.2D. +h.2c.6x. +0.20.6M. +0.2M.2S. +0.6P.1Y. +0.y.7V. +1.2f.6t. +1.33.2w. +1.6L.24. +3.1l.7t. +3.6V.1V. +4.26.6J. +4.p.7Z. +5.1h.7x. +5.1t.7o. +5.2q.5O. +a.2P.2O. +a.2V.2J. +a.2Y.2I. +d.71.1O. +g.2E.30. +k.6N.1+. +0.1e.7A. +0.1m.7t. +0.27.6J. +0.2Q.2N. +0.2w.34. +1.31.2y. +1.6L.25. +2.A.7T. +3.2W.2J. +3.6V.1W. +4.q.7Z. +5.1V.6W. +5.2q.5P. +5.c.81. +8.2+.2F. +8.6z.29. +9.26.6K. +a.2P.2P. +a.32.2x. +c.28.6H. +d.71.1P. +f.7V.z. +j.21.6M. +0.1f.7A. +0.1n.7t. +0.22.6M. +0.2N.2R. +0.2Q.2O. +1.31.2z. +1.6L.26. +1.c.82. +2.1d.7B. +5.1V.6X. +6.6v.2f. +8.6z.2a. +d.71.1Q. +h.2d.6x. +0.1o.7t. +0.23.6M. +0.7A.1g. +1.31.2A. +1.6L.27. +1.c.83. +1.r.7Y. +2.1d.7C. +3.6V.1X. +5.1h.7y. +5.1t.7p. +5.1V.6Y. +5.29.6A. +5.2q.5Q. +5.k.7/. +7.6x.2e. +8.2+.2G. +a.2P.2Q. +b.6+.1R. +c.28.6J. +f.1Z.6O. +f.2R.2O. +0.1+.6O. +0.1p.7t. +0.24.6M. +0.29.6B. +0.2N.2S. +0.A.7V. +0.r.7Z. +1.31.2B. +1.33.2x. +1.s.7Y. +2.1d.7D. +2.2/.2F. +2.22.6N. +5.1t.7q. +5.1V.6Z. +5.c.84. +8.6z.2b. +a.2P.2R. +a.2V.2K. +b.6+.1S. +0.1/.6O. +0.1q.7t. +0.1Z.6P. +0.25.6M. +0.2O.2S. +0.2Q.2Q. +0.B.7V. +1.2f.6w. +1.31.2C. +1.6L.28. +1.k.80. +2.2H.2+. +3.2W.2K. +3.2x.34. +4.s.7Z. +5.1h.7z. +5.1t.7r. +5.2a.6B. +6.85.c. +8.6z.2c. +a.32.2y. +d.2U.2L. +0.1+.6P. +0.1h.7A. +0.1r.7t. +0.20.6O. +0.C.7V. +1.2n.6c. +1.c.86. +1.u.7Y. +2.2/.2G. +2.24.6N. +4.26.6M. +4.t.7Z. +5.29.6C. +5.2q.5R. +7.6x.2f. +8.2z.32. +a.2P.2S. +a.2Y.2J. +d.71.1R. +f.2Q.2R. +0.1/.6P. +0.1i.7A. +0.1s.7t. +0.27.6M. +0.D.7V. +1.2f.6y. +2./.7K. +4.u.7Z. +5.1t.7s. +5.29.6D. +8.2A.32. +b.6+.1V. +d.71.1S. +e.21.6O. +f.2R.2R. +f.37.2w. +0.1j.7A. +0.20.6P. +0.22.6O. +0.29.6E. +0.2c.6B. +0.2Q.2S. +0.E.7V. +0.v.7Z. +1.31.2D. +1.33.2y. +2.1d.7F. +2.26.6N. +2.2H.2/. +3.2B.32. +4.1t.7t. +5.1V.6/. +7.r.7+. +8.6z.2d. +a.2F.30. +b.6+.1W. +0.23.6O. +0.F.7V. +1.31.2E. +1.33.2z. +3.1u.7t. +3.2y.34. +3.6R.1Y. +4.s.7+. +4.w.7Z. +5.1V.70. +5.c.87. +6.85.d. +8.2+.2I. +8.6z.2e. +a.32.2C. +c.28.6M. +d.2U.2M. +e.21.6P. +f.2a.6E. +f.2R.2S. +0.1v.7t. +0.22.6P. +0.24.6O. +0.30.2G. +1.33.2A. +2.1d.7G. +4.x.7Z. +5.1h.7B. +5.1t.7u. +5.29.6F. +5.c.88. +6.85.e. +8.2z.34. +a.2V.2L. +a.2Y.2K. +b.6+.1X. +d.71.1V. +0.1k.7A. +0.1w.7t. +0.23.6P. +0.25.6O. +0.2S.2S. +0.y.7Z. +1.33.2B. +2.1d.7H. +3.2W.2L. +3.2x.37. +4.u.7+. +5.1h.7C. +5.1V.72. +5.c.89. +6.6B.2d. +6.85.f. +8.2A.34. +d.71.1W. +f.39.2w. +0.1x.7t. +0.24.6P. +0.26.6O. +0.G.7V. +1.33.2C. +1.6g.2h. +2.2/.2I. +2.2H.30. +3.1l.7A. +3.2B.34. +5./.7L. +5.1h.7D. +5.1t.7v. +5.1V.73. +5.c.8a. +5.k.81. +6.85.g. +7.r.7/. +8.6z.2f. +a.32.2D. +f.2c.6E. +f.7Z.z. +0.1m.7A. +0.1y.7t. +0.25.6P. +0.27.6O. +0.2C.34. +0.H.7V. +1.6g.2i. +1.k.82. +2./.7M. +5.1h.7E. +5.1t.7w. +5.1V.74. +5.29.6G. +5.c.8b. +6.85.h. +7.s.7/. +d.2U.2N. +d.71.1X. +g.2E.32. +0.1n.7A. +0.1z.7t. +0.26.6P. +1.2f.6A. +1.6g.2j. +1.k.83. +1.r.80. +2.2n.6d. +2.A.7Y. +3.6R.1Z. +3.I.7V. +5.1t.7x. +5.1V.75. +5.c.8c. +6.85.i. +7.1+.6Q. +a.2V.2M. +d.2U.2O. +0.1A.7t. +0.1o.7A. +0.27.6P. +0.2d.6E. +1.33.2D. +1.6g.2k. +1.s.80. +2.2n.6e. +3.2W.2M. +3.2x.39. +3.2y.37. +3.6R.1+. +3.J.7V. +4.A.7Z. +5.1V.76. +5.c.8d. +5.k.84. +6.85.j. +7.u.7/. +8.2+.2J. +c.28.6O. +d.2U.2P. +0.1B.7t. +0.1p.7A. +0.2I.30. +0.34.2D. +0.K.7V. +1.33.2E. +1.6g.2l. +3.6R.1/. +4.B.7Z. +5.1h.7F. +5.1V.77. +5.c.8e. +6.85.k. +8.2z.37. +a.2Y.2L. +f.2w.3e. +0.1C.7t. +0.1q.7A. +0.29.6H. +0.L.7V. +1.2f.6C. +1.2n.6f. +1.31.2F. +1.u.80. +3.6R.20. +3.6V.1Y. +4.C.7Z. +5.1t.7y. +5.1V.78. +5.c.8f. +6.85.l. +7.1+.6T. +8.2A.37. +c.28.6P. +d.2U.2Q. +e.k.86. +g.2E.34. +0.1D.7t. +0.1r.7A. +1.2f.6D. +2.2/.2J. +2.c.8g. +3.2B.37. +3.6R.21. +3.M.7V. +4.3f.2w. +4.D.7Z. +5.1h.7G. +5.1V.79. +5.29.6I. +5.2a.6H. +5.2q.5T. +6.85.m. +7.22.6Q. +a.2V.2N. +d.2U.2R. +0.1s.7A. +1.31.2G. +2.c.8h. +3.2W.2N. +3.2y.39. +3.6R.22. +3.n.85. +4.1t.7z. +4.E.7Z. +5.1h.7H. +5.1V.7a. +8.2+.2K. +a.2V.2O. +f.29.6J. +f.37.2C. +j.2a.6I. +0.1E.7t. +0.1t.7A. +3.2W.2O. +3.2x.3e. +3.6R.23. +4.F.7Z. +5.1h.7I. +5.1V.7b. +5.29.6K. +5.2a.6J. +5.k.87. +6.85.o. +8.2z.39. +a.2P.2V. +a.2Y.2M. +d.2U.2S. +f.24.6Q. +0.1F.7t. +0.N.7V. +1.2f.6F. +1.6L.29. +2.2H.31. +3.1u.7A. +3.2W.2P. +3.6R.24. +5.1h.7J. +5.1V.7c. +5.k.88. +6.85.p. +7.22.6T. +7.r.81. +8.2A.39. +a.32.2F. +f.2c.6H. +j.2a.6K. +0.1G.7t. +0.1v.7A. +0.2w.3g. +0.O.7V. +1.2n.6h. +1.6L.2a. +1.r.82. +2.2/.2K. +3.2B.39. +3.2x.3f. +3.6R.25. +3.6V.1Z. +5.1V.7d. +5.c.8i. +5.k.89. +6.85.q. +7.1+.6U. +7.26.6Q. +7.s.81. +a.2J.30. +a.2V.2Q. +c.37.2D. +0.1H.7t. +0.1w.7A. +0.P.7V. +1.r.83. +1.s.82. +3.2W.2Q. +3.6R.26. +3.6V.1+. +4.G.7Z. +5.1V.7e. +5.k.8a. +a.2V.2R. +a.32.2G. +f.24.6T. +f.25.6S. +f.2C.39. +f.2c.6J. +g.2E.37. +0.1I.7t. +0.1x.7A. +0.2d.6H. +0.Q.7V. +1.2f.6G. +1.33.2F. +1.6L.2b. +1.s.83. +2./.7O. +2.1d.7K. +3.2W.2R. +3.2y.3e. +3.6R.27. +3.6V.1/. +4.H.7Z. +5.1t.7B. +5.1V.7f. +5.c.8j. +5.k.8b. +7.1+.6W. +7.r.84. +7.u.81. +a.2Y.2N. +b.6+.1Y. +0.1J.7t. +0.1y.7A. +0.29.6M. +0.2w.3h. +0.R.7V. +1.2n.6i. +1.31.2I. +1.6L.2c. +1.u.82. +2.2H.32. +3.6V.20. +3.I.7Z. +4.s.84. +5.1t.7C. +5.1V.7g. +5.k.8c. +6.85.r. +7.1+.6X. +7.26.6T. +8.2+.2L. +8.2z.3e. +a.2F.34. +a.2V.2S. +a.2Y.2O. +0.1z.7A. +0.2d.6J. 0.30.2K. -0.2K.35. -b.36.2K. -0.2K.37. -0.2K.38. -5.39.2K. -5.3d.2K. -0.2K.3h. -5.3i.2K. -0.2K.3m. -5.3p.2K. -a.3v.2K. -a.3y.2K. -2.3z.2K. -5.3A.2K. -2.3B.2K. -0.2K.3C. -a.3F.2K. -5.3G.2K. -0.2K.3M. -0.2K.3Q. -5.40.2K. -0.2K.47. -0.2K.4d. -0.2K.4O. -0.2K.4Q. -5.4T.2K. -0.2K.4V. -0.2K.51. -0.2K.52. -a.5a.2K. -2.5b.2K. -5.5d.2K. -0.2K.5B. -0.2K.5C. -5.5F.2K. -0.2K.5H. -0.5I.2K. -0.2K.5K. -7.5M.2K. -5.5O.2K. -0.2K.5R. -0.2K.5U. -0.2K.5W. -1.5Y.2K. -0.2K.5Z. -0.2K.5/. -0.60.2K. -2.62.2K. -2.66.2K. -a.6b.2K. -c.6e.2K. -0.2K.6F. -0.6M.2K. -0.2K.6+. -0.2K.71. -5.79.2K. -0.2K.7D. -0.2p.2K. -0.2p.2K. -1.4s.2L. -1.51.2L. -1.5v.2L. -5.5I.2L. -6.5K.2L. -7.5M.2L. -1.5Y.2L. -1.5+.2L. -4.60.2L. -5.62.2L. -1.6q.2L. -1.6t.2L. -8.6F.2L. -c.6H.2L. -8.6M.2L. -1.86.2L. -1.8d.2L. -c.2M.2M. -c.2M.2N. -c.2M.2O. -c.2M.2Q. -c.2M.2S. -c.2M.2T. -1.2U.2M. -c.2M.2V. -1.2W.2M. -c.2M.2X. -c.2M.2+. -c.2M.30. -c.2M.35. -c.2M.36. -c.2M.37. -c.2M.38. -c.2M.39. -c.2M.3d. -c.2M.3h. -c.2M.3i. -c.2M.3m. -c.2M.3p. -c.2M.3v. -c.2M.3y. -c.2M.3z. -c.2M.3A. -c.2M.3B. -c.2M.3C. -c.2M.3F. -c.2M.3G. -c.2M.3M. -c.2M.3Q. -c.2M.40. -c.2M.47. -c.2M.4d. -c.2M.4O. -c.2M.4Q. -c.2M.4T. -c.2M.4V. -c.2M.51. -c.2M.52. -c.2M.5a. -c.2M.5b. -c.2M.5d. -c.2M.5B. -c.2M.5C. -c.2M.5F. -c.2M.5H. -c.2M.5I. -c.2M.5K. -c.2M.5M. -c.2M.5O. -c.2M.5R. -c.2M.5U. -c.2M.5W. -1.5Y.2M. -c.2M.5Z. -c.2M.5/. -c.2M.60. -c.2M.62. -c.2M.66. -c.2M.6b. -c.6e.2M. -c.2M.6F. -c.2M.6M. -c.2M.6+. -c.2M.71. -c.2M.79. -c.2M.7D. -c.2M.2p. -c.2M.2p. -9.2N.2N. -2.2O.2N. -9.2N.2Q. -7.2S.2N. -9.2N.2T. -1.2U.2N. -9.2V.2N. -1.2W.2N. -9.2N.2X. -9.2N.2+. -9.2N.30. -9.2N.35. -9.2N.36. -9.2N.37. -9.2N.38. -9.2N.39. -9.2N.3d. -9.2N.3h. -9.2N.3i. -9.2N.3m. -9.2N.3p. -a.3v.2N. -a.3y.2N. -2.3z.2N. -9.2N.3A. -2.3B.2N. -9.2N.3C. -a.3F.2N. -5.3G.2N. -9.2N.3M. -9.2N.3Q. -5.40.2N. -9.2N.47. -9.2N.4d. -9.2N.4O. -9.2N.4Q. -7.4T.2N. -9.2N.4V. -9.2N.51. -9.2N.52. -a.5a.2N. -2.5b.2N. -5.5d.2N. -9.2N.5B. -9.2N.5C. -9.2N.5F. -9.2N.5H. -9.2N.5I. -f.2N.5K. -7.5M.2N. -9.2N.5O. -9.2N.5R. -9.2N.5U. -9.2N.5W. -1.5Y.2N. -9.2N.5Z. -9.2N.5/. -9.2N.60. -2.62.2N. -2.66.2N. -a.6b.2N. -c.6e.2N. -9.2N.6F. -9.2N.6M. -9.2N.6+. -9.2N.71. -9.2N.79. -9.2N.7D. -9.2N.2p. -9.2N.2p. -2.2O.2O. -2.2O.2Q. -7.2S.2O. -2.2O.2T. -1.2U.2O. -2.2O.2V. -1.2W.2O. -2.2O.2X. -2.2O.2+. -2.2O.30. -2.2O.35. -2.2O.36. -2.2O.37. -2.2O.38. -2.2O.39. -2.2O.3d. -2.2O.3h. -2.2O.3i. -2.2O.3m. -2.2O.3p. -a.3v.2O. -a.3y.2O. -2.3z.2O. -2.2O.3A. -2.3B.2O. -2.2O.3C. -a.3F.2O. -2.2O.3G. -2.2O.3M. -2.2O.3Q. -2.2O.40. -2.2O.47. -2.2O.4d. -2.2O.4O. -2.2O.4Q. -7.4T.2O. -2.2O.4V. -2.2O.51. -2.2O.52. -a.5a.2O. -2.5b.2O. -2.2O.5d. -2.2O.5B. -2.2O.5C. -2.2O.5F. -2.2O.5H. -2.2O.5I. -f.2O.5K. -7.5M.2O. -2.2O.5O. -2.2O.5R. -2.2O.5U. -2.2O.5W. -1.5Y.2O. -2.2O.5Z. -2.2O.5/. -2.2O.60. -2.62.2O. -2.66.2O. -a.6b.2O. -c.6e.2O. -2.2O.6F. -2.2O.6M. -2.2O.6+. -2.2O.71. -2.2O.79. -2.2O.7D. -2.2O.2p. -2.2O.2p. -1.4s.2P. -1.51.2P. -1.5v.2P. -6.5I.2P. -6.5K.2P. -7.5M.2P. -1.5Y.2P. -1.5+.2P. -4.60.2P. -5.62.2P. -1.6q.2P. -1.6t.2P. -3.6F.2P. -c.6H.2P. -8.6M.2P. -1.7B.2P. -1.7R.2P. -1.86.2P. -1.8d.2P. -9.2Q.2Q. -7.2S.2Q. -9.2Q.2T. -1.2U.2Q. -9.2V.2Q. -1.2W.2Q. -9.2Q.2X. -9.2Q.2+. -9.2Q.30. -9.2Q.35. -9.2Q.36. -9.2Q.37. -9.2Q.38. -9.2Q.39. -9.2Q.3d. -9.2Q.3h. -9.2Q.3i. -9.2Q.3m. -9.2Q.3p. -a.3v.2Q. -a.3y.2Q. -2.3z.2Q. -9.2Q.3A. -2.3B.2Q. -9.2Q.3C. -a.3F.2Q. -5.3G.2Q. -9.2Q.3M. -9.2Q.3Q. -5.40.2Q. -9.2Q.47. -9.2Q.4d. -9.2Q.4O. -9.2Q.4Q. -7.4T.2Q. -9.2Q.4V. -9.2Q.51. -9.2Q.52. -a.5a.2Q. -2.5b.2Q. -5.5d.2Q. -9.2Q.5B. -9.2Q.5C. -9.2Q.5F. -9.2Q.5H. -9.2Q.5I. -9.2Q.5K. -7.5M.2Q. -9.2Q.5O. -9.2Q.5R. -9.2Q.5U. -9.2Q.5W. -1.5Y.2Q. -9.2Q.5Z. -9.2Q.5/. -9.2Q.60. -2.62.2Q. -2.66.2Q. -a.6b.2Q. -c.6e.2Q. -9.2Q.6F. -9.2Q.6M. -9.2Q.6+. -9.2Q.71. -9.2Q.79. -9.2Q.7D. -9.2Q.2p. -9.2Q.2p. -1.51.2R. -1.5v.2R. -8.5I.2R. -8.5K.2R. -7.5M.2R. -1.5Y.2R. -1.5+.2R. -8.60.2R. -5.62.2R. -1.6q.2R. -1.6t.2R. -3.6F.2R. -c.6H.2R. -8.6M.2R. -1.7B.2R. -1.86.2R. -1.8d.2R. -7.2S.2S. -7.2S.2T. -1.2U.2S. -7.2S.2V. -1.2W.2S. -7.2S.2X. -7.2S.2+. -7.2S.30. -7.2S.35. -7.2S.36. -7.2S.37. -7.2S.38. -7.2S.39. -7.2S.3d. -7.2S.3h. -7.2S.3i. -7.2S.3m. -7.2S.3p. -7.2S.3v. -7.2S.3y. -7.2S.3z. -7.2S.3A. -7.2S.3B. -7.2S.3C. -7.2S.3F. -7.2S.3G. -7.2S.3M. -7.2S.3Q. -7.2S.40. -7.2S.47. -7.2S.4d. -7.2S.4O. -7.2S.4Q. -7.2S.4T. -7.2S.4V. -7.2S.51. -7.2S.52. -7.2S.5a. -7.2S.5b. -7.2S.5d. -7.2S.5B. -7.2S.5C. -7.2S.5F. -7.2S.5H. -7.2S.5I. -7.2S.5K. -7.2S.5M. -7.2S.5O. -7.2S.5R. -7.2S.5U. -7.2S.5W. -1.5Y.2S. -7.2S.5Z. -7.2S.5/. -7.2S.60. -7.2S.62. -7.2S.66. -7.2S.6b. -c.6e.2S. -7.2S.6F. -7.2S.6M. -7.2S.6+. -7.2S.71. -7.2S.79. -7.2S.7D. -7.2S.2p. -7.2S.2p. -0.2T.2T. -1.2U.2T. -9.2V.2T. -1.2W.2T. -0.2T.2X. -b.2+.2T. -d.30.2T. -d.2T.35. -b.36.2T. -0.2T.37. -0.2T.38. -5.39.2T. -5.3d.2T. -0.2T.3h. -5.3i.2T. -0.2T.3m. -5.3p.2T. -a.3v.2T. -a.3y.2T. -2.3z.2T. -5.3A.2T. -2.3B.2T. -0.2T.3C. -a.3F.2T. -5.3G.2T. -0.2T.3M. -0.2T.3Q. -5.40.2T. -0.2T.47. -0.2T.4d. -0.2T.4O. -0.2T.4Q. -5.4T.2T. -0.2T.4V. -d.2T.51. -0.2T.52. -a.5a.2T. -2.5b.2T. -5.5d.2T. -0.2T.5B. -0.2T.5C. -5.5F.2T. -0.2T.5H. -0.2T.5I. -f.2T.5K. -7.5M.2T. -d.2T.5O. -0.2T.5R. -0.2T.5U. -0.2T.5W. -1.5Y.2T. -0.2T.5Z. -0.2T.5/. -0.2T.60. -2.62.2T. -2.66.2T. -a.6b.2T. -c.6e.2T. -0.2T.6F. -0.6M.2T. -0.2T.6+. -0.2T.71. -g.79.2T. -0.2T.7D. -0.2p.2T. -0.2p.2T. -1.2U.2U. -1.2U.2V. -1.2U.2X. -1.2U.2+. -1.2U.30. -1.2U.35. -1.2U.36. -1.2U.37. -1.2U.38. -1.2U.39. -1.2U.3d. -1.2U.3h. -1.2U.3i. -1.2U.3m. -1.2U.3p. -1.2U.3v. -1.2U.3y. -1.2U.3z. -1.2U.3A. -1.2U.3B. -1.2U.3C. -1.2U.3F. -1.2U.3G. -1.2U.3M. -1.2U.3Q. -1.2U.40. -1.2U.47. -1.2U.4d. -1.2U.4O. -1.2U.4Q. -1.2U.4T. -1.2U.4V. -1.2U.51. -1.2U.52. -1.2U.5a. -1.2U.5b. -1.2U.5d. -1.2U.5B. -1.2U.5C. -1.2U.5F. -1.2U.5H. -1.2U.5I. -1.2U.5K. -1.2U.5M. -1.2U.5O. -1.2U.5R. -1.2U.5U. -1.2U.5W. -1.5Y.2U. -1.2U.5Z. -1.2U.5/. -1.2U.60. -1.2U.62. -1.2U.66. -1.2U.6b. -1.2U.6e. -1.2U.6F. -1.2U.6M. -1.2U.6+. -1.2U.71. -1.2U.79. -1.2U.7D. -1.2U.2p. -1.2U.2p. -9.2V.2V. -1.2W.2V. -9.2V.2X. -9.2V.2+. -9.2V.30. -9.2V.35. -9.2V.36. -9.2V.37. -9.2V.38. -9.2V.39. -9.2V.3d. -9.2V.3h. -9.2V.3i. -9.2V.3m. -9.2V.3p. -a.3v.2V. -a.3y.2V. -2.3z.2V. -9.2V.3A. -2.3B.2V. -9.2V.3C. -a.3F.2V. -9.2V.3G. -9.2V.3M. -9.2V.3Q. -5.40.2V. -9.2V.47. -9.2V.4d. -9.2V.4O. -9.2V.4Q. -7.4T.2V. -9.2V.4V. -9.2V.51. -9.2V.52. -a.5a.2V. -2.5b.2V. -9.2V.5d. -9.2V.5B. -9.2V.5C. -9.2V.5F. -9.2V.5H. -9.2V.5I. -9.2V.5K. -7.5M.2V. -9.2V.5O. -9.2V.5R. -9.2V.5U. -9.2V.5W. -1.5Y.2V. -9.2V.5Z. -9.2V.5/. -9.2V.60. -2.62.2V. -2.66.2V. -a.6b.2V. -c.6e.2V. -9.2V.6F. -9.2V.6M. -9.2V.6+. -9.2V.71. -9.2V.79. -9.2V.7D. -9.2V.2p. -9.2V.2p. -1.2W.2W. -1.2W.2X. -1.2W.2+. -1.2W.30. -1.2W.35. -1.2W.36. -1.2W.37. -1.2W.38. -1.2W.39. -1.2W.3d. -1.2W.3h. -1.2W.3i. -1.2W.3m. -1.2W.3p. -1.2W.3v. -1.2W.3y. -1.2W.3z. -1.2W.3A. -1.2W.3B. -1.2W.3C. -1.2W.3F. -1.2W.3G. -1.2W.3M. -1.2W.3Q. -1.2W.40. -1.2W.47. -1.2W.4d. -1.2W.4O. -1.2W.4Q. -1.2W.4T. -1.2W.4V. -1.2W.51. -1.2W.52. -1.2W.5a. -1.2W.5b. -1.2W.5d. -1.2W.5B. -1.2W.5C. -1.2W.5F. -1.2W.5H. -1.2W.5I. -1.2W.5K. -1.2W.5M. -1.2W.5O. -1.2W.5R. -1.2W.5U. -1.2W.5W. -1.5Y.2W. -1.2W.5Z. -1.2W.5/. -1.2W.60. -1.2W.62. -1.2W.66. -1.2W.6b. -1.2W.6e. -1.2W.6F. -1.2W.6M. -1.2W.6+. -1.2W.71. -1.2W.79. -1.2W.7D. -1.2W.2p. -1.2W.2p. -0.2X.2X. -b.2+.2X. -0.30.2X. -0.2X.35. -b.36.2X. -0.2X.37. -0.2X.38. -5.39.2X. -5.3d.2X. -d.2X.3h. -5.3i.2X. -0.2X.3m. -5.3p.2X. -a.3v.2X. -a.3y.2X. -2.3z.2X. -5.3A.2X. -2.3B.2X. -0.2X.3C. -a.3F.2X. -5.3G.2X. -0.2X.3M. -0.2X.3Q. -5.40.2X. -0.2X.47. -0.2X.4d. -0.2X.4O. -d.2X.4Q. -5.4T.2X. -d.2X.4V. -0.2X.51. -0.2X.52. -a.5a.2X. -2.5b.2X. -5.5d.2X. -0.2X.5B. -0.2X.5C. -5.5F.2X. -0.2X.5H. -0.2X.5I. -f.2X.5K. -7.5M.2X. -0.2X.5O. -0.2X.5R. -d.2X.5U. -0.2X.5W. -1.5Y.2X. -0.2X.5Z. -0.2X.5/. -0.2X.60. -2.62.2X. -2.66.2X. -a.6b.2X. -c.6e.2X. -0.2X.6F. -0.6M.2X. -0.2X.6+. -d.2X.71. -5.79.2X. -d.2X.7D. -0.2p.2X. -0.2p.2X. -1.4s.2Y. -1.51.2Y. -1.5v.2Y. -8.5I.2Y. -8.5K.2Y. -7.5M.2Y. -1.5Y.2Y. -1.5+.2Y. -8.60.2Y. -8.62.2Y. -1.6q.2Y. -1.6t.2Y. -3.6F.2Y. -c.6H.2Y. -3.6M.2Y. -1.7B.2Y. -1.86.2Y. -1.8d.2Y. -1.51.2Z. -1.5v.2Z. -8.5I.2Z. -8.5K.2Z. -7.5M.2Z. -1.5Y.2Z. -1.5+.2Z. -8.60.2Z. -5.62.2Z. -1.6q.2Z. -1.6t.2Z. -3.6F.2Z. -c.6H.2Z. -8.6M.2Z. -1.7B.2Z. -1.86.2Z. -1.8d.2Z. -d.2+.2+. -b.2+.30. -b.2+.35. -b.2+.36. -b.2+.37. -b.2+.38. -d.2+.39. -b.2+.3d. -d.2+.3h. -b.2+.3i. -d.2+.3m. -b.2+.3p. -a.3v.2+. -a.3y.2+. -2.3z.2+. -5.3A.2+. -2.3B.2+. -b.2+.3C. -a.3F.2+. -5.3G.2+. -b.2+.3M. -b.2+.3Q. -5.40.2+. -d.2+.47. -d.2+.4d. -d.2+.4O. -b.2+.4Q. -5.4T.2+. -b.2+.4V. -b.2+.51. -b.2+.52. -a.5a.2+. -2.5b.2+. -5.5d.2+. -b.2+.5B. -b.2+.5C. -b.2+.5F. -b.2+.5H. -d.2+.5I. -f.2+.5K. -7.5M.2+. -b.2+.5O. -d.2+.5R. -d.2+.5U. -b.2+.5W. -1.5Y.2+. -b.2+.5Z. -b.2+.5/. -b.2+.60. -2.62.2+. -2.66.2+. -a.6b.2+. -c.6e.2+. -b.2+.6F. -d.2+.6M. -b.2+.6+. -d.2+.71. -5.79.2+. -b.2+.7D. -d.2+.2p. -d.2+.2p. -1.51.2/. -1.5v.2/. -8.5I.2/. -8.5K.2/. -7.5M.2/. -1.5Y.2/. -1.5+.2/. -5.60.2/. -5.62.2/. -1.6q.2/. -1.6t.2/. -8.6F.2/. -c.6H.2/. -8.6M.2/. -1.7B.2/. -1.86.2/. -1.8d.2/. +1.2n.6j. +1.33.2G. +1.c.8k. +1.u.83. +2./.7P. +3.2W.2S. +3.2x.3g. +3.2y.3f. +3.6R.28. +3.6V.21. +3.J.7Z. +3.S.7V. +5.1t.7D. +5.1V.7h. +5.k.8d. +6.3i.2w. +6.85.s. +7.1+.6Y. +7.22.6U. +8.2A.3e. +a.2P.2Y. +e.r.86. +f.2a.6M. +f.39.2D. +0.1A.7A. +0.2G.34. +1.c.8l. +2.29.6N. +2.2n.6k. +3.2B.3e. +3.6V.22. +4.K.7Z. +4.u.84. +5.1t.7E. +5.2q.5U. +5.k.8e. +6.85.t. +7.1+.6Z. +8.2z.3f. +c.T.7V. +d.71.1Y. +e.s.86. +g.2E.39. +0.1B.7A. +0.1K.7t. +0.L.7Z. +0.U.7V. +1.2n.6l. +1.6L.2d. +2.2/.2L. +2.2H.33. +3.6V.23. +5.1V.7i. +5.k.8f. +6.85.u. +7.22.6W. +7.24.6U. +8.2A.3f. +a.2Y.2Q. +f.2C.3e. +j.2a.6N. +0.1C.7A. +0.1L.7t. +0.2c.6M. +0.V.7V. +1.2f.6I. +1.6L.2e. +2.1V.7j. +2.2H.34. +2.k.8g. +3.2B.3f. +3.2x.3h. +3.6V.24. +3.M.7Z. +4.r.87. +5.2q.5V. +6.85.v. +7.22.6X. +8.2+.2M. +a.2Y.2R. +a.32.2I. +b.6+.1Z. +d.2U.2U. +e.u.86. +0.1D.7A. +0.1M.7t. +0.W.7V. +1.6N.2b. +2./.7Q. +2.1d.7M. +2.k.8h. +3.2x.3i. +3.2y.3g. +3.6V.25. +4.s.87. +5.1t.7F. +5.1V.7k. +5.c.8m. +6.3m.2w. +6.85.w. +7.22.6Y. +7.24.6W. +7.26.6U. +7.r.88. +b.6+.1+. +c.3f.2C. +0.29.6O. +0.2D.3e. +0.X.7V. +1.2f.6K. +1.31.2J. +2.2n.6m. +3.6V.26. +4.1N.7t. +5.1V.7l. +5.c.8n. +6.85.x. +7.1+.6/. +7.22.6Z. +7.24.6X. +7.r.89. +7.s.88. +8.2z.3g. +a.2F.37. +a.2Y.2S. +b.6+.1/. +0.1E.7A. +0.1O.7t. +0.2L.30. +1.2f.6L. +1.33.2I. +1.c.8o. +2.1h.7K. +2.2/.2M. +3.6V.27. +4.N.7Z. +4.u.87. +5.1t.7G. +5.1V.7m. +6.6M.2d. +6.85.y. +7.1+.70. +7.24.6Y. +7.26.6W. +7.r.8a. +7.s.89. +8.2A.3g. +b.6+.20. +c.Y.7V. +d.71.1Z. +f.2a.6O. +g.2E.3e. +0.1F.7A. +0.1P.7t. +0.29.6P. +0.2I.34. +0.Z.7V. +1./.7S. +1.c.8p. +3.2B.3g. +3.2y.3h. +4.O.7Z. +4.r.8b. +5.1t.7H. +5.1V.7n. +5.2g.6o. +5.k.8i. +6.85.z. +7.24.6Z. +7.26.6X. +7.s.8a. +7.u.88. +8.2+.2N. +b.6+.21. +c.37.2G. +c.3f.2D. +d.71.1+. +0.1G.7A. +0.1Q.7t. +0.2C.3g. +3.2x.3m. +3.2y.3i. +3.6V.28. +4.P.7Z. +4.s.8b. +4.u.89. +5.1t.7I. +5.1V.7o. +5.2a.6P. +5.2g.6p. +7.1+.72. +7.26.6Y. +7.r.8c. +8.2+.2O. +8.2z.3h. +b.6+.22. +d.2U.2V. +d.71.1/. +f.2w.3q. +g.2E.3f. +0.1H.7A. +0.2c.6O. +1.31.2K. +1.6N.2e. +1.c.8q. +2.2H.37. +2.A.84. +4.Q.7Z. +4.s.8c. +5./.7T. +5.1t.7J. +5.k.8j. +6.3r.2w. +7.1+.73. +7.22.6/. +7.26.6Z. +7.r.8d. +7.u.8a. +8.2+.2P. +8.2A.3h. +8.2z.3i. +a.2F.39. +a.32.2J. +b.6+.23. +d.2U.2W. +d.71.20. +0.1I.7A. +0.2M.30. +0.R.7Z. +2.2/.2N. +3.2B.3h. +4.s.8d. +4.u.8b. +5.2q.5Z. +5.6P.2b. +6.85.A. +7.1+.74. +7.22.70. +7.r.8e. +8.2A.3i. +b.6+.24. +d.71.21. +0.+.7V. +0.1J.7A. +0.2C.3h. +0.2c.6P. +0.2D.3g. +1./.7U. +1.k.8k. +2.2/.2O. +2.A.86. +3.2B.3i. +3.S.7Z. +4.u.8c. +5.1h.7L. +5.1V.7p. +6.85.B. +7.22.71. +7.24.6/. +7.r.8f. +7.s.8e. +8.2+.2Q. +b.6+.25. +f.1+.75. +f.39.2G. +0./.7V. +0.1R.7t. +0.2d.6O. +1.33.2J. +1.6N.2f. +1.k.8l. +2.1h.7M. +2.2/.2P. +2.r.8g. +3.2x.3q. +3.2y.3m. +4.s.8f. +4.u.8d. +5.1V.7q. +5.2g.6s. +6.3i.2C. +6.85.C. +7.1+.76. +7.22.72. +7.24.70. +8.2+.2R. +b.6+.26. +c.T.7Z. +d.71.23. +g.2E.3g. +0.10.7V. +0.1S.7t. +2.2H.39. +2.r.8h. +2.s.8g. +3.2x.3r. +4.U.7Z. +5.1V.7r. +6.85.D. +7.1+.77. +7.22.73. +7.26.6/. +7.u.8e. +8.2z.3m. +a.2J.34. +a.2V.2V. +a.32.2K. +b.6+.27. +d.71.24. +e.2g.6t. +f.37.2I. +0.11.7V. +0.1K.7A. +0.2N.30. +0.3h.2D. +0.6P.2d. +2.2/.2Q. +2.s.8h. +3.2W.2V. +4.u.8f. +4.V.7Z. +5.2g.6u. +6.85.E. +7.1+.78. +7.22.74. +7.24.72. +7.26.70. +8.2+.2S. +8.2A.3m. +9.c.8r. +a.2F.3e. +d.2U.2Y. +d.71.25. +0.1L.7A. +0.2O.30. +0.2w.3v. +1./.7X. +1.31.2L. +2.1d.7O. +2.2/.2R. +2.u.8g. +3.2B.3m. +3.2W.2W. +4.12.7V. +4.W.7Z. +5.1V.7s. +5.29.6Q. +5.6P.2e. +5.c.8s. +5.k.8m. +6.3i.2D. +6.85.F. +7.22.75. +7.24.73. +7.26.71. +8.2g.6v. +b.6+.28. +f.1+.79. +g.2E.3h. +0.1M.7A. +0.1V.7t. +1.33.2K. +2.A.89. +2.u.8h. +3.2y.3q. +3.6R.29. +4.2G.3e. +4.X.7Z. +5.k.8n. +6.3m.2C. +7.1+.7a. +7.22.76. +7.24.74. +7.26.72. +7.r.8i. +a.2F.3f. +a.2P.30. +d.71.27. +g.2E.3i. +0.1W.7t. +0.2I.39. +0.2K.34. +1.k.8o. +2.1d.7P. +2.2/.2S. +2.A.8a. +3.2y.3r. +3.6R.2a. +4.1N.7A. +7.1+.7b. +7.22.77. +7.24.75. +7.26.73. +7.s.8i. +8.2z.3q. +c.Y.7Z. +0.13.7V. +0.1O.7A. +0.2Q.30. +0.2w.3y. +1.k.8p. +2.2H.3e. +4.Z.7Z. +5.1V.7u. +5.29.6T. +5.2q.61. +5.6P.2f. +6.85.G. +7.1+.7c. +7.22.78. +7.26.74. +7.3f.2G. +7.r.8j. +8.2A.3q. +8.2z.3r. +d.71.28. +f.24.76. +0.14.7V. +0.1P.7A. +0.1X.7t. +0.2R.30. +1.31.2M. +3.2B.3q. +3.2x.3v. +5.2q.62. +6.3m.2D. +6.6R.2b. +6.85.H. +7.1+.7d. +7.22.79. +7.24.77. +7.26.75. +7.s.8j. +7.u.8i. +8.2A.3r. +a.2J.37. +a.2V.2Y. +a.32.2L. +f.2g.6x. +0.15.7V. +0.1Q.7A. +1.k.8q. +1.r.8k. +2.1t.7K. +2.2g.6y. +2.2H.3f. +3.2B.3r. +3.2W.2Y. +3.6R.2c. +3.I.85. +5.1V.7v. +7.1+.7e. +7.22.7a. +7.24.78. +7.26.76. +a.2F.3g. +f.2C.3q. +g.2E.3m. +0.16.7V. +0.30.2S. +1.r.8l. +1.s.8k. +2.1d.7Q. +2.A.8e. +3.J.85. +5.1V.7w. +5.c.8t. +6.3r.2C. +7.1+.7f. +7.22.7b. +7.24.79. +7.26.77. +7.u.8j. +0.+.7Z. +0.17.7V. +0.2G.3g. +1./.7Y. +1.33.2L. +1.s.8l. +3.2x.3y. +5.1V.7x. +6.85.K. +7.1+.7g. +7.22.7c. +7.24.7a. +7.26.78. +f.2I.3e. +0.18.7V. +0.2L.34. +0.3q.2D. +1.31.2N. +1.u.8k. +2.0.8A. +2.1h.7O. +3.2y.3v. +3.6R.2d. +4./.7Z. +5.29.6U. +6.85.L. +7.1+.7h. +7.22.7d. +7.24.7b. +7.26.79. +9.c.8u. +a.2F.3h. +a.2J.39. +a.32.2M. +c.37.2K. +d.2U.2+. +0.19.7V. +0.1R.7A. +1.31.2O. +1.u.8l. +2.1.8A. +2.2H.3g. +3.6V.29. +3.M.85. +4.10.7Z. +5.c.8v. +6.3r.2D. +6.6R.2e. +7.22.7e. +7.24.7c. +7.26.7a. +7.r.8m. +8.2z.3v. +a.2F.3i. +c.3f.2I. +g.2E.3q. +0.1a.7V. +0.1S.7A. +0.2G.3h. +1.31.2P. +2.1h.7P. +2.2.8A. +3.6V.2a. +4.11.7Z. +4.k.8r. +5.1t.7L. +5.1V.7y. +5.29.6W. +5.c.8w. +7.1+.7i. +7.22.7f. +7.26.7b. +7.r.8n. +7.s.8m. +8.2A.3v. +8.2g.6z. +a.2Y.2Y. +f.24.7d. +g.2E.3r. +0.1b.7V. +1.2f.6Q. +1.33.2M. +1.r.8o. +2.1+.7j. +2.1d.7T. +2.1t.7M. +2.2/.2U. +2.3.8A. +3.2B.3v. +3.2y.3y. +4.12.7Z. +5.29.6X. +5.k.8s. +6.3i.2G. +7.22.7g. +7.24.7e. +7.26.7c. +7.s.8n. +j.2a.6W. +0.1c.7V. +0.2C.3v. +0.2M.34. +0.39.2K. +1.31.2Q. +1.r.8p. +1.s.8o. +2.2g.6A. +2.2H.3h. +2.4.8A. +5./.7+. +5.1V.7z. +5.29.6Y. +5.2q.65. +6.6R.2f. +6.85.N. +7.1+.7k. +7.22.7h. +7.24.7f. +7.26.7d. +7.u.8m. +8.2z.3y. +a.32.2N. +j.2a.6X. +0.2I.3g. +1.31.2R. +1.c.8x. +1.s.8p. +2.2H.3i. +2.5.8A. +3.6V.2c. +5.29.6Z. +6.85.O. +7.1+.7l. +7.24.7g. +7.26.7e. +7.u.8n. +8.2+.2V. +8.2A.3y. +a.2F.3m. +a.2J.3e. +a.32.2O. +b.3G.2w. +f.1V.7A. +f.2g.6B. +j.2a.6Y. +0.1d.7V. +0.1W.7A. +1.2f.6T. +1.r.8q. +1.u.8o. +2.1h.7Q. +2.6.8A. +3.2B.3y. +4.13.7Z. +6.85.P. +7.1+.7m. +7.22.7i. +7.24.7h. +7.26.7f. +8.2+.2W. +a.2P.32. +f.37.2L. +j.2a.6Z. +0.2C.3y. +0.2D.3v. +1.31.2S. +1.33.2N. +1.s.8q. +1.u.8p. +2.22.7j. +2.2g.6C. +2.7.8A. +4.14.7Z. +5.2q.67. +5.c.8y. +6.3m.2G. +6.85.Q. +7.1+.7n. +7.26.7g. +a.2J.3f. +d.2U.30. +0.1X.7A. +0.2I.3h. +0.2N.34. +1.33.2O. +2.2/.2V. +2.8.8A. +3.6V.2d. +4.15.7Z. +5./.7/. +6.85.R. +7.1+.7o. +7.22.7k. +7.24.7i. +7.26.7h. +a.32.2Q. +b.6+.29. +g.2E.3v. +0.1e.7V. +0.2O.34. +1.1h.7S. +1.33.2P. +1.u.8q. +2.2/.2W. +2.24.7j. +2.2H.3m. +2.9.8A. +4.16.7Z. +4.2K.3e. +4.S.85. +5.1V.7B. +5.29.6/. +5.2g.6E. +5.k.8t. +6.3i.2I. +7.22.7l. +9.c.8z. +a.2F.3q. +a.32.2R. +b.3G.2x. +b.6+.2a. +0.1f.7V. +0.1Y.7t. +0.2L.39. +1./.80. +2.a.8A. +4.17.7Z. +5.1V.7C. +5.29.70. +6.3y.2D. +6.85.T. +7.22.7m. +7.24.7k. +7.26.7i. +a.2F.3r. +a.2P.34. +f.37.2M. +j.2a.6/. +1.2f.6U. +1.33.2Q. +2.26.7j. +2.b.8A. +4.18.7Z. +4.2G.3q. +4.r.8r. +5.1h.7T. +5.1V.7D. +5.29.71. +5.2q.69. +5.k.8u. +6.85.U. +7.1+.7p. +7.22.7n. +7.24.7l. +8.2+.2Y. +a.2J.3g. +a.32.2S. +b.3J.2w. +c.3f.2K. +f.7V.1g. +g.2E.3y. +j.2a.70. +0.19.7Z. +0.2Q.34. +1.33.2R. +3.3K.2w. +5.1V.7E. +5.29.72. +5.2q.6a. +5.k.8v. +6.3r.2G. +6.85.V. +7.1+.7q. +7.22.7o. +7.24.7m. +7.26.7k. +7.r.8s. +9.s.8r. +a.2V.30. +b.6+.2c. +d.71.2a. +0.2R.34. +1.1h.7U. +1.2f.6W. +2.2H.3q. +3.2W.30. +4.1a.7Z. +5.29.73. +5.k.8w. +6.3L.2w. +6.3m.2I. +6.85.W. +7.1+.7r. +7.24.7n. +7.26.7l. +7.s.8s. +b.3G.2y. +0.1h.7V. +0.2M.39. +1.2f.6X. +1.33.2S. +2.1t.7O. +2.2/.2Y. +2.2H.3r. +2.A.8o. +3.3M.2w. +4.1b.7Z. +5.29.74. +6.85.X. +7.24.7o. +7.26.7m. +9.u.8r. +a.2J.3h. +e.2g.6G. +f.37.2N. +j.2a.73. +0.1i.7V. +0.1Z.7t. +0.2w.3N. +0.34.2S. +1.2f.6Y. +2.A.8p. +4.1c.7Z. +4.2K.3g. +5.1V.7F. +5.29.75. +6.85.Y. +7.1+.7s. +7.22.7p. +7.26.7n. +7.u.8s. +a.2J.3i. +b.3J.2x. +b.6+.2d. +c.37.2O. +d.71.2c. +f.2L.3e. +0.1+.7t. +0.1j.7V. +1.2f.6Z. +1.31.2U. +1.k.8x. +2.1d.7Y. +2.1t.7P. +3.3K.2x. +5.29.76. +6.85.Z. +7.22.7q. +7.26.7o. +a.2F.3v. +a.2P.37. +b.3G.2B. +0.1/.7t. +1.1h.7X. +3.2x.3L. +4.1d.7Z. +5./.81. +5.1V.7G. +5.29.77. +7.22.7r. +7.24.7p. +b.3G.2C. +c.3f.2L. +f.2I.3q. +0.20.7t. +0.2G.3v. +0.2N.39. +1./.82. +3.3M.2x. +4.2K.3h. +5.1V.7H. +5.29.78. +5.2g.6H. +5.k.8y. +6.3r.2I. +7.1+.7u. +7.24.7q. +7.r.8t. +a.2Y.30. +d.71.2d. +f.37.2Q. +0.1k.7V. +0.39.2O. +1./.83. +1.2n.6n. +2.2g.6I. +3.2x.3N. +4.26.7p. +5.1V.7I. +5.29.79. +6.3i.2K. +7.22.7s. +7.24.7r. +7.s.8t. +a.2F.3y. +a.2J.3m. +b.3J.2y. +e.21.7t. +f.2M.3e. +f.37.2R. +0.22.7t. +1.2f.6/. +2.1t.7Q. +2.2H.3v. +3.1l.7V. +3.3K.2y. +4.1e.7Z. +5./.84. +5.1V.7J. +5.29.7a. +6.6o.2n. +6.85.+. +7.1+.7v. +7.26.7q. +7.r.8u. +8.2+.2+. +8.2z.3J. +9.k.8z. +a.2P.39. +b.3G.2D. +d.2U.32. +f.2g.6J. +0.1m.7V. +0.23.7t. +0.2L.3g. +1.2f.70. +1.31.2V. +3.2y.3L. +4.1f.7Z. +4.3f.2M. +4.r.8v. +5.29.7b. +5.c.8B. +6.3y.2G. +6.6p.2n. +6.85./. +7.1+.7w. +7.24.7s. +7.26.7r. +7.s.8u. +7.u.8t. +8.2A.3J. +8.2z.3K. +b.3G.2E. +b.3R.2w. +c.37.2S. +e.2g.6K. +0.1n.7V. +0.24.7t. +0.2Q.39. +0.7A.1Y. +1.2f.71. +1.2n.6q. +1.31.2W. +1.6L.2g. +3.3M.2y. +4.s.8v. +5.29.7c. +5.2q.6c. +6.3S.2w. +6.85.10. +7.1+.7x. +7.22.7u. +7.6v.2h. +7.r.8w. +8.2A.3K. +8.2z.3L. +b.3J.2B. +e./.86. +f.7Z.1g. +0.1o.7V. +0.2N.3e. +1.1t.7S. +1.2f.72. +1.33.2U. +2.2/.2+. +2.2H.3y. +2.A.8s. +3.2B.3K. +3.2y.3N. +5.29.7d. +6.3m.2K. +6.6v.2i. +6.85.11. +7.26.7s. +7.2p.6o. +7.s.8w. +7.u.8u. +8.2A.3L. +8.2z.3M. +a.2J.3q. +b.3J.2C. +f.25.7t. +f.39.2R. +0.1p.7V. +0.26.7t. +0.2I.3v. +0.2L.3h. +0.2O.3e. +1.1h.7Y. +1.2f.73. +1.2n.6r. +3.2B.3L. +3.3K.2C. +4.u.8v. +5.29.7e. +6.85.12. +7.22.7v. +7.24.7u. +7.2p.6p. +7.6v.2j. +8.2A.3M. +8.2z.3N. +a.2J.3r. +d.2U.34. +0.1q.7V. +0.27.7t. +0.2M.3g. +0.39.2S. +1.2f.74. +1.r.8x. +3.2B.3M. +4.1h.7Z. +5./.87. +5.1t.7T. +5.29.7f. +6.3i.2L. +6.3L.2C. +6.6s.2n. +7.1+.7y. +7.22.7w. +7.6v.2k. +7.u.8w. +8.2A.3N. +a.2P.3e. +a.32.2V. +b.3R.2x. +c.3f.2N. +0.1r.7V. +1.2f.75. +1.2n.6t. +1.7d.2b. +1.s.8x. +2.2/.2/. +3.2B.3N. +3.2W.32. +3.3M.2C. +4.1i.7Z. +4.3f.2O. +5./.88. +5.29.7g. +6.3S.2x. +6.6v.2l. +7.22.7x. +7.24.7v. +7.26.7u. +7.6x.2h. +b.3J.2D. +f.2g.6M. +0.1j.7Z. +0.1s.7V. +0.2C.3N. +0.2I.3y. +0.6u.2n. +0.7A.1Z. +1.1t.7U. +1.2f.76. +1.31.2Y. +3.3K.2D. +4.2K.3q. +5./.89. +5.29.7h. +6.85.13. +7.24.7w. +7.6x.2i. +7.r.8y. +8.2+.30. +9.1+.7z. +a.2P.3f. +b.3J.2E. +c.28.7t. +f.2Q.3e. +i.c.8C. +j.2a.7g. +0.1+.7A. +0.1t.7V. +0.6v.2n. +1.2f.77. +1.33.2V. +1.u.8x. +5./.8a. +6.3L.2D. +6.3r.2K. +6.85.14. +7.24.7x. +7.26.7v. +7.2p.6s. +7.6x.2j. +7.s.8y. +f.2M.3h. +f.2R.3e. +g.2E.3K. +j.2a.7h. +0.1/.7A. +0.2N.3g. +1.2f.78. +1.33.2W. +1.7g.2b. +3.1u.7V. +3.3M.2D. +4.r.8z. +5./.8b. +5.1h.7+. +5.29.7i. +6.3i.2M. +6.3m.2L. +6.85.15. +7.22.7y. +7.26.7w. +7.6x.2k. +a.2V.34. +b.3G.2F. +b.3R.2y. +c.3f.2Q. +g.2E.3L. +0.1v.7V. +0.20.7A. +0.2O.3g. +0.2S.3e. +0.2w.3Y. +1.2f.79. +2.2/.30. +2.29.7j. +3.2W.34. +4.1k.7Z. +4.3f.2R. +5./.8c. +6.3N.2D. +6.3S.2y. +6.85.16. +7.26.7x. +7.2p.6u. +7.6x.2l. +7.u.8y. +8.2z.3R. +9.s.8z. +a.2J.3v. +d.2U.37. +g.2E.3M. +0.1w.7V. +1.1t.7X. +1.2f.7a. +2.1V.7K. +3.1l.7Z. +4.d.8C. +5./.8d. +5.29.7k. +6.85.17. +7.24.7y. +7.2p.6v. +8.2A.3R. +8.2z.3S. +9.22.7z. +a.2P.3g. +a.32.2Y. +b.3G.2G. +e.21.7A. +g.2E.3N. +0.1x.7V. +0.22.7A. +0.2N.3h. +1.2f.7b. +4.1m.7Z. +4.e.8C. +4.u.8z. +5./.8e. +5.29.7l. +5.2g.6O. +5.k.8B. +6.85.18. +7.1+.7B. +8.2A.3S. +8.6z.2h. +b.3R.2B. +c.3f.2S. +h.2n.6x. +0.1y.7V. +0.23.7A. +0.2L.3q. +0.2O.3h. +0.2Q.3g. +1.2f.7c. +1.2n.6y. +2.2H.3G. +2.A.8w. +3.2B.3S. +4.1n.7Z. +4.f.8C. +5./.8f. +5.1h.7/. +5.29.7m. +5.2q.6f. +6.3i.2N. +6.3m.2M. +6.85.19. +7.1+.7C. +7.26.7y. +8.6z.2i. +9.24.7z. +a.2J.3y. +b.3R.2C. +0.1o.7Z. +0.1z.7V. +0.2K.3v. +0.2R.3g. 0.30.30. -d.30.35. -b.36.30. -d.30.37. -d.30.38. -5.39.30. -5.3d.30. -d.30.3h. -5.3i.30. -0.30.3m. -0.30.3p. -a.3v.30. -a.3y.30. -2.3z.30. -5.3A.30. -2.3B.30. -d.30.3C. -a.3F.30. -5.3G.30. -d.30.3M. -0.30.3Q. -5.40.30. -0.30.47. -0.30.4d. -d.30.4O. -d.30.4Q. -5.4T.30. -0.30.4V. -d.30.51. -0.30.52. -a.5a.30. -2.5b.30. -5.5d.30. -0.30.5B. -0.30.5C. -5.5F.30. -d.30.5H. -0.30.5I. -f.30.5K. -7.5M.30. -d.30.5O. -0.30.5R. -d.30.5U. -0.30.5W. -1.5Y.30. -0.30.5Z. -d.30.5/. -d.30.60. -2.62.30. -2.66.30. -a.6b.30. -c.6e.30. -0.30.6F. -0.6M.30. -0.30.6+. -d.30.71. -5.79.30. -0.30.7D. -d.30.2p. -d.30.2p. -1.51.31. -1.5v.31. -8.5I.31. -8.5K.31. -7.5M.31. -1.5Y.31. -1.5+.31. -5.60.31. -5.62.31. -1.6q.31. -1.6t.31. -3.6F.31. -c.6H.31. -8.6M.31. -1.86.31. -1.8d.31. -1.4s.32. -1.51.32. -1.5v.32. -8.5I.32. -8.5K.32. -7.5M.32. -1.5Y.32. -1.5+.32. -5.60.32. -8.62.32. -1.6q.32. -1.6t.32. -8.6F.32. -c.6H.32. -8.6M.32. -1.86.32. -1.8d.32. -1.4s.33. -1.51.33. -1.5v.33. -8.5I.33. -8.5K.33. -7.5M.33. -1.5Y.33. -1.5+.33. -8.60.33. -5.62.33. -1.6q.33. -1.6t.33. -3.6F.33. -c.6H.33. -8.6M.33. -1.7B.33. -1.7R.33. -1.86.33. -1.8d.33. -1.51.34. -1.5v.34. -8.5I.34. -8.5K.34. -7.5M.34. -1.5Y.34. -1.5+.34. -5.60.34. -5.62.34. -1.6q.34. -1.6t.34. -8.6F.34. -c.6H.34. -8.6M.34. -1.86.34. -1.8d.34. -0.35.35. -b.36.35. -0.35.37. -d.38.35. -5.39.35. -5.3d.35. -0.3h.35. -5.3i.35. -0.35.3m. -5.3p.35. -a.3v.35. -a.3y.35. -2.3z.35. -5.3A.35. -2.3B.35. -5.3C.35. -a.3F.35. -5.3G.35. -0.35.3M. -0.35.3Q. -5.40.35. -0.35.47. -5.4d.35. -0.35.4O. -3.4Q.35. -7.4T.35. -0.35.4V. -5.51.35. -0.52.35. -a.5a.35. -2.5b.35. -5.5d.35. -5.5B.35. -5.5C.35. -5.5F.35. -0.5H.35. -0.5I.35. -f.35.5K. -7.5M.35. -5.5O.35. -5.5R.35. -3.35.5U. -3.5W.35. -1.5Y.35. -5.5Z.35. -5.5/.35. -0.60.35. -2.62.35. -2.66.35. -a.6b.35. -c.6e.35. -0.35.6F. -0.6M.35. -d.6+.35. -d.71.35. -5.79.35. -0.35.7D. -d.2p.35. -d.2p.35. -b.36.36. -b.36.37. -b.36.38. -b.36.39. -b.36.3d. -b.36.3h. -b.36.3i. -b.36.3m. -b.36.3p. -a.3v.36. -a.3y.36. -2.3z.36. -5.3A.36. -2.3B.36. -3.36.3C. -a.3F.36. -5.3G.36. -b.36.3M. -b.36.3Q. -5.40.36. -b.36.47. -b.36.4d. -b.36.4O. -b.36.4Q. -7.4T.36. -b.36.4V. -b.36.51. -3.36.52. -a.5a.36. -2.5b.36. -5.5d.36. -b.36.5B. -b.36.5C. -3.36.5F. -b.36.5H. -b.36.5I. -f.36.5K. -7.5M.36. -b.36.5O. -b.36.5R. -b.36.5U. -3.36.5W. -1.5Y.36. -b.36.5Z. -b.36.5/. -b.36.60. -2.62.36. -2.66.36. -a.6b.36. -c.6e.36. -b.36.6F. -b.36.6M. -b.36.6+. -b.36.71. -5.79.36. -b.36.7D. -3.36.2p. -3.36.2p. -0.37.37. -0.38.37. -5.39.37. -5.3d.37. -0.3h.37. -5.3i.37. -0.37.3m. -5.3p.37. -a.3v.37. -a.3y.37. -2.3z.37. -5.3A.37. -2.3B.37. -5.3C.37. -a.3F.37. -5.3G.37. -0.37.3M. -0.37.3Q. -5.40.37. -0.37.47. -5.4d.37. -0.37.4O. -0.4Q.37. -7.4T.37. -3.37.4V. -5.51.37. -0.52.37. -a.5a.37. -2.5b.37. -5.5d.37. -5.5B.37. -5.5C.37. -5.5F.37. -0.5H.37. -0.5I.37. -f.37.5K. -7.5M.37. -5.5O.37. -5.5R.37. -3.37.5U. -0.5W.37. -1.5Y.37. -5.5Z.37. -5.5/.37. -0.60.37. -2.62.37. -2.66.37. -a.6b.37. -c.6e.37. -0.37.6F. -0.6M.37. -0.6+.37. -0.71.37. -5.79.37. -0.37.7D. -0.2p.37. -0.2p.37. -0.38.38. -5.39.38. -5.3d.38. -d.38.3h. -5.3i.38. -0.38.3m. -5.3p.38. -a.3v.38. -a.3y.38. -2.3z.38. -5.3A.38. -2.3B.38. -5.3C.38. -a.3F.38. -5.3G.38. -0.38.3M. -0.38.3Q. -5.40.38. -0.38.47. -5.4d.38. -0.38.4O. -d.38.4Q. -5.4T.38. -0.38.4V. -d.38.51. -0.52.38. -a.5a.38. -2.5b.38. -5.5d.38. -5.5B.38. -5.5C.38. -5.5F.38. -0.38.5H. -0.5I.38. -f.38.5K. -7.5M.38. -5.5O.38. -0.38.5R. -0.38.5U. -0.38.5W. -1.5Y.38. -5.5Z.38. -0.38.5/. -0.60.38. -2.62.38. -2.66.38. -a.6b.38. -c.6e.38. -0.38.6F. -0.6M.38. -0.6+.38. -0.38.71. -5.79.38. -0.38.7D. -0.2p.38. -0.2p.38. -5.39.39. -5.39.3d. -5.39.3h. -5.39.3i. -5.39.3m. -5.39.3p. -a.3v.39. -a.3y.39. -2.3z.39. -5.3A.39. -2.3B.39. -5.39.3C. -a.3F.39. -5.3G.39. -5.39.3M. -5.39.3Q. -5.40.39. -5.39.47. -5.39.4d. -5.39.4O. -5.39.4Q. -7.4T.39. -5.39.4V. -5.39.51. -5.39.52. -a.5a.39. -2.5b.39. -5.5d.39. -5.39.5B. -5.39.5C. -5.39.5F. -5.39.5H. -5.39.5I. -f.39.5K. -7.5M.39. -5.39.5O. -5.39.5R. -5.39.5U. -5.39.5W. -1.5Y.39. -5.39.5Z. -5.39.5/. -5.39.60. -2.62.39. -2.66.39. -a.6b.39. -c.6e.39. -5.39.6F. -5.39.6M. -5.39.6+. -5.39.71. -5.79.39. -5.39.7D. -5.39.2p. -5.39.2p. -1.51.3a. -5.5I.3a. -6.5K.3a. -7.5M.3a. -1.5Y.3a. -1.5+.3a. -4.60.3a. -5.62.3a. -1.6q.3a. -1.6t.3a. -8.6F.3a. -c.6H.3a. -8.6M.3a. -1.7B.3a. -1.7R.3a. -1.86.3a. -1.8d.3a. -1.51.3c. -5.5I.3c. -6.5K.3c. -7.5M.3c. -1.5Y.3c. -1.5+.3c. -4.60.3c. -5.62.3c. -1.6q.3c. -1.6t.3c. -8.6F.3c. -c.6H.3c. -8.6M.3c. -1.7B.3c. -1.7R.3c. -1.86.3c. -1.8d.3c. -5.3d.3d. -5.3d.3h. -5.3i.3d. -5.3d.3m. -5.3d.3p. -a.3v.3d. -a.3y.3d. -2.3z.3d. -5.3A.3d. -2.3B.3d. -5.3d.3C. -a.3F.3d. -5.3G.3d. -5.3d.3M. -5.3d.3Q. -5.40.3d. -5.3d.47. -5.3d.4d. -5.3d.4O. -5.3d.4Q. -5.4T.3d. -5.3d.4V. -5.3d.51. -5.3d.52. -a.5a.3d. -2.5b.3d. -5.3d.5B. -5.3d.5C. -5.5F.3d. -5.3d.5H. -5.3d.5I. -f.3d.5K. -7.5M.3d. -5.3d.5O. -5.3d.5R. -5.3d.5U. -5.3d.5W. -1.5Y.3d. -5.3d.5Z. -5.3d.5/. -5.3d.60. -2.62.3d. -2.66.3d. -a.6b.3d. -c.6e.3d. -5.3d.6F. -0.6M.3d. -5.3d.6+. -5.3d.71. -5.79.3d. -5.3d.7D. -5.3d.2p. -5.3d.2p. -1.51.3e. -1.5v.3e. -1.5I.3e. -1.5M.3e. -1.5Y.3e. -1.5+.3e. -1.60.3e. -1.62.3e. -1.6q.3e. -1.6t.3e. -1.6F.3e. -1.6H.3e. -1.6M.3e. -1.7B.3e. -1.86.3e. -1.8d.3e. -1.51.3f. -1.5v.3f. -5.5I.3f. -6.5K.3f. -7.5M.3f. -1.5Y.3f. -1.5+.3f. -4.60.3f. -5.62.3f. -1.6q.3f. -1.6t.3f. -8.6F.3f. -c.6H.3f. -8.6M.3f. -1.7B.3f. -1.86.3f. -1.8d.3f. -1.51.3g. -1.5v.3g. -8.5I.3g. -8.5K.3g. -7.5M.3g. -1.5Y.3g. -1.5+.3g. -8.60.3g. -8.62.3g. -1.6q.3g. -1.6t.3g. -8.6F.3g. -c.6H.3g. -8.6M.3g. -1.7B.3g. -1.86.3g. -1.8d.3g. +0.7A.24. +1.2f.7d. +1.33.2Y. +2./.8g. +3.2x.3Y. +4.g.8C. +5.29.7n. +5.2q.6g. +6.3i.2O. +6.3r.2L. +6.3S.2C. +6.85.1a. +7.1+.7D. +8.2g.6P. +8.6z.2j. +a.2P.3h. +d.2U.39. +j.2a.7m. +0.1A.7V. +0.2w.40. +0.7A.25. +1.1h.80. +1.2f.7e. +2./.8h. +4.1p.7Z. +4.h.8C. +5.29.7o. +6.85.1b. +7.1+.7E. +8.6z.2k. +9.26.7z. +a.2P.3i. +a.2V.37. +a.2Y.34. +b.3J.2F. +h.2p.6x. +0.1B.7V. +0.2Q.3h. +0.2S.3g. +0.7A.26. +1.2f.7f. +1.31.2+. +3.2W.37. +3.3K.2F. +4.1q.7Z. +4.i.8C. +5.1V.7L. +6.85.1c. +7.22.7B. +8.6z.2l. +b.3R.2D. +j.2a.7o. +0.1C.7V. +0.7A.27. +1.2f.7g. +2.1d.84. +2.1V.7M. +4.1r.7Z. +4.j.8C. +6.3i.2Q. +6.3m.2N. +6.3S.2D. +6.3y.2K. +7.22.7C. +a.2F.3L. +b.3G.2I. +b.3J.2G. +b.3R.2E. +f.2M.3q. +f.2R.3h. +0.1D.7V. +1.1t.7Y. +1.2f.7h. +3.2y.3Y. +3.3K.2G. +3.3M.2F. +4.1s.7Z. +4.k.8C. +5./.8i. +5.29.7p. +6.3i.2R. +6.3m.2O. +6.3r.2M. +6.85.1d. +7.1+.7F. +7.22.7D. +7.24.7B. +8.6z.2n. +g.2E.3S. +0.2S.3h. +2.1d.86. +2.2/.31. +2.2H.3J. +3.2x.40. +4.1t.7Z. +4.l.8C. +5.29.7q. +6.3L.2G. +7.22.7E. +8.2z.3Y. +a.2F.3N. +a.2P.3m. +a.2V.39. +c.28.7A. +d.2U.3e. +f.24.7C. +0.1E.7V. +0.2L.3v. +1.2f.7i. +1.2n.6A. +2.2H.3K. +2.A.8z. +3.1u.7Z. +3.2W.39. +3.3M.2G. +4.m.8C. +5./.8j. +5.29.7r. +5.c.8D. +6.3i.2S. +7.1+.7G. +7.24.7D. +7.26.7B. +8.2+.32. +8.2A.3Y. +0.1F.7V. +0.2G.3N. +0.2N.3q. +2.2f.7j. +2.2H.3L. +3.2B.3Y. +3.n.8C. +4.1v.7Z. +6.3m.2Q. +6.6B.2n. +6.85.1e. +7.1+.7H. +7.24.7E. +7.26.7C. +7.r.8B. +8.6z.2p. +a.2Y.37. +d.2U.3f. +0.1G.7V. +0.2C.3Y. +1./.8k. +1.2f.7k. +2.2H.3M. +4.1w.7Z. +4.2O.3q. +4.o.8C. +4.s.8B. +5.1h.81. +5.29.7s. +6.3m.2R. +6.3r.2N. +6.85.1f. +7.1+.7I. +7.22.7F. +7.26.7D. +0.1H.7V. +0.1x.7Z. +0.29.7t. +0.2L.3y. +1./.8l. +1.1h.82. +1.2f.7l. +1.2n.6C. +1.31.30. +1.33.2+. +2.2/.32. +2.2H.3N. +3.2y.40. +4.p.8C. +5.1t.7+. +6.3r.2O. +6.85.1g. +7.1+.7J. +7.26.7E. +8.2g.6R. +a.2P.3q. +b.3G.2J. +b.3J.2I. +0.1I.7V. +0.1y.7Z. +0.2M.3v. +1.1h.83. +1.2f.7m. +1.2n.6D. +2.1d.89. +3.3K.2I. +4.q.8C. +4.u.8B. +5.2a.7t. +6.3m.2S. +7.22.7G. +7.24.7F. +7.2p.6B. +8.2+.34. +8.2z.40. +a.2P.3r. +a.2V.3e. +0.1J.7V. +0.2D.3Y. +1.2f.7n. +2.1d.8a. +3.2W.3e. +4.1z.7Z. +5.1h.84. +5.29.7u. +6.3L.2I. +6.6E.2n. +7.22.7H. +8.2A.40. +a.2Y.39. +b.3R.2F. +d.2U.3g. +f.2Q.3q. +1.2f.7o. +2.2/.33. +3.2B.40. +3.3M.2I. +4.1A.7Z. +4.7t.2b. +5./.8m. +6.3r.2Q. +6.3S.2F. +6.85.1h. +7.22.7I. +7.24.7G. +7.26.7F. +a.2V.3f. +f.2R.3q. +g.2E.3Y. +j.2a.7u. +0.1B.7Z. +0.2c.7t. +0.2I.3N. +0.2M.3y. +0.r.8C. +1.2n.6F. +2.2/.34. +3.2W.3f. +5./.8n. +5.1t.7/. +5.29.7v. +6.3r.2R. +6.85.1i. +7.22.7J. +7.24.7H. +a.32.30. +b.3G.2K. +b.3R.2G. +e.1h.86. +f.2C.40. +0.1K.7V. +0.2N.3v. +0.2S.3q. +1./.8o. +2.1V.7O. +4.1C.7Z. +4.s.8C. +5.29.7w. +6.3S.2G. +6.85.1j. +7.24.7I. +7.26.7G. +7.2p.6E. +d.2U.3h. +j.2a.7v. +0.1L.7V. +0.2O.3v. +0.t.8C. +1./.8p. +1.1t.80. +1.2f.7p. +2.1d.8e. +2.2H.3R. +4.1D.7Z. +5.29.7x. +6.3r.2S. +6.4c.2w. +7.24.7J. +7.26.7H. +b.3J.2J. +d.2U.3i. +0.1M.7V. +0.2D.40. +0.2d.7t. +0.u.8C. +1.2f.7q. +1.2n.6G. +1.33.30. +2.1V.7P. +2.2H.3S. +3.3K.2J. +5.1h.87. +7.26.7I. +8.2+.37. +a.2P.3v. +a.2V.3g. +a.2Y.3e. +d.7v.2b. +j.2a.7x. +0.2N.3y. +0.30.34. +0.v.8C. +1./.8q. +1.2f.7r. +3.2W.3g. +4.1E.7Z. +4.1N.7V. +5.1h.88. +5.k.8D. +6.85.1k. +7.26.7J. +9.7t.2e. +a.2J.3L. +f.2g.6V. +g.2E.40. +0.1O.7V. +0.2O.3y. +0.2Q.3v. +0.w.8C. +1.31.31. +1.6L.2h. +2.A.8B. +2.c.8E. +3.1l.85. +3.3M.2J. +4.1F.7Z. +5.1h.89. +5.29.7y. +a.2Y.3f. +0.1P.7V. +0.2R.3v. +0.x.8C. +1.2f.7s. +1.6L.2i. +2.2/.37. +2.6J.2k. +4.1G.7Z. +5.1h.8a. +6.4c.2x. +6.85.1m. +a.2J.3N. +a.2P.3y. +a.2V.3h. +b.3G.2L. +b.3J.2K. +b.3R.2I. +d.2U.3m. +j.2a.7y. +0.1Q.7V. +0.2n.6H. +0.y.8C. +1.6L.2j. +2.0.8K. +2.1+.7K. +2.1V.7Q. +2.6J.2l. +3.2W.3h. +3.3K.2K. +4.1H.7Z. +5.1h.8b. +5.29.7z. +6.3S.2I. +6.85.1n. +8.2+.39. +9.7t.2f. +a.2F.3Y. +a.2V.3i. +0.2Q.3y. +0.2S.3v. +0.7A.29. +1.2n.6I. +1.6L.2k. +2.1.8K. +3.2W.3i. +4.1I.7Z. +4.z.8C. +5.1h.8c. +6.3L.2K. +6.85.1o. +d.7v.2e. +0.2G.3Y. +0.2R.3y. +0.6J.2n. +0.7A.2a. +1.2f.7u. +1.31.32. +1.6L.2l. +2.2.8K. +3.3M.2K. +4.1J.7Z. +5.1h.8d. +5.1t.81. +6.85.1p. +9./.8r. +a.2Y.3g. +0.2K.3N. +0.2w.4j. +1.1t.82. +1.1V.7S. +1.2n.6K. +2.2/.39. +2.3.8K. +5./.8s. +5.1h.8e. +6.4c.2y. +6.85.1q. +7.2p.6H. +b.3G.2M. +c.37.30. +d.2U.3q. +1.1t.83. +1.2f.7v. +1.6L.2n. +1.6N.2h. +2.22.7K. +2.2H.3Y. +2.c.8F. +4.A.8C. +5.1h.8f. +6.3y.2S. +6.85.1r. +8.2z.4c. +9.7A.2b. +a.2V.3m. +d.2U.3r. +e.4.8K. +f.1R.7V. +f.2g.6+. +0.1S.7V. +0.7A.2c. +1.2f.7w. +1.6N.2i. +2.1h.8g. +2.2g.6/. +2.5.8K. +3.2W.3m. +4.1K.7Z. +4.B.8C. +5.1t.84. +5.1V.7T. +5.29.7B. +6.85.1s. +7.1+.7L. +7.2p.6J. +8.2+.3e. +8.2A.4c. +a.2F.40. +a.2Y.3h. +b.3J.2L. +b.3R.2J. +1.2f.7x. +1.31.34. +1.6N.2j. +2.1+.7M. +2.1h.8h. +2.24.7K. +2.2g.70. +2.6.8K. +3.2B.4c. +3.3K.2L. +4.1L.7Z. +4.C.8C. +5.29.7C. +6.3S.2J. +6.85.1t. +7.r.8D. +a.2Y.3i. +a.32.32. +1.6L.2p. +1.6N.2k. +3.1u.85. +3.2x.4j. +4.1M.7Z. +4.2G.40. +4.D.8C. +5.29.7D. +5.c.8G. +6.3L.2L. +6.4c.2C. +7.s.8D. +8.2+.3f. +b.3G.2N. +d.71.2g. +e.1t.86. +e.7.8K. +f.39.30. +o.1V.7U. +0.2I.3Y. +0.7A.2d. +1.6N.2l. +2.2/.3e. +2.26.7K. +2.8.8K. +3.3M.2L. +4.1N.7Z. +4.E.8C. +5.29.7E. +5.c.8H. +6.6M.2n. +6.85.1v. +a.2V.3q. +b.3G.2O. +f.1V.7V. +0.1O.7Z. +0.1W.7V. +0.2L.3N. +1.2f.7y. +1.33.32. +2.2H.40. +2.k.8E. +3.2W.3q. +4.F.8C. +5./.8t. +5.1h.8i. +6.85.1w. +7.22.7L. +7.u.8D. +9.7A.2e. +a.2V.3r. +b.3G.2P. +b.3J.2M. +b.3R.2K. +e.9.8K. +0.1P.7Z. +2.2/.3f. +2.22.7M. +2.2n.6N. +3.2W.3r. +3.3K.2M. +5.1t.87. +5.6P.2h. +5.c.8I. +6.3S.2K. +6.4c.2D. +6.85.1x. +a.2Y.3m. +a.32.34. +d.2U.3v. +e.a.8K. +0.1X.7V. +0.2w.4q. +1.1V.7X. +1.2f.7z. +2.1d.8o. +2.2g.75. +2.b.8K. +3.2y.4j. +5./.8u. +5.1h.8j. +5.1t.88. +5.29.7F. +5.6P.2i. +5.c.8J. +6.3L.2M. +6.85.1y. +7.24.7L. +7.2p.6M. +8.2+.3g. +b.3G.2Q. +f.1Q.7Z. +g.2E.4c. +1.31.37. +1.33.33. +2.1d.8p. +2.24.7M. +3.3M.2M. +4.7A.2f. +4.G.8C. +5./.8v. +5.1t.89. +5.6P.2j. +5.c.8K. +6.85.1z. +8.2z.4j. +b.3G.2R. +f.30.3e. +0.2M.3N. +1.1h.8k. +1.33.34. +4.H.8C. +5./.8w. +5.1t.8a. +5.29.7G. +5.6P.2k. +6.85.1A. +7.26.7L. +8.2A.4j. +b.3J.2N. +d.2U.3y. +f.2I.40. +0.34.34. +1.1h.8l. +2.2/.3g. +2.26.7M. +3.2B.4j. +3.3K.2N. +3.I.8C. +5.1t.8b. +5.29.7H. +5.6P.2l. +6.6O.2n. +6.85.1B. +8.2+.3h. +a.2J.3Y. +a.2Y.3q. +b.3G.2S. +b.3J.2O. +c.3f.30. +0.2C.4j. +2.k.8F. +3.2x.4q. +3.3K.2O. +3.J.8C. +5.1t.8c. +5.29.7I. +6.3L.2N. +6.85.1C. +8.2+.3i. +a.2V.3v. +a.2Y.3r. +b.3R.2L. +e.3J.2P. +f.1R.7Z. +0.6P.2n. +1./.8x. +1.2f.7B. +1.31.39. +3.2W.3v. +3.3K.2P. +3.3M.2N. +4.1S.7Z. +4.K.8C. +5.1t.8d. +5.29.7J. +6.3L.2O. +6.3S.2L. +6.85.1D. +a.32.37. +0.2N.3N. +0.L.8C. +1.2f.7C. +2.1+.7O. +2.2/.3h. +3.3M.2O. +5.1h.8m. +5.1t.8e. +7.2p.6O. +a.2P.3L. +b.3J.2Q. +0.2D.4j. +0.2K.3Y. +0.2O.3N. +0.30.3g. +1.1V.7Y. +1.2f.7D. +2.2/.3i. +2.4G.2v. +2.r.8E. +3.3K.2Q. +3.3M.2P. +3.M.8C. +5./.8y. +5.1h.8n. +5.1t.8f. +5.k.8G. +6.85.1E. +a.2V.3y. +b.3J.2R. +1.1h.8o. +1.2f.7E. +1.33.37. +2.1+.7P. +2.1t.8g. +2.s.8E. +3.2W.3y. +3.2y.4q. +3.3K.2R. +5.k.8H. +6.3L.2Q. +6.4c.2F. +6.6R.2h. +6.85.1F. +7.2p.6P. +8.2+.3m. +a.2J.40. +a.2P.3N. +b.3R.2M. +f.1V.7Z. +g.2E.4j. +1.1h.8p. +2.1d.8s. +2.1t.8h. +3.3M.2Q. +6.3L.2R. +6.3S.2M. +6.6R.2i. +6.85.1G. +8.2z.4q. +9./.8z. +a.32.39. +b.3J.2S. +c.37.34. +f.1W.7Z. +0.2Q.3N. +0.30.3h. +1.31.3e. +2.22.7O. +2.u.8E. +3.3K.2S. +3.3M.2R. +4.N.8C. +5.k.8I. +6.4c.2G. +6.6R.2j. +6.85.1H. +8.2A.4q. +a.2Y.3v. +0.2R.3N. +1.1h.8q. +1.2f.7F. +2.2/.3m. +3.2B.4q. +4.1X.7Z. +4.O.8C. +5.k.8J. +6.3i.30. +6.3L.2S. +6.6R.2k. +6.85.1I. +0.2C.4q. +0.2K.40. +1.31.3f. +1.33.39. +2.1+.7Q. +2.22.7P. +2.24.7O. +2.2H.4c. +3.3M.2S. +4.P.8C. +5.1t.8i. +5.1V.7+. +5.k.8K. +6.6R.2l. +6.85.1J. +8.2+.3q. +b.3R.2N. +d.2U.3G. +0.2L.3Y. +0.2S.3N. +0.39.34. +1.2f.7G. +1.2n.6Q. +2.r.8F. +4.Q.8C. +6.3S.2N. +8.2+.3r. +a.2Y.3y. +b.3R.2O. +0.7V.1Y. +1.2f.7H. +1.c.8L. +2.24.7P. +2.26.7O. +2.s.8F. +3.6R.2n. +4.R.8C. +5.1t.8j. +6.3S.2O. +a.32.3e. +b.3R.2P. +1.1+.7S. +1.2f.7I. +1.c.8M. +2.2/.3q. +3.S.8C. +6.3m.30. +6.4q.2D. +6.85.1K. +a.2P.3S. +f.37.37. +1.1t.8k. +1.2f.7J. +1.2n.6T. +1.31.3g. +1.c.8N. +2.2/.3r. +2.22.7Q. +2.26.7P. +2.29.7K. +2.u.8F. +5.1h.8r. +5.1V.7/. +6.4c.2I. +6.85.1L. +7.r.8G. +a.2F.4j. +a.32.3f. +b.3R.2Q. +c.T.8C. +g.2E.4q. +0.2M.3Y. +1.1t.8l. +1.33.3e. +4.r.8H. +4.s.8G. +4.U.8C. +5.1h.8s. +6.3S.2Q. +6.85.1M. +7.1+.7T. +7.2p.6R. +b.3G.2V. +b.3R.2R. +0.2G.4j. +0.34.3e. +1.1V.80. +2.24.7Q. +4.s.8H. +4.V.8C. +6.3S.2R. +6.85.1N. +b.3G.2W. +d.2U.3J. +f.2L.40. +0.1Z.7V. +0.30.3q. +1.1+.7U. +1.22.7S. +1.31.3h. +1.33.3f. +2.1d.8w. +4.u.8G. +4.W.8C. +6.85.1O. +7.r.8I. +8.2+.3v. +b.3R.2S. +c.37.39. +d.2U.3K. +0.1+.7V. +1.31.3i. +2.26.7Q. +2.2H.4j. +4.r.8J. +4.s.8I. +4.u.8H. +4.X.8C. +5.1t.8m. +5.2q.6o. +6.3r.30. +6.3S.2S. +6.85.1P. +a.32.3g. +c.3f.34. +d.2U.3L. +0.1/.7V. +0.2N.3Y. +1.24.7S. +1.2n.6U. +1.c.8O. +4.r.8K. +4.s.8J. +5./.8B. +5.1t.8n. +5.29.7L. +5.2q.6p. +6.85.1Q. +7.22.7T. +c.Y.8C. +d.2U.3M. +0.20.7V. +0.Z.8C. +1.1t.8o. +1.c.8P. +2.2/.3v. +2.29.7M. +3.6V.2n. +4.2O.3Y. +4.s.8K. +4.u.8I. +6.4c.2J. +8.2+.3y. +d.2U.3N. +f.2M.40. +0.39.39. +1.1+.7X. +1.1t.8p. +1.22.7U. +1.26.7S. +1.2n.6W. +1.33.3g. +4.u.8J. +5.1h.8t. +7.24.7T. +a.2P.3Y. +a.32.3h. +b.3G.2Y. +b.3J.2V. +j.21.7V. +0.22.7V. +0.2I.4j. +0.34.3g. +1.2n.6X. +1.31.3m. +1.c.8Q. +3.3K.2V. +4.u.8K. +a.2F.4q. +a.32.3i. +c.37.3e. +e.3J.2W. +0.23.7V. +0.2Q.3Y. +0.7Z.1Y. +1.1t.8q. +1.24.7U. +1.2n.6Y. +1.k.8L. +2.2/.3y. +3.3K.2W. +5.1h.8u. +5.1V.81. +5.2q.6s. +6.85.1R. +7.26.7T. +7.2p.6V. +a.2V.3L. +0.+.8C. +0.24.7V. +0.2G.4q. +0.2N.40. +0.2R.3Y. +0.30.3v. +1.1V.82. +1.2n.6Z. +1.33.3h. +1.k.8M. +2.1d.8z. +3.2W.3L. +3.3M.2V. +5.1h.8v. +5.2q.6t. +5.c.8R. +6.4c.2K. +6.85.1S. +c.37.3f. +0.25.7V. +0.34.3h. +1.1V.83. +1.22.7X. +1.26.7U. +1.33.3i. +1.k.8N. +2.2f.7K. +3.3M.2W. +4./.8C. +4.2O.40. +5.1h.8w. +5.2q.6u. +a.2V.3N. +0.2S.3Y. +1.31.3q. +2.2H.4q. +3.2W.3N. +4.10.8C. +4.26.7V. +5.1V.84. +5.2q.6v. +6.3i.34. +9.c.8S. +a.2P.40. +a.32.3m. +d.2U.3R. +f.2g.7t. +f.39.3e. +0.27.7V. +1.24.7X. +1.31.3r. +4.11.8C. +6.3y.30. +6.85.1V. +b.6+.2n. +d.2U.3S. +e.3J.2Y. +1.1+.7Y. +1.1h.8x. +1.1V.86. +1.2n.6/. +2.A.8I. +3.3K.2Y. +4.12.8C. +4.1t.8r. +6.85.1W. +a.2J.4j. +c.37.3g. +c.3f.39. +f.1Z.7Z. +f.2Q.40. +0.1+.7Z. +1.26.7X. +1.2n.70. +1.33.3m. +5.1t.8s. +5.c.8T. +a.2Y.3L. +c.28.7V. +f.2R.40. +0.1/.7Z. +0.2I.4q. +1.2f.7L. +1.k.8O. +3.3M.2Y. +4.2q.6x. +5.1h.8y. +6.3m.34. +6.4c.2L. +6.85.1X. +8.2+.3G. +a.32.3q. +b.6+.2p. +d.71.2n. +0.20.7Z. +0.2S.40. +0.3e.3e. +1.2n.72. +1.k.8P. +2.29.7O. +2.2f.7M. +4.13.8C. +5.1V.87. +5.2q.6y. +a.2Y.3N. +a.32.3r. +b.3R.2V. +c.37.3h. +0.2K.4j. +1.22.7Y. +1.2n.73. +1.r.8L. +4.14.8C. +5.1h.8z. +5.1V.88. +6.3S.2V. +b.3R.2W. +f.37.3i. +f.39.3g. +j.21.7Z. +1.2n.74. +1.31.3v. +1.33.3q. +1.k.8Q. +1.r.8M. +1.s.8L. +2.2/.3G. +2.29.7P. +3.2W.3S. +4.15.8C. +5.1V.89. +7.1+.7+. +c.3f.3e. +d.71.2p. +f.22.7Z. +0.16.8C. +0.23.7Z. +1.24.7Y. +1.2n.75. +1.33.3r. +1.r.8N. +1.s.8M. +5.1V.8a. +5.c.8U. +6.4c.2M. +f.34.3q. +0.24.7Z. +1.2n.76. +1.s.8N. +1.u.8L. +4.17.8C. +5.1t.8t. +5.1V.8b. +5.k.8R. +6.3r.34. +c.3f.3f. +d.2U.3Y. +f.39.3h. +0.25.7Z. +1.26.7Y. +1.31.3y. +1.7d.2h. +1.u.8M. +2.1d.8B. +4.18.8C. +5.1V.8c. +6.3i.39. +8.2+.3J. +a.2J.4q. +c.37.3m. +0.19.8C. +0.26.7Z. +0.3e.3g. +1.2n.78. +1.7d.2i. +1.u.8N. +2.29.7Q. +2.2g.7z. +4.k.8S. +5.1t.8u. +5.1V.8d. +7.1+.7/. +7.22.7+. +8.2+.3K. +a.32.3v. +b.3G.30. +b.3R.2Y. +0.27.7Z. +0.2L.4j. +1.2n.79. +1.7d.2j. +4.1a.8C. +5./.8D. +5.1t.8v. +5.1V.8e. +5.2q.6A. +6.3S.2Y. +6.4c.2N. +8.2+.3L. +1.1+.80. +1.2n.7a. +1.7d.2k. +1.7g.2h. +1.c.8V. +1.r.8O. +2.2/.3J. +4.1b.8C. +5.1t.8w. +5.1V.8f. +5.2q.6B. +6.4c.2O. +7.24.7+. +8.2+.3M. +c.3f.3g. +0.2K.4q. +1.29.7S. +1.2n.7b. +1.33.3v. +1.7d.2l. +1.7g.2i. +1.r.8P. +1.s.8O. +2.1V.8g. +2.2/.3K. +4.1c.8C. +5.k.8T. +6.3m.39. +6.4c.2P. +8.2+.3N. +a.2V.3Y. +a.32.3y. +c.28.7Z. +d.2U.40. +f.37.3q. +f.3h.3e. +0.34.3v. +1.2n.7c. +1.7g.2j. +1.s.8P. +2.1V.8h. +2.2/.3L. +3.2W.3Y. +6.3i.3e. +7.22.7/. +7.26.7+. +9.c.8W. +c.37.3r. +0.1d.8C. +0.2M.4j. +1.1t.8x. +1.2n.7d. +1.7g.2k. +1.r.8Q. +1.u.8O. +2.2/.3M. +2.2f.7O. +5.29.7T. +5.2q.6D. +6.4c.2Q. +c.3f.3h. +0.3g.3g. +1.22.80. +1.2n.7e. +1.33.3y. +1.7g.2l. +1.s.8Q. +1.u.8P. +2.2/.3N. +5.2q.6E. +6.4c.2R. +7.24.7/. +b.3J.30. +c.3f.3i. +1.2n.7f. +2.2f.7P. +3.3K.30. +5.1h.8B. +5.1t.8y. +5.1V.8i. +6.3y.34. +6.85.1Y. +7.r.8R. +f.39.3q. +0.29.7V. +1.24.80. +1.2n.7g. +1.u.8Q. +4.1e.8C. +5.2q.6F. +5.c.8X. +5.k.8U. +6.3L.30. +6.3m.3e. +6.3r.39. +6.4c.2S. +7.26.7/. +7.s.8R. +a.2V.40. +0.2L.4q. +0.2N.4j. +0.3h.3g. +1.2n.7h. +1.31.3G. +3.2W.40. +3.3M.30. +4.1f.8C. +4.r.8S. +5.1V.8j. +5.c.8Y. +7.1+.81. +8.2+.3R. +9.1t.8z. +a.2Y.3Y. +f.2a.7V. +0.30.3N. +1.1+.82. +1.26.80. +4.2O.4j. +4.s.8S. +6.3i.3g. +7.u.8R. +8.2+.3S. +c.3f.3m. +f.37.3v. +h.1g.8C. +1.1+.83. +1.1V.8k. +1.29.7X. +1.2n.7i. +2.2f.7Q. +5.2q.6G. +a.2P.4j. +0.2c.7V. 0.3h.3h. -5.3i.3h. -0.3h.3m. -5.3p.3h. -a.3v.3h. -a.3y.3h. -2.3z.3h. -5.3A.3h. -2.3B.3h. -5.3C.3h. -a.3F.3h. -5.3G.3h. -0.3h.3M. -0.3h.3Q. -5.40.3h. -0.3h.47. -5.4d.3h. -0.3h.4O. -0.3h.4Q. -5.4T.3h. -0.3h.4V. -0.3h.51. -0.52.3h. -a.5a.3h. -2.5b.3h. -5.5d.3h. -5.5B.3h. -5.5C.3h. -5.5F.3h. -0.5H.3h. -0.5I.3h. -f.3h.5K. -7.5M.3h. -5.5O.3h. -0.3h.5R. -0.3h.5U. -0.5W.3h. -1.5Y.3h. -5.5Z.3h. -5.5/.3h. -0.60.3h. -2.62.3h. -2.66.3h. -a.6b.3h. -c.6e.3h. -0.3h.6F. -0.6M.3h. -d.6+.3h. -0.3h.71. -5.79.3h. -0.3h.7D. -d.2p.3h. -d.2p.3h. -5.3i.3i. -5.3i.3m. -5.3i.3p. -a.3v.3i. -a.3y.3i. -2.3z.3i. -5.3A.3i. -2.3B.3i. -5.3i.3C. -a.3F.3i. -5.3G.3i. -5.3i.3M. -5.3i.3Q. -5.40.3i. -5.3i.47. -5.3i.4d. -5.3i.4O. -5.3i.4Q. -5.4T.3i. -5.3i.4V. -5.3i.51. -5.3i.52. -a.5a.3i. -2.5b.3i. -5.5d.3i. -5.3i.5B. -5.3i.5C. -5.5F.3i. -5.3i.5H. -5.3i.5I. -f.3i.5K. -7.5M.3i. -5.3i.5O. -5.3i.5R. -5.3i.5U. -5.3i.5W. -1.5Y.3i. -5.3i.5Z. -5.3i.5/. -5.3i.60. -2.62.3i. -2.66.3i. -a.6b.3i. -c.6e.3i. -5.3i.6F. -0.6M.3i. -5.3i.6+. -5.3i.71. -5.79.3i. -5.3i.7D. -5.3i.2p. -5.3i.2p. -1.4s.3j. -1.51.3j. -1.5v.3j. -1.5I.3j. -1.5M.3j. -1.5Y.3j. -1.5+.3j. -1.60.3j. -1.62.3j. -1.6q.3j. -1.6t.3j. -1.6F.3j. -1.6H.3j. -1.6M.3j. -1.7B.3j. -1.86.3j. -1.8d.3j. -1.51.3k. -1.5v.3k. -8.5I.3k. -8.5K.3k. -7.5M.3k. -1.5Y.3k. -1.5+.3k. -5.60.3k. -5.62.3k. -1.6q.3k. -1.6t.3k. -8.6F.3k. -c.6H.3k. -8.6M.3k. -1.7B.3k. -1.86.3k. -1.8d.3k. -1.51.3l. -1.5v.3l. -5.5I.3l. -6.5K.3l. -7.5M.3l. -1.5Y.3l. -1.5+.3l. -4.60.3l. -5.62.3l. -1.6q.3l. -1.6t.3l. -8.6F.3l. -c.6H.3l. -8.6M.3l. -1.7B.3l. -1.86.3l. -1.8d.3l. -5.3m.3m. -5.3p.3m. -a.3v.3m. -a.3y.3m. -2.3z.3m. -5.3A.3m. -2.3B.3m. -5.3C.3m. -a.3F.3m. -5.3G.3m. -5.3m.3M. -5.3m.3Q. -5.40.3m. -5.3m.47. -5.4d.3m. -5.3m.4O. -0.4Q.3m. -7.4T.3m. -5.3m.4V. -5.51.3m. -0.52.3m. -a.5a.3m. -2.5b.3m. -5.5d.3m. -5.5B.3m. -5.5C.3m. -5.5F.3m. -0.5H.3m. -0.5I.3m. -f.3m.5K. -7.5M.3m. -5.5O.3m. -5.5R.3m. -5.3m.5U. -3.5W.3m. -1.5Y.3m. -5.5Z.3m. -5.5/.3m. -0.60.3m. -2.62.3m. -2.66.3m. -a.6b.3m. -c.6e.3m. -0.6F.3m. -0.6M.3m. -0.6+.3m. -0.71.3m. -5.79.3m. -5.3m.7D. -0.2p.3m. -0.2p.3m. -1.51.3n. -1.5v.3n. -8.5I.3n. -8.5K.3n. -7.5M.3n. -1.5Y.3n. -1.5+.3n. -5.60.3n. -8.62.3n. -1.6q.3n. -1.6t.3n. -8.6F.3n. -c.6H.3n. -8.6M.3n. -1.7B.3n. -1.86.3n. -1.8d.3n. -1.51.3o. -1.5v.3o. -8.5I.3o. -8.5K.3o. -7.5M.3o. -1.5Y.3o. -1.5+.3o. -8.60.3o. -8.62.3o. -1.6q.3o. -1.6t.3o. -8.6F.3o. -c.6H.3o. -8.6M.3o. -1.7B.3o. -1.86.3o. -1.8d.3o. -5.3p.3p. -a.3v.3p. -a.3y.3p. -2.3z.3p. -5.3A.3p. -2.3B.3p. -5.3p.3C. -a.3F.3p. -5.3G.3p. -5.3p.3M. -5.3p.3Q. -5.40.3p. -5.3p.47. -5.3p.4d. -5.3p.4O. -5.3p.4Q. -5.4T.3p. -5.3p.4V. -5.3p.51. -5.3p.52. -a.5a.3p. -2.5b.3p. -5.5d.3p. -5.3p.5B. -5.3p.5C. -5.5F.3p. -5.3p.5H. -5.3p.5I. -f.3p.5K. -7.5M.3p. -5.3p.5O. -5.3p.5R. -5.3p.5U. -5.3p.5W. -1.5Y.3p. -5.3p.5Z. -5.3p.5/. -5.3p.60. -2.62.3p. -2.66.3p. -a.6b.3p. -c.6e.3p. -5.3p.6F. -0.6M.3p. -5.3p.6+. -5.3p.71. -5.79.3p. -5.3p.7D. -0.2p.3p. -0.2p.3p. -1.51.3q. -1.5v.3q. -8.5I.3q. -8.5K.3q. -7.5M.3q. -1.5Y.3q. -1.5+.3q. -5.60.3q. -5.62.3q. -1.6q.3q. -1.6t.3q. -8.6F.3q. -c.6H.3q. -8.6M.3q. -1.7B.3q. -1.86.3q. -1.8d.3q. -1.51.3r. -1.5v.3r. -6.5I.3r. -6.5K.3r. -7.5M.3r. -1.5Y.3r. -1.5+.3r. -4.60.3r. -5.62.3r. -1.6q.3r. -1.6t.3r. -8.6F.3r. -c.6H.3r. -8.6M.3r. -1.7B.3r. -1.86.3r. -1.8d.3r. -1.51.3s. -1.5v.3s. -4.5I.3s. -3.5K.3s. -7.5M.3s. -1.5Y.3s. -1.5+.3s. -4.60.3s. -4.62.3s. -1.6q.3s. -1.6t.3s. -4.6F.3s. -c.6H.3s. -4.6M.3s. -1.7B.3s. -1.86.3s. -1.8d.3s. -1.51.3t. -1.5v.3t. -8.5I.3t. -8.5K.3t. -7.5M.3t. -1.5Y.3t. -1.5+.3t. -5.60.3t. -8.62.3t. -1.6q.3t. -1.6t.3t. -8.6F.3t. -c.6H.3t. -8.6M.3t. -1.7B.3t. -1.86.3t. -1.8d.3t. -1.51.3u. -1.5v.3u. -8.5I.3u. -8.5K.3u. -7.5M.3u. -1.5Y.3u. -1.5+.3u. -5.60.3u. -8.62.3u. -1.6q.3u. -1.6t.3u. -8.6F.3u. -c.6H.3u. -8.6M.3u. -1.7B.3u. -1.7R.3u. -1.86.3u. -1.8d.3u. -a.3v.3v. -a.3v.3y. -a.3v.3z. -a.3v.3A. -a.3v.3B. -a.3v.3C. -a.3v.3F. -a.3v.3G. -a.3v.3M. -a.3v.3Q. -a.3v.40. -a.3v.47. -a.3v.4d. -a.3v.4O. -a.3v.4Q. -7.4T.3v. -a.3v.4V. -a.3v.51. -a.3v.52. -a.3v.5a. -a.3v.5b. -a.3v.5d. -a.3v.5B. -a.3v.5C. -a.3v.5F. -a.3v.5H. -a.3v.5I. -f.3v.5K. -7.5M.3v. -a.3v.5O. -a.3v.5R. -a.3v.5U. -a.3v.5W. -1.5Y.3v. -a.3v.5Z. -a.3v.5/. -a.3v.60. -a.3v.62. -a.3v.66. -a.3v.6b. -c.6e.3v. -a.3v.6F. -a.3v.6M. -a.3v.6+. -a.3v.71. -a.3v.79. -a.3v.7D. -a.3v.2p. -a.3v.2p. -1.51.3w. -1.5v.3w. -5.5I.3w. -6.5K.3w. -7.5M.3w. -1.5Y.3w. -1.5+.3w. -4.60.3w. -5.62.3w. -1.6q.3w. -1.6t.3w. -3.6F.3w. -c.6H.3w. -8.6M.3w. -1.7B.3w. -1.86.3w. -1.8d.3w. -1.51.3x. -1.5v.3x. -6.5I.3x. -6.5K.3x. -7.5M.3x. -1.5Y.3x. -1.5+.3x. -4.60.3x. -5.62.3x. -1.6q.3x. -1.6t.3x. -8.6F.3x. -c.6H.3x. -8.6M.3x. -1.7B.3x. -1.86.3x. -1.8d.3x. -a.3y.3y. -a.3y.3z. -a.3y.3A. -a.3y.3B. -a.3y.3C. -a.3F.3y. -a.3y.3G. -a.3y.3M. -a.3y.3Q. -a.3y.40. -a.3y.47. -a.3y.4d. -a.3y.4O. -a.3y.4Q. -7.4T.3y. -a.3y.4V. -a.3y.51. -a.3y.52. -a.3y.5a. -a.3y.5b. -a.3y.5d. -a.3y.5B. -a.3y.5C. -a.3y.5F. -a.3y.5H. -a.3y.5I. -f.3y.5K. -7.5M.3y. -a.3y.5O. -a.3y.5R. -a.3y.5U. -a.3y.5W. -1.5Y.3y. -a.3y.5Z. -a.3y.5/. -a.3y.60. -a.3y.62. -a.3y.66. -a.6b.3y. -c.6e.3y. -a.3y.6F. -a.3y.6M. -a.3y.6+. -a.3y.71. -a.3y.79. -a.3y.7D. -a.3y.2p. -a.3y.2p. -2.3z.3z. -2.3z.3A. -2.3B.3z. -2.3z.3C. -a.3F.3z. -2.3z.3G. -2.3z.3M. -2.3z.3Q. -2.3z.40. -2.3z.47. -2.3z.4d. -2.3z.4O. -2.3z.4Q. -7.4T.3z. -2.3z.4V. -2.3z.51. -2.3z.52. -a.5a.3z. -2.5b.3z. -2.3z.5d. -2.3z.5B. -2.3z.5C. -2.3z.5F. -2.3z.5H. -2.3z.5I. -f.3z.5K. -7.5M.3z. -2.3z.5O. -2.3z.5R. -2.3z.5U. -2.3z.5W. -1.5Y.3z. -2.3z.5Z. -2.3z.5/. -2.3z.60. -2.3z.62. -2.3z.66. -a.6b.3z. -c.6e.3z. -2.3z.6F. -2.3z.6M. -2.3z.6+. -2.3z.71. -2.3z.79. -2.3z.7D. -2.3z.2p. -2.3z.2p. -5.3A.3A. -2.3B.3A. -5.3A.3C. -a.3F.3A. -5.3G.3A. -5.3A.3M. -5.3A.3Q. -5.40.3A. -5.3A.47. -5.3A.4d. -5.3A.4O. -5.3A.4Q. -7.4T.3A. -5.3A.4V. -5.3A.51. -5.3A.52. -a.5a.3A. -2.5b.3A. -5.5d.3A. -5.3A.5B. -5.3A.5C. -5.3A.5F. -5.3A.5H. -5.3A.5I. -f.3A.5K. -7.5M.3A. -5.3A.5O. -5.3A.5R. -5.3A.5U. -5.3A.5W. -1.5Y.3A. -5.3A.5Z. -5.3A.5/. -5.3A.60. -2.62.3A. -2.66.3A. -a.6b.3A. -c.6e.3A. -5.3A.6F. -5.3A.6M. -5.3A.6+. -5.3A.71. -5.79.3A. -5.3A.7D. -5.3A.2p. -5.3A.2p. -2.3B.3B. -2.3B.3C. -a.3F.3B. -2.3B.3G. -2.3B.3M. -2.3B.3Q. -2.3B.40. -2.3B.47. -2.3B.4d. -2.3B.4O. -2.3B.4Q. -7.4T.3B. -2.3B.4V. -2.3B.51. -2.3B.52. -a.5a.3B. -2.5b.3B. -2.3B.5d. -2.3B.5B. -2.3B.5C. -2.3B.5F. -2.3B.5H. -2.3B.5I. -f.3B.5K. -7.5M.3B. -2.3B.5O. -2.3B.5R. -2.3B.5U. -2.3B.5W. -1.5Y.3B. -2.3B.5Z. -2.3B.5/. -2.3B.60. -2.3B.62. -2.3B.66. -a.6b.3B. -c.6e.3B. -2.3B.6F. -2.3B.6M. -2.3B.6+. -2.3B.71. -2.3B.79. -2.3B.7D. -2.3B.2p. -2.3B.2p. -5.3C.3C. -a.3F.3C. -5.3G.3C. -5.3C.3M. -5.3C.3Q. -5.40.3C. -5.3C.47. -5.4d.3C. -5.3C.4O. -5.3C.4Q. -5.4T.3C. -5.3C.4V. -5.3C.51. -5.3C.52. -a.5a.3C. -2.5b.3C. -5.5d.3C. -5.3C.5B. -5.3C.5C. -5.5F.3C. -5.3C.5H. -0.5I.3C. -f.3C.5K. -7.5M.3C. -5.5O.3C. -5.3C.5R. -5.3C.5U. -5.3C.5W. -1.5Y.3C. -5.3C.5Z. -5.3C.5/. -0.60.3C. -2.62.3C. -2.66.3C. -a.6b.3C. -c.6e.3C. -5.3C.6F. -0.6M.3C. -5.3C.6+. -5.3C.71. -5.79.3C. -5.3C.7D. -0.2p.3C. -0.2p.3C. -1.4s.3D. -1.51.3D. -1.5v.3D. -5.5I.3D. -6.5K.3D. -7.5M.3D. -1.5Y.3D. -1.5+.3D. -4.60.3D. -5.62.3D. -1.6q.3D. -1.6t.3D. -8.6F.3D. -c.6H.3D. -8.6M.3D. -1.7B.3D. -1.86.3D. -1.8d.3D. -1.51.3E. -1.5v.3E. -6.5I.3E. -6.5K.3E. -7.5M.3E. -1.5Y.3E. -1.5+.3E. -4.60.3E. -5.62.3E. -1.6q.3E. -1.6t.3E. -3.6F.3E. -c.6H.3E. -8.6M.3E. -1.7B.3E. -1.86.3E. -1.8d.3E. -a.3F.3F. -a.3F.3G. -a.3F.3M. -a.3F.3Q. -a.3F.40. -a.3F.47. -a.3F.4d. -a.3F.4O. -a.3F.4Q. -7.4T.3F. -a.3F.4V. -a.3F.51. -a.3F.52. -a.3F.5a. -a.3F.5b. -a.3F.5d. -a.3F.5B. -a.3F.5C. -a.3F.5F. -a.3F.5H. -a.3F.5I. -f.3F.5K. -7.5M.3F. -a.3F.5O. -a.3F.5R. -a.3F.5U. -a.3F.5W. -1.5Y.3F. -a.3F.5Z. -a.3F.5/. -a.3F.60. -a.3F.62. -a.3F.66. -a.6b.3F. -c.6e.3F. -a.3F.6F. -a.3F.6M. -a.3F.6+. -a.3F.71. -a.3F.79. -a.3F.7D. -a.3F.2p. -a.3F.2p. -5.3G.3G. -5.3G.3M. -5.3G.3Q. -5.40.3G. -5.3G.47. -5.3G.4d. -5.3G.4O. -5.3G.4Q. -7.4T.3G. -5.3G.4V. -5.3G.51. -5.3G.52. -a.5a.3G. -2.5b.3G. -5.5d.3G. -5.3G.5B. -5.3G.5C. -5.3G.5F. -5.3G.5H. -5.3G.5I. -f.3G.5K. -7.5M.3G. -5.3G.5O. -5.3G.5R. -5.3G.5U. -5.3G.5W. -1.5Y.3G. -5.3G.5Z. -5.3G.5/. -5.3G.60. -2.62.3G. -2.66.3G. -a.6b.3G. -c.6e.3G. -5.3G.6F. -5.3G.6M. -5.3G.6+. -5.3G.71. -5.3G.79. -5.3G.7D. -5.3G.2p. -5.3G.2p. -1.4s.3H. -1.51.3H. -1.5v.3H. -6.5I.3H. -6.5K.3H. -7.5M.3H. -1.5Y.3H. -1.5+.3H. -4.60.3H. -5.62.3H. -1.6q.3H. -1.6t.3H. -8.6F.3H. -c.6H.3H. -8.6M.3H. -1.7B.3H. -1.7R.3H. -1.86.3H. -1.8d.3H. -1.5v.3I. -6.5I.3I. -6.5K.3I. -7.5M.3I. -1.5Y.3I. -1.5+.3I. -4.60.3I. -5.62.3I. -1.6q.3I. -1.6t.3I. -8.6F.3I. -c.6H.3I. -8.6M.3I. -1.7B.3I. -1.86.3I. -1.8d.3I. -1.51.3J. -1.5v.3J. -6.5I.3J. -6.5K.3J. -7.5M.3J. -1.5Y.3J. -1.5+.3J. -4.60.3J. -5.62.3J. -1.6q.3J. -1.6t.3J. -8.6F.3J. -c.6H.3J. -8.6M.3J. -1.7B.3J. -1.86.3J. -1.8d.3J. -1.4s.3K. -1.51.3K. -1.5v.3K. -5.5I.3K. -6.5K.3K. -7.5M.3K. -1.5Y.3K. -1.5+.3K. -4.60.3K. -8.62.3K. -1.6q.3K. -1.6t.3K. -8.6F.3K. -c.6H.3K. -8.6M.3K. -1.7B.3K. -1.7R.3K. -1.86.3K. -1.8d.3K. -1.4s.3L. -1.51.3L. -1.5v.3L. -6.5I.3L. -6.5K.3L. -7.5M.3L. -1.5Y.3L. -1.5+.3L. -4.60.3L. -5.62.3L. -1.6q.3L. -1.6t.3L. -3.6F.3L. -c.6H.3L. -8.6M.3L. -1.7B.3L. -1.7R.3L. -1.86.3L. -1.8d.3L. -0.3M.3M. -5.3Q.3M. -5.40.3M. -0.47.3M. -5.4d.3M. -5.4O.3M. -0.4Q.3M. -7.4T.3M. -0.4V.3M. -5.51.3M. -0.52.3M. -a.5a.3M. -2.5b.3M. -5.5d.3M. -5.5B.3M. -5.5C.3M. -5.5F.3M. -0.5H.3M. -0.5I.3M. -f.5K.3M. -7.5M.3M. -5.5O.3M. -5.5R.3M. -0.5U.3M. -0.5W.3M. -1.5Y.3M. -5.5Z.3M. -5.5/.3M. -0.60.3M. -2.62.3M. -2.66.3M. -a.6b.3M. -c.6e.3M. -0.6F.3M. -0.6M.3M. -0.6+.3M. -0.71.3M. -5.79.3M. -0.7D.3M. -0.2p.3M. -0.2p.3M. -1.4s.3N. -1.51.3N. -1.5v.3N. -6.5I.3N. -6.5K.3N. -7.5M.3N. -1.5Y.3N. -1.5+.3N. -4.60.3N. -8.62.3N. -1.6q.3N. -1.6t.3N. -3.6F.3N. -c.6H.3N. -8.6M.3N. -1.7B.3N. -1.7R.3N. -1.86.3N. -1.8d.3N. -1.51.3O. -1.5v.3O. -5.5I.3O. -6.5K.3O. -7.5M.3O. -1.5Y.3O. -1.5+.3O. -4.60.3O. -5.62.3O. -1.6q.3O. -1.6t.3O. -8.6F.3O. -c.6H.3O. -8.6M.3O. -1.7B.3O. -1.7R.3O. -1.86.3O. -1.8d.3O. -1.51.3P. -1.5v.3P. -5.5I.3P. -6.5K.3P. -7.5M.3P. -1.5Y.3P. -1.5+.3P. -4.60.3P. -5.62.3P. -1.6q.3P. -1.6t.3P. -8.6F.3P. -c.6H.3P. -8.6M.3P. -1.7B.3P. -1.86.3P. -1.8d.3P. -5.3Q.3Q. -5.40.3Q. -0.47.3Q. -5.4d.3Q. -5.4O.3Q. -0.4Q.3Q. -7.4T.3Q. -0.4V.3Q. -5.51.3Q. -0.52.3Q. -a.5a.3Q. -2.5b.3Q. -5.5d.3Q. -5.5B.3Q. -5.5C.3Q. -5.5F.3Q. -0.5H.3Q. -0.5I.3Q. -f.5K.3Q. -7.5M.3Q. -5.5O.3Q. -5.5R.3Q. -0.5U.3Q. -0.5W.3Q. -1.5Y.3Q. -5.5Z.3Q. -5.5/.3Q. -0.60.3Q. -2.62.3Q. -2.66.3Q. -a.6b.3Q. -c.6e.3Q. -0.6F.3Q. -0.6M.3Q. -0.6+.3Q. -0.71.3Q. -5.79.3Q. -0.7D.3Q. -0.2p.3Q. -0.2p.3Q. -1.4s.3R. -1.51.3R. -1.5v.3R. -6.5I.3R. -6.5K.3R. -7.5M.3R. -1.5Y.3R. -1.5+.3R. -4.60.3R. -5.62.3R. -1.6q.3R. -1.6t.3R. -3.6F.3R. -c.6H.3R. -8.6M.3R. -1.7B.3R. -1.7R.3R. -1.86.3R. -1.8d.3R. -1.51.3S. -1.5v.3S. -5.5I.3S. -6.5K.3S. -7.5M.3S. -1.5Y.3S. -1.5+.3S. -4.60.3S. -5.62.3S. -1.6q.3S. -1.6t.3S. -8.6F.3S. -c.6H.3S. -8.6M.3S. -1.7B.3S. -1.7R.3S. -1.86.3S. -1.8d.3S. -1.4s.3T. -1.51.3T. -1.5v.3T. -1.5I.3T. -1.5M.3T. -1.5Y.3T. -1.5+.3T. -1.60.3T. -1.62.3T. -1.6q.3T. -1.6t.3T. -1.6F.3T. -1.6H.3T. -1.6M.3T. -1.7B.3T. -1.86.3T. -1.8d.3T. -1.4s.3U. -1.51.3U. -1.5v.3U. -5.5I.3U. -6.5K.3U. -7.5M.3U. -1.5Y.3U. -1.5+.3U. -4.60.3U. -5.62.3U. -1.6q.3U. -1.6t.3U. -8.6F.3U. -c.6H.3U. -8.6M.3U. -1.7B.3U. -1.7R.3U. -1.86.3U. -1.8d.3U. -1.51.3V. -1.5v.3V. -5.5I.3V. -6.5K.3V. -7.5M.3V. -1.5Y.3V. -1.5+.3V. -4.60.3V. -8.62.3V. -1.6q.3V. -1.6t.3V. -8.6F.3V. -c.6H.3V. -8.6M.3V. -1.7B.3V. -1.7R.3V. -1.86.3V. -1.8d.3V. -1.51.3W. -1.5v.3W. -6.5I.3W. -6.5K.3W. -7.5M.3W. -1.5Y.3W. -1.5+.3W. -4.60.3W. -8.62.3W. -1.6q.3W. -1.6t.3W. -8.6F.3W. -c.6H.3W. -8.6M.3W. -1.7B.3W. -1.7R.3W. -1.86.3W. -1.8d.3W. -1.4s.3X. -1.51.3X. -1.5v.3X. -5.5I.3X. -6.5K.3X. -7.5M.3X. -1.5Y.3X. -1.5+.3X. -4.60.3X. -5.62.3X. -1.6q.3X. -1.6t.3X. -8.6F.3X. -c.6H.3X. -8.6M.3X. -1.7B.3X. -1.86.3X. -1.8d.3X. -1.4s.3Y. -1.51.3Y. -1.5v.3Y. -5.5I.3Y. -6.5K.3Y. -7.5M.3Y. -1.5Y.3Y. -1.5+.3Y. -4.60.3Y. -5.62.3Y. -1.6q.3Y. -1.6t.3Y. -8.6F.3Y. -c.6H.3Y. -8.6M.3Y. -1.7B.3Y. -1.7R.3Y. -1.86.3Y. -1.8d.3Y. -1.51.3Z. -1.5v.3Z. -6.5I.3Z. -6.5K.3Z. -7.5M.3Z. -1.5Y.3Z. -1.5+.3Z. -4.60.3Z. -8.62.3Z. -1.6q.3Z. -1.6t.3Z. -8.6F.3Z. -c.6H.3Z. -8.6M.3Z. -1.7B.3Z. -1.86.3Z. -1.8d.3Z. -1.4s.3+. -1.51.3+. -1.5v.3+. -1.5I.3+. -1.5M.3+. -1.5Y.3+. -1.5+.3+. -1.60.3+. -1.62.3+. -1.6q.3+. -1.6t.3+. -1.6F.3+. -1.6H.3+. -1.6M.3+. -1.7B.3+. -1.7R.3+. -1.86.3+. -1.8d.3+. -1.4s.3/. -1.51.3/. -1.5v.3/. -1.5I.3/. -1.5M.3/. -1.5Y.3/. -1.5+.3/. -1.60.3/. -1.62.3/. -1.6q.3/. -1.6t.3/. -1.6F.3/. -1.6H.3/. -1.6M.3/. -1.7B.3/. -1.7R.3/. -1.86.3/. -1.8d.3/. -5.40.40. -5.40.47. -5.40.4d. -5.40.4O. -5.40.4Q. -7.4T.40. -5.40.4V. -5.40.51. -5.40.52. -a.5a.40. -2.5b.40. -5.40.5d. -5.40.5B. -5.40.5C. -5.40.5F. -5.40.5H. -5.40.5I. -f.40.5K. -7.5M.40. -5.40.5O. -5.40.5R. -5.40.5U. -5.40.5W. -1.5Y.40. -5.40.5Z. -5.40.5/. -5.40.60. -2.62.40. -2.66.40. -a.6b.40. -c.6e.40. -5.40.6F. -5.40.6M. -5.40.6+. -5.40.71. -5.40.79. -5.40.7D. -5.40.2p. -5.40.2p. -1.51.41. -1.5v.41. -6.5I.41. -6.5K.41. -7.5M.41. -1.5Y.41. -1.5+.41. -4.60.41. -5.62.41. -1.6q.41. -1.6t.41. -3.6F.41. -c.6H.41. -8.6M.41. -1.7B.41. -1.86.41. -1.8d.41. -1.4s.42. -1.51.42. -1.5v.42. -6.5I.42. -6.5K.42. -7.5M.42. -1.5Y.42. -1.5+.42. -4.60.42. -8.62.42. -1.6q.42. -1.6t.42. -8.6F.42. -c.6H.42. -8.6M.42. -1.7B.42. -1.7R.42. -1.86.42. -1.8d.42. -1.4s.43. -1.51.43. -1.5v.43. -5.5I.43. -6.5K.43. -7.5M.43. -1.5Y.43. -1.5+.43. -4.60.43. -5.62.43. -1.6q.43. -1.6t.43. -3.6F.43. -c.6H.43. -8.6M.43. -1.7B.43. -1.7R.43. -1.86.43. -1.8d.43. -1.51.44. -1.5v.44. -6.5I.44. -6.5K.44. -7.5M.44. -1.5Y.44. -1.5+.44. -4.60.44. -5.62.44. -1.6q.44. -1.6t.44. -8.6F.44. -c.6H.44. -8.6M.44. -1.7B.44. -1.86.44. -1.8d.44. -1.4s.45. -1.51.45. -1.5v.45. -6.5I.45. -6.5K.45. -7.5M.45. -1.5Y.45. -1.5+.45. -4.60.45. -5.62.45. -1.6q.45. -1.6t.45. -8.6F.45. -c.6H.45. -8.6M.45. -1.7B.45. -1.86.45. -1.8d.45. -0.47.47. -5.4d.47. -5.4O.47. -0.4Q.47. -7.4T.47. -0.4V.47. -5.51.47. -0.52.47. -a.5a.47. -2.5b.47. -5.5d.47. -5.5B.47. -5.5C.47. -5.5F.47. -0.5H.47. -0.5I.47. -f.5K.47. -7.5M.47. -5.5O.47. -5.5R.47. -0.5U.47. -0.5W.47. -1.5Y.47. -5.5Z.47. -5.5/.47. -0.60.47. -2.62.47. -2.66.47. -a.6b.47. -c.6e.47. -0.6F.47. -0.6M.47. -0.6+.47. -0.71.47. -5.79.47. -0.7D.47. -0.2p.47. -0.2p.47. -1.51.48. -1.5v.48. -5.5I.48. -6.5K.48. -7.5M.48. -1.5Y.48. -1.5+.48. -4.60.48. -8.62.48. -1.6q.48. -1.6t.48. -8.6F.48. -c.6H.48. -8.6M.48. -1.7B.48. -1.86.48. -1.8d.48. -1.4s.49. -1.51.49. -1.5v.49. -6.5I.49. -6.5K.49. -7.5M.49. -1.5Y.49. -1.5+.49. -4.60.49. -8.62.49. -1.6q.49. -1.6t.49. -3.6F.49. -c.6H.49. -8.6M.49. -1.7B.49. -1.7R.49. -1.86.49. -1.8d.49. -1.51.4a. -1.5v.4a. -1.5I.4a. -1.5M.4a. -1.5Y.4a. -1.5+.4a. -1.60.4a. -1.62.4a. -1.6q.4a. -1.6t.4a. -1.6F.4a. -1.6H.4a. -1.6M.4a. -1.7B.4a. -1.7R.4a. -1.86.4a. -1.8d.4a. -1.51.4b. -1.5v.4b. -6.5I.4b. -6.5K.4b. -7.5M.4b. -1.5Y.4b. -1.5+.4b. -4.60.4b. -5.62.4b. -1.6q.4b. -1.6t.4b. -8.6F.4b. -c.6H.4b. -8.6M.4b. -1.7B.4b. -1.86.4b. -1.8d.4b. -1.4s.4c. -1.51.4c. -1.5v.4c. -6.5I.4c. -6.5K.4c. -7.5M.4c. -1.5Y.4c. -1.5+.4c. -4.60.4c. -5.62.4c. -1.6q.4c. -1.6t.4c. -8.6F.4c. -c.6H.4c. -8.6M.4c. -1.7B.4c. -1.86.4c. -1.8d.4c. -5.4d.4d. -5.4d.4O. -5.4d.4Q. -5.4T.4d. -5.4d.4V. -5.4d.51. -5.4d.52. -a.5a.4d. -2.5b.4d. -5.5d.4d. -5.4d.5B. -5.4d.5C. -5.5F.4d. -5.4d.5H. -0.5I.4d. -f.4d.5K. -7.5M.4d. -5.5O.4d. -5.4d.5R. -5.4d.5U. -5.4d.5W. -1.5Y.4d. -5.4d.5Z. -5.4d.5/. -0.60.4d. -2.62.4d. -2.66.4d. -a.6b.4d. -c.6e.4d. -5.4d.6F. -0.6M.4d. -5.4d.6+. -5.4d.71. -5.79.4d. -5.4d.7D. -0.2p.4d. -0.2p.4d. -1.4s.4e. -1.51.4e. -1.5v.4e. -6.5I.4e. -6.5K.4e. -7.5M.4e. -1.5Y.4e. -1.5+.4e. -4.60.4e. -8.62.4e. -1.6q.4e. -1.6t.4e. -8.6F.4e. -c.6H.4e. -8.6M.4e. -1.7B.4e. -1.86.4e. -1.8d.4e. -1.51.4f. -1.5v.4f. -6.5I.4f. -6.5K.4f. -7.5M.4f. -1.5Y.4f. -1.5+.4f. -4.60.4f. -8.62.4f. -1.6q.4f. -1.6t.4f. -8.6F.4f. -c.6H.4f. -8.6M.4f. -1.7B.4f. -1.86.4f. -1.8d.4f. -1.51.4g. -1.5v.4g. -6.5I.4g. -6.5K.4g. -7.5M.4g. -1.5Y.4g. -1.5+.4g. -4.60.4g. -8.62.4g. -1.6q.4g. -1.6t.4g. -8.6F.4g. -c.6H.4g. -8.6M.4g. -1.7B.4g. -1.86.4g. -1.8d.4g. -1.4s.4h. -1.51.4h. -1.5v.4h. -5.5I.4h. -6.5K.4h. -7.5M.4h. -1.5Y.4h. -1.5+.4h. -4.60.4h. -8.62.4h. -1.6q.4h. -1.6t.4h. -3.6F.4h. -c.6H.4h. -8.6M.4h. -1.7B.4h. -1.7R.4h. -1.86.4h. -1.8d.4h. -1.51.4i. -1.5v.4i. -6.5I.4i. -6.5K.4i. -7.5M.4i. -1.5Y.4i. -1.5+.4i. -4.60.4i. -8.62.4i. -1.6q.4i. -1.6t.4i. -8.6F.4i. -c.6H.4i. -8.6M.4i. -1.7B.4i. -1.7R.4i. -1.86.4i. -1.8d.4i. -1.51.4j. -1.5v.4j. -6.5I.4j. -6.5K.4j. -7.5M.4j. -1.5Y.4j. -1.5+.4j. -4.60.4j. -8.62.4j. -1.6q.4j. -1.6t.4j. -8.6F.4j. -c.6H.4j. -8.6M.4j. -1.7B.4j. -1.86.4j. -1.8d.4j. -1.51.4k. -1.5v.4k. -8.5I.4k. -8.5K.4k. -7.5M.4k. -1.5Y.4k. -1.5+.4k. -8.60.4k. -8.62.4k. -1.6q.4k. -1.6t.4k. -3.6F.4k. -c.6H.4k. -8.6M.4k. -1.7B.4k. -1.86.4k. -1.8d.4k. -1.51.4l. -1.5v.4l. -6.5I.4l. -6.5K.4l. -7.5M.4l. -1.5Y.4l. -1.5+.4l. -4.60.4l. -8.62.4l. -1.6q.4l. -1.6t.4l. -8.6F.4l. -c.6H.4l. -8.6M.4l. -1.7B.4l. -1.7R.4l. -1.86.4l. -1.8d.4l. -1.51.4m. -1.5v.4m. -5.5I.4m. -6.5K.4m. -7.5M.4m. -1.5Y.4m. -1.5+.4m. -4.60.4m. -5.62.4m. -1.6q.4m. -1.6t.4m. -8.6F.4m. -c.6H.4m. -8.6M.4m. -1.7B.4m. -1.86.4m. -1.8d.4m. -1.4s.4n. -1.51.4n. -1.5v.4n. -6.5I.4n. -6.5K.4n. -7.5M.4n. -1.5Y.4n. -1.5+.4n. -4.60.4n. -5.62.4n. -1.6q.4n. -1.6t.4n. -3.6F.4n. -c.6H.4n. -8.6M.4n. -1.7B.4n. -1.7R.4n. -1.86.4n. -1.8d.4n. -1.4s.4o. -1.51.4o. -1.5v.4o. -6.5I.4o. -6.5K.4o. -7.5M.4o. -1.5Y.4o. -1.5+.4o. -4.60.4o. -5.62.4o. -1.6q.4o. -1.6t.4o. -8.6F.4o. -c.6H.4o. -8.6M.4o. -1.7B.4o. -1.7R.4o. -1.86.4o. -1.8d.4o. -1.51.4p. -1.5v.4p. -6.5I.4p. -6.5K.4p. -7.5M.4p. -1.5Y.4p. -1.5+.4p. -4.60.4p. -8.62.4p. -1.6q.4p. -1.6t.4p. -8.6F.4p. -c.6H.4p. -8.6M.4p. -1.7B.4p. -1.7R.4p. -1.86.4p. -1.8d.4p. -1.4s.4q. -1.51.4q. -1.5v.4q. -5.5I.4q. -6.5K.4q. -7.5M.4q. -1.5Y.4q. -1.5+.4q. -4.60.4q. -5.62.4q. -1.6q.4q. -1.6t.4q. -8.6F.4q. -c.6H.4q. -8.6M.4q. -1.7B.4q. -1.86.4q. -1.8d.4q. -1.51.4r. -1.5v.4r. -6.5I.4r. -6.5K.4r. -7.5M.4r. -1.5Y.4r. -1.5+.4r. -4.60.4r. -8.62.4r. -1.6q.4r. -1.6t.4r. -8.6F.4r. -c.6H.4r. -8.6M.4r. -1.7B.4r. -1.86.4r. -1.8d.4r. -1.4s.4s. -1.4s.4y. -1.4s.4z. -1.4s.4F. -1.4s.4G. -1.4s.4N. -1.4s.4R. -1.4s.4S. -1.4s.4T. -1.4s.4U. -1.4s.4Z. -1.4s.50. -1.51.4s. -1.4s.54. -1.4s.55. -1.4s.56. -1.4s.57. -1.4s.59. -1.4s.5e. -1.4s.5f. -1.4s.5g. -1.4s.5i. -1.4s.5l. -1.4s.5q. -1.4s.5t. -1.5v.4s. -1.4s.5y. -1.4s.5z. -1.4s.5G. -6.5I.4s. -6.5K.4s. -1.4s.5L. -7.5M.4s. -1.4s.5S. -1.4s.5T. -1.4s.5V. -1.5Y.4s. -1.5+.4s. -4.60.4s. -8.62.4s. -1.4s.64. -1.4s.67. -1.4s.6a. -1.4s.6c. -1.4s.6d. -1.4s.6e. -1.4s.6f. -1.4s.6g. -1.4s.6h. -1.4s.6i. -1.4s.6j. -1.4s.6k. -1.4s.6l. -1.4s.6m. -1.4s.6n. -1.4s.6o. -1.4s.6p. -1.6q.4s. -1.4s.6r. -1.4s.6s. -1.6t.4s. -1.4s.6u. -1.4s.6v. -1.4s.6w. -1.4s.6x. -1.4s.6A. -1.4s.6C. -1.4s.6D. -1.4s.6E. -8.6F.4s. -1.4s.6G. -c.6H.4s. -1.4s.6I. -1.4s.6K. -1.4s.6L. -8.6M.4s. -1.4s.6N. -1.4s.6O. -1.4s.6Q. -1.4s.6R. -1.4s.6S. -1.4s.6U. -1.4s.6Y. -1.4s.6Z. -1.4s.70. -1.4s.76. -1.4s.77. -1.4s.78. -1.4s.7c. -1.4s.7h. -1.4s.7j. -1.4s.7l. -1.4s.7o. -1.4s.7p. -1.4s.7v. -1.4s.7w. -1.4s.7A. -1.7B.4s. -1.4s.7F. -1.4s.7G. -1.4s.7H. -1.4s.7I. -1.4s.7J. -1.4s.7P. -1.4s.7W. -1.4s.7Y. -1.4s.81. -1.4s.82. -1.4s.83. -1.4s.84. -1.86.4s. -1.4s.87. -1.4s.8d. -1.4s.8g. -1.4s.8h. -1.4s.8i. -1.4s.8j. -1.4s.8k. -1.4s.8l. -1.4s.8m. -1.4s.8n. -1.4s.8o. -1.4s.8p. -1.4s.8q. -1.4s.8r. -1.4s.8s. -1.4s.8u. -1.4s.8v. -1.4s.8w. -1.4s.8x. -1.4s.8y. -1.4s.8z. -1.4s.8A. -1.4s.8B. -1.4s.8C. -1.4s.8D. -1.4s.8E. -1.4s.8F. -1.4s.8G. -1.4s.8H. -1.4s.8I. -1.4s.8J. -1.4s.3b. -1.4s.5x. -1.4s.7q. -1.51.4t. -1.5v.4t. -6.5I.4t. -6.5K.4t. -7.5M.4t. -1.5Y.4t. -1.5+.4t. -4.60.4t. -5.62.4t. -1.6q.4t. -1.6t.4t. -8.6F.4t. -c.6H.4t. -8.6M.4t. -1.7B.4t. -1.86.4t. -1.8d.4t. -1.51.4u. -1.5v.4u. -6.5I.4u. -6.5K.4u. -7.5M.4u. -1.5Y.4u. -1.5+.4u. -4.60.4u. -8.62.4u. -1.6q.4u. -1.6t.4u. -8.6F.4u. -c.6H.4u. -8.6M.4u. -1.7B.4u. -1.86.4u. -1.8d.4u. -1.51.4v. -1.5v.4v. -6.5I.4v. -6.5K.4v. -7.5M.4v. -1.5Y.4v. -1.5+.4v. -4.60.4v. -8.62.4v. -1.6q.4v. -1.6t.4v. -8.6F.4v. -c.6H.4v. -8.6M.4v. -1.7B.4v. -1.86.4v. -1.8d.4v. -1.51.4w. -1.5v.4w. -6.5I.4w. -6.5K.4w. -7.5M.4w. -1.5Y.4w. -1.5+.4w. -4.60.4w. -8.62.4w. -1.6q.4w. -1.6t.4w. -8.6F.4w. -c.6H.4w. -8.6M.4w. -1.7B.4w. -1.86.4w. -1.8d.4w. -1.51.4x. -1.5v.4x. -6.5I.4x. -6.5K.4x. -7.5M.4x. -1.5Y.4x. -1.5+.4x. -4.60.4x. -8.62.4x. -1.6q.4x. -1.6t.4x. -8.6F.4x. -c.6H.4x. -8.6M.4x. -1.7B.4x. -1.86.4x. -1.8d.4x. -1.51.4y. -1.5v.4y. -5.5I.4y. -6.5K.4y. -7.5M.4y. -1.5Y.4y. -1.5+.4y. -4.60.4y. -8.62.4y. -1.6q.4y. -1.6t.4y. -8.6F.4y. -c.6H.4y. -8.6M.4y. -1.7B.4y. -1.86.4y. -1.8d.4y. -1.51.4z. -1.5v.4z. -5.5I.4z. -6.5K.4z. -7.5M.4z. -1.5Y.4z. -1.5+.4z. -4.60.4z. -8.62.4z. -1.6q.4z. -1.6t.4z. -8.6F.4z. -c.6H.4z. -8.6M.4z. -1.7B.4z. -1.86.4z. -1.8d.4z. -1.51.4A. -1.5v.4A. -5.5I.4A. -6.5K.4A. -7.5M.4A. -1.5Y.4A. -1.5+.4A. -4.60.4A. -5.62.4A. -1.6q.4A. -1.6t.4A. -3.6F.4A. -c.6H.4A. -8.6M.4A. -1.7B.4A. -1.86.4A. -1.8d.4A. -1.51.4B. -1.5v.4B. -6.5I.4B. -6.5K.4B. -7.5M.4B. -1.5Y.4B. -1.5+.4B. -4.60.4B. -8.62.4B. -1.6q.4B. -1.6t.4B. -8.6F.4B. -c.6H.4B. -8.6M.4B. -1.7B.4B. -1.86.4B. -1.8d.4B. -1.51.4C. -1.5v.4C. -6.5I.4C. -6.5K.4C. -7.5M.4C. -1.5Y.4C. -1.5+.4C. -4.60.4C. -5.62.4C. -1.6q.4C. -1.6t.4C. -3.6F.4C. -c.6H.4C. -8.6M.4C. -1.7B.4C. -1.86.4C. -1.8d.4C. -1.51.4D. -1.5v.4D. -6.5I.4D. -6.5K.4D. -7.5M.4D. -1.5Y.4D. -1.5+.4D. -4.60.4D. -5.62.4D. -1.6q.4D. -1.6t.4D. -3.6F.4D. -c.6H.4D. -8.6M.4D. -1.7B.4D. -1.86.4D. -1.8d.4D. -1.51.4E. -1.5v.4E. -6.5I.4E. -6.5K.4E. -7.5M.4E. -1.5Y.4E. -1.5+.4E. -4.60.4E. -5.62.4E. -1.6q.4E. -1.6t.4E. -3.6F.4E. -c.6H.4E. -8.6M.4E. -1.7B.4E. -1.7R.4E. -1.86.4E. -1.8d.4E. -1.51.4F. -1.5v.4F. -1.5I.4F. -1.5M.4F. -1.5Y.4F. -1.5+.4F. -1.60.4F. -1.62.4F. -1.6q.4F. -1.6t.4F. -1.6F.4F. -1.6H.4F. -1.6M.4F. -1.7B.4F. -1.7R.4F. -1.86.4F. -1.8d.4F. -1.51.4G. -1.5v.4G. -8.5I.4G. -8.5K.4G. -7.5M.4G. -1.5Y.4G. -1.5+.4G. -5.60.4G. -5.62.4G. -1.6q.4G. -1.6t.4G. -3.6F.4G. -c.6H.4G. -8.6M.4G. -1.7B.4G. -1.7R.4G. -1.86.4G. -1.8d.4G. -1.51.4H. -1.5v.4H. -8.5I.4H. -8.5K.4H. -7.5M.4H. -1.5Y.4H. -1.5+.4H. -8.60.4H. -5.62.4H. -1.6q.4H. -1.6t.4H. -3.6F.4H. -c.6H.4H. -8.6M.4H. -1.7B.4H. -1.86.4H. -1.8d.4H. -1.51.4I. -1.5v.4I. -1.5I.4I. -1.5M.4I. -1.5Y.4I. -1.5+.4I. -1.60.4I. -h.62.4I. -1.6q.4I. -1.6t.4I. -1.6F.4I. -1.6H.4I. -1.6M.4I. -1.7B.4I. -1.7R.4I. -1.86.4I. -1.8d.4I. -1.51.4J. -1.5v.4J. -8.5I.4J. -8.5K.4J. -7.5M.4J. -1.5Y.4J. -1.5+.4J. -5.60.4J. -5.62.4J. -1.6q.4J. -1.6t.4J. -8.6F.4J. -c.6H.4J. -8.6M.4J. -1.7B.4J. -1.86.4J. -1.8d.4J. -1.51.4K. -1.5v.4K. -8.5I.4K. -8.5K.4K. -7.5M.4K. -1.5Y.4K. -1.5+.4K. -5.60.4K. -8.62.4K. -1.6q.4K. -1.6t.4K. -8.6F.4K. -c.6H.4K. -8.6M.4K. -1.7B.4K. -1.86.4K. -1.8d.4K. -1.51.4L. -1.5v.4L. -8.5I.4L. -8.5K.4L. -7.5M.4L. -1.5Y.4L. -1.5+.4L. -5.60.4L. -5.62.4L. -1.6q.4L. -1.6t.4L. -8.6F.4L. -c.6H.4L. -8.6M.4L. -1.7B.4L. -1.86.4L. -1.8d.4L. -1.51.4M. -1.5v.4M. -5.5I.4M. -6.5K.4M. -7.5M.4M. -1.5Y.4M. -1.5+.4M. -4.60.4M. -5.62.4M. -1.6q.4M. -1.6t.4M. -3.6F.4M. -c.6H.4M. -8.6M.4M. -1.7B.4M. -1.86.4M. -1.8d.4M. -1.51.4N. -1.5v.4N. -5.5I.4N. -6.5K.4N. -7.5M.4N. -1.5Y.4N. -1.5+.4N. -4.60.4N. -5.62.4N. -1.6q.4N. -1.6t.4N. -8.6F.4N. -c.6H.4N. -8.6M.4N. -1.7B.4N. -1.86.4N. -1.8d.4N. -5.4O.4O. -0.4Q.4O. -7.4T.4O. -0.4V.4O. -5.51.4O. -0.52.4O. -a.5a.4O. -2.5b.4O. -5.5d.4O. -5.5B.4O. -5.5C.4O. -5.5F.4O. -0.5H.4O. -0.5I.4O. -f.5K.4O. -7.5M.4O. -5.5O.4O. -5.5R.4O. -0.5U.4O. -0.5W.4O. -1.5Y.4O. -5.5Z.4O. -5.5/.4O. -0.60.4O. -2.62.4O. -2.66.4O. -a.6b.4O. -c.6e.4O. -0.6F.4O. -0.6M.4O. -0.6+.4O. -0.71.4O. -5.79.4O. -0.7D.4O. -0.2p.4O. -0.2p.4O. -1.51.4P. -1.5v.4P. -5.5I.4P. -6.5K.4P. -7.5M.4P. -1.5Y.4P. -1.5+.4P. -4.60.4P. -5.62.4P. -1.6q.4P. -1.6t.4P. -8.6F.4P. -c.6H.4P. -8.6M.4P. -1.7B.4P. -1.86.4P. -1.8d.4P. -3.4Q.4Q. -7.4T.4Q. -0.4Q.4V. -3.4Q.51. -0.52.4Q. -a.5a.4Q. -2.5b.4Q. -5.5d.4Q. -5.5B.4Q. -5.5C.4Q. -5.5F.4Q. -0.5H.4Q. -0.5I.4Q. -0.4Q.5K. -7.5M.4Q. -5.5O.4Q. -0.4Q.5R. -0.4Q.5U. -0.5W.4Q. -1.5Y.4Q. -5.5Z.4Q. -5.5/.4Q. -0.60.4Q. -2.62.4Q. -2.66.4Q. -a.6b.4Q. -c.6e.4Q. -0.4Q.6F. -0.6M.4Q. -0.6+.4Q. -0.71.4Q. -5.79.4Q. -0.4Q.7D. -0.2p.4Q. -0.2p.4Q. -1.51.4R. -1.5v.4R. -1.5I.4R. -1.5M.4R. -1.5Y.4R. -1.5+.4R. -1.60.4R. -1.62.4R. -1.6q.4R. -1.6t.4R. -1.6F.4R. -1.6H.4R. -1.6M.4R. -1.7B.4R. -1.7R.4R. -1.86.4R. -1.8d.4R. -1.51.4S. -1.5v.4S. -6.5I.4S. -6.5K.4S. -7.5M.4S. -1.5Y.4S. -1.5+.4S. -4.60.4S. -5.62.4S. -1.6q.4S. -1.6t.4S. -3.6F.4S. -c.6H.4S. -8.6M.4S. -1.7B.4S. -1.86.4S. -1.8d.4S. -5.4T.4T. -7.4T.4V. -5.4T.51. -5.4T.52. -7.4T.5a. -7.4T.5b. -7.4T.5d. -1.5v.4T. -5.4T.5B. -5.4T.5C. -5.4T.5F. -5.4T.5H. -7.4T.5I. -7.4T.5K. -7.4T.5M. -5.4T.5O. -4.4T.5R. -7.4T.5U. -5.4T.5W. -1.5Y.4T. -5.4T.5Z. -1.5+.4T. -5.4T.5/. -7.4T.60. -7.4T.62. -7.4T.66. -7.4T.6b. -c.6e.4T. -1.6q.4T. -1.6t.4T. -7.4T.6F. -c.6H.4T. -5.4T.6M. -5.4T.6+. -5.4T.71. -7.4T.79. -1.7B.4T. -5.4T.7D. -1.86.4T. -1.8d.4T. -5.4T.2p. -5.4T.2p. -1.51.4U. -1.5v.4U. -8.5I.4U. -8.5K.4U. -7.5M.4U. -1.5Y.4U. -1.5+.4U. -8.60.4U. -5.62.4U. -1.6q.4U. -1.6t.4U. -8.6F.4U. -c.6H.4U. -8.6M.4U. -1.7B.4U. -1.86.4U. -1.8d.4U. -0.4V.4V. -5.51.4V. -0.52.4V. -a.5a.4V. -2.5b.4V. -5.5d.4V. -5.5B.4V. -5.5C.4V. -5.5F.4V. -0.5H.4V. -0.5I.4V. -f.5K.4V. -7.5M.4V. -5.5O.4V. -5.5R.4V. -0.5U.4V. -0.5W.4V. -1.5Y.4V. -5.5Z.4V. -5.5/.4V. -0.60.4V. -2.62.4V. -2.66.4V. -a.6b.4V. -c.6e.4V. -0.6F.4V. -0.6M.4V. -0.6+.4V. -d.71.4V. -5.79.4V. -0.7D.4V. -0.2p.4V. -0.2p.4V. -1.51.4W. -1.5v.4W. -5.5I.4W. -6.5K.4W. -7.5M.4W. -1.5Y.4W. -1.5+.4W. -4.60.4W. -8.62.4W. -1.6q.4W. -1.6t.4W. -3.6F.4W. -c.6H.4W. -8.6M.4W. -1.7B.4W. -1.86.4W. -1.8d.4W. -1.51.4Y. -1.5v.4Y. -8.5I.4Y. -8.5K.4Y. -7.5M.4Y. -1.5Y.4Y. -1.5+.4Y. -5.60.4Y. -5.62.4Y. -1.6q.4Y. -1.6t.4Y. -8.6F.4Y. -c.6H.4Y. -8.6M.4Y. -1.7B.4Y. -1.86.4Y. -1.8d.4Y. -1.51.4Z. -1.5v.4Z. -1.5I.4Z. -1.5M.4Z. -1.5Y.4Z. -1.5+.4Z. -1.60.4Z. -1.62.4Z. -1.6q.4Z. -1.6t.4Z. -1.6F.4Z. -1.6H.4Z. -1.6M.4Z. -1.7B.4Z. -1.7R.4Z. -1.86.4Z. -1.8d.4Z. -1.51.4+. -1.5v.4+. -5.5I.4+. -6.5K.4+. -7.5M.4+. -1.5Y.4+. -1.5+.4+. -4.60.4+. -5.62.4+. -1.6q.4+. -1.6t.4+. -8.6F.4+. -c.6H.4+. -8.6M.4+. -1.7B.4+. -1.86.4+. -1.8d.4+. -1.51.4/. -1.5v.4/. -5.5I.4/. -6.5K.4/. -7.5M.4/. -1.5Y.4/. -1.5+.4/. -4.60.4/. -8.62.4/. -1.6q.4/. -1.6t.4/. -3.6F.4/. -c.6H.4/. -8.6M.4/. -1.7B.4/. -1.86.4/. -1.8d.4/. -1.51.50. -1.5v.50. -1.5I.50. -1.5M.50. -1.5Y.50. -1.5+.50. -1.60.50. -1.62.50. -1.6q.50. -1.6t.50. -1.6F.50. -1.6H.50. -1.6M.50. -1.7B.50. -1.7R.50. -1.86.50. -1.8d.50. -5.51.51. -0.52.51. -1.51.53. -1.51.54. -1.51.55. -1.51.56. -1.51.57. -1.51.58. -1.51.59. -a.5a.51. -2.5b.51. -1.51.5c. -5.5d.51. -1.51.5e. -1.51.5f. -1.51.5g. -1.51.5h. -1.51.5i. -1.51.5j. -1.51.5k. -1.51.5l. -1.51.5m. -1.51.5n. -1.51.5o. -1.51.5p. -1.51.5q. -1.51.5r. -1.51.5s. -1.51.5t. -1.51.5u. -1.51.5v. -1.51.5w. -1.51.5y. -1.51.5z. -1.51.5A. -5.5B.51. -5.5C.51. -1.51.5D. -1.51.5E. -5.5F.51. -1.51.5G. -0.5H.51. -0.5I.51. -f.51.5K. -1.51.5L. -7.5M.51. -1.51.5N. -5.5O.51. -1.51.5P. -1.51.5Q. -5.5R.51. -1.51.5S. -1.51.5T. -5.51.5U. -1.51.5V. -0.5W.51. -1.51.5X. -1.5Y.51. -5.5Z.51. -5.5/.51. -0.60.51. -1.51.61. -2.62.51. -1.51.64. -1.51.65. -2.66.51. -1.51.67. -1.51.68. -1.51.69. -1.51.6a. -a.6b.51. -1.51.6c. -1.51.6d. -c.6e.51. -1.51.6f. -1.51.6g. -1.51.6h. -1.51.6i. -1.51.6j. -1.51.6k. -1.51.6l. -1.51.6m. -1.51.6n. -1.51.6o. -1.51.6p. -1.51.6q. -1.51.6r. -1.51.6s. -1.51.6t. -1.51.6u. -1.51.6v. -1.51.6w. -1.51.6x. -1.51.6y. -1.51.6z. -1.51.6A. -1.51.6B. -1.51.6C. -1.51.6D. -1.51.6E. -5.51.6F. -1.51.6G. -1.51.6H. -1.51.6I. -1.51.6J. -1.51.6K. -1.51.6L. -0.6M.51. -1.51.6N. -1.51.6O. -1.51.6P. -1.51.6Q. -1.51.6R. -1.51.6S. -1.51.6T. -1.51.6U. -1.51.6V. -1.51.6W. -1.51.6X. -1.51.6Y. -1.51.6Z. -0.6+.51. -1.51.6/. -1.51.70. -0.71.51. -1.51.72. -1.51.73. -1.51.74. -1.51.75. -1.51.76. -1.51.77. -1.51.78. -5.79.51. -1.51.7a. -1.51.7b. -1.51.7c. -1.51.7d. -1.51.7e. -1.51.7f. -1.51.7g. -1.51.7h. -1.51.7i. -1.51.7j. -1.51.7k. -1.51.7l. -1.51.7m. -1.51.7o. -1.51.7p. -1.51.7r. -1.51.7s. -1.51.7t. -1.51.7u. -1.51.7v. -1.51.7w. -1.51.7x. -1.51.7y. -1.51.7z. -1.51.7A. -1.51.7B. -1.51.7C. -5.51.7D. -1.51.7E. -1.51.7F. -1.51.7G. -1.51.7H. -1.51.7I. -1.51.7J. -1.51.7K. -1.51.7L. -1.51.7M. -1.51.7N. -1.51.7O. -1.51.7P. -1.51.7Q. -1.51.7R. -1.51.7S. -1.51.7T. -1.51.7U. -1.51.7V. -1.51.7W. -1.51.7X. -1.51.7Y. -1.51.7Z. -1.51.7+. -1.51.7/. -1.51.80. -1.51.81. -1.51.82. -1.51.83. -1.51.84. -1.51.85. -1.51.87. -1.51.88. -1.51.89. -1.51.8a. -1.51.8b. -1.51.8c. -1.51.8d. -1.51.8e. -1.51.8f. -1.51.8g. -1.51.8h. -1.51.8i. -1.51.8j. -1.51.8k. -1.51.8l. -1.51.8m. -1.51.8n. -1.51.8o. -1.51.8p. -1.51.8q. -1.51.8r. -1.51.8s. -1.51.8t. -1.51.8u. -1.51.8v. -1.51.8w. -1.51.8x. -1.51.8y. -1.51.8z. -1.51.8A. -1.51.8B. -1.51.8C. -1.51.8D. -1.51.8E. -1.51.8F. -1.51.8G. -1.51.8H. -1.51.8I. -1.51.8J. -1.51.8K. -1.51.8L. -1.51.8M. -1.51.8N. -1.51.8O. -1.51.8P. -d.2p.51. -d.2p.51. -1.51.3b. -1.51.46. -1.51.4X. -1.51.5x. -1.51.5J. -1.51.7n. -1.51.7q. -0.52.52. -a.5a.52. -2.5b.52. -5.5d.52. -0.52.5B. -0.52.5C. -5.5F.52. -0.52.5H. -0.5I.52. -0.52.5K. -7.5M.52. -5.5O.52. -0.52.5R. -0.52.5U. -0.52.5W. -1.5Y.52. -0.52.5Z. -0.52.5/. -0.60.52. -2.62.52. -2.66.52. -a.6b.52. -c.6e.52. -0.52.6F. -0.6M.52. -0.52.6+. -0.52.71. -5.79.52. -0.52.7D. -0.2p.52. -0.2p.52. -1.5v.53. -8.5I.53. -8.5K.53. -7.5M.53. -1.5Y.53. -1.5+.53. -8.60.53. -5.62.53. -1.6q.53. -1.6t.53. -8.6F.53. -c.6H.53. -8.6M.53. -1.7B.53. -1.86.53. -1.8d.53. -8.5I.54. -8.5K.54. -7.5M.54. -1.5Y.54. -1.5+.54. -8.60.54. -8.62.54. -1.6q.54. -1.6t.54. -8.6F.54. -c.6H.54. -8.6M.54. -1.7B.54. -1.7R.54. -1.86.54. -1.8d.54. -8.5I.55. -8.5K.55. -7.5M.55. -1.5Y.55. -1.5+.55. -8.60.55. -8.62.55. -1.6q.55. -1.6t.55. -8.6F.55. -c.6H.55. -8.6M.55. -1.7B.55. -1.7R.55. -1.86.55. -1.8d.55. -8.5I.56. -8.5K.56. -7.5M.56. -1.5Y.56. -1.5+.56. -5.60.56. -8.62.56. -1.6q.56. -1.6t.56. -8.6F.56. -c.6H.56. -8.6M.56. -1.7B.56. -1.7R.56. -1.86.56. -1.8d.56. -1.5I.57. -1.5M.57. -1.5Y.57. -1.5+.57. -1.60.57. -1.62.57. -1.6q.57. -1.6t.57. -1.6F.57. -1.6H.57. -1.6M.57. -1.7B.57. -1.7R.57. -1.86.57. -1.8d.57. -8.5I.58. -8.5K.58. -7.5M.58. -1.5Y.58. -1.5+.58. -8.60.58. -8.62.58. -1.6q.58. -1.6t.58. -8.6F.58. -c.6H.58. -8.6M.58. -1.7B.58. -1.7R.58. -1.86.58. -1.8d.58. -8.5I.59. -8.5K.59. -7.5M.59. -1.5O.59. -1.5Y.59. -1.5+.59. -8.60.59. -8.62.59. -1.6q.59. -1.6t.59. -8.6F.59. -c.6H.59. -8.6M.59. -1.7B.59. -1.7R.59. -1.86.59. -1.8d.59. -a.5a.5a. -a.5a.5b. -a.5a.5d. -a.5a.5B. -a.5a.5C. -a.5a.5F. -a.5a.5H. -a.5a.5I. -f.5a.5K. -7.5M.5a. -a.5a.5O. -a.5a.5R. -a.5a.5U. -a.5a.5W. -1.5Y.5a. -a.5a.5Z. -a.5a.5/. -a.5a.60. -a.5a.62. -a.5a.66. -a.6b.5a. -c.6e.5a. -a.5a.6F. -a.5a.6M. -a.5a.6+. -a.5a.71. -a.5a.79. -a.5a.7D. -a.5a.2p. -a.5a.2p. -2.5b.5b. -2.5b.5d. -2.5b.5B. -2.5b.5C. -2.5b.5F. -2.5b.5H. -2.5b.5I. -f.5b.5K. -7.5M.5b. -2.5b.5O. -2.5b.5R. -2.5b.5U. -2.5b.5W. -1.5Y.5b. -2.5b.5Z. -2.5b.5/. -2.5b.60. -2.5b.62. -2.5b.66. -c.6e.5b. -2.5b.6F. -2.5b.6M. -2.5b.6+. -2.5b.71. -2.5b.79. -2.5b.7D. -2.5b.2p. -2.5b.2p. -5.5I.5c. -6.5K.5c. -7.5M.5c. -1.5Y.5c. -1.5+.5c. -4.60.5c. -5.62.5c. -1.6q.5c. -1.6t.5c. -8.6F.5c. -c.6H.5c. -8.6M.5c. -1.7B.5c. -1.7R.5c. -1.86.5c. -1.8d.5c. -5.5d.5d. -5.5d.5B. -5.5d.5C. -5.5d.5F. -5.5d.5H. -5.5d.5I. -f.5d.5K. -7.5M.5d. -5.5d.5O. -5.5d.5R. -5.5d.5U. -5.5d.5W. -1.5Y.5d. -5.5d.5Z. -5.5d.5/. -5.5d.60. -2.62.5d. -2.66.5d. -a.6b.5d. -c.6e.5d. -5.5d.6F. -5.5d.6M. -5.5d.6+. -5.5d.71. -5.5d.79. -5.5d.7D. -5.5d.2p. -5.5d.2p. -8.5I.5e. -8.5K.5e. -7.5M.5e. -1.5O.5e. -1.5Y.5e. -1.5+.5e. -5.60.5e. -8.62.5e. -1.6q.5e. -1.6t.5e. -8.6F.5e. -c.6H.5e. -8.6M.5e. -1.7B.5e. -1.7R.5e. -1.86.5e. -1.8d.5e. -8.5I.5f. -8.5K.5f. -7.5M.5f. -1.5O.5f. -1.5Y.5f. -1.5+.5f. -5.60.5f. -8.62.5f. -1.6q.5f. -1.6t.5f. -8.6F.5f. -c.6H.5f. -8.6M.5f. -1.7B.5f. -1.7R.5f. -1.86.5f. -1.8d.5f. -6.5I.5g. -6.5K.5g. -7.5M.5g. -1.5O.5g. -1.5Y.5g. -1.5+.5g. -4.60.5g. -8.62.5g. -1.6q.5g. -1.6t.5g. -8.6F.5g. -c.6H.5g. -8.6M.5g. -1.7B.5g. -1.7R.5g. -1.86.5g. -1.8d.5g. -1.5v.5h. -8.5I.5h. -8.5K.5h. -7.5M.5h. -1.5Y.5h. -1.5+.5h. -8.60.5h. -8.62.5h. -1.6q.5h. -1.6t.5h. -8.6F.5h. -c.6H.5h. -8.6M.5h. -1.7B.5h. -1.86.5h. -1.8d.5h. -1.5v.5i. -5.5I.5i. -6.5K.5i. -7.5M.5i. -1.5Y.5i. -1.5+.5i. -4.60.5i. -8.62.5i. -1.6q.5i. -1.6t.5i. -3.6F.5i. -c.6H.5i. -8.6M.5i. -1.7B.5i. -1.86.5i. -1.8d.5i. -1.5v.5j. -5.5I.5j. -6.5K.5j. -7.5M.5j. -1.5Y.5j. -1.5+.5j. -4.60.5j. -8.62.5j. -1.6q.5j. -1.6t.5j. -3.6F.5j. -c.6H.5j. -8.6M.5j. -1.7B.5j. -1.86.5j. -1.8d.5j. -1.5v.5k. -5.5I.5k. -6.5K.5k. -7.5M.5k. -1.5Y.5k. -1.5+.5k. -4.60.5k. -8.62.5k. -1.6q.5k. -1.6t.5k. -3.6F.5k. -c.6H.5k. -8.6M.5k. -1.7B.5k. -1.86.5k. -1.8d.5k. -5.5I.5l. -6.5K.5l. -7.5M.5l. -1.5Y.5l. -1.5+.5l. -4.60.5l. -5.62.5l. -1.6q.5l. -1.6t.5l. -3.6F.5l. -c.6H.5l. -8.6M.5l. -1.7B.5l. -1.86.5l. -1.8d.5l. -1.5v.5m. -5.5I.5m. -6.5K.5m. -7.5M.5m. -1.5Y.5m. -1.5+.5m. -4.60.5m. -5.62.5m. -1.6q.5m. -1.6t.5m. -8.6F.5m. -c.6H.5m. -8.6M.5m. -1.7B.5m. -1.86.5m. -1.8d.5m. -1.5v.5n. -5.5I.5n. -6.5K.5n. -7.5M.5n. -1.5Y.5n. -1.5+.5n. -4.60.5n. -5.62.5n. -1.6q.5n. -1.6t.5n. -8.6F.5n. -c.6H.5n. -8.6M.5n. -1.7B.5n. -1.86.5n. -1.8d.5n. -1.5v.5o. -6.5I.5o. -6.5K.5o. -7.5M.5o. -1.5Y.5o. -1.5+.5o. -4.60.5o. -5.62.5o. -1.6q.5o. -1.6t.5o. -8.6F.5o. -c.6H.5o. -8.6M.5o. -1.7B.5o. -1.7R.5o. -1.86.5o. -1.8d.5o. -1.5v.5p. -6.5I.5p. -f.5K.5p. -7.5M.5p. -1.5Y.5p. -1.5+.5p. -4.60.5p. -8.62.5p. -1.6q.5p. -1.6t.5p. -8.6F.5p. -c.6H.5p. -8.6M.5p. -1.7B.5p. -1.7R.5p. -1.86.5p. -1.8d.5p. -1.5v.5q. -6.5I.5q. -6.5K.5q. -7.5M.5q. -1.5O.5q. -1.5Y.5q. -1.5+.5q. -4.60.5q. -5.62.5q. -1.6q.5q. -1.6t.5q. -8.6F.5q. -c.6H.5q. -8.6M.5q. -1.7B.5q. -1.7R.5q. -1.86.5q. -1.8d.5q. -1.5v.5r. -5.5I.5r. -6.5K.5r. -7.5M.5r. -1.5Y.5r. -1.5+.5r. -4.60.5r. -8.62.5r. -1.6q.5r. -1.6t.5r. -8.6F.5r. -c.6H.5r. -8.6M.5r. -1.7B.5r. -1.7R.5r. -1.86.5r. -1.8d.5r. -1.5v.5s. -1.5I.5s. -1.5M.5s. -1.5Y.5s. -1.5+.5s. -1.60.5s. -1.62.5s. -1.6q.5s. -1.6t.5s. -1.6F.5s. -1.6H.5s. -1.6M.5s. -1.7B.5s. -1.7R.5s. -1.86.5s. -1.8d.5s. -1.5v.5t. -5.5I.5t. -6.5K.5t. -7.5M.5t. -1.5O.5t. -1.5Y.5t. -1.5+.5t. -4.60.5t. -8.62.5t. -1.6q.5t. -1.6t.5t. -8.6F.5t. -c.6H.5t. -8.6M.5t. -1.7B.5t. -1.7R.5t. -1.86.5t. -1.8d.5t. -1.5v.5u. -5.5I.5u. -6.5K.5u. -7.5M.5u. -1.5Y.5u. -1.5+.5u. -4.60.5u. -5.62.5u. -1.6q.5u. -1.6t.5u. -8.6F.5u. -c.6H.5u. -8.6M.5u. -1.7B.5u. -1.86.5u. -1.8d.5u. -1.5v.5v. -1.5v.5w. -1.5v.5y. -1.5v.5z. -1.5v.5A. -1.5v.5D. -1.5v.5E. -1.5v.5G. -5.5I.5v. -6.5K.5v. -1.5v.5L. -7.5M.5v. -1.5v.5N. -1.5v.5P. -1.5v.5Q. -1.5v.5S. -1.5v.5T. -1.5v.5V. -1.5Y.5v. -1.5+.5v. -4.60.5v. -1.5v.61. -5.62.5v. -1.5v.64. -1.5v.65. -1.5v.68. -1.5v.69. -1.5v.6a. -1.5v.6c. -1.5v.6d. -1.5v.6j. -1.5v.6o. -1.6q.5v. -1.5v.6r. -1.5v.6s. -1.6t.5v. -1.5v.6u. -1.5v.6v. -1.5v.6w. -1.5v.6z. -1.5v.6A. -1.5v.6C. -1.5v.6D. -8.6F.5v. -c.6H.5v. -1.5v.6J. -1.5v.6K. -1.5v.6L. -8.6M.5v. -1.5v.6N. -1.5v.6T. -1.5v.6U. -1.5v.6V. -1.5v.6W. -1.5v.6X. -1.5v.6Y. -1.5v.6Z. -1.5v.6/. -1.5v.70. -1.5v.72. -1.5v.74. -1.5v.75. -1.5v.76. -1.5v.77. -1.5v.7c. -1.5v.7h. -1.5v.7i. -1.5v.7j. -1.5v.7l. -1.5v.7o. -1.5v.7p. -1.5v.7v. -1.5v.7A. -1.5v.7F. -1.5v.7G. -1.5v.7H. -1.5v.7I. -1.5v.7J. -1.5v.7N. -1.5v.7P. -1.5v.7S. -1.5v.7U. -1.5v.7V. -1.5v.7W. -1.5v.7Y. -1.5v.7Z. -1.5v.7+. -1.5v.7/. -1.5v.80. -1.5v.81. -1.5v.82. -1.5v.83. -1.5v.84. -1.5v.85. -1.86.5v. -1.5v.87. -1.5v.88. -1.5v.89. -1.5v.8a. -1.5v.8b. -1.5v.8c. -1.5v.8d. -1.5v.8e. -1.5v.8f. -1.5v.8g. -1.5v.8h. -1.5v.8i. -1.5v.8j. -1.5v.8k. -1.5v.8l. -1.5v.8m. -1.5v.8n. -1.5v.8o. -1.5v.8p. -1.5v.8q. -1.5v.8r. -1.5v.8s. -1.5v.8u. -1.5v.8v. -1.5v.8w. -1.5v.8x. -1.5v.8y. -1.5v.8z. -1.5v.8A. -1.5v.8B. -1.5v.8C. -1.5v.8D. -1.5v.8E. -1.5v.8F. -1.5v.8G. -1.5v.8H. -1.5v.8I. -1.5v.8J. -1.5v.8K. -1.5v.8L. -1.5v.8M. -1.5v.8N. -1.5v.8O. -1.5v.8P. -1.5v.3b. -1.5v.46. -1.5v.4X. -1.5v.5x. -1.5v.5J. -1.5v.7q. -1.5I.5w. -1.5M.5w. -1.5Y.5w. -1.5+.5w. -1.60.5w. -1.62.5w. -1.6q.5w. -1.6t.5w. -1.6F.5w. -1.6H.5w. -1.6M.5w. -1.7B.5w. -1.86.5w. -1.8d.5w. -1.5I.5y. -1.5M.5y. -1.5Y.5y. -1.5+.5y. -1.60.5y. -1.62.5y. -1.6q.5y. -1.6t.5y. -1.6F.5y. -1.6H.5y. -1.6M.5y. -1.7B.5y. -1.7R.5y. -1.86.5y. -1.8d.5y. -1.5I.5z. -1.5M.5z. -1.5Y.5z. -1.5+.5z. -1.60.5z. -1.62.5z. -1.6q.5z. -1.6t.5z. -1.6F.5z. -1.6H.5z. -1.6M.5z. -1.7B.5z. -1.7R.5z. -1.86.5z. -1.8d.5z. -8.5I.5A. -8.5K.5A. -7.5M.5A. -1.5Y.5A. -1.5+.5A. -5.60.5A. -5.62.5A. -1.6q.5A. -1.6t.5A. -8.6F.5A. -c.6H.5A. -8.6M.5A. -1.7B.5A. -1.86.5A. -1.8d.5A. -5.5B.5B. -5.5B.5C. -5.5F.5B. -5.5B.5H. -0.5I.5B. -f.5B.5K. -7.5M.5B. -5.5O.5B. -5.5B.5R. -5.5B.5U. -5.5B.5W. -1.5Y.5B. -5.5B.5Z. -5.5B.5/. -0.60.5B. -2.62.5B. -2.66.5B. -a.6b.5B. -c.6e.5B. -5.5B.6F. -0.6M.5B. -5.5B.6+. -5.5B.71. -5.79.5B. -5.5B.7D. -0.2p.5B. -0.2p.5B. -5.5C.5C. -5.5F.5C. -5.5C.5H. -0.5I.5C. -f.5C.5K. -7.5M.5C. -5.5O.5C. -5.5C.5R. -5.5C.5U. -5.5C.5W. -1.5Y.5C. -5.5C.5Z. -5.5C.5/. -0.60.5C. -2.62.5C. -2.66.5C. -a.6b.5C. -c.6e.5C. -5.5C.6F. -0.6M.5C. -5.5C.6+. -5.5C.71. -5.79.5C. -5.5C.7D. -0.2p.5C. -0.2p.5C. -1.5I.5D. -1.5M.5D. -1.5Y.5D. -1.5+.5D. -1.60.5D. -1.62.5D. -1.6q.5D. -1.6t.5D. -1.6F.5D. -1.6H.5D. -1.6M.5D. -1.7B.5D. -1.86.5D. -1.8d.5D. -8.5I.5E. -8.5K.5E. -7.5M.5E. -1.5Y.5E. -1.5+.5E. -5.60.5E. -5.62.5E. -1.6q.5E. -1.6t.5E. -8.6F.5E. -c.6H.5E. -8.6M.5E. -1.7B.5E. -1.86.5E. -1.8d.5E. -5.5F.5F. -5.5F.5H. -5.5F.5I. -f.5F.5K. -7.5M.5F. -5.5F.5O. -5.5F.5R. -5.5F.5U. -5.5F.5W. -1.5Y.5F. -5.5F.5Z. -5.5F.5/. -5.5F.60. -2.62.5F. -2.66.5F. -a.6b.5F. -c.6e.5F. -5.5F.6F. -5.5F.6M. -5.5F.6+. -5.5F.71. -5.79.5F. -5.5F.7D. -5.5F.2p. -5.5F.2p. -8.5I.5G. -8.5K.5G. -7.5M.5G. -1.5Y.5G. -1.5+.5G. -5.60.5G. -8.62.5G. -1.6q.5G. -1.6t.5G. -8.6F.5G. -c.6H.5G. -8.6M.5G. -1.7B.5G. -1.7R.5G. -1.86.5G. -1.8d.5G. -0.5H.5H. -0.5I.5H. -f.5H.5K. -7.5M.5H. -5.5O.5H. -0.5H.5R. -0.5H.5U. -0.5H.5W. -1.5Y.5H. -5.5Z.5H. -5.5/.5H. -0.60.5H. -2.62.5H. -2.66.5H. -a.6b.5H. -c.6e.5H. -0.5H.6F. -0.6M.5H. -0.6+.5H. -0.5H.71. -5.79.5H. -0.5H.7D. -0.2p.5H. -0.2p.5H. -5.5I.5I. -0.5I.5K. -8.5I.5L. -8.5I.5M. -8.5I.5N. -0.5I.5O. -8.5I.5P. -6.5I.5Q. -0.5I.5R. -5.5I.5S. -5.5I.5T. -0.5I.5U. -8.5I.5V. -0.5I.5W. -8.5I.5X. -8.5I.5Y. -0.5I.5Z. -h.5+.5I. -0.5I.5/. -0.5I.60. -6.5I.61. -2.62.5I. -5.5I.64. -5.5I.65. -2.66.5I. -5.5I.67. -5.5I.68. -5.5I.69. -5.5I.6a. -6.5I.6b. -6.5I.6c. -6.5I.6d. -6.5I.6e. -6.5I.6f. -6.5I.6g. -6.5I.6h. -5.5I.6i. -5.5I.6j. -5.5I.6k. -5.5I.6l. -5.5I.6m. -5.5I.6n. -k.5I.6o. -5.5I.6p. -5.5I.6q. -5.5I.6r. -6.5I.6s. -5.5I.6t. -5.5I.6u. -5.5I.6v. -5.5I.6w. -6.5I.6x. -5.5I.6y. -5.5I.6z. -6.5I.6A. -6.5I.6B. -6.5I.6C. -5.5I.6D. -6.5I.6E. -0.5I.6F. -5.5I.6G. -5.5I.6H. -6.5I.6I. -6.5I.6J. -5.5I.6K. -8.5I.6L. -0.6M.5I. -6.5I.6N. -6.5I.6O. -6.5I.6P. -6.5I.6Q. -5.5I.6R. -5.5I.6S. -6.5I.6T. -6.5I.6U. -5.5I.6V. -5.5I.6W. -1.5I.6X. -6.5I.6Y. -1.5I.6Z. -0.5I.6+. -1.5I.6/. -1.5I.70. -0.5I.71. -5.5I.72. -6.5I.73. -1.5I.74. -5.5I.75. -1.5I.76. -1.5I.77. -5.5I.78. -5.79.5I. -1.5I.7a. -5.5I.7b. -5.5I.7c. -5.5I.7d. -6.5I.7e. -5.5I.7f. -5.5I.7g. -5.5I.7h. -6.5I.7i. -5.5I.7j. -6.5I.7k. -6.5I.7l. -1.5I.7m. -6.5I.7o. -6.5I.7p. -1.5I.7r. -1.5I.7s. -8.5I.7t. -6.5I.7u. -6.5I.7v. -6.5I.7w. -5.5I.7x. -5.5I.7y. -1.5I.7z. -5.5I.7A. -8.5I.7B. -6.5I.7C. -0.5I.7D. -5.5I.7E. -5.5I.7F. -5.5I.7G. -5.5I.7H. -5.5I.7I. -6.5I.7J. -1.5I.7K. -1.5I.7L. -1.5I.7M. -1.5I.7N. -1.5I.7O. -1.5I.7P. -5.5I.7Q. -8.5I.7R. -5.5I.7S. -6.5I.7T. -1.5I.7U. -8.5I.7V. -5.5I.7W. -5.5I.7X. -6.5I.7Y. -5.5I.7Z. -1.5I.7+. -1.5I.7/. -1.5I.80. -1.5I.81. -1.5I.82. -5.5I.83. -1.5I.84. -1.5I.85. -h.86.5I. -1.5I.87. -1.5I.88. -8.5I.89. -5.5I.8a. -8.5I.8b. -6.5I.8c. -8.5I.8d. -6.5I.8e. -6.5I.8f. -6.5I.8g. -6.5I.8h. -6.5I.8i. -6.5I.8j. -6.5I.8k. -6.5I.8l. -6.5I.8m. -6.5I.8n. -6.5I.8o. -6.5I.8p. -6.5I.8q. -6.5I.8r. -6.5I.8s. -6.5I.8t. -6.5I.8u. -6.5I.8v. -6.5I.8w. -6.5I.8x. -6.5I.8y. -6.5I.8z. -6.5I.8A. -6.5I.8B. -5.5I.8C. -6.5I.8D. -6.5I.8E. -6.5I.8F. -6.5I.8G. -4.5I.8H. -4.5I.8I. -6.5I.8J. -6.5I.8K. -6.5I.8L. -6.5I.8M. -6.5I.8N. -6.5I.8O. -6.5I.8P. -0.2p.5I. -0.2p.5I. -8.5I.3b. -1.5I.46. -1.5I.4X. -1.5I.5x. -1.5I.5J. -1.5I.7n. -1.5I.7q. -f.5K.5K. -8.5K.5L. -8.5K.5M. -8.5K.5N. -f.5O.5K. -8.5K.5P. -6.5K.5Q. -f.5R.5K. -6.5K.5S. -6.5K.5T. -f.5U.5K. -8.5K.5V. -f.5W.5K. -8.5K.5X. -8.5K.5Y. -f.5Z.5K. -f.5/.5K. -f.60.5K. -6.5K.61. -f.62.5K. -6.5K.64. -6.5K.65. -f.66.5K. -6.5K.67. -6.5K.68. -6.5K.69. -6.5K.6a. -6.5K.6b. -6.5K.6c. -6.5K.6d. -6.5K.6e. -6.5K.6f. -6.5K.6g. -6.5K.6h. -6.5K.6i. -6.5K.6j. -6.5K.6k. -6.5K.6l. -6.5K.6m. -6.5K.6n. -6.5K.6o. -6.5K.6p. -6.5K.6q. -6.5K.6r. -6.5K.6s. -6.5K.6t. -6.5K.6u. -6.5K.6v. -6.5K.6w. -6.5K.6x. -6.5K.6y. -6.5K.6z. -6.5K.6A. -6.5K.6B. -6.5K.6C. -6.5K.6D. -6.5K.6E. -f.6F.5K. -6.5K.6G. -6.5K.6H. -6.5K.6I. -6.5K.6J. -6.5K.6K. -8.5K.6L. -f.6M.5K. -6.5K.6N. -6.5K.6O. -6.5K.6P. -6.5K.6Q. -6.5K.6R. -6.5K.6S. -6.5K.6T. -6.5K.6U. -6.5K.6V. -6.5K.6W. -6.5K.6Y. -f.6+.5K. -f.71.5K. -6.5K.72. -6.5K.73. -6.5K.75. -6.5K.78. -f.79.5K. -6.5K.7b. -6.5K.7c. -6.5K.7d. -6.5K.7e. -6.5K.7f. -6.5K.7g. -6.5K.7h. -6.5K.7i. -6.5K.7j. -6.5K.7k. -6.5K.7l. -6.5K.7o. -6.5K.7p. -8.5K.7t. -6.5K.7u. -6.5K.7v. -6.5K.7w. -f.5K.7x. -6.5K.7y. -6.5K.7A. -8.5K.7B. -6.5K.7C. -f.5K.7D. -6.5K.7E. -6.5K.7F. -6.5K.7G. -6.5K.7H. -6.5K.7I. -6.5K.7J. -6.5K.7Q. -8.5K.7R. -6.5K.7S. -6.5K.7T. -8.5K.7V. -6.5K.7W. -6.5K.7X. -6.5K.7Y. -6.5K.7Z. -6.5K.83. -8.5K.89. -6.5K.8a. -8.5K.8b. -6.5K.8c. -8.5K.8d. -6.5K.8e. -6.5K.8f. -6.5K.8g. -6.5K.8h. -6.5K.8i. -6.5K.8j. -6.5K.8k. -6.5K.8l. -6.5K.8m. -6.5K.8n. -6.5K.8o. -6.5K.8p. -6.5K.8q. -6.5K.8r. -6.5K.8s. -6.5K.8t. -6.5K.8u. -6.5K.8v. -6.5K.8w. -6.5K.8x. -6.5K.8y. -6.5K.8z. -6.5K.8A. -6.5K.8B. -6.5K.8C. -6.5K.8D. -6.5K.8E. -6.5K.8F. -6.5K.8G. -3.5K.8H. -3.5K.8I. -6.5K.8J. -6.5K.8K. -6.5K.8L. -6.5K.8M. -6.5K.8N. -6.5K.8O. -6.5K.8P. -f.2p.5K. -f.2p.5K. -8.5K.3b. -7.5M.5L. -1.5Y.5L. -1.5+.5L. -5.60.5L. -5.62.5L. -1.6q.5L. -1.6t.5L. -8.6F.5L. -c.6H.5L. -8.6M.5L. -1.7B.5L. -1.7R.5L. -1.86.5L. -1.8d.5L. -7.5M.5M. -7.5M.5N. -7.5M.5O. -7.5M.5P. -7.5M.5Q. -7.5M.5R. -7.5M.5S. -7.5M.5T. -7.5M.5U. -7.5M.5V. -7.5M.5W. -7.5M.5X. -7.5M.5Y. -7.5M.5Z. -1.5+.5M. -7.5M.5/. -7.5M.60. -7.5M.61. -7.5M.62. -7.5M.64. -7.5M.65. -7.5M.66. -7.5M.67. -7.5M.68. -7.5M.69. -7.5M.6a. -7.5M.6b. -7.5M.6c. -7.5M.6d. -7.5M.6e. -7.5M.6f. -7.5M.6g. -7.5M.6h. -7.5M.6i. -7.5M.6j. -7.5M.6k. -7.5M.6l. -7.5M.6m. -7.5M.6n. -7.5M.6o. -7.5M.6p. -7.5M.6q. -7.5M.6r. -7.5M.6s. -7.5M.6t. -7.5M.6u. -7.5M.6v. -7.5M.6w. -7.5M.6x. -7.5M.6y. -7.5M.6z. -7.5M.6A. -7.5M.6B. -7.5M.6C. -7.5M.6D. -7.5M.6E. -7.5M.6F. -7.5M.6G. -7.5M.6H. -7.5M.6I. -7.5M.6J. -7.5M.6K. -7.5M.6L. -7.5M.6M. -7.5M.6N. -7.5M.6O. -7.5M.6P. -7.5M.6Q. -7.5M.6R. -7.5M.6S. -7.5M.6T. -7.5M.6U. -7.5M.6V. -7.5M.6W. -1.5M.6X. -7.5M.6Y. -1.5M.6Z. -7.5M.6+. -1.5M.6/. -1.5M.70. -7.5M.71. -7.5M.72. -7.5M.73. -1.5M.74. -7.5M.75. -1.5M.76. -1.5M.77. -7.5M.78. -7.5M.79. -1.5M.7a. -7.5M.7b. -7.5M.7c. -7.5M.7d. -7.5M.7e. -7.5M.7f. -7.5M.7g. -7.5M.7h. -7.5M.7i. -7.5M.7j. -7.5M.7k. -7.5M.7l. -1.5M.7m. -7.5M.7o. -7.5M.7p. -1.5M.7r. -1.5M.7s. -7.5M.7t. -7.5M.7u. -7.5M.7v. -7.5M.7w. -7.5M.7x. -7.5M.7y. -1.5M.7z. -7.5M.7A. -7.5M.7B. -7.5M.7C. -7.5M.7D. -7.5M.7E. -7.5M.7F. -7.5M.7G. -7.5M.7H. -7.5M.7I. -7.5M.7J. -1.5M.7K. -1.5M.7L. -1.5M.7M. -1.5M.7N. -1.5M.7O. -1.5M.7P. -7.5M.7Q. -7.5M.7R. -7.5M.7S. -7.5M.7T. -1.5M.7U. -7.5M.7V. -7.5M.7W. -7.5M.7X. -7.5M.7Y. -7.5M.7Z. -1.5M.7+. -1.5M.7/. -1.5M.80. -1.5M.81. -1.5M.82. -7.5M.83. -1.5M.84. -1.5M.85. -1.86.5M. -1.5M.87. -1.5M.88. -7.5M.89. -7.5M.8a. -7.5M.8b. -7.5M.8c. -7.5M.8d. -7.5M.8e. -7.5M.8f. -7.5M.8g. -7.5M.8h. -7.5M.8i. -7.5M.8j. -7.5M.8k. -7.5M.8l. -7.5M.8m. -7.5M.8n. -7.5M.8o. -7.5M.8p. -7.5M.8q. -7.5M.8r. -7.5M.8s. -7.5M.8t. -7.5M.8u. -7.5M.8v. -7.5M.8w. -7.5M.8x. -7.5M.8y. -7.5M.8z. -7.5M.8A. -7.5M.8B. -7.5M.8C. -7.5M.8D. -7.5M.8E. -7.5M.8F. -7.5M.8G. -7.5M.8H. -7.5M.8I. -7.5M.8J. -7.5M.8K. -7.5M.8L. -7.5M.8M. -7.5M.8N. -7.5M.8O. -7.5M.8P. -7.5M.2p. -7.5M.2p. -7.5M.3b. -1.5M.46. -1.5M.4X. -1.5M.5x. -1.5M.5J. -1.5M.7n. -1.5M.7q. -1.5Y.5N. -1.5+.5N. -5.60.5N. -5.62.5N. -1.6q.5N. -1.6t.5N. -8.6F.5N. -c.6H.5N. -8.6M.5N. -1.7B.5N. -1.86.5N. -1.8d.5N. -5.5O.5O. -5.5O.5R. -1.5O.5S. -5.5O.5U. -5.5O.5W. -1.5Y.5O. -5.5O.5Z. -5.5O.5/. -5.5O.60. -2.62.5O. -2.66.5O. -a.6b.5O. -c.6e.5O. -1.5O.6f. -1.5O.6g. -1.5O.6h. -1.5O.6i. -1.5O.6k. -1.5O.6m. -1.5O.6n. -1.5O.6p. -1.5O.6u. -1.5O.6v. -1.5O.6x. -1.5O.6E. -5.5O.6F. -1.5O.6G. -1.5O.6I. -1.5O.6L. -0.6M.5O. -1.5O.6N. -1.5O.6O. -1.5O.6Q. -1.5O.6R. -1.5O.6S. -5.5O.6+. -5.5O.71. -1.5O.78. -5.79.5O. -1.5O.7b. -1.5O.7d. -1.5O.7e. -1.5O.7f. -1.5O.7g. -1.5O.7l. -1.5O.7w. -5.5O.7D. -1.5O.7F. -1.5O.7G. -1.5O.7P. -1.5O.7Y. -1.5O.8e. -1.5O.8f. -1.5O.8g. -1.5O.8h. -1.5O.8i. -1.5O.8j. -1.5O.8k. -1.5O.8l. -1.5O.8m. -1.5O.8n. -1.5O.8o. -1.5O.8p. -1.5O.8q. -1.5O.8r. -1.5O.8u. -1.5O.8v. -1.5O.8w. -1.5O.8x. -1.5O.8y. -1.5O.8z. -1.5O.8A. -1.5O.8B. -1.5O.8C. -1.5O.8D. -1.5O.8E. -1.5O.8F. -1.5O.8G. -1.5O.8H. -1.5O.8I. -1.5O.8J. -d.2p.5O. -d.2p.5O. -1.5O.5x. -1.5Y.5P. -1.5+.5P. -8.60.5P. -5.62.5P. -1.6q.5P. -1.6t.5P. -8.6F.5P. -c.6H.5P. -8.6M.5P. -1.7B.5P. -1.86.5P. -1.8d.5P. -1.5Y.5Q. -1.5+.5Q. -4.60.5Q. -5.62.5Q. -1.6q.5Q. -1.6t.5Q. -3.6F.5Q. -c.6H.5Q. -8.6M.5Q. -1.7B.5Q. -1.86.5Q. -1.8d.5Q. -5.5R.5R. -5.5R.5U. -0.5W.5R. -1.5Y.5R. -5.5Z.5R. -5.5/.5R. -0.60.5R. -2.62.5R. -5.63.5R. -2.66.5R. -a.6b.5R. -c.6e.5R. -5.5R.6F. -0.6M.5R. -0.6+.5R. -0.71.5R. -5.79.5R. -5.5R.7D. -0.2p.5R. -0.2p.5R. -1.5Y.5S. -1.5+.5S. -4.60.5S. -5.62.5S. -1.6q.5S. -1.6t.5S. -8.6F.5S. -c.6H.5S. -8.6M.5S. -1.7B.5S. -1.7R.5S. -1.86.5S. -1.8d.5S. -1.5Y.5T. -1.5+.5T. -4.60.5T. -5.62.5T. -1.6q.5T. -1.6t.5T. -3.6F.5T. -c.6H.5T. -8.6M.5T. -1.7B.5T. -1.7R.5T. -1.86.5T. -1.8d.5T. -0.5U.5U. -0.5W.5U. -1.5Y.5U. -5.5Z.5U. -5.5/.5U. -0.60.5U. -2.62.5U. -2.66.5U. -a.6b.5U. -c.6e.5U. -0.6F.5U. -0.6M.5U. -0.6+.5U. -0.71.5U. -5.79.5U. -0.5U.7D. -0.2p.5U. -0.2p.5U. -1.5Y.5V. -1.5+.5V. -5.60.5V. -8.62.5V. -1.6q.5V. -1.6t.5V. -8.6F.5V. -c.6H.5V. -8.6M.5V. -1.7B.5V. -1.7R.5V. -1.86.5V. -1.8d.5V. -0.5W.5W. -1.5Y.5W. -5.5Z.5W. -5.5/.5W. -0.60.5W. -2.62.5W. -2.66.5W. -a.6b.5W. -c.6e.5W. -0.5W.6F. -0.6M.5W. -0.6+.5W. -0.5W.71. -5.79.5W. -0.5W.7D. -1.5W.7R. -0.2p.5W. -0.2p.5W. -1.5Y.5X. -1.5+.5X. -5.60.5X. -8.62.5X. -1.6q.5X. -1.6t.5X. -8.6F.5X. -c.6H.5X. -8.6M.5X. -1.7B.5X. -1.7R.5X. -1.86.5X. -1.8d.5X. -1.5Y.5Y. -1.5Y.5Z. -1.5+.5Y. -1.5Y.5/. -1.5Y.60. -1.5Y.61. -1.5Y.62. -1.5Y.64. -1.5Y.65. -1.5Y.66. -1.5Y.67. -1.5Y.68. -1.5Y.69. -1.5Y.6a. -1.5Y.6b. -1.5Y.6c. -1.5Y.6d. -1.5Y.6e. -1.5Y.6f. -1.5Y.6g. -1.5Y.6h. -1.5Y.6i. -1.5Y.6j. -1.5Y.6k. -1.5Y.6l. -1.5Y.6m. -1.5Y.6n. -1.5Y.6o. -1.5Y.6p. -1.5Y.6q. -1.5Y.6r. -1.5Y.6s. -1.6t.5Y. -1.5Y.6u. -1.5Y.6v. -1.5Y.6w. -1.5Y.6x. -1.5Y.6y. -1.5Y.6z. -1.5Y.6A. -1.5Y.6B. -1.5Y.6C. -1.5Y.6D. -1.5Y.6E. -1.5Y.6F. -1.5Y.6G. -c.6H.5Y. -1.5Y.6I. -1.5Y.6J. -1.5Y.6K. -1.5Y.6L. -1.5Y.6M. -1.5Y.6N. -1.5Y.6O. -1.5Y.6P. -1.5Y.6Q. -1.5Y.6R. -1.5Y.6S. -1.5Y.6T. -1.5Y.6U. -1.5Y.6V. -1.5Y.6W. -1.5Y.6X. -1.5Y.6Y. -1.5Y.6Z. -1.5Y.6+. -1.5Y.6/. -1.5Y.70. -1.5Y.71. -1.5Y.72. -1.5Y.73. -1.5Y.74. -1.5Y.75. -1.5Y.76. -1.5Y.77. -1.5Y.78. -1.5Y.79. -1.5Y.7a. -1.5Y.7b. -1.5Y.7c. -1.5Y.7d. -1.5Y.7e. -1.5Y.7f. -1.5Y.7g. -1.5Y.7h. -1.5Y.7i. -1.5Y.7j. -1.5Y.7k. -1.5Y.7l. -1.5Y.7m. -1.5Y.7o. -1.5Y.7p. -1.5Y.7r. -1.5Y.7s. -1.5Y.7t. -1.5Y.7u. -1.5Y.7v. -1.5Y.7w. -1.5Y.7x. -1.5Y.7y. -1.5Y.7z. -1.5Y.7A. -1.7B.5Y. -1.5Y.7C. -1.5Y.7D. -1.5Y.7E. -1.5Y.7F. -1.5Y.7G. -1.5Y.7H. -1.5Y.7I. -1.5Y.7J. -1.5Y.7K. -1.5Y.7L. -1.5Y.7M. -1.5Y.7N. -1.5Y.7O. -1.5Y.7P. -1.5Y.7Q. -1.5Y.7R. -1.5Y.7S. -1.5Y.7T. -1.5Y.7U. -1.5Y.7V. -1.5Y.7W. -1.5Y.7X. -1.5Y.7Y. -1.5Y.7Z. -1.5Y.7+. -1.5Y.7/. -1.5Y.80. -1.5Y.81. -1.5Y.82. -1.5Y.83. -1.5Y.84. -1.5Y.85. -1.86.5Y. -1.5Y.87. -1.5Y.88. -1.5Y.89. -1.5Y.8a. -1.5Y.8b. -1.5Y.8c. -1.8d.5Y. -1.5Y.8e. -1.5Y.8f. -1.5Y.8g. -1.5Y.8h. -1.5Y.8i. -1.5Y.8j. -1.5Y.8k. -1.5Y.8l. -1.5Y.8m. -1.5Y.8n. -1.5Y.8o. -1.5Y.8p. -1.5Y.8q. -1.5Y.8r. -1.5Y.8s. -1.5Y.8t. -1.5Y.8u. -1.5Y.8v. -1.5Y.8w. -1.5Y.8x. -1.5Y.8y. -1.5Y.8z. -1.5Y.8A. -1.5Y.8B. -1.5Y.8C. -1.5Y.8D. -1.5Y.8E. -1.5Y.8F. -1.5Y.8G. -1.5Y.8H. -1.5Y.8I. -1.5Y.8J. -1.5Y.8K. -1.5Y.8L. -1.5Y.8M. -1.5Y.8N. -1.5Y.8O. -1.5Y.8P. -1.5Y.2p. -1.5Y.2p. -1.5Y.3b. -1.5Y.46. -1.5Y.4X. -1.5Y.5x. -1.5Y.5J. -1.5Y.7n. -1.5Y.7q. -5.5Z.5Z. -5.5Z.5/. -0.60.5Z. -2.62.5Z. -2.66.5Z. -a.6b.5Z. -c.6e.5Z. -5.5Z.6F. -0.6M.5Z. -5.5Z.6+. -5.5Z.71. -5.79.5Z. -5.5Z.7D. -0.2p.5Z. -0.2p.5Z. -h.5+.60. -1.5+.61. -h.5+.62. -1.5+.64. -1.5+.65. -1.5+.67. -1.5+.68. -1.5+.69. -1.5+.6a. -1.5+.6c. -1.5+.6d. -1.5+.6e. -1.5+.6f. -1.5+.6g. -1.5+.6h. -1.5+.6i. -1.5+.6j. -1.5+.6k. -1.5+.6l. -1.5+.6m. -1.5+.6n. -1.5+.6o. -1.5+.6p. -1.5+.6q. -1.5+.6r. -1.5+.6s. -1.5+.6t. -1.5+.6u. -1.5+.6v. -1.5+.6w. -1.5+.6x. -1.5+.6y. -1.5+.6z. -1.5+.6A. -1.5+.6B. -1.5+.6C. -1.5+.6D. -1.5+.6E. -1.5+.6G. -1.5+.6H. -1.5+.6I. -1.5+.6J. -1.5+.6K. -1.5+.6L. -1.5+.6N. -1.5+.6O. -1.5+.6P. -1.5+.6Q. -1.5+.6R. -1.5+.6S. -1.5+.6T. -1.5+.6U. -1.5+.6V. -1.5+.6W. -1.5+.6X. -1.5+.6Y. -1.5+.6Z. -1.5+.6/. -1.5+.70. -1.5+.72. -1.5+.73. -1.5+.74. -1.5+.75. -1.5+.76. -1.5+.77. -1.5+.78. -1.5+.7a. -1.5+.7b. -1.5+.7c. -1.5+.7d. -1.5+.7e. -1.5+.7f. -1.5+.7g. -1.5+.7h. -1.5+.7i. -1.5+.7j. -1.5+.7k. -1.5+.7l. -1.5+.7m. -1.5+.7o. -1.5+.7p. -1.5+.7r. -1.5+.7s. -1.5+.7t. -1.5+.7u. -1.5+.7v. -1.5+.7w. -1.5+.7x. -1.5+.7y. -1.5+.7z. -1.5+.7A. -1.5+.7B. -1.5+.7C. -1.5+.7E. -1.5+.7F. -1.5+.7G. -1.5+.7H. -1.5+.7I. -1.5+.7J. -1.5+.7K. -1.5+.7L. -1.5+.7M. -1.5+.7N. -1.5+.7O. -1.5+.7P. -1.5+.7Q. -1.5+.7R. -1.5+.7S. -1.5+.7T. -1.5+.7U. -1.5+.7V. -1.5+.7W. -1.5+.7X. -1.5+.7Y. -1.5+.7Z. -1.5+.7+. -1.5+.7/. -1.5+.80. -1.5+.81. -1.5+.82. -1.5+.83. -1.5+.84. -1.5+.85. -1.5+.87. -1.5+.88. -1.5+.89. -1.5+.8a. -1.5+.8b. -1.5+.8c. -1.5+.8d. -1.5+.8e. -1.5+.8f. -1.5+.8g. -1.5+.8h. -1.5+.8i. -1.5+.8j. -1.5+.8k. -1.5+.8l. -1.5+.8m. -1.5+.8n. -1.5+.8o. -1.5+.8p. -1.5+.8q. -1.5+.8r. -1.5+.8s. -1.5+.8t. -1.5+.8u. -1.5+.8v. -1.5+.8w. -1.5+.8x. -1.5+.8y. -1.5+.8z. -1.5+.8A. -1.5+.8B. -1.5+.8C. -1.5+.8D. -1.5+.8E. -1.5+.8F. -1.5+.8G. -1.5+.8H. -1.5+.8I. -1.5+.8J. -1.5+.8K. -1.5+.8L. -1.5+.8M. -1.5+.8N. -1.5+.8O. -1.5+.8P. -1.5+.3b. -1.5+.46. -1.5+.4X. -1.5+.5x. -1.5+.5J. -1.5+.7n. -1.5+.7q. -5.5/.5/. -0.60.5/. -2.62.5/. -2.66.5/. -a.6b.5/. -c.6e.5/. -5.5/.6F. -0.6M.5/. -0.6+.5/. -5.5/.71. -5.79.5/. -5.5/.7D. -0.2p.5/. -0.2p.5/. -0.60.60. -4.60.61. -2.62.60. -4.60.64. -4.60.65. -2.66.60. -4.60.67. -4.60.68. -4.60.69. -4.60.6a. -a.6b.60. -4.60.6c. -4.60.6d. -4.60.6e. -4.60.6f. -4.60.6g. -4.60.6h. -4.60.6i. -4.60.6j. -4.60.6k. -4.60.6l. -4.60.6m. -4.60.6n. -4.60.6o. -4.60.6p. -4.60.6q. -4.60.6r. -4.60.6s. -4.60.6t. -4.60.6u. -4.60.6v. -4.60.6w. -4.60.6x. -4.60.6y. -4.60.6z. -4.60.6A. -4.60.6B. -4.60.6C. -4.60.6D. -4.60.6E. -0.60.6F. -4.60.6G. -4.60.6H. -4.60.6I. -4.60.6J. -4.60.6K. -5.60.6L. -0.6M.60. -4.60.6N. -4.60.6O. -4.60.6P. -4.60.6Q. -4.60.6R. -4.60.6S. -4.60.6T. -4.60.6U. -4.60.6V. -4.60.6W. -1.60.6X. -4.60.6Y. -1.60.6Z. -0.60.6+. -1.60.6/. -1.60.70. -0.60.71. -4.60.72. -4.60.73. -1.60.74. -4.60.75. -1.60.76. -1.60.77. -4.60.78. -5.79.60. -1.60.7a. -4.60.7b. -4.60.7c. -4.60.7d. -4.60.7e. -4.60.7f. -4.60.7g. -4.60.7h. -4.60.7i. -4.60.7j. -4.60.7k. -4.60.7l. -1.60.7m. -4.60.7o. -4.60.7p. -1.60.7r. -1.60.7s. -8.60.7t. -4.60.7u. -4.60.7v. -4.60.7w. -4.60.7x. -4.60.7y. -1.60.7z. -4.60.7A. -5.60.7B. -4.60.7C. -0.60.7D. -4.60.7E. -4.60.7F. -4.60.7G. -4.60.7H. -4.60.7I. -4.60.7J. -1.60.7K. -1.60.7L. -1.60.7M. -1.60.7N. -1.60.7O. -1.60.7P. -4.60.7Q. -5.60.7R. -4.60.7S. -4.60.7T. -1.60.7U. -5.60.7V. -4.60.7W. -4.60.7X. -4.60.7Y. -4.60.7Z. -1.60.7+. -1.60.7/. -1.60.80. -1.60.81. -1.60.82. -4.60.83. -1.60.84. -1.60.85. -h.86.60. -1.60.87. -1.60.88. -8.60.89. -4.60.8a. -5.60.8b. -4.60.8c. -8.60.8d. -4.60.8e. -4.60.8f. -4.60.8g. -4.60.8h. -4.60.8i. -4.60.8j. -4.60.8k. -4.60.8l. -4.60.8m. -4.60.8n. -4.60.8o. -4.60.8p. -4.60.8q. -4.60.8r. -4.60.8s. -4.60.8t. -4.60.8u. -4.60.8v. -4.60.8w. -4.60.8x. -4.60.8y. -4.60.8z. -4.60.8A. -4.60.8B. -4.60.8C. -4.60.8D. -4.60.8E. -4.60.8F. -4.60.8G. -4.60.8H. -4.60.8I. -4.60.8J. -4.60.8K. -4.60.8L. -4.60.8M. -4.60.8N. -4.60.8O. -4.60.8P. -0.2p.60. -0.2p.60. -5.60.3b. -1.60.46. -1.60.4X. -1.60.5x. -1.60.5J. -1.60.7n. -1.60.7q. -5.62.61. -1.6q.61. -1.6t.61. -3.6F.61. -c.6H.61. -8.6M.61. -1.7B.61. -1.86.61. -1.8d.61. -2.62.62. -8.62.64. -5.62.65. -2.62.66. -5.62.67. -5.62.68. -5.62.69. -5.62.6a. -a.6b.62. -8.62.6c. -8.62.6d. -8.62.6e. -8.62.6f. -8.62.6g. -8.62.6h. -5.62.6i. -8.62.6j. -8.62.6k. -5.62.6l. -8.62.6m. -5.62.6n. -5.62.6o. -8.62.6p. -5.62.6q. -8.62.6r. -8.62.6s. -8.62.6t. -8.62.6u. -8.62.6v. -5.62.6w. -8.62.6x. -5.62.6y. -5.62.6z. -8.62.6A. -8.62.6B. -5.62.6C. -5.62.6D. -8.62.6E. -2.62.6F. -5.62.6G. -5.62.6H. -5.62.6I. -5.62.6J. -5.62.6K. -8.62.6L. -2.62.6M. -8.62.6N. -8.62.6O. -8.62.6P. -8.62.6Q. -5.62.6R. -5.62.6S. -8.62.6T. -8.62.6U. -5.62.6V. -5.62.6W. -1.62.6X. -8.62.6Y. -1.62.6Z. -2.62.6+. -1.62.6/. -1.62.70. -2.62.71. -5.62.72. -8.62.73. -h.62.74. -5.62.75. -h.62.76. -h.62.77. -5.62.78. -2.62.79. -h.62.7a. -8.62.7b. -8.62.7c. -5.62.7d. -5.62.7e. -8.62.7f. -5.62.7g. -8.62.7h. -8.62.7i. -5.62.7j. -5.62.7k. -8.62.7l. -h.62.7m. -5.62.7o. -5.62.7p. -1.62.7r. -h.62.7s. -8.62.7t. -8.62.7u. -8.62.7v. -5.62.7w. -5.62.7x. -5.62.7y. -1.62.7z. -5.62.7A. -5.62.7B. -5.62.7C. -2.62.7D. -8.62.7E. -5.62.7F. -5.62.7G. -8.62.7H. -8.62.7I. -8.62.7J. -h.62.7K. -h.62.7L. -h.62.7M. -h.62.7N. -h.62.7O. -1.62.7P. -8.62.7Q. -8.62.7R. -5.62.7S. -5.62.7T. -1.62.7U. -8.62.7V. -8.62.7W. -8.62.7X. -5.62.7Y. -5.62.7Z. -h.62.7+. -h.62.7/. -h.62.80. -1.62.81. -1.62.82. -5.62.83. -1.62.84. -1.62.85. -h.62.86. -h.62.87. -1.62.88. -8.62.89. -5.62.8a. -8.62.8b. -8.62.8c. -8.62.8d. -5.62.8e. -5.62.8f. -5.62.8g. -5.62.8h. -5.62.8i. -5.62.8j. -5.62.8k. -5.62.8l. -5.62.8m. -5.62.8n. -5.62.8o. -5.62.8p. -5.62.8q. -5.62.8r. -5.62.8s. -8.62.8t. -8.62.8u. -8.62.8v. -8.62.8w. -8.62.8x. -5.62.8y. -5.62.8z. -5.62.8A. -5.62.8B. -8.62.8C. -5.62.8D. -8.62.8E. -5.62.8F. -5.62.8G. -5.62.8H. -5.62.8I. -5.62.8J. -8.62.8K. -8.62.8L. -8.62.8M. -8.62.8N. -8.62.8O. -8.62.8P. -2.62.2p. -2.62.2p. -8.62.3b. -1.62.46. -1.62.4X. -1.62.5x. -1.62.5J. -h.62.7n. -1.62.7q. -5.63.63. -1.6q.64. -1.6t.64. -8.6F.64. -c.6H.64. -8.6M.64. -1.7B.64. -1.86.64. -1.8d.64. -1.6q.65. -1.6t.65. -8.6F.65. -c.6H.65. -8.6M.65. -1.7B.65. -1.86.65. -1.8d.65. -2.66.66. -a.6b.66. -c.6e.66. -2.66.6F. -2.66.6M. -2.66.6+. -2.66.71. -2.66.79. -2.66.7D. -2.66.2p. -2.66.2p. -1.6q.67. -1.6t.67. -8.6F.67. -c.6H.67. -3.6M.67. -1.7B.67. -1.7R.67. -1.86.67. -1.8d.67. -1.6q.68. -1.6t.68. -8.6F.68. -c.6H.68. -3.6M.68. -1.7B.68. -1.86.68. -1.8d.68. -1.6q.69. -1.6t.69. -8.6F.69. -c.6H.69. -3.6M.69. -1.7B.69. -1.86.69. -1.8d.69. -1.6q.6a. -1.6t.6a. -8.6F.6a. -c.6H.6a. -3.6M.6a. -1.7B.6a. -1.86.6a. -1.8d.6a. -a.6b.6b. -c.6e.6b. -a.6b.6F. -a.6b.6M. -a.6b.6+. -a.6b.71. -a.6b.79. -a.6b.7D. -a.6b.2p. -a.6b.2p. -1.6q.6c. -1.6t.6c. -8.6F.6c. -c.6H.6c. -3.6M.6c. -1.7B.6c. -1.7R.6c. -1.86.6c. -1.8d.6c. -1.6q.6d. -1.6t.6d. -8.6F.6d. -c.6H.6d. -3.6M.6d. -1.7B.6d. -1.7R.6d. -1.86.6d. -1.8d.6d. -c.6e.6e. -1.6q.6e. -1.6t.6e. -8.6F.6e. -c.6H.6e. -3.6M.6e. -c.6e.6+. -c.6e.71. -c.6e.79. -1.7B.6e. -c.6e.7D. -1.7R.6e. -1.86.6e. -1.8d.6e. -c.6e.2p. -c.6e.2p. -1.6q.6f. -1.6t.6f. -8.6F.6f. -c.6H.6f. -3.6M.6f. -1.7B.6f. -1.7R.6f. -1.86.6f. -1.8d.6f. -1.6q.6g. -1.6t.6g. -8.6F.6g. -c.6H.6g. -3.6M.6g. -1.7B.6g. -1.7R.6g. -1.86.6g. -1.8d.6g. -1.6q.6h. -1.6t.6h. -8.6F.6h. -c.6H.6h. -3.6M.6h. -1.7B.6h. -1.7R.6h. -1.86.6h. -1.8d.6h. -1.6q.6i. -1.6t.6i. -8.6F.6i. -k.6H.6i. -3.6M.6i. -1.7B.6i. -1.7R.6i. -1.86.6i. -1.8d.6i. -1.6q.6j. -1.6t.6j. -8.6F.6j. -c.6H.6j. -8.6M.6j. -1.7B.6j. -1.86.6j. -1.8d.6j. -1.6q.6k. -1.6t.6k. -3.6F.6k. -c.6H.6k. -8.6M.6k. -1.7B.6k. -1.7R.6k. -1.86.6k. -1.8d.6k. -1.6q.6l. -1.6t.6l. -8.6F.6l. -c.6H.6l. -8.6M.6l. -1.7B.6l. -1.86.6l. -1.8d.6l. -1.6q.6m. -1.6t.6m. -8.6F.6m. -c.6H.6m. -8.6M.6m. -1.7B.6m. -1.86.6m. -1.8d.6m. -1.6q.6n. -1.6t.6n. -8.6F.6n. -c.6H.6n. -8.6M.6n. -1.7B.6n. -1.86.6n. -1.8d.6n. -1.6q.6o. -1.6t.6o. -3.6F.6o. -c.6H.6o. -8.6M.6o. -1.7B.6o. -1.86.6o. -1.8d.6o. -1.6q.6p. -1.6t.6p. -8.6F.6p. -c.6H.6p. -8.6M.6p. -1.7B.6p. -1.7R.6p. -1.86.6p. -1.8d.6p. -1.6q.6q. -1.6q.6r. -1.6q.6s. -1.6q.6t. -1.6q.6u. -1.6q.6v. -1.6q.6w. -1.6q.6x. -1.6q.6y. -1.6q.6z. -1.6q.6A. -1.6q.6B. -1.6q.6C. -1.6q.6D. -1.6q.6E. -8.6F.6q. -1.6q.6G. -k.6H.6q. -1.6q.6I. -1.6q.6J. -1.6q.6K. -1.6q.6L. -8.6M.6q. -1.6q.6N. -1.6q.6O. -1.6q.6P. -1.6q.6Q. -1.6q.6R. -1.6q.6S. -1.6q.6T. -1.6q.6U. -1.6q.6V. -1.6q.6W. -1.6q.6X. -1.6q.6Y. -1.6q.6/. -1.6q.70. -1.6q.72. -1.6q.73. -1.6q.74. -1.6q.75. -1.6q.76. -1.6q.77. -1.6q.78. -1.6q.7a. -1.6q.7b. -1.6q.7c. -1.6q.7d. -1.6q.7e. -1.6q.7f. -1.6q.7g. -1.6q.7h. -1.6q.7i. -1.6q.7j. -1.6q.7k. -1.6q.7l. -1.6q.7m. -1.6q.7o. -1.6q.7p. -1.6q.7r. -1.6q.7s. -1.6q.7t. -1.6q.7u. -1.6q.7v. -1.6q.7w. -1.6q.7x. -1.6q.7y. -1.6q.7z. -1.6q.7A. -1.6q.7B. -1.6q.7C. -1.6q.7E. -1.6q.7F. -1.6q.7G. -1.6q.7H. -1.6q.7I. -1.6q.7J. -1.6q.7K. -1.6q.7L. -1.6q.7M. -1.6q.7N. -1.6q.7O. -1.6q.7P. -1.6q.7Q. -1.6q.7S. -1.6q.7T. -1.6q.7V. -1.6q.7W. -1.6q.7X. -1.6q.7Y. -1.6q.7+. -1.6q.7/. -1.6q.80. -1.6q.81. -1.6q.82. -1.6q.83. -1.6q.84. -1.6q.85. -1.86.6q. -1.6q.87. -1.6q.88. -1.6q.89. -1.6q.8a. -1.6q.8b. -1.6q.8d. -1.6q.8e. -1.6q.8f. -1.6q.8g. -1.6q.8h. -1.6q.8i. -1.6q.8j. -1.6q.8k. -1.6q.8l. -1.6q.8m. -1.6q.8n. -1.6q.8o. -1.6q.8p. -1.6q.8q. -1.6q.8r. -1.6q.8s. -1.6q.8t. -1.6q.8u. -1.6q.8v. -1.6q.8w. -1.6q.8x. -1.6q.8y. -1.6q.8z. -1.6q.8A. -1.6q.8B. -1.6q.8C. -1.6q.8D. -1.6q.8E. -1.6q.8F. -1.6q.8G. -1.6q.8H. -1.6q.8I. -1.6q.8J. -1.6q.8K. -1.6q.8L. -1.6q.8M. -1.6q.8N. -1.6q.8O. -1.6q.8P. -1.6q.3b. -1.6q.46. -1.6q.4X. -1.6q.5x. -1.6q.5J. -1.6q.7n. -1.6q.7q. -1.6t.6r. -3.6F.6r. -c.6H.6r. -8.6M.6r. -1.7B.6r. -1.86.6r. -1.8d.6r. -1.6t.6s. -8.6F.6s. -c.6H.6s. -8.6M.6s. -1.7B.6s. -1.86.6s. -1.8d.6s. -1.6t.6t. -1.6t.6u. -1.6t.6v. -1.6t.6w. -1.6t.6x. -1.6t.6y. -1.6t.6z. -1.6t.6A. -1.6t.6B. -1.6t.6C. -1.6t.6D. -1.6t.6E. -8.6F.6t. -1.6t.6G. -c.6H.6t. -1.6t.6I. -1.6t.6J. -1.6t.6K. -1.6t.6L. -8.6M.6t. -1.6t.6N. -1.6t.6O. -1.6t.6P. -1.6t.6Q. -1.6t.6R. -1.6t.6S. -1.6t.6T. -1.6t.6U. -1.6t.6V. -1.6t.6W. -1.6t.6X. -1.6t.6Y. -1.6t.6Z. -1.6t.6/. -1.6t.70. -1.6t.72. -1.6t.73. -1.6t.74. -1.6t.75. -1.6t.76. -1.6t.77. -1.6t.78. -1.6t.7a. -1.6t.7b. -1.6t.7c. -1.6t.7d. -1.6t.7e. -1.6t.7f. -1.6t.7g. -1.6t.7h. -1.6t.7i. -1.6t.7j. -1.6t.7k. -1.6t.7l. -1.6t.7o. -1.6t.7p. -1.6t.7r. -1.6t.7s. -1.6t.7t. -1.6t.7u. -1.6t.7v. -1.6t.7w. -1.6t.7x. -1.6t.7y. -1.6t.7z. -1.6t.7A. -1.7B.6t. -1.6t.7C. -1.6t.7E. -1.6t.7F. -1.6t.7G. -1.6t.7H. -1.6t.7I. -1.6t.7J. -1.6t.7K. -1.6t.7L. -1.6t.7M. -1.6t.7N. -1.6t.7O. -1.6t.7P. -1.6t.7Q. -1.6t.7R. -1.6t.7S. -1.6t.7T. -1.6t.7U. -1.6t.7V. -1.6t.7W. -1.6t.7X. -1.6t.7Y. -1.6t.7+. -1.6t.7/. -1.6t.80. -1.6t.81. -1.6t.82. -1.6t.83. -1.6t.84. -1.6t.85. -1.86.6t. -1.6t.87. -1.6t.88. -1.6t.89. -1.6t.8a. -1.6t.8b. -1.6t.8c. -1.6t.8d. -1.6t.8e. -1.6t.8f. -1.6t.8g. -1.6t.8h. -1.6t.8i. -1.6t.8j. -1.6t.8k. -1.6t.8l. -1.6t.8m. -1.6t.8n. -1.6t.8o. -1.6t.8p. -1.6t.8q. -1.6t.8r. -1.6t.8s. -1.6t.8t. -1.6t.8u. -1.6t.8v. -1.6t.8w. -1.6t.8x. -1.6t.8y. -1.6t.8z. -1.6t.8A. -1.6t.8B. -1.6t.8C. -1.6t.8D. -1.6t.8E. -1.6t.8F. -1.6t.8G. -1.6t.8H. -1.6t.8I. -1.6t.8J. -1.6t.8K. -1.6t.8L. -1.6t.8M. -1.6t.8N. -1.6t.8O. -1.6t.8P. -1.6t.3b. -1.6t.46. -1.6t.4X. -1.6t.5x. -1.6t.5J. -1.6t.7n. -1.6t.7q. -8.6F.6u. -c.6H.6u. -8.6M.6u. -1.7B.6u. -1.7R.6u. -1.86.6u. -1.8d.6u. -8.6F.6v. -c.6H.6v. -8.6M.6v. -1.7B.6v. -1.7R.6v. -1.86.6v. -1.8d.6v. -8.6F.6w. -c.6H.6w. -8.6M.6w. -1.7B.6w. -1.86.6w. -1.8d.6w. -8.6F.6x. -c.6H.6x. -8.6M.6x. -1.7B.6x. -1.7R.6x. -1.86.6x. -1.8d.6x. -3.6F.6y. -c.6H.6y. -8.6M.6y. -1.7B.6y. -1.86.6y. -1.8d.6y. -3.6F.6z. -c.6H.6z. -8.6M.6z. -1.7B.6z. -1.7R.6z. -1.86.6z. -1.8d.6z. -8.6F.6A. -c.6H.6A. -8.6M.6A. -1.7B.6A. -1.86.6A. -1.8d.6A. -8.6F.6B. -c.6H.6B. -3.6M.6B. -1.7B.6B. -1.7R.6B. -1.86.6B. -1.8d.6B. -3.6F.6C. -c.6H.6C. -8.6M.6C. -1.7B.6C. -1.7R.6C. -1.86.6C. -1.8d.6C. -8.6F.6D. -c.6H.6D. -8.6M.6D. -1.7B.6D. -1.7R.6D. -1.86.6D. -1.8d.6D. -8.6F.6E. -c.6H.6E. -3.6M.6E. -1.7B.6E. -1.7R.6E. -1.86.6E. -1.8d.6E. -0.6F.6F. -8.6F.6G. -3.6F.6H. -8.6F.6I. -8.6F.6J. -8.6F.6K. -8.6F.6L. -0.6M.6F. -8.6F.6N. -8.6F.6O. -8.6F.6P. -8.6F.6Q. -8.6F.6R. -8.6F.6S. -8.6F.6T. -8.6F.6U. -8.6F.6V. -8.6F.6W. -1.6F.6X. -8.6F.6Y. -1.6F.6Z. -0.6+.6F. -1.6F.6/. -1.6F.70. -0.71.6F. -3.6F.72. -8.6F.73. -1.6F.74. -8.6F.75. -1.6F.76. -1.6F.77. -8.6F.78. -5.79.6F. -1.6F.7a. -8.6F.7b. -3.6F.7c. -8.6F.7d. -8.6F.7e. -8.6F.7f. -8.6F.7g. -3.6F.7h. -8.6F.7i. -8.6F.7j. -8.6F.7k. -8.6F.7l. -1.6F.7m. -8.6F.7o. -8.6F.7p. -1.6F.7r. -1.6F.7s. -8.6F.7t. -8.6F.7u. -3.6F.7v. -8.6F.7w. -8.6F.7x. -8.6F.7y. -1.6F.7z. -3.6F.7A. -3.6F.7B. -8.6F.7C. -0.6F.7D. -8.6F.7E. -8.6F.7F. -8.6F.7G. -8.6F.7H. -8.6F.7I. -3.6F.7J. -1.6F.7K. -1.6F.7L. -1.6F.7M. -1.6F.7N. -1.6F.7O. -1.6F.7P. -8.6F.7Q. -8.6F.7R. -3.6F.7S. -3.6F.7T. -1.6F.7U. -8.6F.7V. -8.6F.7W. -3.6F.7X. -8.6F.7Y. -3.6F.7Z. -1.6F.7+. -1.6F.7/. -1.6F.80. -1.6F.81. -1.6F.82. -8.6F.83. -1.6F.84. -1.6F.85. -1.6F.87. -1.6F.88. -8.6F.89. -3.6F.8a. -8.6F.8b. -8.6F.8c. -8.6F.8d. -8.6F.8e. -8.6F.8f. -3.6F.8g. -8.6F.8h. -8.6F.8i. -3.6F.8j. -8.6F.8k. -3.6F.8l. -3.6F.8m. -8.6F.8n. -8.6F.8o. -8.6F.8p. -8.6F.8q. -8.6F.8r. -8.6F.8s. -8.6F.8t. -8.6F.8u. -8.6F.8v. -8.6F.8w. -8.6F.8x. -8.6F.8y. -8.6F.8z. -8.6F.8A. -3.6F.8B. -8.6F.8C. -8.6F.8D. -8.6F.8E. -8.6F.8F. -8.6F.8G. -3.6F.8H. -8.6F.8I. -8.6F.8J. -8.6F.8K. -8.6F.8L. -8.6F.8M. -3.6F.8N. -8.6F.8O. -8.6F.8P. -0.2p.6F. -0.2p.6F. -8.6F.3b. -1.6F.46. -1.6F.4X. -1.6F.5x. -1.6F.5J. -1.6F.7n. -1.6F.7q. -c.6H.6G. -8.6M.6G. -1.7B.6G. -1.7R.6G. -1.86.6G. -1.8d.6G. -c.6H.6H. -c.6H.6I. -c.6H.6J. -c.6H.6K. -c.6H.6L. -8.6M.6H. -c.6H.6N. -c.6H.6O. -c.6H.6P. -c.6H.6Q. -c.6H.6R. -c.6H.6S. -c.6H.6T. -c.6H.6U. -c.6H.6V. -c.6H.6W. -1.6H.6X. -c.6H.6Y. -1.6H.6Z. -1.6H.6/. -1.6H.70. -c.6H.72. -c.6H.73. -1.6H.74. -c.6H.75. -1.6H.76. -1.6H.77. -c.6H.78. -1.6H.7a. -c.6H.7b. -c.6H.7c. -c.6H.7d. -c.6H.7e. -c.6H.7f. -c.6H.7g. -c.6H.7h. -c.6H.7i. -c.6H.7j. -c.6H.7k. -c.6H.7l. -1.6H.7m. -c.6H.7o. -c.6H.7p. -1.6H.7r. -1.6H.7s. -c.6H.7t. -c.6H.7u. -c.6H.7v. -c.6H.7w. -c.6H.7x. -c.6H.7y. -1.6H.7z. -c.6H.7A. -c.6H.7B. -c.6H.7C. -c.6H.7E. -c.6H.7F. -c.6H.7G. -c.6H.7H. -c.6H.7I. -c.6H.7J. -1.6H.7K. -1.6H.7L. -1.6H.7M. -1.6H.7N. -1.6H.7O. -1.6H.7P. -c.6H.7Q. -c.6H.7R. -c.6H.7S. -c.6H.7T. -1.6H.7U. -c.6H.7V. -c.6H.7W. -c.6H.7X. -c.6H.7Y. -c.6H.7Z. -1.6H.7+. -1.6H.7/. -1.6H.80. -1.6H.81. -1.6H.82. -c.6H.83. -1.6H.84. -1.6H.85. -1.86.6H. -1.6H.87. -1.6H.88. -c.6H.89. -c.6H.8a. -c.6H.8b. -c.6H.8c. -c.6H.8d. -c.6H.8e. -c.6H.8f. -c.6H.8g. -c.6H.8h. -c.6H.8i. -c.6H.8j. -c.6H.8k. -c.6H.8l. -c.6H.8m. -c.6H.8n. -c.6H.8o. -c.6H.8p. -c.6H.8q. -c.6H.8r. -c.6H.8s. -c.6H.8t. -c.6H.8u. -c.6H.8v. -c.6H.8w. -c.6H.8x. -c.6H.8y. -c.6H.8z. -c.6H.8A. -c.6H.8B. -c.6H.8C. -c.6H.8D. -c.6H.8E. -c.6H.8F. -c.6H.8G. -c.6H.8H. -c.6H.8I. -c.6H.8J. -c.6H.8K. -c.6H.8L. -c.6H.8M. -c.6H.8N. -c.6H.8O. -c.6H.8P. -c.6H.3b. -1.6H.46. -1.6H.4X. -1.6H.5x. -1.6H.5J. -1.6H.7n. -1.6H.7q. -3.6M.6I. -1.7B.6I. -1.7R.6I. -1.86.6I. -1.8d.6I. -8.6M.6J. -1.7B.6J. -1.86.6J. -1.8d.6J. -8.6M.6K. -1.7B.6K. -1.7R.6K. -1.86.6K. -1.8d.6K. -8.6M.6L. -1.7B.6L. -1.7R.6L. -1.86.6L. -1.8d.6L. -j.6M.6M. -8.6M.6N. -8.6M.6O. -8.6M.6P. -8.6M.6Q. -8.6M.6R. -8.6M.6S. -8.6M.6T. -8.6M.6U. -8.6M.6V. -8.6M.6W. -1.6M.6X. -8.6M.6Y. -1.6M.6Z. -0.6M.6+. -1.6M.6/. -1.6M.70. -0.6M.71. -8.6M.72. -8.6M.73. -1.6M.74. -8.6M.75. -1.6M.76. -1.6M.77. -8.6M.78. -5.79.6M. -1.6M.7a. -8.6M.7b. -8.6M.7c. -8.6M.7d. -8.6M.7e. -8.6M.7f. -8.6M.7g. -8.6M.7h. -8.6M.7i. -8.6M.7j. -8.6M.7k. -8.6M.7l. -1.6M.7m. -8.6M.7o. -8.6M.7p. -1.6M.7r. -1.6M.7s. -8.6M.7t. -8.6M.7u. -8.6M.7v. -8.6M.7w. -8.6M.7x. -8.6M.7y. -1.6M.7z. -8.6M.7A. -8.6M.7B. -8.6M.7C. -0.6M.7D. -8.6M.7E. -8.6M.7F. -8.6M.7G. -8.6M.7H. -8.6M.7I. -8.6M.7J. -1.6M.7K. -1.6M.7L. -1.6M.7M. -1.6M.7N. -1.6M.7O. -1.6M.7P. -8.6M.7Q. -8.6M.7R. -3.6M.7S. -3.6M.7T. -1.6M.7U. -8.6M.7V. -8.6M.7W. -8.6M.7X. -8.6M.7Y. -8.6M.7Z. -1.6M.7+. -1.6M.7/. -1.6M.80. -1.6M.81. -1.6M.82. -8.6M.83. -1.6M.84. -1.6M.85. -1.6M.87. -1.6M.88. -8.6M.89. -8.6M.8a. -8.6M.8b. -3.6M.8c. -8.6M.8d. -3.6M.8e. -3.6M.8f. -3.6M.8g. -3.6M.8h. -3.6M.8i. -3.6M.8j. -3.6M.8k. -3.6M.8l. -3.6M.8m. -3.6M.8n. -3.6M.8o. -3.6M.8p. -3.6M.8q. -3.6M.8r. -3.6M.8s. -8.6M.8t. -8.6M.8u. -8.6M.8v. -8.6M.8w. -8.6M.8x. -3.6M.8y. -8.6M.8z. -8.6M.8A. -8.6M.8B. -8.6M.8C. -8.6M.8D. -8.6M.8E. -8.6M.8F. -8.6M.8G. -8.6M.8H. -8.6M.8I. -8.6M.8J. -3.6M.8K. -3.6M.8L. -3.6M.8M. -3.6M.8N. -3.6M.8O. -3.6M.8P. -0.6M.2p. -0.6M.2p. -8.6M.3b. -1.6M.46. -1.6M.4X. -1.6M.5x. -1.6M.5J. -1.6M.7n. -1.6M.7q. -1.7B.6N. -1.7R.6N. -1.86.6N. -1.8d.6N. -1.7B.6O. -1.7R.6O. -1.86.6O. -1.8d.6O. -1.7B.6P. -1.7R.6P. -1.86.6P. -1.8d.6P. -1.7B.6Q. -1.7R.6Q. -1.86.6Q. -1.8d.6Q. -1.7B.6R. -1.7R.6R. -1.86.6R. -1.8d.6R. -1.7B.6S. -1.7R.6S. -1.86.6S. -1.8d.6S. -1.7B.6T. -1.86.6T. -1.8d.6T. -1.7B.6U. -1.7R.6U. -1.86.6U. -1.8d.6U. -1.7B.6V. -1.86.6V. -1.8d.6V. -1.7B.6W. -1.86.6W. -1.8d.6W. -1.7B.6X. -1.86.6X. -1.8d.6X. -1.7B.6Y. -1.7R.6Y. -1.86.6Y. -1.8d.6Y. -1.7B.6Z. -1.8d.6Z. -d.6+.6+. -d.6+.71. -5.79.6+. -0.6+.7D. -0.2p.6+. -0.2p.6+. -1.7B.6/. -1.86.6/. -1.8d.6/. -1.7B.70. -1.86.70. -1.8d.70. +0.3q.3e. +1.1V.8l. +2.2/.3R. +2.2n.7j. +4.u.8S. +6.85.1Z. +7.1+.84. +7.r.8T. +0.2M.4q. +0.2Q.4j. +0.2w.53. +1.2n.7k. +1.k.8V. +2./.8E. +2.2/.3S. +4.1h.8C. +6.3i.3h. +6.3r.3e. +6.85.1+. +7.22.81. +7.s.8T. +b.3G.32. +c.37.3y. +0.2R.4j. +0.39.3v. +1.1+.86. +1.22.82. +1.2f.7S. +1.2n.7l. +4.1i.8C. +6.3i.3i. +6.3m.3g. +6.85.1/. +a.2Y.40. +c.3f.3q. +0.1j.8C. +0.2w.55. +0.7V.2d. +1.22.83. +1.31.3J. +2.c.8Z. +4.k.8W. +5.1V.8m. +5.2q.6H. +6.85.20. +7.u.8T. +c.3f.3r. +f.24.81. +0.2S.4j. +1.24.82. +1.2f.7T. +1.2n.7n. +1.31.3K. +1.33.3G. +5.1V.8n. +5.2q.6I. +6.85.21. +7.22.84. +b.3R.30. +0.2N.4q. +0.39.3y. +1.1V.8o. +1.24.83. +1.2n.7o. +1.31.3L. +3.2x.53. +5.2q.6J. +5.c.8+. +6.3m.3h. +6.3S.30. +6.85.22. +7.1+.87. +7.26.81. +7.r.8U. +b.3G.34. +d.2U.4c. +0.2O.4q. +0.3q.3g. +1.1V.8p. +1.22.86. +1.26.82. +1.29.7Y. +1.2f.7U. +1.31.3M. +4.1k.8C. +4.s.8U. +5.2q.6K. +6.3i.3m. +6.58.2w. +6.85.23. +7.1+.88. +7.24.84. +8.2+.3Y. +0.3e.3v. +1.26.83. +1.31.3N. +1.6L.2q. +2./.8F. +3.1l.8C. +3.2x.55. +5.k.8X. +6.3r.3g. +6.85.24. +7.1+.89. +9.7t.2h. +a.2P.4q. +b.3J.32. +f.29.7Z. +1.1V.8q. +1.24.86. +1.2n.7p. +3.3K.32. +4.1m.8C. +4.7t.2i. +5.2a.7Z. +5.k.8Y. +6.85.25. +7.1+.8a. +7.26.84. +7.u.8U. +0.2Q.4q. +1.2n.7q. +2.2/.3Y. +3.2y.53. +4.1n.8C. +4.7t.2j. +5.1t.8B. +5.c.8/. +6.85.26. +7.1+.8b. +7.22.87. +a.32.3L. +c.3f.3v. +f.3h.3q. +0.1o.8C. +0.2R.4q. +1.26.86. +1.2f.7X. +1.2n.7r. +1.33.3J. +1.r.8V. +3.3M.32. +5./.8G. +6.3i.3q. +6.3m.3m. +6.3r.3h. +6.3y.3e. +6.4c.2V. +6.85.27. +7.1+.8c. +7.22.88. +8.2z.53. +8.58.2x. +9.7t.2k. +1.33.3K. +1.s.8V. +3.2W.4c. +3.2y.55. +4.1p.8C. +5./.8H. +5.29.7+. +5.2q.6M. +6.3i.3r. +7.1+.8d. +7.22.89. +7.24.87. +8.2+.40. +8.2A.53. +9.7t.2l. +a.32.3N. +b.3G.37. +b.3J.34. +d.7v.2h. +f.2c.7Z. +0.2S.4q. +1.33.3L. +1.c.90. +3.2B.53. +3.3K.34. +4.1q.8C. +6.85.28. +7.1+.8e. +7.22.8a. +7.24.88. +8.2z.55. +9.r.8W. +c.3f.3y. +d.7v.2i. +0.2C.53. +0.2n.7t. +0.30.3Y. +0.3g.3v. +1.31.3R. +1.33.3M. +1.u.8V. +4.1r.8C. +5./.8I. +6.3L.34. +7.1+.8f. +7.22.8b. +7.24.89. +7.26.87. +8.2A.55. +9.1V.8r. +9.s.8W. +d.2U.4j. +d.7v.2j. +0.2w.5e. +1.31.3S. +1.33.3N. +2.1+.8g. +2.2/.40. +2.k.8Z. +3.2B.55. +3.3M.34. +4.1s.8C. +4.2d.7Z. +5./.8J. +5.1h.8D. +5.1V.8s. +6.3m.3q. +7.22.8c. +7.24.8a. +7.26.88. +8.58.2y. +d.7v.2k. +0.34.3N. +1.2n.7u. +2.1+.8h. +4.1t.8C. +4.u.8W. +5./.8K. +5.29.7/. +6.3r.3m. +7.22.8d. +7.24.8b. +7.26.89. +8.58.2z. +b.3G.39. +d.7v.2l. +f.2C.55. +0.2D.53. +0.3h.3v. +3.1u.8C. +5.k.8+. +6.3y.3g. +6.4c.2Y. +7.22.8e. +7.24.8c. +7.26.8a. +7.2p.7t. +7.r.8X. +8.58.2A. +j.2a.7/. +1.29.80. +1.2f.7Y. +1.c.91. +4.1v.8C. +4.s.8X. +5.2q.6O. +6.3i.3v. +7.22.8f. +7.24.8d. +7.26.8b. +7.r.8Y. +8.58.2B. +b.3J.37. +b.3R.32. +g.2E.53. +0.30.40. +0.3q.3q. +2.22.8g. +3.2x.5e. +3.3K.37. +4.1w.8C. +4.55.2D. +4.s.8Y. +6.58.2C. +7.1+.8i. +7.24.8e. +7.26.8c. +a.2V.4j. +a.32.3S. +0.1x.8C. +1.2n.7x. +1.c.92. +2.22.8h. +3.2W.4j. +4.u.8X. +5.2q.6P. +6.3L.37. +6.3r.3q. +6.3y.3h. +7.24.8f. +7.26.8d. +9.7A.2h. +g.2E.55. +0.1y.8C. +1.33.3R. +1.c.93. +2.24.8g. +3.3M.37. +5.1V.8t. +5.k.8/. +6.3i.3y. +6.3r.3r. +7.1+.8j. +7.26.8e. +7.u.8Y. +9.7A.2i. +b.3G.3e. +1.33.3S. +1.c.94. +2.24.8h. +4.1z.8C. +6.3m.3v. +7.26.8f. +8.58.2D. +9.7A.2j. +b.3J.39. +b.3R.34. +c.37.3N. +d.2U.4q. +1.1+.8k. +1.2f.7+. +1.2n.7y. +1.31.3Y. +2.26.8g. +3.2y.5e. +3.3K.39. +4.1A.8C. +5.1V.8u. +6.3S.34. +7.22.8i. +8.58.2E. +9.7A.2k. +b.3G.3f. +1.1+.8l. +1.5s.2u. +1.k.90. +2.26.8h. +2.A.8W. +2.r.8Z. +4.1B.8C. +5.1V.8v. +6.3L.39. +8.2z.5e. +9.7A.2l. +0.1C.8C. +1.2n.7z. +2.5s.2v. +2.s.8Z. +3.3M.39. +5.1V.8w. +5.29.81. +6.3m.3y. +7.22.8j. +7.24.8i. +8.2A.5e. +a.2Y.4j. +0.3q.3v. +0.7A.2n. +1.29.82. +3.2B.5e. +4.1D.8C. +7.r.8+. +8.2+.4c. +a.2F.53. +f.39.3N. +j.2a.81. +0.2C.5e. +1.22.8k. +1.29.83. +1.2f.7/. +2.1h.8E. +2.u.8Z. +4.s.8+. +5.c.95. +6.3r.3v. +7.1+.8m. +7.24.8j. +7.26.8i. +a.2V.4q. +a.32.3Y. +b.3G.3g. +b.3J.3e. +0.2G.53. +1.1V.8x. +1.22.8l. +1.31.40. +3.2W.4q. +3.3K.3e. +4.1E.8C. +5.29.84. +5.2q.6Q. +7.1+.8n. +a.2F.55. +b.3R.37. +1./.8L. +1.1+.8o. +1.24.8k. +1.2f.80. +1.k.91. +2.2/.4c. +2.A.8Y. +4.1F.8C. +4.u.8+. +5.1t.8D. +6.3L.3e. +6.3S.37. +6.3y.3q. +6.6R.2q. +6.85.29. +7.26.8j. +7.2p.7A. +b.3J.3f. +j.2a.84. +0.2D.5e. +0.2G.55. +1./.8M. +1.1+.8p. +1.24.8l. +1.29.86. +1.2n.7B. +1.33.3Y. +2.2H.53. +3.3K.3f. +3.3M.3e. +4.1G.8C. +4.r.8/. +5.1V.8y. +6.3r.3y. +6.85.2a. +b.3G.3h. +0.34.3Y. +1./.8N. +1.26.8k. +1.2n.7C. +1.c.96. +1.k.92. +2.1d.8I. +4.1H.8C. +4.s.8/. +5.2q.6T. +6.3L.3f. +6.3N.3e. +7.22.8m. +8.58.2F. +b.3G.3i. +g.2E.5e. +1.1+.8q. +1.26.8l. +1.2n.7D. +1.k.93. +2.2H.55. +3.3M.3f. +4.1I.8C. +7.22.8n. +9.1V.8z. +a.32.40. +b.3R.39. +1.22.8o. +1.2n.7E. +1.k.94. +1.r.90. +2.1h.8F. +4.1J.8C. +4.3f.3N. +4.u.8/. +5.29.87. +6.3S.39. +6.3v.3v. +6.4c.30. +6.58.2G. +6.85.2c. +7.24.8m. +a.2Y.4q. +b.3J.3g. +0.2I.53. +1.22.8p. +1.s.90. +3.3K.3g. +5.29.88. +7.24.8n. +8.2+.4j. +j.2a.87. +k.98.0. +1.24.8o. +1.33.40. +2.2H.58. +5.29.89. +6.3L.3g. +7.26.8m. +b.3G.3m. +0.2I.55. +0.34.40. +1./.8O. +1.22.8q. +1.24.8p. +1.2f.81. +1.2n.7F. +1.4G.2T. +1.u.90. +3.3M.3g. +4.1K.8C. +5.1h.8G. +5.29.8a. +5.2q.6U. +6.3y.3v. +6.85.2d. +7.26.8n. +b.3J.3h. +f.2w.5s. +0.1L.8C. +1./.8P. +1.26.8o. +1.2f.82. +2.2/.4j. +3.3K.3h. +5.1h.8H. +5.29.8b. +6.3N.3g. +6.6V.2q. +9.1+.8r. +b.3J.3i. +b.3R.3e. +c.37.3Y. +j.2a.8a. +1.24.8q. +1.26.8p. +1.2f.83. +1.2n.7G. +1.r.91. +3.3K.3i. +4.1M.8C. +5.29.8c. +5.2q.6W. +5.k.95. +6.3S.3e. +7.1+.8s. +e.3L.3h. +j.2a.8b. +1./.8Q. +1.2f.84. +1.2n.7H. +1.s.91. +3.3M.3h. +4.1N.8C. +5.1h.8I. +5.29.8d. +5.2q.6X. +6.3L.3i. +6.3y.3y. +6.58.2I. +a.2F.5e. +b.3G.3q. +b.3R.3f. +0.1O.8C. +1.26.8q. +1.2n.7I. +1.c.97. +1.r.92. +3.2x.5s. +3.3M.3i. +5.1h.8J. +5.29.8e. +5.2q.6Y. +6.3N.3h. +6.3S.3f. +a.2J.53. +b.3G.3r. +0.1P.8C. +0.2G.5e. +0.30.4j. +1.2f.86. +1.2n.7J. +1.r.93. +1.s.92. +1.u.91. +2.1t.8E. +5./.8R. +5.1h.8K. +5.29.8f. +5.2q.6Z. +6.3i.3N. +9.22.8r. +b.3J.3m. +f.39.3Y. +j.2a.8e. +0.1Q.8C. +1.31.4c. +1.k.96. +1.r.94. +1.s.93. +2.29.8g. +3.3K.3m. +7.22.8s. +8.2+.4q. +a.2J.55. +c.37.40. +j.2a.8f. +1.s.94. +1.u.92. +2.29.8h. +2.2H.5e. +4./.8S. +6.3L.3m. +9.24.8r. +b.3R.3g. +0.2K.53. +1.2f.87. +1.u.93. +3.2y.5s. +3.3M.3m. +5.1V.8B. +5.6+.2q. +6.3S.3g. +7.1+.8t. +7.24.8s. +0.2w.5u. +1.2f.88. +1.u.94. +2.2/.4q. +5.2q.6/. +6.3m.3N. +8.2z.5s. +8.58.2J. +9.26.8r. +b.3J.3q. +0.2K.55. +0.39.40. +0.3e.3Y. +1.2f.89. +3.3K.3q. +5./.8T. +5.29.8i. +5.2q.70. +6.4c.32. +7.1+.8u. +7.26.8s. +8.2A.5s. +b.3G.3v. +b.3J.3r. +b.3R.3h. +f.1R.8C. +k.98.c. +0.1S.8C. +0.2I.5e. +1.2f.8a. +1.4G.2X. +1.c.99. +2.1t.8F. +3.2B.5s. +3.3K.3r. +4.r.95. +5.2g.7V. +5.2q.71. +6.3L.3q. +6.3S.3h. +7.1+.8v. +b.3R.3i. +1.2f.8b. +3.3M.3q. +5.29.8j. +5.2q.72. +6.3L.3r. +6.3S.3i. +7.1+.8w. +7.22.8t. +7.s.95. +c.3f.3Y. +f.2C.5s. +0.30.4q. +1.2f.8c. +1.33.4c. +3.2x.5u. +3.3M.3r. +5.2q.73. +6.3N.3q. +6.58.2K. +b.3G.3y. +0.1V.8C. +0.2L.53. +1.29.8k. +1.2f.8d. +1.31.4j. +1.c.9a. +4.u.95. +5.1t.8G. +5.2q.74. +6.3r.3N. +6.4c.34. +7.22.8u. +7.24.8t. +0.3e.40. +1.1+.8x. +1.29.8l. +1.2f.8e. +1.k.97. +1.r.96. +2.c.9b. +5./.8U. +5.1t.8H. +5.2q.75. +6.5s.2D. +7.22.8v. +b.3R.3m. +f.1W.8C. +0.2L.55. +0.3g.3Y. +1.2f.8f. +1.s.96. +2.2n.7K. +5.2q.76. +6.3S.3m. +7.22.8w. +7.24.8u. +7.26.8t. +b.3J.3v. +g.2E.5s. +0.1X.8C. +2.2f.8g. +2.A.94. +3.2y.5u. +3.3K.3v. +5.1t.8I. +5.2q.77. +7.1+.8y. +7.24.8v. +a.2J.5e. +c.3f.40. +0.2M.53. +1.1h.8L. +1.u.96. +2.2f.8h. +5.1t.8J. +5.29.8m. +5.2q.78. +6.3L.3v. +7.24.8w. +7.26.8u. +8.2z.5u. +9.c.9c. +a.32.4j. +0.3h.3Y. +1.1h.8M. +1.22.8x. +3.3M.3v. +5.1t.8K. +5.29.8n. +5.2q.79. +6.58.2L. +7.26.8v. +8.2A.5u. +9.1+.8z. +b.3J.3y. +b.3R.3q. +0.2M.55. +1./.8V. +1.1h.8N. +1.29.8o. +3.2B.5u. +3.3K.3y. +5.2q.7a. +5.c.9d. +6.3i.3Y. +6.3N.3v. +6.3S.3q. +6.4c.37. +7.26.8w. +b.3R.3r. +0.2C.5u. +0.3g.40. +1.24.8x. +1.29.8p. +1.2f.8i. +1.2n.7L. +1.33.4j. +4.2K.5e. +5.2q.7b. +6.3L.3y. +6.3S.3r. +7.22.8y. +0.2N.53. +0.34.4j. +1.31.4q. +1.k.99. +2.2n.7M. +3.3M.3y. +5.2g.7Z. +5.2q.7c. +9./.8W. +0.2O.53. +1.26.8x. +1.29.8q. +1.2f.8j. +5.2q.7d. +6.3y.3N. +6.58.2M. +7.24.8y. +9.22.8z. +b.3G.3G. +0.2N.55. +0.3h.40. +0.5u.2D. +1.r.97. +2.c.9e. +5.2q.7e. +6.3m.3Y. +6.4c.39. +a.2F.5s. +a.2P.53. +0.2O.55. +1.1h.8O. +1.2f.8k. +1.k.9a. +1.s.97. +6.3i.40. +7.26.8y. +9.24.8z. +g.2E.5u. +0.2Q.53. +1.1h.8P. +1.2f.8l. +2.k.9b. +4.2G.5s. +5./.8X. +5.2q.7g. +a.2P.55. +a.32.4q. +b.3R.3v. +0.2L.5e. +1.u.97. +5./.8Y. +5.1V.8D. +5.2q.7h. +6.3S.3v. +6.58.2N. +9.26.8z. +f.2R.53. +0.2Q.55. +0.3q.3Y. +1.1h.8Q. +2.2H.5s. +5.29.8r. +6.58.2O. +f.37.4j. +0.2S.53. +1.2f.8m. +1.33.4q. +5.29.8s. +5.2q.7i. +6.3m.40. +6.3r.3Y. +6.4c.3e. +8.58.2P. +9.k.9c. +b.3G.3J. +b.3R.3y. +f.2R.55. +0.34.4q. +1.2f.8n. +5.1h.8R. +6.3S.3y. +b.3G.3K. +k.98.r. +0.1Y.8C. +0.2M.5e. +0.2S.55. +1.2f.8o. +1.r.99. +4.c.9f. +5.k.9d. +6.4c.3f. +6.58.2Q. +7.1+.8B. +b.3G.3L. +k.98.s. +0.39.4j. +1.1t.8L. +1.2f.8p. +1.s.99. +5.1h.8S. +6.58.2R. +b.3G.3M. +f.2I.5s. +0.3q.40. +1.1t.8M. +2./.8Z. +2.2n.7O. +2.c.9g. +5.2q.7m. +a.2F.5u. +b.3G.3N. +b.5F.2w. +k.98.u. +1.1t.8N. +1.2f.8q. +1.r.9a. +1.u.99. +3.5G.2w. +5.2q.7n. +6.3r.40. +6.58.2S. +0.2G.5u. +0.2N.5e. +1.s.9a. +2.2n.7P. +2.k.9e. +2.r.9b. +5./.8+. +5.1h.8T. +5.29.8t. +5.2q.7o. +6.3v.3Y. +6.4c.3g. +7.22.8B. +b.3J.3J. +2.s.9b. +4.2O.5e. +b.3J.3K. +f.1Z.8C. +f.37.4q. +0.1+.8C. +0.3e.4j. +1.u.9a. +2.2H.5u. +3.3K.3K. +5.29.8u. +7.24.8B. +a.2P.5e. +b.3J.3L. +b.5F.2x. +0.1/.8C. +2.u.9b. +3.3K.3L. +3.5G.2x. +5.29.8v. +5.2q.7p. +6.3y.3Y. +6.4c.3h. +9.r.9c. +a.2J.5s. +b.3J.3M. +d.2U.53. +0.20.8C. +0.2Q.5e. +1.1t.8O. +1.2f.8r. +2.1d.8W. +2.1V.8E. +2.2n.7Q. +3.3M.3K. +4.s.9c. +5./.8/. +5.29.8w. +5.2q.7q. +6.3L.3L. +6.4c.3i. +7.26.8B. +b.3G.3R. +b.3J.3N. +c.3f.4j. +0.39.4q. +1.1t.8P. +1.2f.8s. +3.3K.3N. +3.3M.3L. +4.r.9d. +5.1h.8U. +5.2q.7r. +5.c.9h. +6.3v.40. +b.3G.3S. +d.2U.55. +f.2R.5e. +j.21.8C. +0.2I.5u. +3.3M.3M. +6.3L.3N. +7.s.9d. +9.u.9c. +b.5F.2y. +f.22.8C. +0.23.8C. +0.2S.5e. +1.1t.8Q. +1.29.8x. +1.2n.7S. +3.3M.3N. +3.5G.2y. +4.2K.5s. +4.k.9f. +5.2q.7s. +8.2z.5F. +0.24.8C. +0.3g.4j. +4.u.9d. +5.2q.7t. +6.3N.3N. +6.3y.40. +6.4c.3m. +8.2A.5F. +8.2z.5G. +a.2V.53. +d.2U.58. +0.25.8C. +1.2n.7T. +2.1d.8Y. +2.k.9g. +2.r.9e. +3.2B.5F. +3.2W.53. +5.1t.8R. +5.29.8y. +8.2A.5G. +1.1h.8V. +2.1V.8F. +2.s.9e. +3.2B.5G. +4.26.8C. +5.2q.7u. +6.4q.3e. +a.2V.55. +b.3R.3J. +b.5F.2C. +0.27.8C. +0.3h.4j. +1.2f.8t. +1.2n.7U. +2.A.9b. +3.2W.55. +3.5G.2C. +4.1t.8S. +5.29.8z. +b.3J.3S. +b.3R.3K. +0.7V.2n. +1./.91. +2.u.9e. +3.3K.3S. +5.1h.8W. +5.2q.7v. +6.3i.4j. +6.4c.3q. +a.2J.5u. +b.3R.3L. +c.3f.4q. +j.2a.8z. +1.2f.8u. +5.1V.8G. +5.2q.7w. +6.3S.3L. +6.4c.3r. +8.58.2V. +b.3G.3Y. +b.3R.3M. +b.5F.2D. +c.28.8C. +f.2L.5s. +1./.92. +1.2f.8v. +1.4G.35. +1.8z.2b. +3.3M.3S. +3.5G.2D. +5.1t.8T. +5.2q.7x. +8.58.2W. +a.2Y.53. +b.3R.3N. +f.1V.8H. +1./.93. +1.2f.8w. +1.2n.7X. +6.3S.3N. +7.1+.8D. +7.2p.7V. +g.2E.5G. +0.2K.5u. +1./.94. +5.1h.8X. +5.1V.8I. +5.k.9h. +6.3m.4j. +6.4q.3g. +9.r.9f. +a.2Y.55. +d.2U.5e. +5.1h.8Y. +5.1V.8J. +5.2q.7y. +9.s.9f. +f.2M.5s. +1.2f.8x. +2.r.9g. +5.1V.8K. +b.3G.40. +2.s.9g. +4.u.9f. +5.1t.8U. +5.2q.7z. +6.4c.3v. +6.4q.3h. +7.22.8D. +8.58.2Y. +9.c.9i. +b.3J.3Y. +0.3q.4j. +1.2f.8y. +3.3K.3Y. +5.2q.7A. +6.3i.4q. +b.3R.3R. +0.2N.5s. +2.u.9g. +5./.95. +6.3L.3Y. +6.3r.4j. +7.24.8D. +a.2V.5e. +b.3R.3S. +0.2L.5u. +0.2O.5s. +1.2f.8z. +1.2n.7Y. +3.2W.5e. +3.3M.3Y. +6.3S.3S. +6.4c.3y. +b.5F.2F. +0.7Z.2n. +2.1h.8Z. +3.5G.2F. +5.29.8B. +6.3N.3Y. +7.26.8D. +8.2+.53. +a.2P.5s. +1.1t.8V. +5.2q.7B. +6.3m.4q. +b.3J.40. +b.5F.2G. +j.2a.8B. +1./.96. +1.4G.3b. +3.3K.40. +3.5G.2G. +5.1h.8+. +5.2q.7C. +7.r.9h. +8.2+.55. +f.2Q.5s. +0.2M.5u. +1.4G.3c. +2.2/.53. +2.2H.5F. +5.2q.7D. +6.3L.40. +7.2p.7Z. +7.s.9h. +9.1t.8W. +f.2R.5s. +1.2n.7+. +2.1+.8E. +2.2H.5G. +3.3M.40. +5.2q.7E. +6.3v.4j. +a.2Y.5e. +0.2S.5s. +2.2/.55. +6.3N.40. +6.4q.3q. +7.u.9h. +8.2+.58. +2.A.9g. +5.1h.8/. +6.3r.4q. +b.3R.3Y. +f.29.8C. +0.2N.5u. +0.30.53. +2.c.9j. +5.1t.8X. +5.2a.8C. +5.2q.7F. +6.3S.3Y. +6.3y.4j. +9.k.9i. +b.5F.2I. +0.2O.5u. +1.1V.8L. +1.2n.7/. +2.2/.58. +2.22.8E. +2.c.9k. +3.5G.2I. +5.1t.8Y. +0.30.55. +1.1h.90. +1.1V.8M. +5.2q.7G. +a.2P.5u. +b.3G.4c. +1.1V.8N. +1.2n.80. +1.5s.2T. +2.1+.8F. +2.1d.94. +2.24.8E. +5.2q.7H. +f.2c.8C. +0.2Q.5u. +1./.97. +1.2f.8B. +5.2q.7I. +b.3R.40. +0.2R.5u. +2.26.8E. +5.2q.7J. +6.3S.40. +6.4q.3v. +6.58.30. +0.2d.8C. +7.1+.8G. +8.2+.5e. +b.5F.2J. +0.2S.5u. +1.1h.91. +2.1t.8Z. +2.22.8F. +3.5G.2J. +7.1+.8H. +d.2U.5s. +0.3Y.3Y. +1.1V.8O. +6.3y.4q. +b.3J.4c. +1.1h.92. +1.1V.8P. +1.31.53. +2.2/.5e. +2.24.8F. +3.3K.4c. +5.1t.8+. +7.1+.8I. +9.r.9i. +1.1h.93. +1.2n.81. +1.4G.3k. +6.4c.3L. +6.5L.2w. +7.22.8G. +9.s.9i. +b.3G.4j. +b.5F.2K. +f.1+.8J. +1./.99. +1.1h.94. +1.1V.8Q. +1.2n.82. +1.31.55. +2.26.8F. +2.k.9j. +3.3M.4c. +3.5G.2K. +7.1+.8K. +f.22.8H. +1.2n.83. +2.k.9k. +5.29.8D. +6.4c.3N. +9.u.9i. +a.2V.5s. +f.24.8G. +0.30.5e. +1.2n.84. +2.2g.8r. +3.2W.5s. +5.1t.8/. +5.1V.8R. +6.40.3Y. +7.22.8I. +7.24.8H. +a.32.53. +1.31.58. +1.5s.2X. +6.5L.2x. +6.85.2n. +7.22.8J. +7.26.8G. +1.2n.86. +2./.9b. +4.26.8H. +7.22.8K. +7.24.8I. +9.1V.8S. +a.32.55. +1.1t.90. +1.33.53. +5.1h.95. +7.24.8J. +b.3J.4j. +b.5F.2L. +d.2U.5u. +0.34.53. +3.3K.4j. +3.5G.2L. +7.24.8K. +7.26.8I. +7.2p.85. +1.2n.87. +1.33.55. +1.4G.3s. +4./.9c. +5.1V.8T. +6.3L.4j. +6.40.40. +6.5L.2y. +7.26.8J. +8.58.32. +a.2Y.5s. +b.3G.4q. +b.3R.4c. +1.2n.88. +3.3M.4j. +6.4c.3S. +7.26.8K. +8.2z.5L. +f.34.55. +1.1h.96. +1.2n.89. +2.2g.8t. +2.r.9j. +5./.9d. +6.3N.4j. +8.2A.5L. +b.5F.2M. +1.1t.91. +1.2n.8a. +1.33.58. +1.5s.2Z. +2.r.9k. +2.s.9j. +3.2B.5L. +3.5G.2M. +5.2q.7L. +a.2V.5u. +1.2n.8b. +1.31.5e. +2.s.9k. +3.2W.5u. +6.58.34. +6.5L.2C. +1.1t.92. +1.2f.8D. +1.2n.8c. +2.u.9j. +5.1V.8U. +f.37.53. +1.1+.8L. +1.1t.93. +1.2n.8d. +2./.9e. +2.29.8E. +2.2g.8w. +2.u.9k. +b.3J.4q. +b.5F.2N. +1.1+.8M. +1.1t.94. +1.2n.8e. +3.3K.4q. +3.5G.2N. +6.5L.2D. +b.5F.2O. +c.37.55. +1.1+.8N. +1.2n.8f. +3.5G.2O. +6.3L.4q. +a.32.5e. +b.3R.4j. +b.5F.2P. +g.2E.5L. +2.2n.8g. +2.4G.3z. +3.3M.4q. +3.5G.2P. +6.3S.4j. +6.4c.3Y. +8.2+.5s. +a.2Y.5u. +f.39.53. +1.1V.8V. +1.22.8L. +2.2n.8h. +6.4q.3N. +6.58.37. +b.5F.2Q. +1.1h.97. +1.22.8M. +1.33.5e. +3.5G.2Q. +b.5F.2R. +f.39.55. +1.22.8N. +1.24.8L. +2.2/.5s. +2.29.8F. +3.5G.2R. +5.1t.95. +f.1V.8W. +f.34.5e. +1.1+.8O. +1.24.8M. +1.2n.8i. +4./.9f. +b.5F.2S. +e.2g.8z. +0.3e.53. +1.1+.8P. +1.24.8N. +1.26.8L. +2.1d.9b. +2.4G.3D. +3.5G.2S. +6.4c.40. +6.58.39. +1.26.8M. +1.2n.8j. +2./.9g. +5.29.8G. +5.c.9l. +1.1+.8Q. +1.1t.96. +1.26.8N. +4.55.3e. +5.1V.8X. +5.29.8H. +6.5L.2F. +b.3R.4q. +c.3f.53. +f.30.5s. +0.4j.3Y. +1.22.8O. +2.2f.8E. +5.1V.8Y. +6.3S.4q. +k.98.1h. +1.1h.99. +1.22.8P. +1.2n.8l. +5.29.8I. +6.5L.2G. +7.1+.8R. +8.2+.5u. +c.37.5e. +c.3f.55. +1.24.8O. +5.29.8J. +5.c.9m. +8.58.3e. +0.3g.53. +1.22.8Q. +1.24.8P. +2.2H.5L. +5.29.8K. +9.1+.8S. +1.1h.9a. +1.26.8O. +1.2n.8m. +2.2/.5u. +8.58.3f. +0.39.5e. +0.4j.40. +0.55.3g. +1.24.8Q. +1.26.8P. +1.2n.8n. +2.1h.9b. +5./.9h. +7.22.8R. +d.2U.5F. +0.3h.53. +1.2n.8o. +2.1V.8Z. +2.2f.8F. +7.1+.8T. +d.2U.5G. +1.26.8Q. +1.2n.8p. +6.3i.53. +6.5L.2I. +7.24.8R. +9.22.8S. +0.30.5u. +1.1t.97. +1.31.5s. +5.1h.9c. +5.1V.8+. +5.c.9n. +6.4q.3Y. +8.58.3g. +f.3h.55. +1.2f.8G. +1.2n.8q. +5.2q.7T. +5.c.9o. +5.k.9l. +6.3i.55. +7.26.8R. +9.24.8S. +0.3e.5e. +1.2f.8H. +5.1h.9d. +5.c.9p. +7.22.8T. +b.5F.2V. +3.5G.2V. +5.c.9q. +6.3m.53. +6.4c.4c. +6.58.3h. +7.1+.8U. +9.26.8S. +b.5F.2W. +1.2f.8I. +1.4G.3O. +3.5G.2W. +5.1V.8/. +5.2q.7V. +5.c.9r. +7.24.8T. +8.58.3i. +a.32.5s. +c.3f.5e. +1.2f.8J. +2.4G.3P. +5.c.9s. +5.k.9m. +6.3m.55. +6.4q.40. +6.5L.2J. +1.2f.8K. +1.2n.8r. +2.1h.9e. +5.c.9t. +7.26.8T. +f.2g.8C. +0.3q.53. +1.1t.99. +1.1V.90. +1.2n.8s. +1.33.5s. +2.1d.9g. +5.c.9u. +7.22.8U. +0.34.5s. +1.1+.8V. +1.29.8L. +4.3g.5e. +5.c.9v. +6.3r.53. +6.58.3m. +b.5F.2Y. +0.3q.55. +1.29.8M. +1.31.5u. +1.5s.35. +3.5G.2Y. +5.c.9w. +6.5L.2K. +f.24.8U. +1.1t.9a. +1.29.8N. +1.5s.36. +5.c.9x. +6.3r.55. +7.r.9l. +9.1+.8W. +0.3h.5e. +1.4G.3T. +2.1t.9b. +5.c.9y. +5.k.9n. +6.4c.4j. +7.26.8U. +7.s.9l. +9./.9i. +1.1V.91. +1.22.8V. +1.4G.3U. +5.c.9z. +5.k.9o. +6.3i.5e. +6.58.3q. +1.2n.8t. +5.1h.9f. +5.k.9p. +6.58.3r. +7.u.9l. +a.32.5u. +1.1V.92. +1.24.8V. +1.4G.3W. +4.1t.9c. +5.k.9q. +6.3v.53. +7.1+.8X. +7.r.9m. +9.22.8W. +c.37.5s. +1.1V.93. +1.29.8O. +1.2n.8u. +1.4G.3X. +1.5s.38. +2.1h.9g. +5.2q.7Z. +5.k.9r. +6.5L.2L. +7.1+.8Y. +7.s.9m. +0.55.3v. +1.1V.94. +1.26.8V. +1.29.8P. +1.2n.8v. +1.33.5u. +5.1t.9d. +5.k.9s. +6.3m.5e. +9.24.8W. +0.34.5u. +5.k.9t. +6.3y.53. +7.u.9m. +8.2+.5F. +1.29.8Q. +1.8z.2h. +5.k.9u. +7.22.8X. +8.2+.5G. +9.26.8W. +f.39.5s. +0.4j.4j. +1.2f.8L. +1.5s.3a. +1.8z.2i. +5.2q.7+. +5.k.9v. +6.3y.55. +6.4c.4q. +6.5L.2M. +7.22.8Y. +8.58.3v. +0.3q.5e. +1.2f.8M. +1.2n.8x. +1.4G.3Z. +1.5s.3b. +1.8z.2j. +2.1t.9e. +2.2/.5F. +5.29.8R. +5.k.9w. +7.24.8X. +7.r.9n. +1.2f.8N. +1.5s.3c. +1.8z.2k. +2.1+.8Z. +2.2/.5G. +5.1V.95. +5.k.9x. +6.3r.5e. +7.24.8Y. +7.r.9o. +7.s.9n. +1.5s.3d. +1.8z.2l. +2./.9j. +5.1h.9h. +5.29.8S. +5.k.9y. +6.58.3y. +7.26.8X. +7.r.9p. +7.s.9o. +2./.9k. +5.2q.7/. +5.k.9z. +6.5L.2N. +6.5s.3e. +7.1+.8+. +7.26.8Y. +7.r.9q. +7.s.9p. +7.u.9n. +c.37.5u. +j.2a.8S. +1.2n.8z. +1.4G.41. +6.5L.2O. +7.r.9r. +7.s.9q. +7.u.9o. +b.5F.30. +1.1V.96. +2.22.8Z. +3.5G.30. +5.29.8T. +7.r.9s. +7.s.9r. +7.u.9p. +a.2P.5L. +c.3f.5s. +e.8S.2b. +1.2f.8O. +1.4G.43. +7.r.9t. +7.s.9s. +7.u.9q. +0.39.5u. +1.2f.8P. +1.4G.44. +2.24.8Z. +4.1t.9f. +6.3v.5e. +6.4q.4j. +6.5L.2Q. +7.1+.8/. +7.22.8+. +7.r.9u. +7.s.9t. +7.u.9r. +b.3G.53. +6.5L.2R. +7.r.9v. +7.s.9u. +7.u.9s. +1.2f.8Q. +2.1t.9g. +2.26.8Z. +6.5s.3g. +7.24.8+. +7.r.9w. +7.s.9v. +7.u.9t. +b.3G.55. +1.1+.90. +1.4G.47. +1.8S.2e. +5.29.8U. +6.3y.5e. +6.5L.2S. +7.r.9x. +7.s.9w. +7.u.9u. +1.2f.8R. +1.4G.48. +2.A.9n. +7.22.8/. +7.26.8+. +7.r.9y. +7.s.9x. +7.u.9v. +0.5u.3e. +2.A.9o. +5.2q.81. +7.r.9z. +7.s.9y. +7.u.9w. +8.58.3G. +f.3h.5s. +1.31.5F. +1.4G.4a. +2.A.9p. +6.3i.5s. +7.s.9z. +7.u.9x. +b.3J.53. +f.24.8/. +1.1V.97. +1.22.90. +1.31.5G. +1.4G.4b. +1.5s.3j. +2.A.9q. +3.3K.53. +4.3f.5u. +7.u.9y. +1.1+.91. +1.29.8V. +1.5s.3k. +2.A.9r. +5.2q.84. +6.3L.53. +6.4q.4q. +7.26.8/. +7.u.9z. +b.3J.55. +1.24.90. +1.2f.8T. +1.2n.8B. +1.5s.3l. +2.A.9s. +3.3K.55. +3.3M.53. +5.1h.9i. +5.1t.9h. +1.1+.92. +2.A.9t. +5.29.8W. +6.3L.55. +6.3m.5s. +6.3N.53. +b.5F.32. +0.5u.3g. +1.1+.93. +1.26.90. +1.5s.3n. +2.A.9u. +3.3M.55. +3.5G.32. +8.58.3J. +1.1+.94. +1.22.91. +1.5s.3o. +2.A.9v. +6.3N.55. +8.58.3K. +d.2U.5L. +1.33.5F. +1.4G.4e. +1.5s.3p. +2.2g.8G. +2.A.9w. +5.2q.87. +5.c.9A. +8.58.3L. +b.3G.5e. +0.3q.5s. +0.5u.3h. +1.1V.99. +1.22.92. +1.24.91. +1.2f.8U. +1.33.5G. +1.4G.4f. +2.A.9x. +5.29.8X. +5.2q.88. +8.58.3M. +b.5F.34. +0.2n.8C. +1.22.93. +2.A.9y. +3.5G.34. +5.29.8Y. +5.2q.89. +6.3i.5u. +6.3r.5s. +6.58.3N. +j.2a.8X. +1.22.94. +1.24.92. +1.26.91. +1.4G.4h. +1.5s.3s. +2.A.9z. +5.2q.8a. +b.3R.53. +1.1V.9a. +1.24.93. +2.6g.2v. +5.2q.8b. +6.3S.53. +6.5L.2V. +7.1+.95. +1.24.94. +1.26.92. +2.1V.9b. +3.2W.5L. +5.2q.8c. +7.2p.8C. +b.3R.55. +1.26.93. +1.5s.3t. +5.2q.8d. +6.3m.5u. +6.3S.55. +b.3J.5e. +1.26.94. +1.5s.3u. +2.1h.9j. +2.4G.4l. +3.3K.5e. +5.2q.8e. +b.5F.37. +1.1+.96. +1.2f.8W. +1.4G.4m. +2.1h.9k. +2.29.8Z. +3.5G.37. +5.2q.8f. +6.3L.5e. +6.5s.3v. +7.22.95. +8.58.3R. +9.1V.9c. +9.c.9B. +1.5s.3w. +3.3M.5e. +8.58.3S. +9.c.9C. +0.5u.3q. +1.5s.3x. +5.1V.9d. +5.29.8+. +6.3N.5e. +6.5L.2Y. +7.24.95. +9.1t.9i. +9.c.9D. +1.4G.4p. +5.k.9A. +6.3r.5u. +6.3y.5s. +9.c.9E. +b.5F.39. +1.22.96. +1.2f.8X. +2.5s.3z. +3.5G.39. +6.53.3Y. +7.26.95. +1.2f.8Y. +1.5s.3A. +5.2q.8i. +5.c.9F. +0.55.3Y. +1.24.96. +1.4G.4r. +2.1V.9e. +5.29.8/. +1.2n.8D. +1.5s.3B. +5./.9l. +5.2q.8j. +1.26.96. +1.5s.3C. +b.3R.5e. +b.5F.3e. +0.5u.3v. +1.1+.97. +1.29.90. +2.5s.3D. +3.5G.3e. +5.c.9G. +6.3S.5e. +6.53.40. +8.58.3Y. +1.4G.4u. +1.5s.3E. +4.c.9H. +b.5F.3f. +0.55.40. +1.5s.3F. +2.2f.8Z. +3.5G.3f. +5./.9m. +5.k.9B. +8.2+.5L. +5.k.9C. +6.3y.5u. +7.r.9A. +1.22.97. +1.2f.8+. +2.1t.9j. +4.c.9I. +5.2q.8m. +5.k.9D. +7.s.9A. +9.1V.9f. +b.3G.5s. +1.29.91. +2.1t.9k. +2.2/.5L. +5.2q.8n. +5.k.9E. +8.58.40. +9.c.9J. +b.5F.3g. +1.24.97. +2.1V.9g. +3.5G.3g. +7.u.9A. +k.98.1+. +1.1+.99. +1.29.92. +5.k.9F. +0.5e.3Y. +1.26.97. +1.29.93. +1.4G.4A. +1.5s.3H. +5./.9n. +b.5F.3h. +1.29.94. +1.4G.4B. +1.5s.3I. +3.5G.3h. +5./.9o. +6.5L.30. +b.5F.3i. +1.1+.9a. +2.2n.8E. +3.5G.3i. +5./.9p. +b.3J.5s. +1.22.99. +1.2f.90. +1.4G.4D. +2.1+.9b. +3.3K.5s. +5./.9q. +5.k.9G. +7.r.9B. +2.4G.4E. +5./.9r. +5.k.9H. +6.3L.5s. +7.r.9C. +7.s.9B. +k.98.24. +0.5e.40. +1.24.99. +2.2g.8R. +3.3M.5s. +5./.9s. +5.1V.9h. +5.c.9K. +7.r.9D. +7.s.9C. +b.3G.5u. +b.5F.3m. +1.22.9a. +1.4G.4G. +3.5G.3m. +5./.9t. +5.29.95. +5.2q.8r. +6.3N.5s. +6.4c.53. +7.r.9E. +7.s.9D. +7.u.9B. +9.1+.9c. +k.98.26. +1.26.99. +1.5s.3O. +2.22.9b. +2.2g.8S. +5./.9u. +5.2q.8s. +5.k.9I. +7.s.9E. +7.u.9C. +1.24.9a. +1.2f.91. +2.2n.8F. +2.5s.3P. +5./.9v. +5.k.9J. +6.4c.55. +7.1+.9d. +7.r.9F. +7.u.9D. +2.24.9b. +7.s.9F. +7.u.9E. +b.5F.3q. +e./.9w. +1.26.9a. +1.29.96. +1.2f.92. +1.31.5L. +1.5s.3Q. +3.5G.3q. +5./.9x. +9.22.9c. +b.5F.3r. +1.2f.93. +1.2n.8G. +2.26.9b. +3.5G.3r. +5./.9y. +5.c.9L. +7.u.9F. +8.58.4c. +b.3J.5u. +1.2f.94. +1.2n.8H. +2.1+.9e. +3.3K.5u. +5./.9z. +7.22.9d. +7.r.9G. +9.24.9c. +b.3R.5s. +5.2q.8t. +6.3L.5u. +6.3S.5s. +7.r.9H. +7.s.9G. +1.2n.8I. +1.4G.4M. +1.5s.3T. +3.3M.5u. +6.53.4j. +7.s.9H. +9.26.9c. +a.32.5L. +f.24.9d. +1.2n.8J. +1.4G.4N. +1.5s.3U. +5.1h.9l. +5.2q.8u. +6.3N.5u. +7.u.9G. +0.55.4j. +1.2n.8K. +1.5s.3V. +2.22.9e. +5.2q.8v. +5.c.9M. +5.k.9K. +7.26.9d. +7.r.9I. +7.u.9H. +b.5F.3v. +1.2f.95. +1.33.5L. +1.5s.3W. +2.1d.9n. +2.4G.4P. +3.5G.3v. +5.2q.8w. +5.c.9N. +7.r.9J. +7.s.9I. +1.5s.3X. +2.1d.9o. +2.24.9e. +4.s.9J. +6.5L.34. +1.29.97. +2.1d.9p. +5.1h.9m. +6.4c.5e. +7.u.9I. +8.58.4j. +9.1+.9f. +9.1V.9i. +b.5F.3y. +2.1d.9q. +2.26.9e. +3.5G.3y. +7.u.9J. +1.2f.96. +2.1+.9g. +2.1d.9r. +6.5s.3Y. +b.3R.5u. +2.1d.9s. +2.2g.8W. +2.4G.4T. +4.c.9O. +5.2q.8y. +5.k.9L. +6.3S.5u. +6.4q.53. +1.5s.3Z. +2.1d.9t. +2.4G.4U. +4.c.9P. +9.22.9f. +1.5s.3+. +2.1d.9u. +5.2q.8z. +6.4q.55. +6.5L.37. +1.4G.4W. +1.5s.3/. +2.1d.9v. +2.22.9g. +5.1h.9n. +5.c.9Q. +7.r.9K. +9.24.9f. +1.29.99. +2.1d.9w. +5.1h.9o. +6.5s.40. +7.s.9K. +j.2a.98. +0.5e.4j. +1.5s.41. +2.1d.9x. +2.24.9g. +5.1h.9p. +5.k.9M. +6.58.4q. +9.26.9f. +1.4G.4X. +1.5s.42. +1.98.2b. +2.1d.9y. +5.1h.9q. +5.k.9N. +6.5L.39. +7.1+.9h. +7.u.9K. +1.29.9a. +1.2n.8L. +1.5s.43. +2.1d.9z. +2.26.9g. +5.1h.9r. +5.1t.9l. +b.3G.5F. +0.5u.3Y. +1.2f.97. +1.2n.8M. +1.5s.44. +2.1V.9j. +2.29.9b. +5.1h.9s. +b.3G.5G. +1.2n.8N. +1.5s.45. +2.1V.9k. +5.1h.9t. +7.r.9L. +1.5s.46. +5.1h.9u. +7.22.9h. +7.s.9L. +1.5s.47. +5.1h.9v. +5.1t.9m. +5.29.9c. +5.k.9O. +6.5L.3e. +1.5s.48. +5.k.9P. +7.24.9h. +7.u.9L. +e.1h.9w. +0.5u.40. +1.5s.49. +5.1h.9x. +5.29.9d. +6.4q.5e. +6.5L.3f. +b.3J.5F. +1.2n.8O. +1.5s.4a. +5.1h.9y. +5.2q.8B. +5.k.9Q. +7.26.9h. +7.r.9M. +b.3J.5G. +b.5F.3K. +1.2f.99. +1.2n.8P. +1.5s.4b. +1.6v.2u. +3.5G.3K. +5.1h.9z. +7.r.9N. +7.s.9M. +b.5F.3L. +2.6v.2v. +2.8S.2i. +3.5G.3L. +7.s.9N. +b.5F.3M. +1.2n.8Q. +1.4G.52. +1.8S.2j. +2.29.9e. +3.5G.3M. +5.1t.9n. +6.5L.3g. +7.u.9M. +b.5F.3N. +1.2f.9a. +3.5G.3N. +5./.9A. +5.1t.9o. +6.4c.5s. +7.u.9N. +0.2w.6o. +1.2n.8R. +1.5s.4d. +2.2f.9b. +5.1t.9p. +0.2w.6p. +5.1t.9q. +5.2q.8C. +5.r.9O. +6.5L.3h. +9.1+.9i. +1.2n.8S. +1.4G.56. +1.5s.4e. +5.1t.9r. +5.r.9P. +5.s.9O. +6.5L.3i. +1.2f.9c. +1.4G.57. +1.5s.4f. +5.1t.9s. +5.s.9P. +1.4G.58. +1.5s.4g. +2.c.9R. +3.2x.6o. +5.1t.9t. +5.u.9O. +7.r.9Q. +b.3R.5F. +1.2f.9d. +1.2n.8T. +1.5s.4h. +2.4G.59. +3.2x.6p. +5.1t.9u. +5.29.9f. +5.u.9P. +6.6s.2w. +7.s.9Q. +9.22.9i. +b.3R.5G. +b.5F.3S. +1.5s.4i. +1.6z.2u. +2.4G.5a. +3.5G.3S. +5.1t.9v. +j.2a.9f. +0.2w.6u. +2.29.9g. +2.4G.5b. +2.6z.2v. +5./.9B. +5.1t.9w. +6.5s.4j. +7.u.9Q. +9.24.9i. +0.2w.6v. +1.5s.4k. +3.2y.6o. +5./.9C. +5.1t.9x. +6.4c.5u. +2.2f.9e. +2.5s.4l. +3.2x.6s. +3.2y.6p. +5./.9D. +5.1t.9y. +8.2z.6o. +9.26.9i. +1.2n.8U. +1.4G.5c. +1.5s.4m. +2.6B.2v. +5./.9E. +5.1t.9z. +6.5L.3q. +8.2A.6o. +8.2z.6p. +1.5s.4n. +2.4G.5d. +3.2B.6o. +3.2x.6u. +5.c.9S. +6.5L.3r. +8.2A.6p. +0.2C.6o. +1.5s.4o. +2.1+.9j. +3.2B.6p. +3.2x.6v. +5./.9F. +5.c.9T. +h.2w.6x. +0.2C.6p. +1.5s.4p. +2.1+.9k. +2.4G.5f. +3.2y.6s. +b.5F.3Y. +3.5G.3Y. +5.29.9h. +5.2q.8D. +8.2z.6s. +1.2n.8V. +2.k.9R. +3.2y.6u. +6.4q.5s. +6.6o.2D. +8.2A.6s. +0.5u.4j. +1.2f.9f. +1.5s.4r. +2.22.9j. +2.4G.5i. +3.2B.6s. +3.2x.6x. +3.2y.6v. +5./.9G. +5.c.9U. +6.6p.2D. +8.2z.6u. +g.2E.6o. +1.2n.8W. +1.5s.4s. +1.6g.2T. +2.22.9k. +2.4G.5j. +5./.9H. +6.5L.3v. +6.6s.2C. +8.2A.6u. +8.2z.6v. +g.2E.6p. +1.5s.4t. +2.24.9j. +2.2f.9g. +3.2B.6u. +8.2A.6v. +8.6z.2w. +b.5F.40. +0.2C.6u. +2.24.9k. +3.2B.6v. +3.5G.40. +5.c.9V. +0.2C.6v. +1.5s.4u. +2.26.9j. +3.2y.6x. +5./.9I. +6.5L.3y. +6.6s.2D. +1.2n.8X. +1.5s.4v. +2.26.9k. +5./.9J. +5.c.9W. +8.2z.6x. +f.2w.6B. +g.2E.6s. +0.6u.2D. +1.2n.8Y. +1.5s.4w. +2.4G.5l. +5.1V.9l. +5.k.9S. +8.2A.6x. +8.6z.2x. +0.6v.2D. +5.c.9X. +5.k.9T. +g.2E.6u. +h.2B.6x. +0.2C.6x. +1.5s.4x. +2.r.9R. +5.1h.9A. +6.4q.5u. +a.2F.6o. +g.2E.6v. +0.2w.6E. +1.4G.5n. +1.5s.4y. +2.6J.2v. +2.s.9R. +3.2x.6B. +a.2F.6p. +0.2G.6o. +1.5s.4z. +1.6L.2u. +5.1V.9m. +8.6z.2y. +0.2G.6p. +1.5s.4A. +2.4G.5p. +2.6L.2v. +2.u.9R. +5.k.9U. +8.6z.2z. +h.2D.6x. +1.5s.4B. +1.6g.2X. +2.2H.6o. +2.2n.8Z. +5.29.9i. +8.6z.2A. +h.2E.6x. +1.5s.4C. +2.2H.6p. +3.2x.6E. +3.2y.6B. +5./.9K. +8.6z.2B. +a.2F.6s. +j.2a.9i. +1.2n.8+. +1.5s.4D. +5.k.9V. +8.2z.6B. +8.6z.2C. +b.3G.5L. +1.9i.2b. +2.5s.4E. +6.6s.2G. +7.r.9S. +8.2A.6B. +a.2F.6u. +0.2I.6o. +1.4G.5r. +1.5s.4F. +1.6N.2u. +3.2B.6B. +5.1h.9B. +5.1V.9n. +5.k.9W. +7.r.9T. +7.s.9S. +a.2F.6v. +b.5F.4c. +0.2G.6u. +0.2I.6p. +1.5s.4G. +2.2H.6s. +2.6N.2v. +3.2y.6E. +3.5G.4c. +5.1h.9C. +5.1V.9o. +6.53.53. +7.s.9T. +8.6z.2D. +f.2C.6B. +0.2G.6v. +0.2w.6H. +1.2n.8/. +5.1h.9D. +5.1V.9p. +5.k.9X. +7.u.9S. +8.2z.6E. +8.6z.2E. +0.55.53. +1.6g.2Z. +2.2H.6u. +2.A.9R. +5./.9L. +5.1h.9E. +5.1V.9q. +5.2q.8G. +7.u.9T. +8.2A.6E. +0.2w.6J. +1.5s.4H. +2.2H.6v. +3.2B.6E. +5.1V.9r. +5.2q.8H. +6.6B.2D. +7.r.9U. +a.2F.6x. +b.3J.5L. +0.2C.6E. +1.2n.90. +1.5s.4I. +3.3K.5L. +4.55.55. +5.1h.9F. +5.1V.9s. +6.6s.2I. +7.s.9U. +g.2E.6B. +1.5s.4J. +1.6L.2w. +1.6P.2u. +2.4G.5t. +3.2x.6H. +5.1t.9A. +5.1V.9t. +5.2q.8I. +6.5L.3L. +8.58.53. +a.2J.6o. +h.2G.6x. +0.2I.6u. +1.2f.9i. +1.5s.4K. +2.29.9j. +2.6P.2v. +3.3M.5L. +5.1V.9u. +5.2q.8J. +7.r.9V. +7.u.9U. +a.2J.6p. +0.2I.6v. +1.5s.4L. +2.29.9k. +2.2H.6x. +3.2x.6J. +5./.9M. +5.1V.9v. +5.2q.8K. +6.5L.3N. +6.6E.2D. +7.s.9V. +8.58.55. +b.5F.4j. +1.5s.4M. +3.5G.4j. +5./.9N. +5.1h.9G. +5.1V.9w. +7.r.9W. +g.2E.6E. +0.2K.6o. +0.2w.6M. +1.2n.91. +1.5s.4N. +1.6L.2x. +3.2y.6H. +5.1h.9H. +5.1V.9x. +7.s.9W. +7.u.9V. +8.6z.2F. +e.2g.9c. +0.2K.6p. +1.5s.4O. +5.1V.9y. +6.58.58. +7.r.9X. +8.2z.6H. +a.2J.6s. +1.2n.92. +2.5s.4P. +3.2y.6J. +5.1V.9z. +7.s.9X. +7.u.9W. +8.2A.6H. +8.6z.2G. +h.2I.6x. +1.2n.93. +1.5s.4Q. +3.2B.6H. +5.1h.9I. +7.1+.9l. +8.2z.6J. +a.2F.6B. +a.2J.6u. +0.2C.6H. +0.5e.53. +1.2n.94. +1.5s.4R. +1.6L.2y. +2.2H.6z. +3.2x.6M. +5./.9O. +5.1h.9J. +5.1t.9B. +7.u.9X. +8.2A.6J. +a.2J.6v. +b.3R.5L. +1.5s.4S. +1.6L.2z. +1.6R.2u. +3.2B.6J. +4.2G.6B. +5./.9P. +5.1t.9C. +6.5L.3S. +6.6s.2K. +0.2C.6J. +0.2L.6o. +0.2w.6O. +0.55.5e. +1.6L.2A. +2.6R.2v. +5.1t.9D. +b.5F.4q. +0.2D.6H. +0.2K.6u. +0.2L.6p. +1.6L.2B. +2.2H.6B. +2.5s.4T. +3.5G.4q. +5./.9Q. +5.1t.9E. +7.1+.9m. +7.22.9l. +a.2F.6E. +0.2K.6v. +0.2w.6P. +1.6L.2C. +2.2f.9j. +2.5s.4U. +3.2y.6M. +8.6z.2I. +a.2J.6x. +g.2E.6H. +1.2n.95. +1.5s.4V. +2.2f.9k. +2.4G.5v. +4.2G.6E. +4.6J.2D. +5.1t.9F. +7.24.9l. +8.2z.6M. +8.58.5e. +0.2M.6o. +1.5s.4W. +2.4G.5w. +3.2x.6O. +8.2A.6M. +g.2E.6J. +0.2M.6p. +1.6L.2D. +2.2H.6E. +3.2B.6M. +6.6s.2L. +7.22.9m. +7.26.9l. +f.2I.6B. +0.2C.6M. +1.6L.2E. +3.2x.6P. +5.1h.9K. +h.2K.6x. +0.2L.6u. +1.2n.96. +1.4G.5y. +1.5s.4X. +2.2g.9f. +5.1t.9G. +6.5L.3Y. +7.1+.9n. +f.24.9m. +0.2L.6v. +0.2N.6o. +1.4G.5z. +1.5s.4Y. +3.2y.6O. +5.1t.9H. +7.1+.9o. +8.6z.2J. +0.2I.6E. +0.2N.6p. +0.2O.6o. +6.6M.2D. +6.6s.2M. +7.1+.9p. +7.26.9m. +8.2z.6O. +0.2O.6p. +1.4G.5A. +3.2y.6P. +7.1+.9q. +8.2A.6O. +a.2F.6H. +a.2P.6o. +g.2E.6M. +0.2M.6u. +0.5e.5e. +1.4G.5B. +1.5s.4Z. +3.2B.6O. +3.6R.2w. +5.1t.9I. +7.1+.9r. +7.22.9n. +8.2z.6P. +a.2J.6B. +a.2P.6p. +0.2C.6O. +0.2G.6H. +0.2M.6v. +0.2Q.6o. +5.1h.9L. +5.1t.9J. +6.5L.40. +7.1+.9s. +7.22.9o. +8.2A.6P. +8.6z.2K. +a.2F.6J. +h.2L.6x. +0.2Q.6p. +0.2R.6o. +3.2B.6P. +6.6s.2N. +7.1+.9t. +7.22.9p. +7.24.9n. +0.2C.6P. +0.2G.6J. +0.2R.6p. +1.5s.4+. +1.6L.2F. +2.2H.6H. +6.6s.2O. +7.1+.9u. +7.22.9q. +7.24.9o. +0.2N.6u. +0.2S.6o. +1.4G.5D. +1.5s.4/. +3.6R.2x. +4.2K.6B. +6.6O.2D. +7.1+.9v. +7.22.9r. +7.24.9p. +7.26.9n. +a.2J.6E. +a.2P.6s. +0.2N.6v. +0.2S.6p. +1.2n.97. +1.5s.50. +1.6L.2G. +1.98.2h. +2.2g.9h. +2.2H.6J. +4.2O.6u. +7.1+.9w. +7.22.9s. +7.24.9q. +7.26.9o. +g.2E.6O. +h.2M.6x. +0.2O.6v. +0.6P.2D. +1.5s.51. +1.98.2i. +2.4G.5E. +5.1h.9M. +5.2q.8R. +6.6s.2Q. +7.1+.9x. +7.22.9t. +7.24.9r. +7.26.9p. +a.2P.6u. +0.2I.6H. +1.5s.52. +1.6g.35. +1.98.2j. +2.2H.6L. +5.1h.9N. +6.6s.2R. +7.1+.9y. +7.22.9u. +7.24.9s. +7.26.9q. +8.6z.2L. +a.2F.6M. +a.2P.6v. +g.2E.6P. +0.2K.6E. +0.2Q.6u. +1.6g.36. +1.98.2k. +3.6R.2y. +3.6V.2w. +5.2q.8S. +6.5s.53. +7.1+.9z. +7.22.9v. +7.24.9t. +7.26.9r. +0.2G.6M. +0.2I.6J. +0.2Q.6v. +1.5s.54. +1.98.2l. +5.1t.9K. +6.6s.2S. +7.22.9w. +7.24.9u. +7.26.9s. +8.2z.6R. +f.2R.6u. +h.2N.6x. +2./.9R. +4.55.5s. +7.22.9x. +7.24.9v. +7.26.9t. +8.2A.6R. +f.2L.6B. +f.2R.6v. +h.2O.6x. +0.2S.6u. +1.5s.56. +1.6L.2I. +2.2H.6M. +2.4G.5H. +3.2B.6R. +5.2q.8T. +7.22.9y. +7.24.9w. +7.26.9u. +8.6z.2M. +a.2P.6x. +0.6v.2S. +1.2n.99. +1.5s.57. +3.6R.2C. +3.6V.2x. +5.1h.9O. +7.22.9z. +7.24.9x. +7.26.9v. +1.5s.58. +1.6g.38. +5.1h.9P. +5.1V.9A. +5.29.9l. +7.24.9y. +7.26.9w. +a.2F.6O. +a.2J.6H. +d.2U.6o. +h.2Q.6x. +0.2L.6E. +0.2M.6B. +2.5s.59. +7.24.9z. +7.26.9x. +d.2U.6p. +h.2R.6x. +0.2G.6O. +0.2I.6M. +1.2n.9a. +2.5s.5a. +3.6R.2D. +5.1h.9Q. +5.1t.9L. +6.4c.5L. +7.26.9y. +8.6z.2N. +a.2F.6P. +a.2J.6J. +b.6+.2w. +0.2S.6x. +2.2n.9b. +2.5s.5b. +3.6V.2y. +5.2q.8U. +7.26.9z. +8.6z.2O. +g.2E.6R. +0.2G.6P. +0.2K.6H. +0.5u.53. +1.6g.3a. +1.6L.2J. +2.2H.6O. +5./.9S. +5.29.9m. +6.6v.2T. +8.2z.6V. +8.6z.2P. +0.2M.6E. +0.2N.6B. +1.6g.3b. +1.7d.2u. +5./.9T. +8.2A.6V. +a.2V.6o. +d.2U.6s. +d.71.2w. +0.2K.6J. +0.2O.6B. +0.5u.55. +1.2n.9c. +1.5s.5c. +1.6g.3c. +2.2g.9i. +2.2H.6P. +3.2B.6V. +3.2W.6o. +8.6z.2Q. +a.2V.6p. +b.6+.2x. +1.6g.3d. +2.5s.5d. +3.2W.6p. +3.6V.2C. +5.1t.9M. +8.6z.2R. +a.2P.6B. +d.2U.6u. +1.2n.9d. +1.6L.2K. +1.7g.2u. +5.1t.9N. +5.1V.9B. +6.5s.5e. +7.6x.2T. +a.2J.6M. +d.2U.6v. +f.2I.6O. +0.2N.6E. +2.5s.5f. +2.7g.2v. +5./.9U. +5.1V.9C. +6.58.5u. +8.6z.2S. +d.71.2x. +f.2Q.6B. +0.2I.6P. +0.2L.6H. +0.2O.6E. +1.5s.5g. +3.6V.2D. +5.1V.9D. +5.29.9n. +5.2q.8W. +6.5L.4j. +a.2V.6s. +b.6+.2y. +f.2R.6B. +1.2f.9l. +1.5s.5h. +3.2W.6s. +3.6R.2F. +5.1V.9E. +5.29.9o. +8.2z.6+. +a.2P.6E. +a.2Y.6o. +g.2E.6V. +0.2K.6M. +0.2L.6J. +2.2n.9e. +2.5s.5i. +5./.9V. +5.29.9p. +6.6B.2S. +8.2A.6+. +a.2V.6u. +a.2Y.6p. +d.2U.6x. +0.2Q.6E. +2.5s.5j. +3.2W.6u. +3.6R.2G. +5.1t.9O. +5.1V.9F. +5.29.9q. +a.2V.6v. +b.6+.2B. +d.71.2y. +0.2M.6H. +0.2R.6E. +1.6L.2L. +2.1d.9R. +2.4G.5J. +3.2W.6v. +5./.9W. +5.1t.9P. +5.29.9r. +5.2q.8X. +8.6z.2T. +a.2J.6O. +b.6+.2C. +d.71.2z. +1.2f.9m. +1.5s.5k. +2.2H.6R. +5.29.9s. +5.2q.8Y. +7.6v.2X. +d.71.2A. +0.2M.6J. +0.2S.6E. +2.4G.5K. +5./.9X. +5.1t.9Q. +5.29.9t. +a.2J.6P. +a.2Y.6s. +d.71.2B. +0.5u.5e. +5.1V.9G. +5.29.9u. +d.71.2C. +h.2V.6x. +m.6+.2D. +0.2K.6O. +0.2L.6M. +0.2N.6H. +1.6L.2M. +2.5s.5l. +5.1V.9H. +5.29.9v. +6.5L.4q. +a.2Y.6u. +b.6+.2E. +d.2U.6z. +h.2W.6x. +1.2n.9f. +3.6R.2I. +3.6V.2F. +4.2O.6H. +5.29.9w. +7.6x.2X. +a.2Y.6v. +0.2K.6P. +0.2N.6J. +1.6g.3k. +5.29.9x. +8.2+.6o. +a.2P.6H. +d.71.2D. +0.2O.6J. +1.2f.9n. +1.5s.5n. +2.2n.9g. +3.6V.2G. +5.1V.9I. +5.29.9y. +8.2+.6p. +d.2U.6B. +d.71.2E. +0.2M.6M. +0.2Q.6H. +1.2f.9o. +1.5s.5o. +1.6L.2N. +2.1h.9R. +5.1V.9J. +5.29.9z. +9.6v.2Z. +a.2P.6J. +0.2R.6H. +1.2f.9p. +1.6g.3n. +1.6L.2O. +2.2/.6o. +2.2H.6V. +2.5s.5p. +5.2q.8+. +7.1+.9A. +8.6z.2V. +a.2Y.6x. +0.2L.6O. +0.2Q.6J. +1.2f.9q. +1.5s.5q. +1.6g.3o. +1.6L.2P. +2.2/.6p. +8.6z.2W. +0.2S.6H. +1.2f.9r. +1.6g.3p. +1.7t.2u. +3.6R.2J. +8.2+.6s. +8.6z.2X. +d.2U.6E. +f.2R.6J. +0.2L.6P. +0.2N.6M. +1.2f.9s. +1.6L.2Q. +2.7t.2v. +9.6x.2Z. +a.2V.6B. +b.6+.2F. +0.2O.6M. +0.2S.6J. +0.30.6o. +1.2f.9t. +1.6L.2R. +3.2W.6B. +3.6V.2I. +5.2q.8/. +7.22.9A. +8.2+.6u. +0.30.6p. +1.2f.9u. +1.5s.5r. +1.6g.3s. +2.2/.6s. +8.2+.6v. +a.2P.6M. +b.5F.53. +b.6+.2G. +f.2M.6O. +1.2f.9v. +1.6L.2S. +1.7v.2u. +3.5G.53. +3.6R.2K. +5.1h.9S. +6.5s.5s. +7.24.9A. +8.6z.2Y. +d.71.2F. +0.2M.6P. +0.2Q.6M. +1.2f.9w. +2.2/.6u. +2.2H.6+. +2.7v.2v. +5.1h.9T. +5.1V.9K. +a.2V.6E. +b.5F.55. +0.2R.6M. +1.2f.9x. +1.6g.3t. +2.2/.6v. +3.2W.6E. +3.5G.55. +7.1+.9B. +7.26.9A. +d.71.2G. +0.2N.6O. +1.2f.9y. +1.6g.3u. +6.6s.30. +7.1+.9C. +8.2+.6x. +8.6z.2Z. +a.2Y.6B. +0.2O.6O. +0.2S.6M. +1.2f.9z. +2.2H.71. +3.6V.2J. +7.1+.9D. +8.58.5F. +0.2N.6P. +0.30.6u. +1.6g.3w. +1.6L.2T. +2.5s.5t. +5.1h.9U. +7.1+.9E. +8.58.5G. +a.2P.6O. +b.6+.2I. +d.2U.6H. +0.2O.6P. +0.30.6v. +1.6g.3x. +2.2/.6x. +3.6R.2L. +7.22.9B. +0.2Q.6O. +1.31.6o. +1.9i.2h. +2.1t.9R. +5.1V.9L. +7.1+.9F. +7.22.9C. +a.2P.6P. +a.2Y.6E. +d.2U.6J. +0.2w.7t. +1.31.6p. +1.7A.2u. +1.9i.2i. +2.6g.3z. +3.6V.2K. +5.1h.9V. +7.22.9D. +7.24.9B. +d.71.2I. +f.2R.6O. +0.2Q.6P. +0.5u.5s. +1.6g.3A. +1.6L.2U. +1.9i.2j. +2.4G.5M. +2.7A.2v. +7.22.9E. +7.24.9C. +8.2+.6z. +0.2R.6P. +0.2S.6O. +1.9i.2k. +2.4G.5N. +3.6R.2M. +5.1h.9W. +7.24.9D. +7.26.9B. +a.2V.6H. +h.30.6x. +1.6g.3B. +1.6N.2T. +1.9i.2l. +3.2W.6H. +7.1+.9G. +7.22.9F. +7.24.9E. +7.26.9C. +a.32.6o. +b.6+.2J. +0.6P.2S. +1.31.6s. +1.4G.5O. +1.6g.3C. +2.2/.6z. +3.2x.7t. +5.1h.9X. +5.1V.9M. +7.1+.9H. +7.26.9D. +8.2+.6B. +a.2V.6J. +a.32.6p. +b.5F.5e. +1.2n.9i. +1.4G.5P. +2.6g.3D. +3.2W.6J. +3.5G.5e. +5.1V.9N. +7.24.9F. +7.26.9E. +d.2U.6M. +1.31.6u. +1.33.6o. +1.6g.3E. +1.6L.2V. +3.6R.2N. +3.6V.2L. +5.1t.9S. +d.71.2J. +0.34.6o. +1.31.6v. +1.33.6p. +1.4G.5Q. +1.6g.3F. +1.6L.2W. +2.2/.6B. +3.6R.2O. +5.1t.9T. +5.2q.95. +7.1+.9I. +7.22.9G. +7.26.9F. +b.6+.2K. +0.34.6p. +1.6L.2X. +3.2y.7t. +3.6R.2P. +7.1+.9J. +7.22.9H. +8.2+.6E. +8.6z.30. +a.2Y.6H. +a.32.6s. +5.6P.2T. +7.24.9G. +8.2z.7t. +0.5u.5u. +2.5s.5v. +3.6R.2Q. +3.6V.2M. +5.1V.9O. +7.24.9H. +8.2A.7t. +a.2V.6M. +a.2Y.6J. +a.32.6u. +d.71.2K. +1.31.6x. +1.33.6s. +2.2/.6E. +2.5s.5w. +3.2B.7t. +3.2W.6M. +3.6R.2R. +5.1t.9U. +5.1V.9P. +7.22.9I. +7.26.9G. +a.32.6v. +d.2U.6O. +f.30.6B. +0.2C.7t. +0.7A.2w. +1.5s.5x. +1.6L.2Y. +6.6s.34. +7.22.9J. +7.26.9H. +1.33.6u. +1.6g.3H. +3.6R.2S. +5.1V.9Q. +5.29.9A. +7.24.9I. +b.6+.2L. +c.37.6o. +d.2U.6P. +0.34.6u. +1.33.6v. +1.5s.5y. +1.6g.3I. +1.6N.2X. +3.6V.2N. +5.1t.9V. +c.37.6p. +f.24.9J. +0.2D.7t. +0.30.6E. +0.34.6v. +1.5s.5z. +1.6L.2Z. +2.4G.5S. +3.6V.2O. +7.26.9I. +a.32.6x. +2.2n.9j. +3.2x.7A. +3.6V.2P. +5.1t.9W. +7.1+.9K. +7.26.9J. +9.6v.35. +a.2V.6O. +a.2Y.6M. +d.71.2L. +g.2E.7t. +0.39.6o. +1.31.6z. +1.5s.5A. +2.2n.9k. +3.2W.6O. +8.2+.6H. +9.6v.36. +b.6+.2M. +0.39.6p. +1.33.6x. +1.5s.5B. +3.6V.2Q. +5.1t.9X. +6.6R.2T. +a.2V.6P. +c.37.6s. +3.2W.6P. +3.6V.2R. +8.2+.6J. +h.34.6x. +1.31.6B. +1.6g.3O. +2.2/.6H. +3.2y.7A. +5.6P.2X. +7.22.9K. +9.6x.35. +c.37.6u. +d.71.2M. +1.5s.5C. +1.6L.2+. +1.6N.2Z. +2.6g.3P. +3.6V.2S. +5.29.9B. +8.2z.7A. +8.6z.32. +9.6x.36. +b.6+.2N. +f.37.6v. +1.4G.5T. +1.5s.5D. +2.2/.6J. +5.29.9C. +6.6o.3e. +6.6s.39. +7.1+.9L. +7.24.9K. +8.2A.7A. +9.6v.38. +a.2Y.6O. +b.6+.2O. +d.2U.6R. +1.6g.3Q. +2.2g.9n. +3.2B.7A. +5.29.9D. +6.6p.3e. +b.6+.2P. +0.30.6H. +0.7A.2C. +1.2f.9A. +1.31.6E. +1.33.6z. +2.2/.6L. +2.2g.9o. +2.5s.5E. +5.29.9E. +7.26.9K. +a.2F.7t. +a.2Y.6P. +a.32.6B. +c.3f.6o. +d.71.2N. +f.39.6u. +0.39.6v. +2.2g.9p. +8.2+.6M. +8.6z.34. +b.5F.5s. +b.6+.2Q. +c.3f.6p. +d.71.2O. +h.37.6x. +0.2G.7t. +0.30.6J. +2.2g.9q. +3.5G.5s. +5.29.9F. +7.22.9L. +8.6z.35. +9.6v.3a. +9.6x.38. +b.6+.2R. +d.71.2P. +0.7A.2D. +1.33.6B. +1.6g.3T. +2.2g.9r. +3.6R.2V. +6.6s.3e. +7.1+.9M. +8.6z.36. +9.6P.2Z. +9.6v.3b. +0.34.6B. +1.6g.3U. +1.6L.30. +2.2/.6M. +2.2g.9s. +2.2H.7t. +3.6R.2W. +6.6o.3g. +7.1+.9N. +7.24.9L. +9.6v.3c. +a.32.6E. +b.6+.2S. +d.71.2Q. +g.2E.7A. +0.6u.3e. +1.6g.3V. +2.2g.9t. +2.5s.5H. +4.3f.6s. +6.6p.3g. +6.6R.2X. +9.6v.3d. +d.71.2R. +h.39.6x. +0.6v.3e. +1.5s.5I. +1.6g.3W. +2.2g.9u. +5.29.9G. +7.26.9L. +9.6x.3a. +d.2U.6V. +1.33.6E. +1.6g.3X. +2.2g.9v. +5.29.9H. +6.6o.3h. +7.22.9M. +8.2+.6O. +8.6z.37. +9.6x.3b. +c.3f.6u. +d.71.2S. +0.2I.7t. +0.30.6M. +0.34.6E. +1.2f.9B. +2.2g.9w. +6.3i.6o. +6.6p.3h. +7.22.9N. +8.6z.38. +9.6x.3c. +c.3f.6v. +1.2f.9C. +1.31.6H. +2.1V.9R. +2.2g.9x. +3.6R.2Y. +5.1+.9O. +6.3i.6p. +6.5L.53. +6.6s.3g. +7.24.9M. +8.2+.6P. +9.6x.3d. +1.2f.9D. +2.2/.6O. +2.2g.9y. +5.1+.9P. +5.29.9I. +5.2q.9c. +7.24.9N. +b.5F.5u. +c.37.6B. +h.3e.6x. +0.6u.3g. +1.2f.9E. +1.31.6J. +2.2g.9z. +3.5G.5u. +3.6V.2V. +5.29.9J. +6.5L.55. +7.26.9M. +8.6z.39. +0.6v.3g. +1.6g.3Z. +2.2/.6P. +3.6V.2W. +5.2q.9d. +6.3m.6o. +6.6R.2Z. +6.6s.3h. +7.1+.9Q. +7.26.9N. +8.6z.3a. +a.2F.7A. +h.3f.6x. +1.2f.9F. +1.6g.3+. +1.6L.31. +2.4G.5W. +5.22.9O. +6.3i.6s. +6.3m.6p. +8.6z.3b. +a.32.6H. +d.2U.6+. +0.30.6O. +0.3h.6u. +0.7A.2G. +1.6g.3/. +2.4G.5X. +5.22.9P. +8.58.5L. +8.6z.3c. +a.2J.7t. +f.37.6E. +f.39.6B. +0.6v.3h. +2.4G.5Y. +5.24.9O. +6.3i.6u. +8.6z.3d. +a.32.6J. +0.30.6P. +1.33.6H. +1.4G.5Z. +1.6g.41. +2.2H.7A. +5.1V.9S. +5.24.9P. +6.3i.6v. +6.6o.3q. +7.22.9Q. +8.6z.3e. +d.71.2U. +h.3g.6x. +1.2f.9G. +1.31.6M. +1.6g.42. +1.6L.32. +2.4G.5+. +3.6V.2Y. +5.1V.9T. +5.26.9O. +6.3r.6o. +6.6p.3q. +6.6s.3m. +6.6v.3j. +f.34.6H. +0.2K.7t. +0.39.6E. +1.2f.9H. +1.33.6J. +1.6g.43. +5.26.9P. +6.3r.6p. +7.24.9Q. +8.2+.6R. +8.6z.3f. +9.6v.3k. +b.6+.2V. +0.34.6J. +1.6g.44. +5.29.9K. +6.3m.6u. +6.6B.3e. +6.6v.3l. +b.6+.2W. +h.3h.6x. +0.7A.2I. +1.6g.45. +1.6L.33. +2.6J.35. +6.3m.6v. +7.26.9Q. +h.3i.6x. +1.2f.9I. +1.6g.46. +1.6L.34. +1.6v.3n. +2.2/.6R. +2.5s.5J. +5.1V.9U. +6.6s.3q. +7.6x.3j. +a.32.6M. +c.3f.6B. +d.71.2V. +1.2f.9J. +1.6g.47. +1.6L.35. +2.4G.5/. +6.5L.5e. +6.6s.3r. +6.6v.3o. +8.6z.3g. +9.6x.3k. +d.71.2W. +0.6u.3q. +1.31.6O. +1.6g.48. +1.6L.36. +2.4G.60. +2.5s.5K. +5.2q.9f. +6.6E.3e. +6.6o.3v. +7.6x.3l. +9.6v.3p. +f.37.6H. +0.2L.7t. +0.6v.3q. +1.33.6M. +1.6g.49. +1.7d.2T. +5.1V.9V. +6.3r.6u. +6.6p.3v. +b.6+.2Y. +h.3m.6x. +0.34.6M. +1.2n.9l. +1.31.6P. +1.6g.4a. +3.6R.30. +5.29.9L. +6.3r.6v. +6.6B.3g. +8.6z.3h. +c.37.6J. +c.3f.6E. +1.6g.4b. +1.6v.3s. +2.6J.38. +5.1V.9W. +6.3y.6o. +7.6x.3o. +8.2+.6V. +8.6z.3i. +a.2J.7A. +1.6L.37. +1.7g.2T. +6.3y.6p. +8.6z.3j. +9.6x.3p. +a.32.6O. +d.71.2Y. +f.39.6H. +0.2M.7t. +1.6L.38. +1.6N.35. +2.4G.63. +5.1V.9X. +6.6B.3h. +6.6s.3v. +8.6z.3k. +h.3q.6x. +0.39.6J. +1.2n.9m. +1.6N.36. +2.2/.6V. +6.3i.6B. +6.6E.3g. +8.6z.3l. +9.6v.3t. +a.32.6P. +h.3r.6x. +0.6u.3v. +0.7A.2K. +1.2f.9K. +1.33.6O. +1.6g.4d. +5.29.9M. +6.6v.3u. +8.6z.3m. +0.34.6O. +0.6v.3v. +1.6L.39. +1.6z.3n. +2.4G.64. +5.29.9N. +6.6s.3y. +c.37.6M. +0.2N.7t. +0.3h.6E. +1.33.6P. +1.6g.4e. +1.6L.3a. +2.1+.9R. +4.3e.6H. +5.2q.9h. +8.6z.3o. +9.6v.3w. +0.2O.7t. +0.34.6P. +1.6g.4f. +1.6L.3b. +3.6V.30. +6.3i.6E. +6.3m.6B. +6.3y.6u. +8.2+.6+. +8.6z.3p. +9.6v.3x. +9.6x.3t. +1.31.6R. +1.6g.4g. +1.6L.3c. +1.6N.38. +4.6J.3e. +6.3y.6v. +7.6x.3u. +8.6z.3q. +9.6P.35. +a.2P.7t. +b.5F.5F. +c.3f.6H. +0.39.6M. +1.2n.9n. +1.6g.4h. +1.6L.3d. +1.7d.2X. +2.4G.66. +2.6v.3z. +8.6z.3r. +9.6P.36. +b.5F.5G. +h.3v.6x. +0.2Q.7t. +0.7A.2L. +1.2f.9L. +1.2n.9o. +1.6g.4i. +1.6L.3e. +1.6z.3s. +2.2/.6+. +2.22.9R. +3.5G.5G. +4.3f.6J. +5.29.9O. +9.6v.3A. +9.6x.3w. +b.3G.6o. +d.71.2+. +0.2R.7t. +1.2n.9p. +2.4G.68. +5.29.9P. +6.3m.6E. +6.6B.3q. +9.6x.3x. +b.3G.6p. +c.37.6O. +j.2a.9O. +1.2n.9q. +1.6g.4k. +1.6L.3f. +1.6N.3a. +1.7g.2X. +2.24.9R. +3.6R.32. +4.3g.6H. +6.3r.6B. +7.6v.3B. +h.3y.6x. +j.2a.9P. +0.2S.7t. +1.2n.9r. +1.6N.3b. +2.2/.71. +2.2g.9A. +2.6g.4l. +5.29.9Q. +5.6v.3C. +7.1+.9S. +8.6z.3t. +c.37.6P. +0.6J.3g. +0.7A.2M. +1.2n.9s. +1.4G.69. +1.6g.4m. +1.6N.3c. +2.26.9R. +2.6v.3D. +6.6M.3e. +6.6P.38. +7.1+.9T. +8.6z.3u. +9.6x.3A. +b.6+.30. +j.2a.9Q. +0.3h.6H. +0.3q.6E. +1.2f.9M. +1.2n.9t. +1.33.6R. +1.6g.4n. +1.6N.3d. +8.6z.3v. +9.6v.3E. +b.3G.6s. +f.39.6O. +1.2f.9N. +1.2n.9u. +1.31.6V. +1.6g.4o. +1.6L.3g. +1.7d.2Z. +3.6R.34. +6.3i.6H. +6.3r.6E. +7.6x.3B. +8.6z.3w. +9.6v.3F. +b.3J.6o. +c.3f.6M. +0.3h.6J. +1.2n.9v. +1.6g.4p. +3.3K.6o. +4.6x.3C. +6.5L.5s. +7.22.9S. +8.6z.3x. +9.6R.35. +b.3G.6u. +b.3J.6p. +d.71.30. +f.39.6P. +0.7A.2N. +1.2n.9w. +3.3K.6p. +6.3i.6J. +6.3L.6o. +6.6B.3v. +6.6P.3a. +6.6R.36. +7.1+.9U. +7.22.9T. +8.6z.3y. +9.7t.2T. +b.3G.6v. +0.2w.7V. +0.7A.2O. +1.2n.9x. +1.6L.3h. +1.7g.2Z. +2.5s.5M. +2.6z.3z. +3.3M.6o. +6.3L.6p. +6.6P.3b. +7.24.9S. +9.6x.3E. +1.2n.9y. +1.6g.4r. +1.6L.3i. +2.5s.5N. +3.3M.6p. +3.6V.32. +5.2q.9i. +6.3m.6H. +6.3N.6o. +6.6M.3g. +6.6O.3e. +7.24.9T. +8.6z.3A. +9.6P.3c. +9.6x.3F. +a.2P.7A. +1.2f.9O. +1.2n.9z. +1.6g.4s. +1.6L.3j. +6.3N.6p. +6.3y.6B. +6.6P.3d. +7.1+.9V. +7.26.9S. +b.3J.6s. +0.6P.3e. +0.7A.2Q. +1.2f.9P. +1.5s.5O. +1.6g.4t. +1.6L.3k. +2.6B.3z. +3.3K.6s. +3.6R.37. +6.3m.6J. +6.6E.3v. +6.6v.3H. +7.22.9U. +7.26.9T. +8.6z.3B. +c.3f.6O. +d.2U.7t. +d.7v.2T. +h.3G.6x. +0.7A.2R. +1.31.6+. +1.33.6V. +1.5s.5P. +1.6L.3l. +3.2x.7V. +6.3L.6s. +6.6M.3h. +6.6R.38. +7.1+.9W. +7.6v.3I. +8.6z.3C. +b.3J.6u. +0.3q.6H. +1.2f.9Q. +1.6g.4u. +1.6L.3m. +2.6z.3D. +3.3K.6u. +3.3M.6s. +3.6V.34. +6.3i.6M. +7.24.9U. +b.3J.6v. +c.3f.6P. +0.7A.2S. +1.4G.6c. +1.5s.5Q. +1.6g.4v. +1.6L.3n. +3.3K.6v. +6.3L.6u. +6.3r.6H. +6.3y.6E. +6.6s.3N. +7.1+.9X. +7.22.9V. +8.6z.3E. +0.6J.3q. +1.31.71. +1.6g.4w. +1.6L.3o. +3.3M.6u. +3.6R.39. +6.3L.6v. +6.5L.5u. +6.6O.3g. +7.26.9U. +7.6x.3H. +8.6z.3F. +b.3R.6o. +1.6L.3p. +1.6N.3j. +2.6B.3D. +3.2y.7V. +3.3M.6v. +6.3N.6u. +6.3r.6J. +6.3S.6o. +6.6R.3a. +7.22.9W. +7.24.9V. +7.6x.3I. +a.2V.7t. +b.3R.6p. +b.6+.32. +0.6P.3g. +0.6v.3N. +1.5s.5R. +1.6g.4x. +1.6L.3q. +1.6N.3k. +3.2W.7t. +6.3m.6M. +6.3S.6p. +8.2z.7V. +8.6z.3G. +9.6R.3b. +h.3J.6x. +0.3h.6O. +1.6g.4y. +1.6L.3r. +1.6N.3l. +4.7t.2X. +6.6R.3c. +6.6v.3O. +7.22.9X. +7.26.9V. +8.2A.7V. +f.24.9W. +h.3K.6x. +0.2w.7Z. +1.33.6+. +1.6g.4z. +1.6L.3s. +2.2g.9G. +2.6v.3P. +3.2B.7V. +3.6V.37. +6.3i.6O. +6.6R.3d. +9.7A.2T. +d.71.32. +h.3L.6x. +0.6P.3h. +1.6g.4A. +1.6N.3n. +2.2g.9H. +3.6R.3e. +6.3v.6H. +7.26.9W. +b.3G.6B. +b.3R.6s. +b.6+.34. +f.24.9X. +f.2C.7V. +h.3M.6x. +1.6g.4B. +1.6N.3o. +6.3i.6P. +6.3S.6s. +6.6M.3q. +7.6v.3Q. +8.6z.3H. +h.3N.6x. +1.33.71. +1.6g.4C. +1.6L.3t. +1.6N.3p. +2.29.9R. +2.4G.6d. +2.5s.5S. +3.6R.3f. +4.6J.3v. +5.6P.3j. +6.3r.6M. +7.26.9X. +7.6x.3O. +8.6z.3I. +a.2Y.7t. +b.3R.6u. +d.7v.2X. +0.7V.2D. +1.6g.4D. +1.6L.3u. +2.2g.9I. +2.4G.6e. +2.6J.3w. +3.2x.7Z. +3.6V.39. +6.3m.6O. +6.3S.6u. +6.3y.6H. +6.6P.3k. +8.6z.3J. +b.3R.6v. +d.2U.7A. +d.71.34. +1.6L.3v. +2.6g.4E. +2.6J.3x. +5.6P.3l. +6.3S.6v. +6.6o.3Y. +8.6z.3K. +b.3G.6E. +g.2E.7V. +1.6g.4F. +1.6L.3w. +1.6N.3s. +4.7t.2Z. +6.3m.6P. +6.3y.6J. +6.6p.3Y. +7.6v.3T. +7.6x.3Q. +8.6z.3L. +1.6g.4G. +1.6L.3x. +1.6P.3n. +2.6J.3z. +3.6R.3g. +7.6v.3U. +8.6z.3M. +b.3J.6B. +b.6+.37. +1.6L.3y. +2.6J.3A. +3.2y.7Z. +3.3K.6B. +5.6P.3o. +6.6O.3q. +7.6v.3V. +8.6z.3N. +h.3R.6x. +1.6N.3t. +2.6L.3z. +3.6V.3e. +6.3L.6B. +6.3r.6O. +6.6M.3v. +6.6v.3W. +8.2z.7Z. +8.6z.3O. +9.6P.3p. +a.2V.7A. +h.3S.6x. +0.6P.3q. +1.5s.5T. +1.6g.4H. +1.6L.3A. +1.6N.3u. +2.6z.3P. +3.2W.7A. +3.3M.6B. +3.6R.3h. +5.29.9S. +6.6o.40. +6.6s.3Y. +7.6v.3X. +7.6x.3T. +8.2A.7Z. +d.71.37. +d.7v.2Z. +1.6g.4I. +3.2B.7Z. +3.6R.3i. +3.6V.3f. +5.29.9T. +6.3r.6P. +6.6B.3N. +6.6p.40. +7.6x.3U. +9.7A.2X. +b.6+.39. +e.3J.6E. +0.6u.3Y. +1.6g.4J. +1.6L.3B. +1.6N.3w. +1.6P.3s. +3.3K.6E. +6.3y.6M. +6.6R.3j. +7.6x.3V. +8.2+.7t. +8.6z.3Q. +f.2C.7Z. +0.6v.3Y. +1.6g.4K. +1.6L.3C. +1.6N.3x. +2.2g.9K. +2.6B.3P. +6.3L.6E. +7.6x.3W. +9.6R.3k. +a.2F.7V. +1.4G.6i. +1.6g.4L. +2.2f.9R. +2.6L.3D. +3.3M.6E. +6.6R.3l. +7.6x.3X. +8.6z.3R. +b.3G.6H. +d.71.39. +0.2G.7V. +1.4G.6j. +1.6g.4M. +1.6L.3E. +2.2/.7t. +2.6N.3z. +3.6R.3m. +3.6V.3g. +5.29.9U. +6.3N.6E. +6.6O.3v. +6.6P.3t. +6.6s.40. +7.6v.3Z. +8.6z.3S. +a.2Y.7A. +f.7Z.2D. +1.6g.4N. +1.6L.3F. +1.6N.3A. +1.6R.3n. +2.4G.6k. +5.6P.3u. +6.6v.3+. +8.6z.3T. +b.3G.6J. +b.6+.3e. +e.2E.7Z. +0.6P.3v. +0.6u.40. +1.4G.6l. +1.6g.4O. +1.7d.35. +2.2H.7V. +6.6R.3o. +6.6v.3/. +8.6z.3U. +b.3R.6B. +h.6x.3Y. +0.6v.40. +1.6L.3G. +1.6N.3B. +1.7d.36. +2.6g.4P. +3.6V.3h. +5.29.9V. +6.3S.6B. +6.3y.6O. +6.6P.3w. +8.6z.3V. +9.6R.3p. +9.7A.2Z. +b.6+.3f. +0.30.7t. +1.2n.9A. +1.5s.5U. +1.6g.4Q. +1.6N.3C. +3.6R.3q. +3.6V.3i. +7.6v.41. +7.6x.3Z. +8.6z.3W. +9.6P.3x. +d.71.3e. +j.2a.9V. +1.6g.4R. +1.7g.35. +2.4G.6m. +2.6N.3D. +3.6R.3r. +5.29.9W. +6.3y.6P. +6.6v.42. +7.6x.3+. +8.6z.3X. +b.3J.6H. +b.5F.5L. +1.5s.5V. +1.6g.4S. +1.6N.3E. +1.6R.3s. +1.6v.43. +1.7g.36. +2.6J.3I. +2.6P.3z. +3.3K.6H. +3.5G.5L. +6.85.2w. +7.6x.3/. +b.3R.6E. +d.71.3f. +f.2I.7V. +1.6L.3H. +1.6N.3F. +5.29.9X. +6.3L.6H. +6.3S.6E. +6.6P.3A. +6.6v.44. +b.3G.6M. +b.3J.6J. +b.6+.3g. +h.6x.40. +1.6L.3I. +1.7d.38. +2.6g.4T. +3.3K.6J. +3.3M.6H. +3.6V.3m. +6.6v.45. +7.6x.41. +8.6z.3Y. +1.6L.3J. +2.6g.4U. +5.6P.3B. +6.3L.6J. +6.3N.6H. +6.4c.6o. +6.6R.3t. +7.6v.46. +7.6x.42. +8.2+.7A. +a.2F.7Z. +1.6g.4V. +1.6L.3K. +2.5s.5W. +3.2x.85. +3.3M.6J. +5.6P.3C. +6.4c.6p. +6.6R.3u. +6.6v.47. +8.6z.3Z. +b.6+.3h. +d.71.3g. +1.6g.4W. +1.6L.3L. +1.7g.38. +2.5s.5X. +2.6P.3D. +3.6R.3v. +4.2G.7Z. +6.3N.6J. +6.6B.3Y. +6.6v.48. +7.6x.44. +8.6z.3+. +b.6+.3i. +1.2n.9B. +1.6L.3M. +1.7d.3a. +2.2/.7A. +2.5s.5Y. +2.6J.3O. +3.6V.3q. +6.6P.3E. +7.6v.49. +7.6x.45. +8.6z.3/. +9.6R.3w. +a.2J.7V. +1.2n.9C. +1.31.7t. +1.5s.5Z. +1.6L.3N. +1.6N.3H. +1.6v.4a. +1.7d.3b. +2.2H.7Z. +2.6J.3P. +3.6V.3r. +6.6P.3F. +7.6x.46. +8.6z.40. +9.6R.3x. +b.3G.6O. +b.3J.6M. +d.71.3h. +1.2n.9D. +1.6g.4X. +1.6L.3O. +1.6N.3I. +1.6v.4b. +1.7d.3c. +2.5s.5+. +3.2y.85. +3.3K.6M. +3.6R.3y. +6.4c.6s. +7.6x.47. +8.6z.41. +d.71.3i. +1.2n.9E. +1.6g.4Y. +1.7d.3d. +1.7g.3a. +2.6J.3Q. +2.6L.3P. +2.6R.3z. +6.3L.6M. +6.6E.3Y. +7.6x.48. +8.2z.85. +8.6z.42. +b.3G.6P. +b.3R.6H. +b.6+.3m. +0.2K.7V. +0.7A.30. +1.6z.43. +1.7g.3b. +3.3M.6M. +6.3S.6H. +6.4c.6u. +6.6B.40. +6.6R.3A. +7.6x.49. +8.2A.85. +1.2n.9F. +1.6L.3Q. +1.7g.3c. +3.2B.85. +6.3N.6M. +6.4c.6v. +6.6o.4j. +8.6z.44. +a.32.7t. +b.3R.6J. +f.2I.7Z. +1.6g.4Z. +1.7g.3d. +6.3S.6J. +6.6p.4j. +6.6R.3B. +6.85.2C. +7.6v.4d. +8.6z.45. +d.71.3m. +1.6L.3R. +2.5s.5/. +3.6V.3v. +5.2q.9l. +5.6P.3H. +5.6R.3C. +8.6z.46. +b.3J.6O. +b.6+.3q. +1.33.7t. +1.6L.3S. +1.6N.3O. +2.5s.60. +2.6R.3D. +3.3K.6O. +5.6P.3I. +6.6E.40. +7.6v.4e. +8.6z.47. +b.6+.3r. +0.34.7t. +1.2n.9G. +1.5s.61. +1.6g.4+. +1.6L.3T. +2.6J.3V. +2.6N.3P. +6.3L.6O. +6.6v.4f. +6.85.2D. +8.6z.48. +9.6R.3E. +b.3J.6P. +h.4c.6x. +0.2L.7V. +1.2n.9H. +1.5s.62. +1.6g.4/. +1.6L.3U. +3.3K.6P. +3.3M.6O. +3.6V.3y. +4.7t.35. +6.6s.4j. +7.6v.4g. +7.6x.4d. +8.6z.49. +9.6R.3F. +d.71.3q. +g.2E.85. +1.6g.50. +1.6L.3V. +1.6N.3Q. +1.6z.4a. +2.6J.3X. +4.7t.36. +5.2q.9m. +6.3L.6P. +6.3N.6O. +7.6v.4h. +a.2J.7Z. +b.3R.6M. +d.71.3r. +0.6H.3Y. +0.6u.4j. +1.6g.51. +1.6L.3W. +1.6v.4i. +1.6z.4b. +3.3M.6P. +6.3S.6M. +7.6x.4e. +b.3G.6R. +0.6P.3N. +0.6v.4j. +1.2n.9I. +1.31.7A. +1.6g.52. +1.6L.3X. +2.5s.63. +6.4q.6o. +7.6x.4f. +0.6J.3Y. +1.2n.9J. +5.6P.3O. +6.4q.6p. +6.6v.4k. +7.6x.4g. +b.6+.3v. +d.7v.35. +f.2M.7V. +0.2K.7Z. +1.6g.54. +1.6N.3T. +1.7d.3j. +2.6P.3P. +2.6v.4l. +7.6x.4h. +8.6z.4c. +c.37.7t. +d.7v.36. +1.6L.3Y. +1.6N.3U. +1.7d.3k. +2.5s.64. +2.6J.3Z. +6.6R.3H. +7.6v.4m. +8.6z.4d. +9.7t.38. +0.6H.40. +1.6g.56. +1.6N.3V. +1.6v.4n. +1.7d.3l. +2.6J.3+. +5.2q.9n. +5.6P.3Q. +6.6R.3I. +a.32.7A. +b.3R.6O. +b.6+.3y. +d.71.3v. +h.6x.4j. +0.2N.7V. +1.5s.65. +1.6g.57. +1.6L.3Z. +1.6N.3W. +1.7g.3j. +2.6J.3/. +5.2q.9o. +6.3S.6O. +6.4c.6B. +6.6s.4q. +7.6v.4o. +7.6x.4k. +8.6z.4e. +a.2F.85. +b.3J.6R. +0.2O.7V. +0.39.7t. +0.6J.40. +1.6g.58. +1.6L.3+. +1.6N.3X. +1.7d.3n. +1.7g.3k. +3.3K.6R. +5.2q.9p. +7.6v.4p. +8.6z.4f. +b.3R.6P. +1.33.7A. +1.6L.3/. +1.7d.3o. +1.7g.3l. +2.5s.66. +2.6g.59. +3.6R.3L. +4.7t.3a. +5.2q.9q. +6.3S.6P. +6.4q.6u. +6.6M.3Y. +6.85.2G. +7.6x.4m. +8.6z.4g. +a.2P.7V. +b.3G.6V. +d.71.3y. +d.7v.38. +0.6v.4q. +0.7A.34. +1.5s.67. +1.6L.40. +1.7d.3p. +2.6g.5a. +2.6J.42. +3.3M.6R. +5.2q.9r. +5.6P.3T. +8.6z.4h. +9.7t.3b. +f.2L.7Z. +0.2Q.7V. +1.2n.9K. +1.6L.41. +1.6z.4i. +1.7g.3n. +2.2H.85. +2.5s.68. +2.6g.5b. +2.6J.43. +3.6R.3N. +4.7A.35. +4.7t.3c. +5.2q.9s. +5.6P.3U. +6.4c.6E. +7.6v.4r. +7.6x.4o. +1.6L.42. +1.7g.3o. +2.6J.44. +5.2q.9t. +5.6P.3V. +6.6R.3O. +7.6v.4s. +7.6x.4p. +8.6z.4j. +9.7A.36. +9.7t.3d. +f.2R.7V. +0.3e.7t. +1.6L.43. +1.6N.3Z. +1.7d.3s. +1.7g.3p. +2.6R.3P. +5.2q.9u. +5.6P.3W. +7.6v.4t. +8.6z.4k. +d.7v.3a. +0.2S.7V. +1.5s.69. +1.6g.5c. +1.6L.44. +1.6N.3+. +2.6J.46. +2.6z.4l. +5.2q.9v. +5.6P.3X. +6.6M.40. +d.7v.3b. +f.2M.7Z. +h.4q.6x. +1.5s.6a. +1.6L.45. +1.6N.3/. +2.6g.5d. +2.6J.47. +5.2q.9w. +6.6B.4j. +6.6O.3Y. +6.6R.3Q. +6.6v.4u. +6.85.2I. +7.6x.4r. +8.6z.4m. +c.3f.7t. +d.7v.3c. +e.3J.6V. +1.6L.46. +1.6z.4n. +1.7d.3t. +1.7g.3s. +3.3K.6V. +5.2q.9x. +7.6v.4v. +7.6x.4s. +b.3G.6+. +d.7v.3d. +f.37.7A. +0.6P.3Y. +1.2n.9L. +1.5s.6b. +1.6L.47. +1.6N.41. +1.7d.3u. +2.6B.4l. +2.6g.5f. +3.6V.3L. +5.2q.9y. +7.6v.4w. +7.6x.4t. +8.6z.4o. +9.7A.38. +b.3R.6R. +0.2N.7Z. +1.6g.5g. +1.6L.48. +1.6N.42. +3.3M.6V. +3.6R.3S. +5.2q.9z. +8.6z.4p. +0.2O.7Z. +0.3g.7t. +1.6g.5h. +1.6L.49. +1.6N.43. +1.7d.3w. +1.7g.3t. +3.6V.3N. +5.6P.3Z. +6.6E.4j. +6.6R.3T. +7.6x.4u. +9.6v.4x. +d.71.3G. +0.7A.39. +1.6L.4a. +1.6N.44. +1.7d.3x. +1.7g.3u. +2.6g.5i. +5.6P.3+. +6.4c.6H. +6.6O.40. +6.6R.3U. +7.6v.4y. +7.6x.4v. +8.6z.4q. +a.2P.7Z. +1.6L.4b. +1.6N.45. +2.6g.5j. +5.6P.3/. +6.6R.3V. +6.6v.4z. +7.6x.4w. +8.6z.4r. +9.7A.3a. +a.2J.85. +0.3h.7t. +0.6P.40. +1.2n.9M. +1.6N.46. +1.7g.3w. +2.2g.9S. +6.4c.6J. +7.6v.4A. +8.6z.4s. +9.6R.3W. +9.7A.3b. +b.6+.3J. +f.2Q.7Z. +1.2n.9N. +1.6g.5k. +1.6N.47. +1.7d.3A. +1.7g.3x. +2.2g.9T. +2.6J.4d. +5.6P.41. +6.3i.7t. +6.6B.4q. +6.6R.3X. +7.6v.4B. +8.6z.4t. +9.6x.4x. +9.7A.3c. +b.6+.3K. +d.2U.7V. +f.2R.7Z. +1.6L.4c. +1.6N.48. +1.8z.2u. +5.6P.42. +7.6v.4C. +7.6x.4y. +9.7A.3d. +9.7t.3j. +b.6+.3L. +e.5L.5L. +0.2S.7Z. +0.7A.3e. +1.5s.6c. +1.6L.4d. +1.6N.49. +1.6P.43. +1.7d.3B. +2.7g.3z. +2.8z.2v. +6.6v.4D. +6.85.2K. +7.6x.4z. +8.6z.4u. +9.7t.3k. +b.3R.6V. +b.6+.3M. +d.71.3J. +1.4G.6t. +1.6N.4a. +1.7d.3C. +1.7g.3A. +2.6g.5l. +2.6J.4f. +2.6v.4E. +3.6R.3Y. +3.6V.3S. +5.6P.44. +7.6x.4A. +8.6z.4v. +9.7t.3l. +b.6+.3N. +d.71.3K. +1.6L.4e. +1.6N.4b. +2.2g.9U. +2.6J.4g. +5.6P.45. +6.3m.7t. +6.4q.6E. +7.6v.4F. +7.6x.4B. +8.6z.4w. +c.3f.7A. +d.71.3L. +0.6H.4j. +1.2n.9O. +1.6L.4f. +1.7d.3E. +1.7g.3B. +1.7t.3n. +2.6J.4h. +5.6P.46. +6.4c.6M. +7.6v.4G. +7.6x.4C. +9.6R.3Z. +a.2V.7V. +d.71.3M. +d.7v.3j. +1.2n.9P. +1.6g.5n. +1.6L.4g. +1.7d.3F. +1.7g.3C. +3.2W.7V. +5.6P.47. +6.6R.3+. +7.6x.4D. +8.6z.4x. +9.7t.3o. +d.71.3N. +d.7v.3k. +0.6J.4j. +1.6g.5o. +1.6L.4h. +2.2g.9V. +2.7g.3D. +5.6P.48. +6.6R.3/. +8.6z.4y. +9.7t.3p. +d.7v.3l. +0.3q.7t. +0.7A.3g. +1.2n.9Q. +1.6L.4i. +1.6N.4d. +1.7g.3E. +2.6g.5p. +2.6J.4k. +3.6R.40. +5.6P.49. +6.85.2L. +7.6v.4H. +7.6x.4F. +8.6z.4z. +1.6g.5q. +1.6L.4j. +1.6P.4a. +1.7g.3F. +1.7v.3n. +2.2g.9W. +2.6J.4l. +6.3r.7t. +6.6R.41. +7.6v.4I. +7.6x.4G. +8.6z.4A. +b.6+.3R. +1.4G.6y. +1.6L.4k. +1.6N.4e. +1.6P.4b. +1.7t.3s. +2.5s.6d. +2.6J.4m. +6.6R.42. +7.6v.4J. +8.6z.4B. +b.6+.3S. +d.7v.3o. +0.7A.3h. +1.6N.4f. +1.6R.43. +2.2g.9X. +2.5s.6e. +2.6J.4n. +2.6L.4l. +3.6V.3Y. +6.4c.6O. +7.6v.4K. +8.6z.4C. +a.2Y.7V. +d.2U.7Z. +d.7v.3p. +1.6L.4m. +1.6N.4g. +1.7d.3H. +2.6J.4o. +6.3i.7A. +6.6R.44. +6.85.2M. +7.6v.4L. +7.6x.4H. +8.6z.4D. +d.71.3R. +1.5s.6f. +1.6g.5r. +1.6L.4n. +1.6N.4h. +1.7d.3I. +2.6J.4p. +2.6z.4E. +6.4c.6P. +6.4q.6H. +6.6M.4j. +6.6v.4M. +7.6x.4I. +9.6R.45. +9.7A.3j. +9.7t.3t. +d.71.3S. +1.5s.6g. +1.6L.4o. +1.6N.4i. +1.7v.3s. +5.6P.4d. +6.6v.4N. +7.6x.4J. +8.6z.4F. +9.6R.46. +9.7A.3k. +9.7t.3u. +0.7t.3v. +1.6L.4p. +1.7g.3H. +6.4q.6J. +6.6R.47. +6.6v.4O. +7.6x.4K. +8.6z.4G. +9.7A.3l. +0.7A.3m. +1.6N.4k. +1.7g.3I. +2.6B.4E. +2.6J.4r. +2.6v.4P. +3.6V.40. +5.6P.4e. +6.6R.48. +6.85.2N. +7.6x.4L. +9.7t.3w. +a.2V.7Z. +1.6L.4q. +1.7A.3n. +2.6J.4s. +2.6N.4l. +3.2W.7Z. +5.6P.4f. +6.85.2O. +7.6v.4Q. +7.6x.4M. +9.6R.49. +9.7t.3x. +d.7v.3t. +1.5s.6h. +1.6L.4r. +1.6N.4m. +1.6R.4a. +2.6J.4t. +5.6P.4g. +6.3y.7t. +7.6v.4R. +7.6x.4N. +8.6z.4H. +9.7A.3o. +a.2P.85. +b.6+.3Y. +d.7v.3u. +1.6L.4s. +1.6N.4n. +1.6R.4b. +1.7d.3O. +2.6g.5t. +2.7t.3z. +5.6P.4h. +6.6O.4j. +7.6v.4S. +7.6x.4O. +8.6z.4I. +9.7A.3p. +0.7A.3q. +1.6L.4t. +1.6N.4o. +1.6P.4i. +6.85.2Q. +8.2+.7V. +8.6z.4J. +9.7t.3A. +d.7v.3w. +0.6P.4j. +0.7A.3r. +1.5s.6i. +1.6N.4p. +2.6J.4v. +2.6v.4T. +6.4q.6M. +6.85.2R. +7.6x.4Q. +8.6z.4K. +d.71.3Y. +d.7v.3x. +1.5s.6j. +1.6L.4u. +1.7A.3s. +1.7d.3Q. +1.7g.3O. +2.6J.4w. +2.6v.4U. +3.6R.4c. +5.6P.4k. +7.6x.4R. +8.6z.4L. +9.7t.3B. +a.2Y.7Z. +1.6L.4v. +2.2/.7V. +2.5s.6k. +2.6P.4l. +2.7g.3P. +2.7v.3z. +5.2q.9A. +5.7t.3C. +6.6R.4d. +6.85.2S. +7.6v.4V. +7.6x.4S. +8.6z.4M. +b.6+.40. +1.4G.6F. +1.5s.6l. +1.6L.4w. +1.6v.4W. +2.6J.4x. +2.7t.3D. +5.6P.4m. +8.6z.4N. +d.7v.3A. +e.6N.4r. +1.6P.4n. +1.7g.3Q. +2.6J.4y. +8.6z.4O. +9.6R.4e. +9.7A.3t. +9.7t.3E. +e.6N.4s. +1.6L.4x. +1.6N.4t. +1.7d.3T. +2.6J.4z. +2.6z.4P. +5.6P.4o. +6.6R.4f. +9.7A.3u. +9.7t.3F. +d.71.40. +d.7v.3B. +0.30.7V. +0.7A.3v. +1.4G.6G. +1.6L.4y. +1.7d.3U. +2.5s.6m. +5.6P.4p. +6.4q.6O. +6.6R.4g. +7.6x.4V. +8.6z.4Q. +9.6v.4X. +d.7v.3C. +1.6L.4z. +1.6N.4u. +1.7d.3V. +2.2n.9R. +2.6J.4B. +2.7v.3D. +6.6R.4h. +8.6z.4R. +9.6v.4Y. +9.7A.3w. +b.3G.7t. +0.6P.4q. +1.6L.4A. +1.6N.4v. +1.6R.4i. +1.7d.3W. +1.7g.3T. +2.6B.4P. +2.6J.4C. +8.6z.4S. +9.7A.3x. +d.7v.3E. +0.7A.3y. +1.6L.4B. +1.6N.4w. +1.7d.3X. +1.7g.3U. +2.6J.4D. +3.6R.4j. +3.6V.4c. +5.6P.4r. +d.7v.3F. +0.2w.8C. +1.6L.4C. +1.6v.4Z. +1.7g.3V. +2.6z.4T. +2.7A.3z. +5.6P.4s. +6.6o.53. +8.2+.7Z. +9.6R.4k. +9.6x.4X. +1.4G.6I. +1.6L.4D. +1.6N.4x. +1.7g.3W. +2.6g.5v. +2.6R.4l. +2.6z.4U. +4.7t.3H. +5.6P.4t. +6.6p.53. +9.6x.4Y. +9.7A.3A. +d.2U.85. +1.6N.4y. +1.7g.3X. +2.6g.5w. +2.6L.4E. +6.6o.55. +8.6z.4V. +9.6R.4m. +9.7t.3I. +1.6g.5x. +1.6L.4F. +1.6N.4z. +1.6R.4n. +1.6z.4W. +2.2/.7Z. +2.6B.4T. +5.6P.4u. +6.6p.55. +9.6v.4+. +9.7A.3B. +b.3J.7t. +1.6L.4G. +1.6N.4A. +1.7d.3Z. +2.6B.4U. +3.2x.8C. +3.3K.7t. +5.6P.4v. +5.7A.3C. +6.6R.4o. +9.6v.4/. +1.31.7V. +1.6N.4B. +1.7d.3+. +2.6J.4H. +2.7A.3D. +5.2q.9F. +5.6P.4w. +6.3L.7t. +6.58.6o. +6.6R.4p. +6.6s.53. +9.6v.50. +d.7v.3H. +1.6N.4C. +1.7d.3/. +3.3M.7t. +6.58.6p. +6.6v.51. +8.6z.4X. +9.7A.3E. +a.2V.85. +b.6+.4c. +d.7v.3I. +0.30.7Z. +0.6u.53. +1.6L.4H. +1.7g.3Z. +2.6J.4J. +3.2W.85. +3.6R.4q. +3.6V.4j. +6.3N.7t. +6.6s.55. +6.6v.52. +8.6z.4Y. +9.6P.4x. +9.6x.4+. +9.7A.3F. +e.6N.4D. +0.6v.53. +1.6L.4I. +1.7d.41. +1.7g.3+. +2.6J.4K. +2.6N.4E. +3.2y.8C. +5.6P.4y. +9.6R.4r. +9.6x.4/. +9.7t.3O. +0.6u.55. +1.6L.4J. +1.6N.4F. +1.7d.42. +1.7g.3/. +2.6J.4L. +2.7t.3P. +5.2q.9G. +5.6P.4z. +6.6v.54. +8.2z.8C. +9.6R.4s. +9.6x.50. +a.32.7V. +b.3G.7A. +d.71.4c. +0.6v.55. +1.6L.4K. +1.6N.4G. +1.6z.4Z. +1.7d.43. +2.6J.4M. +5.2q.9H. +5.6P.4A. +6.58.6s. +7.6x.51. +8.2A.8C. +9.6R.4t. +1.6L.4L. +1.6v.56. +1.7d.44. +1.7g.41. +2.6J.4N. +3.2B.8C. +4.7t.3Q. +5.6P.4B. +7.6x.52. +0.2C.8C. +1.33.7V. +1.6L.4M. +1.7d.45. +1.7g.42. +2.6J.4O. +5.6P.4C. +6.58.6u. +7.6v.57. +9.6R.4u. +a.2Y.85. +d.7v.3O. +h.6x.53. +0.34.7V. +1.6L.4N. +1.6N.4H. +1.7d.46. +1.7g.43. +2.6J.4P. +2.7v.3P. +5.2q.9I. +5.6P.4D. +6.6o.5e. +7.6x.54. +8.58.6v. +8.6z.4+. +9.6R.4v. +9.7A.3H. +b.3R.7t. +0.55.6x. +1.6L.4O. +1.6N.4I. +1.7d.47. +1.7g.44. +2.6J.4Q. +2.6P.4E. +2.6v.59. +6.3S.7t. +6.6p.5e. +8.6z.4/. +9.6R.4w. +9.7A.3I. +b.6+.4j. +0.2D.8C. +1.6N.4J. +1.7d.48. +1.7g.45. +2.6g.5E. +2.6J.4R. +2.6L.4P. +2.6v.5a. +3.6V.4q. +5.6P.4F. +8.6z.50. +9.7t.3T. +b.3J.7A. +d.7v.3Q. +1.31.7Z. +1.6L.4Q. +1.6N.4K. +1.7d.49. +1.7g.46. +2.6J.4S. +2.6v.5b. +3.3K.7A. +5.6P.4G. +7.6x.57. +8.6z.51. +9.6R.4x. +9.7t.3U. +g.2E.8C. +1.6L.4R. +1.6N.4L. +1.7d.4a. +1.7g.47. +6.3L.7A. +8.58.6x. +8.6z.52. +9.6R.4y. +9.7t.3V. +d.71.4j. +1.6L.4S. +1.6N.4M. +1.7d.4b. +1.7g.48. +2.6J.4T. +3.3M.7A. +6.6R.4z. +6.6s.5e. +8.6z.53. +9.7t.3W. +0.7A.3N. +1.6N.4N. +1.7g.49. +2.6J.4U. +4.7t.3X. +5.6P.4H. +6.6R.4A. +8.6z.54. +9.6v.5c. +c.37.7V. +d.7v.3T. +0.6u.5e. +1.6N.4O. +1.7g.4a. +2.6g.5H. +2.6L.4T. +2.6v.5d. +5.6P.4I. +6.6R.4B. +8.6z.55. +9.7A.3O. +a.32.7Z. +d.7v.3U. +0.6v.5e. +1.6z.56. +1.7g.4b. +2.6J.4W. +2.6L.4U. +2.6N.4P. +2.7A.3P. +5.6P.4J. +6.6B.53. +8.2+.85. +9.6R.4C. +d.7v.3V. +0.7t.3Y. +1.6L.4V. +1.6N.4Q. +1.7d.4d. +2.6v.5f. +5.2q.9K. +5.6P.4K. +6.6R.4D. +8.6z.57. +b.6+.4q. +d.7v.3W. +0.39.7V. +1.33.7Z. +1.6L.4W. +1.6N.4R. +2.6R.4E. +5.6P.4L. +6.6B.55. +6.6v.5g. +8.58.6z. +9.6x.5c. +9.7A.3Q. +d.7v.3X. +1.6N.4S. +1.6v.5h. +1.7d.4e. +2.2/.85. +2.6J.4X. +2.6z.59. +4.7t.3Z. +5.6P.4M. +9.6R.4F. +a.2F.8C. +f.34.7Z. +1.7d.4f. +1.7g.4d. +2.6J.4Y. +2.6v.5i. +2.6z.5a. +5.6P.4N. +6.6E.53. +9.6R.4G. +9.7t.3+. +b.3R.7A. +d.71.4q. +h.6x.5e. +0.2G.8C. +1.6L.4X. +1.7d.4g. +2.6N.4T. +2.6v.5j. +2.6z.5b. +5.6P.4O. +6.3S.7A. +6.58.6B. +9.7t.3/. +0.55.6E. +0.7t.40. +1.4G.6T. +1.6L.4Y. +1.7d.4h. +1.7g.4e. +2.6B.59. +2.6N.4U. +2.6P.4P. +7.6x.5g. +9.7A.3T. +1.6N.4V. +1.7d.4i. +1.7g.4f. +2.2H.8C. +2.6B.5a. +2.6J.4Z. +4.7t.41. +5.2q.9L. +5.6P.4Q. +6.6R.4H. +9.6v.5k. +9.7A.3U. +d.7v.3Z. +f.7V.3e. +i.85.30. +1.7g.4g. +2.6B.5b. +5.6P.4R. +8.6z.5c. +9.6R.4I. +9.7A.3V. +9.7t.42. +d.7v.3+. +e.6N.4W. +1.6L.4Z. +1.7d.4k. +1.7g.4h. +1.7t.43. +2.6z.5d. +5.58.6E. +5.6P.4S. +9.6R.4J. +9.7A.3W. +c.3f.7V. +d.7v.3/. +f.37.7Z. +1.7g.4i. +2.6J.4+. +2.6v.5l. +8.6z.5e. +9.6R.4K. +9.7A.3X. +9.7t.44. +0.2I.8C. +1.6N.4X. +1.7d.4m. +2.6J.4/. +2.6P.4T. +2.6z.5f. +9.6R.4L. +9.6x.5k. +9.7t.45. +d.7v.41. +1.5s.6n. +1.6L.4+. +1.6N.4Y. +1.7d.4n. +1.7g.4k. +2.6B.5d. +2.6J.50. +2.6P.4U. +8.6z.5g. +9.6R.4M. +9.7t.46. +d.7v.42. +0.6H.53. +0.7A.3Y. +0.7V.3g. +1.4G.6W. +1.6L.4/. +1.6v.5n. +1.6z.5h. +1.7d.4o. +1.7v.43. +2.6J.51. +2.7g.4l. +5.6P.4V. +6.6B.5e. +6.6o.5s. +9.6R.4N. +9.7t.47. +f.39.7Z. +1.6L.50. +1.6P.4W. +1.7d.4p. +1.7g.4m. +2.6B.5f. +2.6J.52. +2.6z.5i. +6.6p.5s. +6.6R.4O. +6.6v.5o. +9.7t.48. +d.7v.44. +0.55.6H. +0.6J.53. +1.5s.6q. +1.6L.51. +1.6N.4Z. +1.7g.4n. +2.6g.5J. +2.6R.4P. +2.6v.5p. +2.6z.5j. +9.7A.3Z. +9.7t.49. +d.7v.45. +0.7V.3h. +1.31.85. +1.4G.6Z. +1.6L.52. +1.7g.4o. +1.7t.4a. +2.6J.54. +6.6v.5q. +9.6R.4Q. +9.7A.3+. +d.7v.46. +0.6J.55. +1.5s.6r. +1.6L.53. +1.7d.4r. +1.7g.4p. +1.7t.4b. +2.6B.5i. +2.6g.5K. +6.3i.7V. +6.6E.5e. +6.6P.4X. +6.6R.4R. +8.6z.5k. +9.7A.3/. +a.2J.8C. +d.7v.47. +0.7A.40. +1.6L.54. +1.6N.4+. +1.7d.4s. +2.6B.5j. +2.6J.56. +5.2q.9O. +6.6R.4S. +6.6s.5s. +7.6x.5o. +8.58.6H. +9.6P.4Y. +d.7v.48. +f.7Z.3e. +1.5s.6t. +1.6L.55. +1.6N.4/. +1.7d.4t. +2.6J.57. +5.2q.9P. +9.7A.41. +d.7v.49. +0.6u.5s. +1.4G.6/. +1.6L.56. +1.6N.50. +1.6v.5r. +1.7g.4r. +1.7v.4a. +2.6R.4T. +2.6z.5l. +6.4c.7t. +6.58.6J. +7.6x.5q. +9.7A.42. +a.32.85. +c.3f.7Z. +0.2K.8C. +0.6v.5s. +1.4G.70. +1.6L.57. +1.6N.51. +1.6P.4Z. +1.7A.43. +1.7d.4u. +1.7g.4s. +1.7v.4b. +2.6R.4U. +4.7t.4d. +5.2q.9Q. +6.3m.7V. +6.6M.53. +0.5u.6o. +1.4G.71. +1.6L.58. +1.6N.52. +1.7d.4v. +1.7g.4t. +2.6J.5a. +6.6R.4V. +9.7A.44. +0.5u.6p. +1.33.85. +1.4G.72. +1.6R.4W. +1.6z.5n. +1.7d.4w. +2.6B.5l. +2.6J.5b. +2.6L.59. +6.6M.55. +9.7A.45. +9.7t.4e. +0.7Z.3g. +1.4G.73. +1.5s.6w. +1.6N.54. +1.7g.4u. +2.6L.5a. +4.7t.4f. +6.6P.4+. +6.85.34. +8.6z.5o. +9.7A.46. +1.4G.74. +1.7d.4x. +1.7g.4v. +2.6L.5b. +2.6z.5p. +6.6P.4/. +9.7A.47. +9.7t.4g. +d.7v.4d. +f.7V.3q. +h.5s.6x. +0.6H.5e. +1.4G.75. +1.5s.6y. +1.6N.56. +1.7d.4y. +1.7g.4w. +2.6v.5t. +6.3r.7V. +6.58.6M. +6.6P.50. +6.6R.4X. +8.6z.5q. +9.7A.48. +9.7t.4h. +0.2L.8C. +0.3h.7Z. +1.4G.76. +1.6N.57. +1.7d.4z. +1.7t.4i. +2.6J.5d. +5.6P.51. +6.6O.53. +6.6R.4Y. +6.6s.5u. +9.7A.49. +d.7v.4e. +0.6J.5e. +0.7t.4j. +1.4G.77. +1.6L.5c. +1.6N.58. +1.7A.4a. +1.7d.4A. +1.7g.4x. +2.6B.5p. +5.6P.52. +6.3i.7Z. +d.7v.4f. +0.5u.6u. +0.6P.53. +1.4G.78. +1.7A.4b. +1.7d.4B. +1.7g.4y. +2.6L.5d. +2.6N.59. +6.6O.55. +9.7t.4k. +d.7v.4g. +0.6v.5u. +1.4G.79. +1.6L.5e. +1.6z.5r. +1.7d.4C. +1.7g.4z. +2.6J.5g. +2.6N.5a. +2.7t.4l. +5.6P.54. +6.85.37. +d.7v.4h. +k.6R.4Z. +0.2M.8C. +0.6P.55. +1.4G.7a. +1.7d.4D. +1.7g.4A. +1.7v.4i. +2.6J.5h. +2.6L.5f. +2.6N.5b. +4.7t.4m. +8.6z.5s. +0.7V.3v. +1.4G.7b. +1.6L.5g. +1.6P.56. +1.7g.4B. +1.7t.4n. +6.3m.7Z. +6.4c.7A. +6.58.6O. +1.4G.7c. +1.5s.6A. +1.6L.5h. +1.7d.4F. +1.7g.4C. +5.6P.57. +6.6R.4+. +9.7A.4d. +9.7t.4o. +d.7v.4k. +e.8S.2u. +0.5u.6x. +1.6N.5c. +1.7d.4G. +1.7g.4D. +2.6L.5i. +2.7v.4l. +2.8S.2v. +6.6B.5s. +6.6M.5e. +6.85.39. +8.58.6P. +9.6R.4/. +9.7t.4p. +0.2N.8C. +1.4G.7e. +2.6J.5k. +2.6L.5j. +2.6N.5d. +2.6P.59. +2.7g.4E. +6.3y.7V. +6.6R.50. +9.7A.4e. +d.7v.4m. +0.2O.8C. +0.3q.7Z. +1.4G.7f. +1.5s.6C. +1.7g.4F. +1.7v.4n. +2.6P.5a. +2.6z.5t. +6.4q.7t. +6.6R.51. +9.7A.4f. +1.5s.6D. +1.6L.5k. +1.7d.4H. +1.7g.4G. +2.6N.5f. +2.6P.5b. +6.3r.7Z. +6.6R.52. +9.7A.4g. +9.7t.4r. +a.2P.8C. +d.7v.4o. +1.4G.7h. +1.6N.5g. +1.7d.4I. +3.6R.53. +6.6E.5s. +9.7A.4h. +9.7t.4s. +d.7v.4p. +0.2Q.8C. +1.6N.5h. +1.7A.4i. +1.7d.4J. +2.6B.5t. +2.6g.5M. +2.6v.5v. +6.6R.54. +6.85.3e. +9.7t.4t. +0.2R.8C. +0.7A.4j. +1.4G.7i. +1.5s.6F. +1.7d.4K. +1.7g.4H. +1.8z.2X. +2.6g.5N. +2.6L.5l. +2.6N.5i. +2.6v.5w. +3.6R.55. +6.6O.5e. +8.6z.5u. +9.6P.5c. +1.6R.56. +1.7d.4L. +1.7g.4I. +2.4G.7j. +2.6J.5n. +2.6N.5j. +2.6P.5d. +4.7t.4u. +6.85.3f. +9.6v.5x. +9.7A.4k. +d.7v.4r. +0.2S.8C. +0.6P.5e. +1.4G.7k. +1.7d.4M. +1.7g.4J. +2.7A.4l. +6.6R.57. +9.7t.4v. +d.7v.4s. +0.7Z.3v. +1.4G.7l. +1.5s.6G. +1.6L.5n. +1.6N.5k. +1.7d.4N. +1.7g.4K. +2.6P.5f. +6.6B.5u. +8.58.6R. +9.6v.5y. +9.7A.4m. +9.7t.4w. +d.7v.4t. +1.6L.5o. +1.7A.4n. +1.7d.4O. +1.7g.4L. +2.6R.59. +5.6P.5g. +9.6v.5z. +1.6P.5h. +1.7g.4M. +2.6L.5p. +2.6R.5a. +4.7t.4x. +6.85.3g. +9.6x.5x. +9.7A.4o. +b.3G.7V. +d.7v.4u. +1.4G.7o. +1.6L.5q. +1.7d.4Q. +1.7g.4N. +2.6N.5l. +2.6P.5i. +2.6R.5b. +3.6V.53. +6.3y.7Z. +9.6v.5A. +9.7A.4p. +9.7t.4y. +b.5F.6o. +d.7v.4v. +0.5u.6E. +1.6v.5B. +1.7d.4R. +1.7g.4O. +1.8z.2Z. +2.6P.5j. +3.5G.6o. +6.5s.6H. +9.6x.5y. +9.7t.4z. +b.5F.6p. +d.7v.4w. +0.7A.4q. +1.5s.6I. +1.6g.5R. +1.7d.4S. +2.6J.5r. +2.7g.4P. +3.5G.6p. +3.6V.55. +4.7t.4A. +5.2q.9S. +6.85.3h. +9.6x.5z. +0.6J.5s. +1.6N.5n. +1.7g.4Q. +2.6z.5v. +5.2q.9T. +6.6P.5k. +6.6R.5c. +6.85.3i. +9.7A.4r. +9.7t.4B. +d.7v.4x. +1.4G.7q. +1.5s.6K. +1.6L.5r. +1.6N.5o. +1.7g.4R. +2.6R.5d. +2.6z.5w. +9.6v.5C. +9.6x.5A. +9.7A.4s. +9.7t.4C. +d.7v.4y. +1.4G.7r. +1.6L.5s. +1.7g.4S. +2.6N.5p. +3.6R.5e. +8.58.6V. +8.6z.5x. +9.6v.5D. +9.7A.4t. +9.7t.4D. +b.3J.7V. +b.5F.6s. +d.2U.8C. +d.7v.4z. +1.6N.5q. +1.7d.4V. +2.6B.5v. +2.6P.5l. +2.6R.5f. +2.7t.4E. +3.3K.7V. +3.5G.6s. +d.7v.4A. +1.4G.7s. +1.7d.4W. +2.6B.5w. +2.6g.5S. +2.6v.5E. +2.7g.4T. +5.2q.9U. +6.3L.7V. +6.85.3m. +8.6z.5y. +9.6R.5g. +9.7A.4u. +9.7t.4F. +b.5F.6u. +b.6+.53. +d.7v.4B. +1.6R.5h. +2.6J.5t. +2.7g.4U. +3.3M.7V. +3.5G.6u. +8.6z.5z. +9.6x.5C. +9.7A.4v. +9.7t.4G. +b.5F.6v. +d.7v.4C. +1.6P.5n. +1.7g.4V. +2.6R.5i. +3.5G.6v. +6.3N.7V. +6.6M.5s. +9.6x.5D. +9.7A.4w. +b.6+.55. +d.7v.4D. +0.5u.6H. +1.4G.7u. +1.6N.5r. +1.7d.4X. +1.7g.4W. +2.6L.5t. +2.6R.5j. +2.7v.4E. +5.2q.9V. +5.6P.5o. +8.6z.5A. +a.2V.8C. +b.3G.7Z. +d.71.53. +1.6z.5B. +1.7d.4Y. +2.5s.6N. +2.6P.5p. +3.2W.8C. +6.85.3q. +9.7A.4x. +9.7t.4H. +d.7v.4F. +0.5u.6J. +2.6v.5H. +5.2q.9W. +5.6P.5q. +6.6R.5k. +6.85.3r. +8.58.6+. +9.7A.4y. +9.7t.4I. +d.71.55. +d.7v.4G. +h.5F.6x. +1.4G.7w. +1.7g.4X. +3.6V.5e. +6.6v.5I. +9.7A.4z. +9.7t.4J. +h.5G.6x. +1.6g.5T. +1.6L.5u. +1.7d.4Z. +1.7g.4Y. +5.2q.9X. +8.6z.5C. +9.7A.4A. +9.7t.4K. +2.6R.5l. +6.6O.5s. +8.6z.5D. +9.7A.4B. +9.7t.4L. +b.3R.7V. +d.71.58. +d.7v.4H. +1.6P.5r. +2.6N.5t. +6.3S.7V. +9.7A.4C. +9.7t.4M. +a.2Y.8C. +b.3J.7Z. +d.7v.4I. +0.6P.5s. +1.4G.7y. +1.7d.4+. +1.7g.4Z. +2.6z.5E. +3.3K.7Z. +7.6x.5I. +9.7A.4D. +9.7t.4N. +d.7v.4J. +0.5u.6M. +1.6B.5D. +1.6R.5n. +1.7d.4/. +2.7A.4E. +4.7t.4O. +6.3L.7Z. +6.85.3v. +8.6z.5F. +d.7v.4K. +1.4G.7z. +1.7d.50. +2.7t.4P. +3.3M.7Z. +6.6R.5o. +8.6z.5G. +9.7A.4F. +d.7v.4L. +1.7d.51. +1.7g.4+. +2.6B.5E. +2.6R.5p. +6.3N.7Z. +9.7A.4G. +9.7t.4Q. +b.6+.5e. +d.7v.4M. +1.7d.52. +1.7g.4/. +4.7t.4R. +6.85.3y. +9.6R.5q. +b.5F.6B. +d.7v.4N. +1.7g.50. +2.6J.5w. +2.6P.5t. +2.6z.5H. +3.5G.6B. +4.7t.4S. +d.7v.4O. +1.6g.5U. +1.7d.54. +1.7g.51. +2.6J.5x. +2.6L.5v. +2.7v.4P. +8.6z.5I. +9.7A.4H. +d.71.5e. +0.5u.6O. +0.7V.3Y. +1.4G.7B. +1.7g.52. +2.6L.5w. +2.7t.4T. +9.7A.4I. +d.7v.4Q. +1.4G.7C. +1.5s.6Q. +1.6g.5V. +1.6L.5x. +1.6R.5r. +1.7d.56. +2.6B.5H. +2.6J.5y. +2.7t.4U. +8.2+.8C. +9.7A.4J. +b.5F.6E. +d.7v.4R. +0.6P.5u. +1.7d.57. +1.7g.54. +2.6J.5z. +3.5G.6E. +3.6R.5s. +4.7t.4V. +9.7A.4K. +b.3R.7Z. +d.7v.4S. +1.4G.7E. +1.6L.5y. +1.7d.58. +1.7t.4W. +6.3S.7Z. +9.7A.4L. +1.5s.6T. +1.6L.5z. +1.7g.56. +2.2/.8C. +2.6v.5J. +2.7v.4T. +9.7A.4M. +0.7V.40. +1.7g.57. +2.6g.5W. +2.6J.5B. +2.6N.5v. +2.7v.4U. +9.7A.4N. +1.4G.7F. +1.6L.5A. +1.7g.58. +2.6g.5X. +2.6N.5w. +2.6v.5K. +4.7t.4X. +9.7A.4O. +d.7v.4V. +1.6L.5B. +1.6N.5x. +1.7v.4W. +2.6g.5Y. +2.6R.5t. +2.7A.4P. +2.7g.59. +4.7t.4Y. +b.3G.85. +0.30.8C. +1.4G.7G. +1.8z.35. +2.7g.5a. +9.7A.4Q. +1.5s.6U. +1.6N.5y. +1.7d.5c. +1.8z.36. +2.6g.5+. +2.6J.5D. +2.7g.5b. +9.7A.4R. +1.4G.7I. +1.6L.5C. +1.6N.5z. +1.7t.4Z. +3.6V.5s. +9.7A.4S. +b.5F.6H. +d.7v.4X. +0.7Z.3Y. +1.5s.6W. +1.6L.5D. +2.6J.5E. +2.6P.5v. +3.5G.6H. +3.6R.5u. +d.7v.4Y. +1.5s.6X. +1.6N.5A. +1.7g.5c. +2.6P.5w. +2.7A.4T. +b.5F.6J. +1.5s.6Y. +1.6N.5B. +1.7d.5g. +2.6L.5E. +2.7A.4U. +2.7g.5d. +3.5G.6J. +9.6P.5x. +9.7t.4+. +b.3J.85. +1.5s.6Z. +1.6L.5F. +1.7d.5h. +1.7v.4Z. +1.8z.38. +2.6g.5/. +2.6z.5J. +3.3K.85. +9.7A.4V. +9.7t.4/. +1.6L.5G. +1.7A.4W. +2.6g.60. +2.7g.5f. +6.85.3L. +9.6P.5y. +9.7t.50. +0.7Z.40. +1.6g.61. +1.6N.5C. +1.7g.5g. +2.6z.5K. +3.3M.85. +4.7t.51. +9.6P.5z. +1.31.8C. +1.6g.62. +1.6N.5D. +1.7g.5h. +2.6B.5J. +2.6J.5I. +6.85.3N. +9.7t.52. +b.6+.5s. +d.7v.4+. +0.7t.53. +1.5s.6/. +1.7d.5k. +2.6L.5H. +2.7g.5i. +6.5L.6o. +6.6P.5A. +9.7A.4X. +b.5F.6M. +d.7v.4/. +1.5s.70. +1.6L.5I. +1.6P.5B. +2.6B.5K. +2.6N.5E. +2.7g.5j. +3.5G.6M. +3.6V.5u. +6.4c.7V. +6.5L.6p. +9.7A.4Y. +9.7t.54. +d.7v.50. +0.55.7t. +1.8z.3c. +2.6g.63. +2.6R.5v. +d.71.5s. +d.7v.51. +1.5s.72. +1.7g.5k. +1.7t.56. +2.6R.5w. +a.32.8C. +d.7v.52. +1.5s.73. +1.7A.4Z. +2.4G.7K. +4.7t.57. +6.6R.5x. +9.6P.5C. +1.5s.74. +2.6g.64. +6.5L.6s. +8.58.7t. +9.6P.5D. +b.3R.85. +d.7v.54. +1.33.8C. +1.5s.75. +1.7d.5n. +2.6N.5H. +2.7g.5l. +2.7t.59. +6.3S.85. +9.6R.5y. +b.5F.6O. +1.5s.76. +1.6g.65. +1.6N.5I. +1.7d.5o. +1.7v.56. +2.6P.5E. +2.7t.5a. +3.5G.6O. +6.5L.6u. +9.6R.5z. +9.7A.4+. +f.34.8C. +1.5s.77. +2.7t.5b. +6.5L.6v. +9.7A.4/. +b.5F.6P. +b.6+.5u. +d.7v.57. +0.7V.4j. +1.5s.78. +1.7d.5q. +1.7g.5n. +2.6g.66. +3.5G.6P. +9.6R.5A. +9.7A.50. +d.7v.58. +1.5s.79. +1.6g.67. +1.6R.5B. +1.7g.5o. +2.4G.7M. +2.6v.5M. +2.7v.59. +9.7A.51. +1.5s.7a. +2.6g.68. +2.6v.5N. +2.7g.5p. +2.7v.5a. +9.7A.52. +9.7t.5c. +d.71.5u. +0.7A.53. +1.5s.7b. +1.7g.5q. +2.6P.5H. +2.7t.5d. +2.7v.5b. +6.4c.7Z. +h.5L.6x. +0.7t.5e. +1.5s.7c. +1.7d.5r. +1.98.2u. +5.6P.5I. +9.6R.5C. +9.6v.5O. +9.7A.54. +c.37.8C. +0.7A.55. +1.5s.7d. +1.6g.69. +2.6J.5J. +2.7t.5f. +2.98.2v. +6.85.3Y. +9.6R.5D. +9.6v.5P. +1.5s.7e. +1.6g.6a. +1.7A.56. +1.8z.3j. +4.7t.5g. +d.7v.5c. +1.5s.7f. +1.7g.5r. +1.7t.5h. +1.8z.3k. +2.6J.5K. +2.6L.5J. +2.6R.5E. +2.7v.5d. +7.6v.5Q. +9.7A.57. +0.39.8C. +1.5s.7g. +1.6g.6b. +1.8z.3l. +2.7t.5i. +6.4q.7V. +6.58.7A. +9.6x.5O. +b.5F.6R. +1.5s.7h. +2.6L.5K. +2.7A.59. +2.7t.5j. +2.7v.5f. +3.5G.6R. +8.6z.5L. +9.6x.5P. +1.8z.3n. +2.7A.5a. +6.85.40. +9.6v.5R. +d.7v.5g. +0.7Z.4j. +1.5s.7i. +1.7v.5h. +1.8z.3o. +2.6z.5M. +2.7A.5b. +7.6x.5Q. +9.7t.5k. +1.8z.3p. +2.4G.7O. +2.5s.7j. +2.6R.5H. +2.6z.5N. +2.7v.5i. +6.5L.6B. +0.3e.8C. +1.5s.7k. +2.6N.5J. +2.7g.5t. +2.7v.5j. +6.6R.5I. +1.5s.7l. +2.4G.7P. +2.6B.5M. +2.7t.5l. +8.6z.5O. +9.6x.5R. +9.7A.5c. +1.5s.7m. +1.8z.3s. +2.6B.5N. +2.6N.5K. +2.6v.5S. +2.7A.5d. +8.6z.5P. +c.3f.8C. +d.7v.5k. +0.7A.5e. +1.5s.7n. +1.6g.6c. +6.5L.6E. +b.5F.6V. +1.5s.7o. +1.6B.5O. +1.7t.5n. +2.7A.5f. +3.5G.6V. +8.6z.5Q. +1.6B.5P. +1.8z.3t. +2.4G.7Q. +2.7v.5l. +9.7A.5g. +9.7t.5o. +0.3g.8C. +1.7A.5h. +1.8z.3u. +2.6P.5J. +2.7t.5p. +6.4q.7Z. +1.5s.7p. +1.6B.5Q. +2.7A.5i. +4.7t.5q. +8.6z.5R. +1.5s.7q. +1.7v.5n. +1.8z.3w. +2.6P.5K. +2.7A.5j. +0.3h.8C. +1.5s.7r. +1.8z.3x. +6.6v.5T. +d.7v.5o. +1.4G.7T. +2.7v.5p. +6.3i.8C. +9.7A.5k. +b.6+.5F. +1.5s.7s. +1.7t.5r. +2.6g.6d. +2.8z.3z. +6.4c.85. +b.6+.5G. +d.7v.5q. +1.4G.7U. +1.7d.5x. +1.8z.3A. +2.6g.6e. +2.6z.5S. +6.5L.6H. +6.5s.7t. +2.7A.5l. +2.7g.5v. +7.6x.5T. +d.71.5F. +1.5s.7u. +1.6g.6f. +1.7d.5y. +1.8S.2X. +1.8z.3B. +2.7g.5w. +6.3m.8C. +6.5L.6J. +d.71.5G. +1.6g.6g. +1.7d.5z. +1.7g.5x. +1.7v.5r. +1.8z.3C. +2.6B.5S. +2.6R.5J. +1.5s.7v. +1.6L.5L. +1.7A.5n. +2.6J.5M. +2.8z.3D. +1.5s.7w. +1.7d.5A. +1.7g.5y. +1.8z.3E. +2.6J.5N. +2.6R.5K. +2.7t.5t. +9.7A.5o. +0.3q.8C. +1.5s.7x. +1.7d.5B. +1.7g.5z. +1.8z.3F. +2.6L.5M. +2.7A.5p. +6.6v.5U. +1.6g.6h. +2.6J.5O. +2.6L.5N. +6.3r.8C. +6.85.4j. +8.6z.5T. +9.7A.5q. +1.7g.5A. +2.6J.5P. +6.5L.6M. +6.6v.5V. +0.5u.7t. +1.5s.7y. +1.6L.5O. +1.7d.5C. +1.7g.5B. +2.7v.5t. +1.6g.6i. +1.6L.5P. +1.7d.5D. +2.6J.5Q. +7.6x.5U. +1.4G.7Y. +1.5s.7z. +1.6g.6j. +1.7A.5r. +0.7A.5s. +1.6L.5Q. +1.7g.5C. +1.8z.3H. +2.6g.6k. +2.6N.5M. +2.6v.5W. +7.6x.5V. +1.6g.6l. +1.7g.5D. +1.8z.3I. +2.6N.5N. +2.6v.5X. +6.3v.8C. +2.6v.5Y. +6.5L.6O. +1.6L.5R. +1.6N.5O. +2.7g.5E. +6.6v.5Z. +6.85.4q. +1.5s.7B. +1.6N.5P. +2.6g.6m. +2.6v.5+. +6.3y.8C. +6.5L.6P. +8.6z.5U. +1.5s.7C. +2.7A.5t. +1.5s.7D. +1.6N.5Q. +1.7d.5I. +2.6P.5M. +2.7t.5v. +8.6z.5V. +1.5s.7E. +1.8z.3O. +2.6P.5N. +2.7t.5w. +7.6x.5Z. +2.6L.5S. +2.7g.5H. +2.8z.3P. +9.7t.5x. +0.7A.5u. +1.6N.5R. +1.7g.5I. +2.6v.5/. +6.6P.5O. +1.5s.7F. +1.8z.3Q. +2.6v.60. +2.6z.5W. +2.7v.5v. +6.6P.5P. +9.7t.5y. +1.9i.2u. +2.6z.5X. +2.7v.5w. +6.6v.61. +9.7t.5z. +1.5s.7G. +2.6z.5Y. +2.9i.2v. +5.6P.5Q. +6.6v.62. +d.7v.5x. +1.5s.7H. +2.6B.5W. +3.6R.5L. +8.6z.5Z. +9.7t.5A. +1.5s.7I. +1.7t.5B. +1.8z.3T. +2.6B.5X. +2.6N.5S. +2.6z.5+. +b.3G.8C. +d.7v.5y. +1.5s.7J. +1.6L.5T. +1.8z.3U. +2.6B.5Y. +2.6R.5M. +2.6v.63. +7.6x.61. +9.6P.5R. +d.7v.5z. +1.8z.3V. +2.6R.5N. +7.6x.62. +1.8z.3W. +2.6B.5+. +9.7t.5C. +d.7v.5A. +1.4G.82. +1.7v.5B. +1.8z.3X. +2.6v.64. +2.7A.5v. +9.6R.5O. +9.7t.5D. +1.4G.83. +2.6z.5/. +2.7A.5w. +9.6R.5P. +1.4G.84. +2.6P.5S. +2.6z.60. +2.7t.5E. +7.6v.65. +9.7A.5x. +b.3J.8C. +1.6N.5T. +3.3K.8C. +3.6V.5L. +8.6z.61. +9.6R.5Q. +b.5F.7t. +d.7v.5C. +2.6B.5/. +2.6v.66. +3.5G.7t. +6.3L.8C. +8.6z.62. +9.7A.5y. +d.7v.5D. +0.7V.53. +1.8z.3Z. +2.6B.60. +3.3M.8C. +7.6v.67. +9.7A.5z. +1.6L.5U. +1.8z.3+. +2.6J.5V. +2.6v.68. +2.7g.5J. +2.7v.5E. +6.3N.8C. +7.6x.65. +9.6R.5R. +0.7V.55. +1.8z.3/. +2.6z.63. +2.7t.5H. +9.7A.5A. +1.4G.88. +1.6L.5V. +1.7A.5B. +2.5s.7K. +2.7g.5K. +9.7t.5I. +1.8z.41. +5.6P.5T. +7.6v.69. +h.6x.67. +1.8z.42. +2.6B.63. +2.6J.5W. +2.6z.64. +6.58.7V. +6.6v.6a. +b.6+.5L. +1.8z.43. +2.6R.5S. +2.7v.5H. +9.7A.5C. +1.6N.5U. +1.6v.6b. +1.8z.44. +2.6J.5Y. +2.6L.5W. +8.6z.65. +9.7A.5D. +b.3R.8C. +d.7v.5I. +1.4G.8d. +1.5s.7L. +1.8z.45. +2.6B.64. +2.6J.5Z. +2.6L.5X. +6.3S.8C. +7.6x.69. +d.71.5L. +1.6N.5V. +1.8z.46. +2.5s.7M. +2.6L.5Y. +2.6z.66. +2.7A.5E. +7.6x.6a. +1.4G.8f. +1.6L.5Z. +1.8z.47. +8.6z.67. +b.5F.7A. +0.7Z.53. +1.8z.48. +2.4G.8g. +2.6L.5+. +2.6z.68. +3.5G.7A. +1.8z.49. +2.4G.8h. +2.6B.66. +0.7V.5e. +0.7Z.55. +1.8z.4a. +2.6N.5W. +5.6P.5U. +9.6R.5T. +1.8z.4b. +2.6B.68. +2.6J.5/. +2.6N.5X. +2.7A.5H. +8.6z.69. +2.6J.60. +2.6N.5Y. +5.6P.5V. +6.6v.6c. +8.6z.6a. +9.7A.5I. +0.8C.3Y. +1.6N.5Z. +2.6J.61. +2.6L.5/. +6.58.7Z. +1.4G.8j. +1.6B.69. +1.6z.6b. +1.8S.3c. +2.6L.60. +2.6N.5+. +1.6g.6n. +1.6L.61. +1.8z.4d. +2.7t.5J. +1.6L.62. +2.6P.5W. +7.6x.6c. +1.8z.4e. +2.5s.7O. +2.6P.5X. +2.7t.5K. +0.8C.40. +1.6g.6q. +1.8z.4f. +2.6P.5Y. +1.8z.4g. +2.5s.7P. +2.6L.63. +2.6N.5/. +2.7v.5J. +5.6P.5Z. +9.6R.5U. +1.4G.8m. +1.6g.6r. +1.8z.4h. +2.6N.60. +2.6P.5+. +2.6v.6d. +1.4G.8n. +1.6N.61. +1.8z.4i. +1.98.2T. +2.6v.6e. +2.7v.5K. +9.6R.5V. +f.7Z.5e. +1.4G.8o. +1.6g.6t. +1.6N.62. +1.7d.5O. +2.6L.64. +2.7g.5M. +8.6z.6c. +1.7d.5P. +1.8z.4k. +2.5s.7Q. +2.7g.5N. +6.6v.6f. +1.6L.65. +2.8z.4l. +6.6v.6g. +1.6B.6c. +1.7d.5Q. +1.7g.5O. +1.8z.4m. +2.6N.63. +2.6P.5/. +2.6R.5W. +1.5s.7S. +1.7g.5P. +1.8z.4n. +2.6L.66. +2.6P.60. +2.6R.5X. +1.6g.6w. +1.6L.67. +1.8S.3j. +1.8z.4o. +2.6R.5Y. +2.7A.5J. +5.6P.61. +7.6x.6f. +1.5s.7T. +1.6v.6h. +1.7d.5R. +1.7g.5Q. +1.8S.3k. +1.8z.4p. +2.6L.68. +2.6N.64. +5.6P.62. +6.6R.5Z. +7.6x.6g. +1.6g.6y. +1.8S.3l. +2.6R.5+. +2.7A.5K. +6.85.53. +1.5s.7U. +1.6N.65. +2.6z.6d. +0.7V.5s. +1.6L.69. +1.6v.6i. +1.7g.5R. +1.8z.4r. +2.6P.63. +2.6z.6e. +6.85.55. +1.6L.6a. +1.6v.6j. +1.8z.4s. +1.98.2X. +2.6J.6b. +2.6N.66. +6.4c.8C. +1.6N.67. +1.8z.4t. +2.6B.6d. +2.6v.6k. +8.6z.6f. +1.5s.7X. +1.6L.6b. +1.6v.6l. +2.6B.6e. +2.6N.68. +2.6P.64. +2.6R.5/. +8.58.85. +8.6z.6g. +1.8z.4u. +2.6R.60. +1.6g.6A. +1.8z.4v. +2.7g.5S. +5.6P.65. +6.6R.61. +1.4G.8t. +1.6N.69. +1.8z.4w. +2.6v.6m. +6.5L.7t. +6.6R.62. +1.6N.6a. +1.6z.6h. +2.6P.66. +1.4G.8u. +1.6g.6C. +1.8z.4x. +1.98.2Z. +2.7t.5M. +2.8S.3t. +5.6P.67. +0.5u.7V. +0.8C.4j. +1.6g.6D. +1.6N.6b. +1.7d.5T. +1.8z.4y. +2.6P.68. +2.6R.63. +2.7t.5N. +2.8S.3u. +1.5s.7Y. +1.6z.6i. +1.8z.4z. +0.7Z.5s. +1.6L.6c. +1.6z.6j. +1.8z.4A. +6.85.5e. +9.7t.5O. +1.6g.6F. +1.7g.5T. +1.8z.4B. +2.6R.64. +2.6z.6k. +2.7v.5M. +5.6P.69. +9.7t.5P. +1.6B.6i. +1.6z.6l. +1.8z.4C. +2.7v.5N. +5.6P.6a. +1.8z.4D. +2.8S.3z. +6.6R.65. +9.7t.5Q. +1.4G.8y. +1.5s.7+. +1.6g.6G. +1.6P.6b. +2.6B.6k. +2.8z.4E. +d.7v.5O. +1.8z.4F. +2.6R.66. +2.6z.6m. +d.7v.5P. +1.6N.6c. +1.8z.4G. +6.4q.8C. +9.6R.67. +9.7t.5R. +1.7d.5U. +2.6R.68. +6.5L.7A. +d.7v.5Q. +1.5s.7/. +2.6B.6m. +2.6L.6d. +2.8S.3D. +0.5u.7Z. +1.6g.6I. +1.7d.5V. +1.8z.4H. +2.6L.6e. +2.7A.5M. +1.5s.80. +1.7g.5U. +1.8S.3F. +1.8z.4I. +2.7A.5N. +6.6R.69. +d.7v.5R. +1.6L.6f. +1.8z.4J. +2.7t.5S. +9.6R.6a. +1.6L.6g. +1.7g.5V. +1.8z.4K. +5.6P.6c. +9.7A.5O. +1.6R.6b. +1.8z.4L. +9.7A.5P. +1.8z.4M. +2.6N.6d. +1.8z.4N. +2.6N.6e. +2.7v.5S. +9.7A.5Q. +1.6L.6h. +1.7d.5Z. +1.8z.4O. +2.7g.5W. +1.5s.81. +1.6N.6f. +2.7g.5X. +2.8z.4P. +1.5s.82. +1.6N.6g. +1.8z.4Q. +2.7g.5Y. +4.7t.5T. +9.7A.5R. +1.5s.83. +1.6L.6i. +1.7g.5Z. +1.8z.4R. +2.6J.6k. +1.5s.84. +1.6L.6j. +1.8z.4S. +2.6P.6d. +2.7g.5+. +2.6L.6k. +2.6P.6e. +6.85.5s. +9.6R.6c. +1.5s.86. +1.6L.6l. +1.6N.6h. +2.8z.4T. +b.5F.7V. +d.7v.5T. +1.9i.2T. +2.6J.6m. +2.7A.5S. +2.8z.4U. +3.5G.7V. +5.6P.6f. +1.7d.61. +1.8z.4V. +2.8S.3P. +5.6P.6g. +1.5s.87. +1.6N.6i. +1.7d.62. +1.8z.4W. +2.6L.6m. +2.7g.5/. +6.6o.6o. +1.5s.88. +1.6N.6j. +2.7g.60. +6.6o.6p. +1.5s.89. +1.7g.61. +2.6N.6k. +4.7t.5U. +6.6p.6p. +1.5s.8a. +1.6N.6l. +1.6P.6h. +1.7g.62. +1.8z.4X. +1.98.35. +1.5s.8b. +1.8z.4Y. +1.98.36. +2.6R.6d. +4.7t.5V. +1.5s.8c. +1.8S.3T. +2.6R.6e. +6.6s.6o. +6.85.5u. +9.7A.5T. +1.5s.8d. +1.6P.6i. +2.6N.6m. +2.7g.63. +6.6s.6p. +d.7v.5U. +1.5s.8e. +1.6g.6Q. +1.6P.6j. +1.8z.4Z. +2.8S.3V. +6.6o.6u. +6.6R.6f. +9.6v.6n. +0.6v.6o. +1.5s.8f. +1.7d.65. +1.8S.3W. +2.6P.6k. +2.7t.5W. +6.6p.6u. +6.6R.6g. +b.5F.7Z. +d.7v.5V. +0.6v.6p. +1.6P.6l. +1.8S.3X. +1.98.38. +1.9i.2X. +2.5s.8g. +2.7g.64. +2.7t.5X. +3.5G.7Z. +1.6g.6T. +1.6v.6q. +1.8z.4+. +2.5s.8h. +2.7t.5Y. +6.6s.6s. +1.7d.67. +1.7g.65. +1.8z.4/. +4.7t.5Z. +9.6x.6n. +1.6R.6h. +1.8z.50. +2.6P.6m. +2.7t.5+. +2.7v.5W. +6.6s.6u. +9.6v.6r. +h.6o.6x. +1.5s.8i. +1.8z.51. +1.98.3a. +2.7g.66. +2.7v.5X. +6.6s.6v. +h.6p.6x. +0.6u.6u. +1.7g.67. +1.8S.3Z. +1.8z.52. +1.98.3b. +2.7v.5Y. +9.6v.6t. +9.7A.5U. +0.6v.6u. +1.5s.8j. +1.6g.6U. +1.6R.6i. +1.7d.69. +1.8S.3+. +1.98.3c. +2.7g.68. +d.7v.5Z. +1.6R.6j. +1.7d.6a. +1.8z.54. +1.98.3d. +1.9i.2Z. +2.7v.5+. +6.6v.6v. +9.6x.6r. +9.7A.5V. +1.5s.8k. +2.6R.6k. +2.7t.5/. +8.6z.6n. +h.6s.6x. +1.5s.8l. +1.6g.6X. +1.6R.6l. +1.7d.6b. +1.7g.69. +1.8S.41. +1.8z.56. +2.7t.60. +8.6z.6o. +9.6x.6t. +1.6g.6Y. +1.6v.6w. +1.7g.6a. +1.8S.42. +1.8z.57. +8.6z.6p. +9.7t.61. +h.6u.6x. +0.6v.6x. +1.6g.6Z. +1.6z.6q. +1.8z.58. +2.7A.5W. +9.7t.62. +1.5s.8m. +1.7g.6b. +1.8S.44. +2.6R.6m. +2.7A.5X. +2.7v.5/. +2.8z.59. +6.6B.6o. +9.6v.6y. +1.5s.8n. +1.8S.45. +2.7A.5Y. +2.7v.60. +2.8z.5a. +6.6B.6p. +8.6z.6r. +1.5s.8o. +1.8S.46. +2.7t.63. +2.8z.5b. +8.6z.6s. +9.7A.5Z. +d.7v.61. +1.5s.8p. +1.6g.6/. +2.4G.8E. +2.7A.5+. +8.6z.6t. +d.7v.62. +h.6x.6x. +1.6g.70. +1.8S.48. +6.6o.6E. +8.6z.6u. +9.6x.6y. +1.5s.8q. +1.7d.6c. +1.8z.5c. +2.7t.64. +6.6p.6E. +6.6s.6B. +8.6z.6v. +1.8S.4a. +2.7v.63. +2.8z.5d. +1.8S.4b. +1.98.3j. +6.6B.6u. +9.6v.6A. +9.7t.65. +0.6v.6B. +1.6z.6w. +1.7g.6c. +1.98.3k. +2.7A.5/. +2.8z.5f. +b.5F.85. +1.8z.5g. +1.98.3l. +2.4G.8F. +2.7A.60. +2.7t.66. +2.7v.64. +3.5G.85. +6.6s.6E. +9.6x.6z. +1.5s.8r. +1.6g.76. +1.8z.5h. +8.6z.6y. +9.6v.6C. +9.7A.61. +9.7t.67. +0.6u.6E. +1.5s.8s. +1.98.3n. +2.7t.68. +2.8z.5i. +7.6v.6D. +9.6x.6A. +9.7A.62. +d.7v.65. +0.6v.6E. +0.8C.53. +1.4G.8G. +1.98.3o. +2.8z.5j. +h.6B.6x. +1.4G.8H. +1.8S.4e. +1.98.3p. +2.7v.66. +6.6o.6H. +0.55.8C. +1.8S.4f. +1.8z.5k. +2.7A.63. +6.6p.6H. +6.6v.6F. +9.6x.6C. +9.7t.69. +d.7v.67. +1.4G.8I. +1.6g.7b. +2.7v.68. +6.6o.6J. +7.6x.6D. +8.6z.6z. +9.7t.6a. +1.4G.8J. +1.6L.6n. +1.7d.6f. +1.98.3s. +2.7g.6d. +6.6p.6J. +h.6E.6x. +1.4G.8K. +1.5s.8t. +1.6L.6o. +1.7d.6g. +1.7t.6b. +2.7A.64. +2.7g.6e. +2.8z.5l. +6.58.8C. +6.6v.6G. +8.6z.6A. +1.6g.7e. +1.6L.6p. +6.5L.7V. +6.6s.6H. +7.6x.6F. +8.6z.6B. +d.7v.69. +1.5s.8u. +1.6g.7f. +1.6L.6q. +1.7g.6f. +1.98.3t. +9.7A.65. +d.7v.6a. +0.6u.6H. +1.5s.8v. +1.7g.6g. +1.8z.5n. +1.98.3u. +2.8S.4l. +6.6s.6J. +8.6z.6C. +0.6v.6H. +1.5s.8w. +1.6g.7h. +1.6L.6r. +1.7d.6h. +1.7v.6b. +1.8S.4m. +1.8z.5o. +2.7A.66. +6.6B.6B. +6.6o.6M. +7.6x.6G. +8.6z.6D. +0.6u.6J. +1.6L.6s. +1.6N.6n. +1.8S.4n. +1.98.3w. +2.8z.5p. +6.6p.6M. +8.6z.6E. +9.6v.6I. +9.7A.67. +0.6v.6J. +1.6g.7i. +1.6L.6t. +1.8z.5q. +1.98.3x. +2.7A.68. +1.5s.8x. +1.6L.6u. +1.7d.6i. +1.7g.6h. +2.6g.7j. +8.6z.6F. +9.6v.6K. +0.8C.5e. +1.6g.7k. +1.6L.6v. +1.6N.6q. +1.7d.6j. +2.98.3z. +6.6B.6E. +9.7t.6c. +h.6H.6x. +1.5s.8y. +1.98.3A. +1.9i.35. +6.6s.6M. +9.6x.6I. +9.7A.69. +1.6B.6F. +1.6N.6r. +1.7d.6l. +1.7g.6i. +1.8z.5r. +1.9i.36. +6.6o.6O. +8.6z.6G. +9.7A.6a. +h.6J.6x. +1.5s.8z. +1.6g.7n. +1.6L.6w. +1.7g.6j. +1.98.3B. +6.6M.6u. +6.6P.6n. +6.6p.6O. +9.6x.6K. +0.6P.6o. +0.6v.6M. +1.6g.7o. +1.6L.6x. +1.6N.6t. +1.7A.6b. +1.98.3C. +2.7g.6k. +6.5L.7Z. +6.6E.6E. +d.7v.6c. +0.6P.6p. +1.6L.6y. +1.7g.6l. +2.98.3D. +1.6P.6q. +1.8S.4u. +1.98.3E. +8.6z.6H. +k.6N.6v. +1.8S.4v. +1.98.3F. +1.9i.38. +6.6s.6O. +8.6z.6I. +1.6g.7q. +2.7g.6m. +2.7t.6d. +2.8z.5t. +6.6P.6r. +8.6z.6J. +h.6M.6x. +1.6g.7r. +1.6N.6w. +2.7t.6e. +6.6B.6H. +6.6O.6u. +6.6s.6P. +8.6z.6K. +0.6v.6O. +6.6P.6t. +8.6z.6L. +0.6P.6u. +1.6N.6y. +1.8S.4y. +1.9i.3a. +6.6B.6J. +9.7t.6f. +0.6v.6P. +1.6L.6A. +1.9i.3b. +2.7v.6d. +6.6R.6n. +9.7A.6c. +9.7t.6g. +1.6L.6B. +1.8S.4A. +1.98.3H. +1.9i.3c. +2.7v.6e. +3.6R.6o. +6.6E.6H. +1.8S.4B. +1.98.3I. +1.9i.3d. +3.6R.6p. +8.6z.6M. +h.6O.6x. +0.6J.6E. +1.4G.8Q. +1.5s.8B. +1.6L.6C. +1.6P.6w. +1.6R.6q. +1.8S.4C. +d.7v.6f. +1.6L.6D. +1.6N.6z. +1.7t.6h. +d.7v.6g. +h.6P.6x. +1.6L.6E. +2.8S.4E. +6.6B.6M. +6.6P.6y. +6.6R.6r. +1.6g.7x. +1.6N.6A. +3.6R.6s. +1.6L.6F. +1.7t.6i. +2.6B.6N. +9.6R.6t. +1.7t.6j. +1.7v.6h. +1.98.3O. +2.7A.6d. +3.6R.6u. +7.6v.6Q. +8.6z.6O. +1.6g.7y. +1.6N.6C. +2.7A.6e. +2.7t.6k. +2.8z.5v. +2.98.3P. +3.6R.6v. +3.6V.6o. +6.5s.8C. +6.6M.6E. +0.6H.6H. +1.6L.6G. +1.6N.6D. +1.7t.6l. +2.8z.5w. +3.6V.6p. +8.6z.6P. +1.6g.7z. +1.7v.6i. +1.8z.5x. +1.98.3Q. +6.6B.6O. +6.6v.6T. +9.7A.6f. +0.6J.6H. +1.6R.6w. +1.7v.6j. +6.6P.6A. +7.6x.6Q. +9.7A.6g. +1.6N.6F. +1.8z.5y. +2.7t.6m. +2.7v.6k. +6.5L.85. +6.6B.6P. +h.6R.6x. +0.6J.6J. +1.6L.6H. +1.7v.6l. +1.8z.5z. +1.9i.3j. +3.6V.6s. +6.6R.6y. +1.6L.6I. +1.98.3T. +1.9i.3k. +6.6O.6E. +7.6x.6T. +9.6P.6C. +1.6g.7B. +1.6L.6J. +1.6N.6G. +1.7A.6h. +1.8z.5A. +1.98.3U. +1.9i.3l. +3.6V.6u. +5.6P.6D. +6.6v.6U. +b.6+.6o. +0.6P.6E. +1.6L.6K. +1.8z.5B. +1.98.3V. +2.7v.6m. +3.6V.6v. +b.6+.6p. +0.5u.8C. +1.6L.6L. +1.98.3W. +1.9i.3n. +2.8S.4P. +6.6M.6H. +6.6v.6W. +8.6z.6Q. +1.7A.6i. +1.98.3X. +1.9i.3o. +5.6P.6F. +6.6v.6X. +8.6z.6R. +d.71.6o. +1.7A.6j. +1.8z.5C. +1.9i.3p. +6.6M.6J. +6.6v.6Y. +7.6x.6U. +d.71.6p. +1.6N.6I. +1.8z.5D. +2.7A.6k. +6.6R.6A. +6.6v.6Z. +8.6z.6T. +b.6+.6s. +h.6V.6x. +1.5s.8D. +1.6L.6M. +1.7A.6l. +2.6J.6N. +3.6R.6B. +5.6P.6G. +7.6x.6W. +1.6N.6K. +1.9i.3s. +2.8S.4T. +2.8z.5E. +7.6x.6X. +b.6+.6u. +1.98.3Z. +2.6L.6N. +2.8S.4U. +6.6O.6H. +6.6R.6C. +7.6x.6Y. +b.6+.6v. +d.71.6s. +1.6g.7H. +1.8S.4V. +1.98.3+. +2.7A.6m. +6.6R.6D. +7.6v.6/. +7.6x.6Z. +0.6P.6H. +1.4G.8X. +1.6g.7I. +1.8S.4W. +1.98.3/. +1.9i.3t. +3.6R.6E. +6.6M.6M. +6.6O.6J. +7.6v.70. +8.6z.6U. +d.71.6u. +1.6g.7J. +1.9i.3u. +6.6P.6I. +6.6S.6E. +7.6v.71. +8.6z.6V. +0.6P.6J. +1.6L.6O. +1.98.41. +2.8z.5H. +6.6R.6F. +7.6v.72. +8.6z.6W. +h.6+.6x. +1.8S.4X. +1.8z.5I. +1.98.42. +1.9i.3w. +6.6P.6K. +7.6v.73. +7.6x.6/. +8.6z.6X. +1.6L.6P. +1.7d.6n. +1.98.43. +1.9i.3x. +2.6N.6N. +3.6V.6B. +7.6v.74. +7.6x.70. +8.6z.6Y. +1.98.44. +6.6R.6G. +6.6v.75. +7.6x.71. +8.6z.6Z. +1.98.45. +2.9i.3z. +6.6M.6O. +6.6v.76. +7.6x.72. +1.7d.6q. +1.7g.6n. +1.8S.4Z. +1.98.46. +1.9i.3A. +2.4G.8Z. +6.6v.77. +7.6x.73. +0.6P.6M. +1.98.47. +2.5s.8E. +3.6V.6E. +6.6v.78. +7.6x.74. +8.6z.6+. +1.4G.8+. +1.7d.6r. +1.98.48. +1.9i.3B. +3.6R.6H. +6.6v.79. +7.6x.75. +8.6z.6/. +1.7g.6q. +1.98.49. +1.9i.3C. +2.6P.6N. +2.8S.4+. +6.6v.7a. +7.6x.76. +8.6z.70. +9.6R.6I. +1.7d.6t. +1.98.4a. +2.9i.3D. +3.6R.6J. +7.6x.77. +8.6z.71. +b.6+.6B. +m.6v.7b. +1.6L.6Q. +1.7g.6r. +1.98.4b. +1.9i.3E. +2.6g.7K. +2.8S.50. +6.6O.6O. +6.6v.7c. +7.6x.78. +8.6z.72. +9.6R.6K. +1.6L.6R. +1.9i.3F. +6.6v.7d. +7.6x.79. +8.6z.73. +0.6P.6O. +1.7g.6t. +2.5s.8F. +6.6v.7e. +7.6x.7a. +8.6z.74. +d.71.6B. +1.6B.72. +1.6L.6T. +7.6v.7f. +7.6x.7b. +8.6z.75. +b.6+.6E. +0.6P.6P. +1.6B.73. +1.7d.6w. +1.98.4d. +6.6v.7g. +7.6x.7c. +8.6z.76. +b.5F.8C. +1.5s.8G. +1.6B.74. +1.6g.7L. +3.5G.8C. +3.6R.6M. +3.6V.6H. +6.6v.7h. +7.6x.7d. +8.6z.77. +1.5s.8H. +1.6B.75. +1.6N.6Q. +1.7d.6y. +1.8S.56. +1.98.4e. +2.6g.7M. +2.8z.5J. +7.6x.7e. +8.6z.78. +d.71.6E. +1.7g.6w. +1.98.4f. +1.9i.3H. +2.6R.6N. +3.6V.6J. +6.6v.7i. +7.6x.7f. +8.6z.79. +1.5s.8I. +1.6B.77. +1.6L.6U. +1.98.4g. +1.9i.3I. +2.6v.7j. +2.8z.5K. +7.6x.7g. +8.6z.7a. +1.5s.8J. +1.6L.6V. +1.6N.6T. +1.7g.6y. +1.98.4h. +2.8S.59. +6.6v.7k. +7.6x.7h. +8.6z.7b. +1.5s.8K. +1.6B.79. +1.6L.6W. +1.98.4i. +2.8S.5a. +7.6v.7l. +8.6z.7c. +1.6B.7a. +1.6L.6X. +2.8S.5b. +3.6R.6O. +6.6v.7m. +7.6x.7i. +8.6z.7d. +1.4G.93. +1.6L.6Y. +1.98.4k. +5.6P.6Q. +6.6v.7n. +8.6z.7e. +9.7t.6n. +b.6+.6H. +1.4G.94. +1.6B.7c. +1.6L.6Z. +1.7d.6A. +2.98.4l. +3.6R.6P. +3.6V.6M. +6.6o.7t. +7.6v.7o. +7.6x.7k. +8.6z.7f. +1.6N.6U. +1.98.4m. +1.9i.3O. +2.8S.5c. +6.6p.7t. +7.6x.7l. +8.6z.7g. +b.6+.6J. +1.7t.6q. +1.98.4n. +2.6J.6/. +2.8S.5d. +2.9i.3P. +5.6P.6T. +7.6x.7m. +8.6z.7h. +d.71.6H. +1.6L.6+. +1.6N.6W. +1.7d.6C. +1.7g.6A. +1.98.4o. +7.6v.7p. +7.6x.7n. +d.7v.6n. +1.6L.6/. +1.6N.6X. +1.7d.6D. +1.98.4p. +1.9i.3Q. +2.6g.7O. +2.8S.5f. +7.6v.7q. +7.6x.7o. +8.6z.7i. +9.7t.6r. +d.71.6J. +1.4G.95. +1.6B.7h. +1.6L.70. +1.6N.6Y. +2.6J.72. +2.6z.7j. +6.6s.7t. +6.6v.7r. +1.6L.71. +1.6N.6Z. +1.7g.6C. +1.7v.6q. +2.6g.7P. +3.6V.6O. +8.6z.7k. +9.7t.6t. +0.6u.7t. +1.6B.7i. +1.6L.72. +1.7d.6F. +1.7g.6D. +1.98.4r. +2.8S.5i. +5.6P.6U. +7.6v.7s. +7.6x.7p. +8.6z.7l. +b.6+.6M. +0.6v.7t. +1.6L.73. +1.98.4s. +1.9i.3T. +2.6B.7j. +2.6J.75. +2.8S.5j. +3.6V.6P. +6.6R.6Q. +7.6x.7q. +8.6z.7m. +d.7v.6r. +1.4G.96. +1.6L.74. +1.98.4t. +1.9i.3U. +2.6J.76. +3.6R.6R. +5.6P.6W. +7.6x.7r. +8.6z.7n. +1.5s.8L. +1.6B.7l. +1.6L.75. +1.6N.6/. +1.7d.6G. +1.7g.6F. +1.9i.3V. +2.6g.7Q. +2.6J.77. +2.8S.5k. +5.6P.6X. +6.6v.7u. +8.6z.7o. +d.71.6M. +d.7v.6t. +1.5s.8M. +1.6L.76. +1.6N.70. +1.7t.6w. +1.98.4u. +1.9i.3W. +2.6J.78. +5.6P.6Y. +6.6S.6S. +7.6x.7s. +9.6R.6T. +9.7A.6n. +0.7A.6o. +1.5s.8N. +1.6L.77. +1.6N.71. +1.98.4v. +1.9i.3X. +5.6P.6Z. +6.6v.7v. +h.7t.6x. +0.7A.6p. +1.6g.7S. +1.6L.78. +1.6N.72. +1.7g.6G. +1.98.4w. +2.6J.7a. +2.8S.5l. +7.6v.7w. +8.6z.7p. +9.7t.6y. +b.6+.6O. +1.6L.79. +1.6N.73. +1.7A.6q. +7.6v.7x. +7.6x.7u. +8.6z.7q. +1.6g.7T. +1.6L.7a. +1.6N.74. +1.7d.6I. +1.7v.6w. +1.98.4x. +2.6J.7c. +8.6z.7r. +b.6+.6P. +1.6L.7b. +1.6N.75. +1.8S.5n. +1.98.4y. +2.6J.7d. +5.6P.6/. +6.6R.6U. +7.6x.7v. +9.7A.6r. +d.71.6O. +1.5s.8O. +1.6g.7U. +1.6L.7c. +1.6N.76. +1.7d.6K. +1.98.4z. +1.9i.3Z. +2.6J.7e. +2.8z.5M. +3.6R.6V. +5.6P.70. +6.6s.7A. +6.6v.7y. +7.6x.7w. +8.6z.7s. +d.7v.6y. +1.5s.8P. +1.6L.7d. +1.6N.77. +1.7g.6I. +1.98.4A. +1.9i.3+. +2.6J.7f. +2.8S.5p. +2.8z.5N. +5.6P.71. +6.6R.6W. +7.6x.7x. +8.6z.7t. +9.7A.6t. +0.7A.6u. +1.6L.7e. +1.6N.78. +1.98.4B. +1.9i.3/. +5.6P.72. +6.6R.6X. +9.6v.7z. +0.7A.6v. +1.5s.8Q. +1.6B.7s. +1.6L.7f. +1.6N.79. +1.7g.6K. +1.8z.5O. +1.98.4C. +2.6J.7h. +5.6P.73. +6.6R.6Y. +8.6z.7u. +9.7t.6A. +1.6g.7X. +1.6N.7a. +1.7g.6L. +1.8z.5P. +1.98.4D. +1.9i.41. +5.6P.74. +6.6B.7t. +6.6R.6Z. +7.6x.7y. +1.5s.8R. +1.6L.7h. +1.6N.7b. +1.9i.42. +2.6J.7i. +2.98.4E. +5.6P.75. +8.6z.7v. +1.6B.7u. +1.6N.7c. +1.7A.6w. +1.8S.5r. +1.8z.5Q. +1.98.4F. +1.9i.43. +2.6J.7j. +5.6P.76. +8.6z.7w. +9.6x.7z. +9.7t.6C. +1.5s.8S. +1.6L.7i. +1.6N.7d. +1.98.4G. +1.9i.44. +2.6J.7k. +4.7t.6D. +5.6P.77. +7.6v.7B. +8.6z.7x. +b.6+.6R. +d.7v.6A. +h.7A.6x. +1.4G.99. +1.6N.7e. +1.9i.45. +2.6L.7j. +3.6V.6V. +5.6P.78. +6.6E.7t. +7.6v.7C. +9.6R.6/. +9.7A.6y. +1.6B.7w. +1.6L.7k. +1.6N.7f. +1.8z.5R. +1.9i.46. +2.6J.7m. +5.6P.79. +7.6v.7D. +9.6R.70. +1.5s.8T. +1.6L.7l. +1.6N.7g. +1.98.4H. +1.9i.47. +2.6J.7n. +5.6P.7a. +7.6v.7E. +8.6z.7y. +9.6R.71. +9.7t.6F. +d.7v.6C. +1.6g.7Y. +1.6L.7m. +1.6N.7h. +1.98.4I. +1.9i.48. +2.6J.7o. +5.6P.7b. +7.6x.7B. +9.6R.72. +d.7v.6D. +1.6L.7n. +1.98.4J. +1.9i.49. +2.4G.9b. +2.8S.5t. +5.6P.7c. +7.6x.7C. +8.6z.7z. +9.6R.73. +1.6L.7o. +1.6N.7i. +1.98.4K. +1.9i.4a. +4.7t.6G. +5.6P.7d. +6.6v.7F. +7.6x.7D. +8.6z.7A. +9.6R.74. +1.98.4L. +1.9i.4b. +2.6J.7p. +2.6N.7j. +2.8z.5S. +5.6P.7e. +6.6R.75. +7.6x.7E. +d.7v.6F. +1.5s.8U. +1.6B.7z. +1.6N.7k. +1.98.4M. +5.6P.7f. +6.6v.7G. +9.6R.76. +9.7A.6A. +b.6+.6V. +0.7A.6B. +1.6g.7+. +1.6L.7p. +1.6N.7l. +1.98.4N. +5.6P.7g. +6.5L.8C. +7.6v.7H. +9.6R.77. +0.7t.6H. +1.6L.7q. +1.6N.7m. +1.98.4O. +5.6P.7h. +6.6R.78. +7.6v.7I. +7.6x.7F. +8.6z.7B. +d.7v.6G. +1.6L.7r. +1.6N.7n. +1.9i.4d. +2.6J.7s. +2.98.4P. +6.6v.7J. +8.6z.7C. +9.6R.79. +9.7A.6C. +9.7t.6I. +d.71.6V. +0.6J.7t. +1.6N.7o. +1.98.4Q. +5.6P.7i. +6.6R.7a. +7.6x.7G. +8.6z.7D. +9.7A.6D. +0.7A.6E. +1.5s.8V. +1.6B.7B. +1.6L.7s. +1.98.4R. +1.9i.4e. +2.6P.7j. +6.6R.7b. +7.6x.7H. +8.6z.7E. +9.7t.6K. +1.6B.7C. +1.6L.7t. +1.7d.6Q. +1.8z.5T. +1.98.4S. +1.9i.4f. +2.4G.9e. +2.6J.7u. +5.6P.7k. +7.6x.7I. +9.6R.7c. +1.5s.8W. +1.6g.80. +1.6N.7p. +1.9i.4g. +5.6P.7l. +6.6R.7d. +7.6x.7J. +9.7A.6F. +b.6+.6+. +d.7v.6I. +1.6B.7E. +1.6L.7u. +1.6N.7q. +1.9i.4h. +2.6J.7v. +2.98.4T. +5.6P.7m. +8.6z.7F. +9.6R.7e. +1.6N.7r. +1.7d.6T. +1.7g.6Q. +1.9i.4i. +2.98.4U. +5.6P.7n. +9.6R.7f. +d.7v.6K. +1.98.4V. +2.6J.7x. +2.8S.5v. +5.6P.7o. +6.6M.7t. +8.6z.7G. +9.6R.7g. +9.7A.6G. +d.71.6+. +d.7v.6L. +1.5s.8X. +1.6B.7F. +1.6L.7w. +1.6N.7s. +1.98.4W. +1.9i.4k. +2.8S.5w. +8.6z.7H. +9.6R.7h. +1.5s.8Y. +1.6L.7x. +1.7g.6T. +2.7t.6N. +2.9i.4l. +8.6z.7I. +1.6B.7G. +1.9i.4m. +5.6P.7p. +8.6z.7J. +9.6R.7i. d.71.71. -5.79.71. -0.71.7D. -0.2p.71. -0.2p.71. -1.7B.72. -1.86.72. -1.8d.72. -1.7B.73. -1.7R.73. -1.86.73. -1.8d.73. -1.7B.74. -1.86.74. -1.8d.74. -1.7B.75. -1.86.75. -1.8d.75. -1.7B.76. -1.86.76. -1.8d.76. -1.7B.77. -1.86.77. -1.8d.77. -1.7B.78. -1.7R.78. -1.86.78. -1.8d.78. -5.79.79. -5.79.7D. -5.79.2p. -5.79.2p. -1.7B.7a. -1.7R.7a. -1.86.7a. -1.8d.7a. -1.7B.7b. -1.86.7b. -1.8d.7b. -1.7B.7c. -1.7R.7c. -1.86.7c. -1.8d.7c. -1.7B.7d. -1.86.7d. -1.8d.7d. -1.7B.7e. -1.7R.7e. -1.86.7e. -1.8d.7e. -1.7B.7f. -1.86.7f. -1.8d.7f. -1.7B.7g. -1.86.7g. -1.8d.7g. -1.7B.7h. -1.7R.7h. -1.86.7h. -1.8d.7h. -1.7B.7i. -1.86.7i. -1.8d.7i. -1.7B.7j. -1.7R.7j. -1.86.7j. -1.8d.7j. -1.7B.7k. -1.7R.7k. -1.86.7k. -1.8d.7k. -1.7B.7l. -1.7R.7l. -1.86.7l. -1.8d.7l. -1.7B.7m. -1.7R.7m. -1.86.7m. -1.8d.7m. -1.7B.7o. -1.86.7o. -1.8d.7o. -1.7B.7p. -1.7R.7p. -1.86.7p. -1.8d.7p. -1.7B.7r. -1.7R.7r. -1.86.7r. -1.8d.7r. -1.7B.7s. -1.7R.7s. -1.86.7s. -1.8d.7s. -1.7B.7t. -1.7R.7t. -1.86.7t. -1.8d.7t. -1.7B.7u. -1.7R.7u. -1.86.7u. -1.8d.7u. -1.7B.7v. -1.86.7v. -1.8d.7v. -1.7B.7w. -1.7R.7w. -1.86.7w. -1.8d.7w. -1.7B.7x. -1.7R.7x. -1.86.7x. -1.8d.7x. -1.7B.7y. -1.7R.7y. -1.86.7y. -1.8d.7y. -1.7B.7z. -1.7R.7z. -1.86.7z. -1.8d.7z. -1.86.7A. -1.7B.7C. -1.7B.7E. -1.7B.7F. -1.7B.7G. -1.7B.7H. -1.7B.7I. -1.7B.7J. -1.7B.7K. -1.7B.7L. -1.7B.7M. -1.7B.7N. -1.7B.7O. -1.7B.7P. -1.7B.7Q. -1.7B.7R. -1.7B.7S. -1.7B.7T. -1.7B.7U. -1.7B.7V. -1.7B.7W. -1.7B.7X. -1.7B.7Y. -1.7B.7Z. -1.7B.7+. -1.7B.7/. -1.7B.80. -1.7B.81. -1.7B.82. -1.7B.83. -1.7B.84. -1.86.7B. -1.7B.87. -1.7B.88. -1.7B.89. -1.7B.8a. -1.7B.8b. -1.8d.7B. -1.7B.8e. -1.7B.8f. -1.7B.8g. -1.7B.8h. -1.7B.8i. -1.7B.8j. -1.7B.8k. -1.7B.8l. -1.7B.8m. -1.7B.8n. -1.7B.8o. -1.7B.8p. -1.7B.8q. -1.7B.8r. -1.7B.8s. -1.7B.8t. -1.7B.8u. -1.7B.8v. -1.7B.8w. -1.7B.8x. -1.7B.8y. -1.7B.8z. -1.7B.8A. -1.7B.8B. -1.7B.8C. -1.7B.8D. -1.7B.8E. -1.7B.8F. -1.7B.8G. -1.7B.8H. -1.7B.8I. -1.7B.8J. -1.7B.8K. -1.7B.8L. -1.7B.8M. -1.7B.8N. -1.7B.8O. -1.7B.8P. -1.7B.3b. -1.7B.46. -1.7B.4X. -1.7B.5x. -1.7B.5J. -1.7B.7n. -1.7B.7q. -1.7R.7C. -1.86.7C. -1.8d.7C. -0.7D.7D. -0.2p.7D. -0.2p.7D. -1.7R.7E. -1.86.7E. -1.8d.7E. -1.7R.7F. -1.86.7F. -1.8d.7F. -1.7R.7G. -1.86.7G. -1.8d.7G. -1.86.7H. -1.8d.7H. -1.86.7I. -1.8d.7I. -1.86.7J. -1.8d.7J. -1.7R.7K. -1.86.7K. -1.8d.7K. -1.7R.7L. -1.86.7L. -1.8d.7L. -1.7R.7M. -1.86.7M. -1.8d.7M. -1.86.7N. -1.8d.7N. -1.7R.7O. -1.86.7O. -1.8d.7O. -1.7R.7P. -1.86.7P. -1.8d.7P. -1.7R.7Q. -1.86.7Q. -1.8d.7Q. -1.7R.7R. -1.7R.7T. -1.7R.7V. -1.7R.7W. -1.7R.7X. -1.7R.7Y. -1.7R.83. -1.7R.84. -1.7R.85. -1.86.7R. -1.7R.87. -1.7R.8a. -1.7R.8c. -1.7R.8d. -1.7R.8e. -1.7R.8f. -1.7R.8g. -1.7R.8h. -1.7R.8i. -1.7R.8j. -1.7R.8k. -1.7R.8l. -1.7R.8m. -1.7R.8n. -1.7R.8o. -1.7R.8p. -1.7R.8q. -1.7R.8r. -1.7R.8s. -1.7R.8t. -1.7R.8u. -1.7R.8v. -1.7R.8w. -1.7R.8x. -1.7R.8y. -1.7R.8z. -1.7R.8A. -1.7R.8B. -1.7R.8C. -1.7R.8D. -1.7R.8E. -1.7R.8F. -1.7R.8G. -1.7R.8H. -1.7R.8I. -1.7R.8J. -1.7R.3b. -1.7R.5x. -1.7R.7n. -1.86.7S. -1.8d.7S. -1.86.7T. -1.8d.7T. -1.86.7U. -1.8d.7U. -1.86.7V. -1.8d.7V. -1.86.7W. -1.8d.7W. -1.86.7X. -1.8d.7X. -1.86.7Y. -1.8d.7Y. -1.86.7Z. -1.8d.7Z. -1.86.7+. -1.8d.7+. -1.86.7/. -1.8d.7/. -1.86.80. -1.8d.80. -1.86.81. -1.8d.81. -1.86.82. -1.8d.82. -1.86.83. -1.8d.83. -1.86.84. -1.8d.84. -1.86.85. -1.8d.85. -1.86.87. -1.86.88. -1.86.89. -1.86.8a. -1.86.8b. -1.86.8c. -1.86.8d. -1.86.8e. -1.86.8f. -1.86.8g. -1.86.8h. -1.86.8i. -1.86.8j. -1.86.8k. -1.86.8l. -1.86.8m. -1.86.8n. -1.86.8o. -1.86.8p. -1.86.8q. -1.86.8r. -1.86.8s. -1.86.8t. -1.86.8u. -1.86.8v. -1.86.8w. -1.86.8x. -1.86.8y. -1.86.8z. -1.86.8A. -1.86.8B. -1.86.8C. -1.86.8D. -1.86.8E. -1.86.8F. -1.86.8G. -1.86.8H. -1.86.8I. -1.86.8J. -1.86.8K. -1.86.8L. -1.86.8M. -1.86.8N. -1.86.8O. -1.86.8P. -1.86.3b. -1.86.46. -1.86.4X. -1.86.5x. -1.86.5J. -1.86.7n. -1.86.7q. -1.8d.87. -1.8d.88. -1.8d.89. -1.8d.8a. -1.8d.8b. -1.8d.8d. -1.8d.8e. -1.8d.8f. -1.8d.8g. -1.8d.8h. -1.8d.8i. -1.8d.8j. -1.8d.8k. -1.8d.8l. -1.8d.8m. -1.8d.8n. -1.8d.8o. -1.8d.8p. -1.8d.8q. -1.8d.8r. -1.8d.8s. -1.8d.8t. -1.8d.8u. -1.8d.8v. -1.8d.8w. -1.8d.8x. -1.8d.8y. -1.8d.8z. -1.8d.8A. -1.8d.8B. -1.8d.8C. -1.8d.8D. -1.8d.8E. -1.8d.8F. -1.8d.8G. -1.8d.8H. -1.8d.8I. -1.8d.8J. -1.8d.8K. -1.8d.8L. -1.8d.8M. -1.8d.8N. -1.8d.8O. -1.8d.8P. -1.8d.3b. -1.8d.46. -1.8d.4X. -1.8d.5x. -1.8d.5J. -1.8d.7n. -1.8d.7q. -0.2p.2p. -0.2p.2p. -0.2p.2p. -2.M.J. -6.5I.3v. -4.5I.4T. -5.60.5Y. -8.60.5M. -4.60.4T. -8.6M.5Y. -8.6M.5M. -a.6b.5b. -1.5Y.5K. -4.5K.4T. -1.4s.3I. -1.51.3I. -c.6H.2d. -` \ No newline at end of file +0.7A.6H. +1.6g.81. +1.6N.7u. +1.7d.6U. +1.8S.5y. +1.98.4X. +1.9i.4n. +2.6R.7j. +2.6v.7K. +5.6P.7q. +1.6g.82. +1.6L.7y. +1.8S.5z. +1.8z.5U. +1.98.4Y. +1.9i.4o. +2.4G.9g. +5.6P.7r. +6.6R.7k. +9.7A.6I. +0.7A.6J. +1.6g.83. +1.7d.6W. +1.9i.4p. +2.7v.6N. +6.6O.7t. +9.6R.7l. +1.6L.7z. +1.6N.7w. +1.7d.6X. +1.7g.6U. +1.8S.5A. +1.8z.5V. +5.6P.7s. +6.6R.7m. +9.7A.6K. +0.6P.7t. +1.6L.7A. +1.6N.7x. +1.7d.6Y. +1.8S.5B. +1.98.4Z. +2.5s.8Z. +6.6R.7n. +1.7d.6Z. +1.7g.6W. +1.9i.4r. +6.6v.7L. +9.6R.7o. +1.5s.8+. +1.7g.6X. +1.9i.4s. +2.6J.7B. +2.6v.7M. +5.6P.7u. +1.6N.7y. +1.7g.6Y. +1.8S.5C. +1.98.4+. +1.9i.4t. +2.6J.7C. +2.8z.5W. +0.7A.6M. +1.6L.7B. +1.7g.6Z. +1.8S.5D. +1.98.4/. +2.6J.7D. +2.8z.5X. +5.6P.7v. +9.6R.7p. +1.6g.88. +1.6L.7C. +1.6N.7z. +1.7d.6/. +1.98.50. +1.9i.4u. +2.8z.5Y. +5.6P.7w. +6.6R.7q. +7.6x.7L. +1.5s.8/. +1.6L.7D. +1.7d.70. +1.8z.5Z. +1.98.51. +1.9i.4v. +2.6z.7K. +2.7A.6N. +2.8S.5E. +5.6P.7x. +6.6R.7r. +1.6L.7E. +1.7d.71. +1.98.52. +1.9i.4w. +2.8z.5+. +1.7d.72. +1.7g.6/. +2.6J.7F. +4.7t.6Q. +9.6R.7s. +1.5s.90. +1.7d.73. +1.7g.70. +1.98.54. +1.9i.4x. +2.6B.7K. +3.6R.7t. +5.6P.7y. +0.7A.6O. +1.6g.8d. +1.6L.7F. +1.6N.7B. +1.7d.74. +1.7g.71. +1.9i.4y. +2.6J.7G. +1.6g.8e. +1.6N.7C. +1.7d.75. +1.7g.72. +1.98.56. +1.9i.4z. +2.6J.7H. +2.8S.5H. +6.6P.7z. +6.6R.7u. +8.6z.7L. +9.7t.6T. +0.7A.6P. +1.6g.8f. +1.6L.7G. +1.6N.7D. +1.7d.76. +1.7g.73. +1.8S.5I. +1.98.57. +1.9i.4A. +2.6z.7M. +2.8z.5/. +d.7v.6Q. +1.6L.7H. +1.6N.7E. +1.7d.77. +1.7g.74. +1.98.58. +1.9i.4B. +2.6g.8g. +2.6v.7O. +2.8z.60. +6.6R.7v. +1.5s.91. +1.6L.7I. +1.7d.78. +1.7g.75. +1.8z.61. +1.9i.4C. +2.6g.8h. +2.98.59. +6.6R.7w. +1.6L.7J. +1.7d.79. +1.7g.76. +1.8z.62. +1.9i.4D. +2.6B.7M. +2.6v.7P. +2.98.5a. +6.6R.7x. +d.7v.6T. +1.5s.92. +1.6N.7F. +1.7d.7a. +1.7g.77. +2.98.5b. +2.9i.4E. +5.6P.7B. +9.7t.6U. +1.5s.93. +1.7d.7b. +1.7g.78. +1.9i.4F. +3.6V.7t. +5.6P.7C. +1.4G.9i. +1.5s.94. +1.6N.7G. +1.7d.7c. +1.7g.79. +2.8z.63. +5.6P.7D. +6.6R.7y. +9.7t.6W. +1.6g.8j. +1.6N.7H. +1.7d.7d. +1.7g.7a. +1.98.5c. +2.6v.7Q. +5.6P.7E. +6.6o.7V. +9.7t.6X. +1.6N.7I. +1.7d.7e. +1.7g.7b. +2.98.5d. +6.6p.7V. +9.6R.7z. +9.7A.6Q. +9.7t.6Y. +d.7v.6U. +1.6N.7J. +1.7d.7f. +1.7g.7c. +1.9i.4H. +2.8z.64. +3.6R.7A. +9.7t.6Z. +1.6v.7S. +1.7d.7g. +1.9i.4I. +2.98.5f. +5.6P.7F. +d.7v.6W. +1.5s.95. +1.7d.7h. +1.7g.7e. +1.8z.65. +1.98.5g. +1.9i.4J. +2.6z.7O. +9.7A.6T. +d.7v.6X. +1.7g.7f. +1.98.5h. +1.9i.4K. +2.6J.7K. +5.6P.7G. +6.6s.7V. +7.6v.7T. +b.6+.7t. +d.7v.6Y. +1.6g.8m. +1.7d.7i. +1.7g.7g. +1.9i.4L. +2.6z.7P. +2.8z.66. +2.98.5i. +5.6P.7H. +9.6R.7B. +9.7t.6/. +d.7v.6Z. +0.7V.6u. +1.6g.8n. +1.6v.7U. +1.7g.7h. +1.8z.67. +1.9i.4M. +2.6B.7O. +2.6L.7K. +2.98.5j. +5.6P.7I. +9.6R.7C. +9.7t.70. +0.6v.7V. +1.5s.96. +1.6g.8o. +1.7d.7k. +1.9i.4N. +2.8S.5J. +2.8z.68. +5.6P.7J. +9.6R.7D. +9.7t.71. +1.7d.7l. +1.7g.7i. +1.98.5k. +1.9i.4O. +2.6B.7P. +7.6x.7T. +9.6R.7E. +9.7A.6U. +9.7t.72. +1.7d.7m. +2.4G.9j. +2.6z.7Q. +2.7g.7j. +2.8S.5K. +2.9i.4P. +3.6V.7A. +9.7t.73. +d.7v.6/. +1.6v.7X. +1.7d.7n. +1.7g.7k. +1.8z.69. +1.9i.4Q. +2.4G.9k. +2.6J.7M. +4.7A.6W. +6.6o.7Z. +9.7t.74. +d.7v.70. +1.6L.7L. +1.7d.7o. +1.7g.7l. +1.8z.6a. +1.9i.4R. +2.98.5l. +4.7A.6X. +6.6p.7Z. +6.6R.7F. +9.7t.75. +d.7v.71. +h.7V.6x. +1.6z.7S. +1.7g.7m. +1.9i.4S. +2.6B.7Q. +2.6L.7M. +2.6N.7K. +4.7A.6Y. +9.7t.76. +d.7v.72. +1.7g.7n. +1.8z.6b. +4.7A.6Z. +4.7t.77. +6.6R.7G. +d.7v.73. +1.7d.7p. +1.7g.7o. +1.98.5n. +2.9i.4T. +8.6z.7T. +9.6R.7H. +9.7t.78. +d.7v.74. +1.7d.7q. +1.98.5o. +2.9i.4U. +6.6s.7Z. +9.6R.7I. +9.7t.79. +m.7v.75. +1.5s.97. +1.6z.7U. +1.7d.7r. +1.9i.4V. +2.98.5p. +6.6R.7J. +9.7t.7a. +b.6+.7A. +d.7v.76. +0.6u.7Z. +1.6N.7L. +1.6v.7Y. +1.7g.7p. +1.98.5q. +1.9i.4W. +4.7A.6/. +4.7t.7b. +8.6z.7V. +d.7v.77. +0.6v.7Z. +1.7d.7s. +1.7g.7q. +2.6N.7M. +2.6P.7K. +4.7A.70. +9.7t.7c. +d.7v.78. +1.7g.7r. +4.7A.71. +9.7t.7d. +d.7v.79. +1.6z.7X. +1.9i.4X. +4.7A.72. +4.7t.7e. +6.6B.7V. +d.7v.7a. +1.7d.7u. +1.7g.7s. +1.8z.6c. +1.98.5r. +1.9i.4Y. +2.6J.7O. +4.7A.73. +9.7t.7f. +d.7v.7b. +1.6g.8t. +4.7A.74. +6.6v.7+. +9.7t.7g. +d.7v.7c. +h.7Z.6x. +1.5s.99. +2.6J.7P. +2.6L.7O. +4.7A.75. +5.6P.7L. +9.7t.7h. +m.7v.7d. +0.7V.6E. +1.7d.7w. +1.7g.7u. +1.9i.4Z. +2.6P.7M. +9.7A.76. +d.7v.7e. +1.7d.7x. +2.6L.7P. +9.7A.77. +9.7t.7i. +d.7v.7f. +1.5s.9a. +2.7t.7j. +7.6v.7/. +7.6x.7+. +9.7A.78. +d.7v.7g. +1.6z.7Y. +1.7g.7w. +1.9i.4+. +2.5s.9b. +2.6J.7Q. +2.6R.7K. +2.98.5t. +9.7A.79. +9.7t.7k. +d.7v.7h. +1.6v.80. +1.7d.7y. +1.7g.7x. +1.9i.4/. +8.6z.7Z. +9.7A.7a. +9.7t.7l. +1.9i.50. +2.6L.7Q. +2.6N.7O. +2.8z.6d. +4.7t.7m. +9.7A.7b. +d.7v.7i. +1.5s.9c. +1.7d.7z. +1.9i.51. +2.7v.7j. +2.8z.6e. +4.7t.7n. +7.6x.7/. +9.7A.7c. +1.6g.8y. +1.7g.7y. +1.9i.52. +2.6N.7P. +2.8S.5M. +6.6B.7Z. +6.85.6o. +9.7A.7d. +9.7t.7o. +d.7v.7k. +1.5s.9d. +1.6L.7S. +1.8z.6f. +2.6J.7T. +2.8S.5N. +6.6R.7L. +6.85.6p. +8.6z.7+. +9.7A.7e. +d.7v.7l. +0.7V.6H. +1.7g.7z. +1.9i.54. +2.6R.7M. +9.7A.7f. +d.7v.7m. +1.6L.7T. +1.8S.5O. +9.7A.7g. +9.7t.7p. +d.7v.7n. +0.7V.6J. +0.7Z.6E. +1.7d.7B. +1.8S.5P. +1.9i.56. +2.6N.7Q. +2.6P.7O. +4.7t.7q. +6.6v.81. +9.7A.7h. +d.7v.7o. +1.6L.7U. +1.6v.82. +1.7d.7C. +1.9i.57. +2.5s.9e. +6.85.6s. +8.6z.7/. +9.7t.7r. +1.6L.7V. +1.6v.83. +1.7d.7D. +1.8S.5Q. +1.8z.6h. +1.9i.58. +2.6P.7P. +9.7A.7i. +1.6N.7S. +1.6z.80. +1.7d.7E. +1.7g.7B. +2.7A.7j. +2.9i.59. +6.6v.84. +6.85.6u. +9.7t.7s. +d.7v.7p. +0.7t.7t. +1.7g.7C. +2.9i.5a. +6.85.6v. +7.6x.81. +9.7A.7k. +d.7v.7q. +1.6L.7X. +1.6N.7T. +1.6v.86. +1.7g.7D. +1.8z.6i. +2.98.5v. +2.9i.5b. +9.7A.7l. +d.7v.7r. +1.7d.7F. +1.7g.7E. +1.8z.6j. +2.6P.7Q. +2.98.5w. +6.6M.7V. +9.7A.7m. +9.7t.7u. +1.6N.7U. +1.98.5x. +2.8z.6k. +7.6x.84. +9.7A.7n. +d.7v.7s. +1.5s.9f. +1.7d.7G. +1.8z.6l. +1.9i.5c. +4.7t.7v. +6.6v.87. +9.7A.7o. +h.85.6x. +0.7Z.6H. +1.6P.7S. +1.7d.7H. +1.7g.7F. +1.98.5y. +2.6R.7O. +2.9i.5d. +6.6v.88. +9.7t.7w. +1.7d.7I. +1.98.5z. +2.5s.9g. +2.6J.7Y. +2.8S.5S. +6.6v.89. +8.6z.81. +9.7t.7x. +d.7v.7u. +0.6J.7Z. +1.6N.7X. +1.6z.82. +1.7d.7J. +1.7g.7G. +2.6R.7P. +2.8z.6m. +2.9i.5f. +4.7A.7p. +5.6P.7T. +7.6v.8a. +0.7V.6O. +1.6L.7Y. +1.6z.83. +1.7g.7H. +1.98.5A. +1.9i.5g. +6.6v.8b. +7.6x.87. +9.7A.7q. +d.7v.7v. +1.6L.7Z. +1.6P.7U. +1.7g.7I. +1.98.5B. +1.9i.5h. +6.6v.8c. +7.6x.88. +8.6z.84. +9.7A.7r. +9.7t.7y. +d.7v.7w. +0.6P.7V. +1.7g.7J. +2.9i.5i. +6.6v.8d. +7.6x.89. +8.6z.85. +d.7v.7x. +1.6z.86. +2.6R.7Q. +2.9i.5j. +4.7A.7s. +7.6v.8e. +7.6x.8a. +9.7t.7z. +0.7A.7t. +1.6B.84. +1.98.5C. +6.6v.8f. +7.6x.8b. +1.4G.9n. +1.5s.9h. +1.6L.7+. +1.6P.7X. +1.98.5D. +1.9i.5k. +2.6v.8g. +6.6M.7Z. +6.85.6B. +7.6x.8c. +d.7v.7y. +1.4G.9o. +1.6N.7Y. +1.6R.7S. +2.6v.8h. +7.6x.8d. +8.6z.87. +9.7A.7u. +1.4G.9p. +2.98.5E. +7.6x.8e. +8.6z.88. +d.7v.7z. +1.4G.9q. +2.9i.5l. +7.6x.8f. +8.6z.89. +9.6R.7T. +9.7A.7v. +9.7t.7B. +1.4G.9r. +1.6B.87. +1.6L.7/. +4.7A.7w. +6.85.6E. +7.6v.8i. +8.6z.8a. +9.7t.7C. +1.4G.9s. +1.6R.7U. +8.6z.8b. +9.7A.7x. +9.7t.7D. +1.4G.9t. +1.6B.89. +1.6L.80. +1.6N.7+. +1.9i.5n. +3.6R.7V. +6.6O.7Z. +7.6v.8j. +8.6z.8c. +9.7t.7E. +1.4G.9u. +1.6B.8a. +1.6P.7Y. +1.9i.5o. +2.98.5H. +8.6z.8d. +d.7v.7B. +0.6P.7Z. +1.4G.9v. +1.6B.8b. +1.6v.8k. +1.98.5I. +2.7g.7K. +2.9i.5p. +7.6x.8i. +8.6z.8e. +9.7A.7y. +d.7v.7C. +1.4G.9w. +1.6B.8c. +1.6R.7X. +1.6v.8l. +1.9i.5q. +8.6z.8f. +9.7t.7F. +d.7v.7D. +1.4G.9x. +1.6N.7/. +1.7d.7L. +2.6z.8g. +7.6x.8j. +9.7A.7z. +d.7v.7E. +1.4G.9y. +2.6z.8h. +9.7t.7G. +l.7A.7A. +1.4G.9z. +1.6N.80. +5.6P.7+. +7.6v.8m. +9.7t.7H. +1.6L.81. +1.7g.7L. +1.9i.5r. +2.6B.8g. +6.85.6H. +7.6v.8n. +9.7t.7I. +d.7v.7F. +1.5s.9i. +1.6L.82. +1.6v.8o. +2.6B.8h. +2.6J.84. +2.7g.7M. +3.6V.7V. +8.6z.8i. +9.7t.7J. +1.6L.83. +1.6v.8p. +2.8S.5W. +6.85.6J. +9.7A.7B. +d.7v.7G. +1.6L.84. +1.6R.7Y. +2.6J.86. +2.8S.5X. +5.6P.7/. +7.6x.8m. +8.6z.8j. +9.7A.7C. +d.7v.7H. +1.6L.85. +1.6v.8q. +2.8S.5Y. +3.6R.7Z. +7.6x.8n. +9.7A.7D. +d.7v.7I. +1.6L.86. +1.6P.80. +1.6z.8k. +9.7A.7E. +d.7v.7J. +1.6B.8j. +1.6N.81. +1.6z.8l. +2.8S.5+. +2.9i.5t. +1.6N.82. +1.6L.87. +1.6N.83. +2.6J.89. +6.6R.7+. +6.85.6M. +9.7A.7F. +b.6+.7V. +1.6L.88. +1.6N.84. +2.6J.8a. +8.6z.8m. +9.6v.8r. +1.6L.89. +2.98.5J. +7.6v.8s. +8.6z.8n. +9.7A.7G. +1.6L.8a. +1.6N.86. +1.6z.8o. +2.6g.8E. +2.8S.5/. +9.7A.7H. +d.71.7V. +1.6L.8b. +1.6z.8p. +2.5s.9j. +2.7g.7O. +2.7t.7K. +2.8S.60. +2.98.5K. +3.6V.7Z. +5.6P.81. +9.6R.7/. +9.7A.7I. +1.6L.8c. +1.6P.82. +2.5s.9k. +2.6J.8e. +9.6x.8r. +9.7A.7J. +1.6L.8d. +1.6N.87. +1.6P.83. +1.6z.8q. +2.7g.7P. +6.85.6O. +7.6x.8s. +k.6R.80. +1.6L.8e. +1.6N.88. +5.6P.84. +1.6L.8f. +1.6N.89. +2.7v.7K. +6.85.6P. +7.6v.8t. +1.6N.8a. +1.6P.86. +2.6g.8F. +2.6L.8g. +2.8S.63. +9.7t.7L. +1.6N.8b. +1.7d.7S. +1.8z.6n. +2.6L.8h. +2.7g.7Q. +2.7t.7M. +7.6v.8u. +1.6N.8c. +2.9i.5v. +6.6v.8v. +8.6z.8r. +b.6+.7Z. +1.6g.8G. +1.6N.8d. +1.7d.7T. +2.8S.64. +2.9i.5w. +5.6P.87. +6.6v.8w. +7.6x.8t. +8.6z.8s. +1.6g.8H. +1.6L.8i. +1.6N.8e. +1.7g.7S. +1.8z.6q. +1.9i.5x. +5.6P.88. +6.6R.81. +d.7v.7L. +1.6N.8f. +1.8S.65. +2.7v.7M. +5.6P.89. +7.6x.8u. +d.71.7Z. +k.6R.82. +1.6g.8I. +1.6L.8j. +1.6v.8x. +1.7g.7T. +1.8z.6r. +1.9i.5y. +2.6N.8g. +5.6P.8a. +h.6x.8v. +k.6R.83. +1.6g.8J. +1.9i.5z. +2.6N.8h. +2.7A.7K. +2.8S.66. +5.6P.8b. +6.6R.84. +7.6x.8w. +1.6g.8K. +1.6L.8k. +1.7g.7U. +1.8S.67. +1.8z.6t. +3.6R.85. +5.6P.8c. +6.6v.8y. +1.6L.8l. +1.7d.7X. +1.9i.5A. +2.8S.68. +5.6P.8d. +8.6z.8t. +k.6R.86. +1.6N.8i. +1.9i.5B. +5.6P.8e. +9.6v.8z. +5.6P.8f. +8.6z.8u. +1.6L.8m. +1.6N.8j. +1.7g.7X. +1.8S.69. +2.6J.8o. +2.6P.8g. +2.7t.7O. +7.6x.8y. +8.6z.8v. +9.6R.87. +9.7A.7L. +1.6L.8n. +1.8S.6a. +1.8z.6w. +1.9i.5C. +2.6J.8p. +2.6P.8h. +2.7A.7M. +8.6z.8w. +9.6R.88. +1.6B.8u. +1.6L.8o. +1.6N.8k. +1.9i.5D. +2.7t.7P. +6.6R.89. +9.6x.8z. +1.6L.8p. +1.6N.8l. +1.8S.6b. +1.8z.6y. +6.6R.8a. +1.6z.8x. +1.7d.7Y. +2.7v.7O. +2.9i.5E. +3.6V.85. +5.6P.8i. +9.6R.8b. +1.6L.8q. +2.98.5M. +6.6R.8c. +1.4G.9B. +1.6N.8m. +2.7t.7Q. +2.7v.7P. +2.98.5N. +5.6P.8j. +8.6z.8y. +9.6R.8d. +1.4G.9C. +1.6N.8n. +1.7g.7Y. +9.6R.8e. +1.4G.9D. +1.6N.8o. +1.6P.8k. +1.98.5O. +6.6o.8C. +6.6R.8f. +8.6z.8z. +1.4G.9E. +1.6N.8p. +1.6P.8l. +1.7d.7+. +1.7t.7S. +1.98.5P. +2.6J.8s. +2.6R.8g. +2.9i.5H. +6.6p.8C. +1.6L.8r. +1.8z.6A. +1.9i.5I. +2.6R.8h. +2.7v.7Q. +7.6v.8B. +1.4G.9F. +1.6L.8s. +1.6N.8q. +1.8S.6c. +1.98.5Q. +9.7t.7T. +b.6+.85. +1.7g.7+. +2.7A.7O. +5.6P.8m. +1.7d.7/. +1.7t.7U. +1.7v.7S. +1.8z.6C. +5.6P.8n. +6.6R.8i. +6.6s.8C. +0.7V.7t. +1.6P.8o. +1.8z.6D. +1.98.5R. +2.7A.7P. +7.6x.8B. +d.71.85. +0.6u.8C. +1.4G.9G. +1.6P.8p. +1.7d.80. +9.6R.8j. +d.7v.7T. +0.6v.8C. +1.4G.9H. +1.5s.9l. +1.6g.8O. +1.6N.8r. +1.7g.7/. +1.6L.8t. +1.6N.8s. +1.6P.8q. +1.7t.7X. +1.7v.7U. +1.8z.6F. +k.6R.8k. +1.7g.80. +2.7A.7Q. +k.6R.8l. +1.4G.9I. +1.6g.8Q. +1.6L.8u. +2.6J.8w. +2.8S.6d. +2.98.5S. +1.4G.9J. +1.5s.9m. +1.6L.8v. +1.8z.6G. +2.8S.6e. +8.6z.8B. +h.6x.8C. +1.6L.8w. +1.7A.7S. +1.7v.7X. +6.6R.8m. +1.7d.81. +6.6R.8n. +9.6P.8r. +1.6N.8t. +1.6R.8o. +1.7d.82. +5.6P.8s. +9.7A.7T. +1.6L.8x. +1.6R.8p. +1.7d.83. +1.7t.7Y. +0.7Z.7t. +1.6N.8u. +1.7A.7U. +1.7d.84. +1.7g.81. +1.8z.6I. +2.9i.5J. +0.7A.7V. +1.5s.9n. +1.6g.8T. +1.6L.8y. +1.6N.8v. +1.7g.82. +1.98.5T. +2.6J.8z. +8.6z.8C. +k.6R.8q. +1.5s.9o. +1.6N.8w. +1.7d.86. +1.7g.83. +1.8z.6K. +2.9i.5K. +1.4G.9K. +1.5s.9p. +1.7g.84. +1.7v.7Y. +1.8z.6L. +1.5s.9q. +1.7A.7X. +4.7t.7+. +5.6P.8t. +6.6B.8C. +6.6v.8D. +1.5s.9r. +1.6N.8x. +1.7d.87. +1.7g.86. +1.8S.6i. +1.5s.9s. +1.7d.88. +1.8S.6j. +5.6P.8u. +9.6R.8r. +1.5s.9t. +1.6N.8y. +1.7d.89. +2.8S.6k. +5.6P.8v. +9.6R.8s. +1.5s.9u. +1.7d.8a. +1.7g.87. +1.8S.6l. +5.6P.8w. +6.6E.8C. +7.6x.8D. +9.7t.7/. +d.7v.7+. +1.4G.9L. +1.5s.9v. +1.6N.8z. +1.7d.8b. +1.7g.88. +1.5s.9w. +1.7d.8c. +1.7g.89. +1.7t.80. +1.98.5U. +1.5s.9x. +1.6g.8V. +1.6P.8x. +1.7A.7Y. +1.7d.8d. +1.7g.8a. +2.8S.6m. +0.7A.7Z. +1.5s.9y. +1.7d.8e. +1.7g.8b. +1.98.5V. +d.7v.7/. +1.5s.9z. +1.6g.8W. +1.7d.8f. +1.7g.8c. +2.6J.8B. +5.6P.8y. +9.6R.8t. +1.4G.9M. +1.7g.8d. +1.7v.80. +8.6z.8D. +1.4G.9N. +1.6L.8B. +1.7g.8e. +6.6P.8z. +6.6R.8u. +1.7g.8f. +2.6v.8E. +2.98.5W. +6.6R.8v. +9.7A.7+. +0.6H.8C. +1.6g.8X. +2.7g.8g. +2.98.5X. +6.6R.8w. +9.7t.81. +1.7d.8i. +1.7t.82. +2.7g.8h. +2.98.5Y. +0.6J.8C. +1.7t.83. +1.98.5Z. +1.4G.9O. +1.6R.8x. +1.7d.8j. +2.98.5+. +9.7A.7/. +9.7t.84. +1.4G.9P. +1.6L.8C. +1.6N.8B. +1.7g.8i. +6.85.7t. +d.7v.81. +1.7A.80. +1.7d.8k. +1.7t.86. +1.7v.82. +2.6v.8F. +6.6R.8y. +1.4G.9Q. +1.7d.8l. +1.7g.8j. +1.7v.83. +1.8z.6Q. +2.6g.8Z. +2.9i.5M. +6.6R.8z. +d.7v.84. +2.98.5/. +2.9i.5N. +6.6M.8C. +6.6v.8G. +9.7t.87. +1.6g.8+. +1.7d.8m. +1.7g.8l. +1.7v.86. +1.8z.6T. +2.6z.8E. +2.98.60. +4.7t.88. +6.6v.8H. +1.7d.8n. +1.98.61. +1.9i.5O. +5.6P.8B. +9.7t.89. +1.7d.8o. +1.98.62. +1.9i.5P. +6.6v.8I. +9.7t.8a. +1.7d.8p. +1.7g.8m. +2.6B.8E. +6.6v.8J. +7.6x.8G. +9.7A.81. +9.7t.8b. +d.7v.87. +1.6g.8/. +1.7A.82. +1.7g.8n. +1.9i.5Q. +7.6v.8K. +7.6x.8H. +9.7t.8c. +d.7v.88. +1.7A.83. +1.7d.8q. +1.7g.8o. +1.8z.6U. +2.98.63. +4.7t.8d. +6.6O.8C. +d.7v.89. +1.7g.8p. +2.6z.8F. +7.6x.8I. +9.7A.84. +9.7t.8e. +d.7v.8a. +0.6P.8C. +1.6g.90. +1.8z.6W. +1.9i.5R. +6.85.7A. +7.6x.8J. +9.7t.8f. +d.7v.8b. +1.6L.8D. +1.7A.86. +1.7g.8q. +1.8z.6X. +2.7t.8g. +2.98.64. +7.6x.8K. +d.7v.8c. +1.8z.6Y. +2.6B.8F. +2.7t.8h. +8.6z.8G. +d.7v.8d. +1.7d.8r. +1.8z.6Z. +1.98.65. +6.6R.8B. +8.6z.8H. +d.7v.8e. +1.7d.8s. +9.7A.87. +d.7v.8f. +1.6B.8G. +1.6g.91. +2.7v.8g. +2.98.66. +2.9i.5S. +8.6z.8I. +9.7A.88. +9.7t.8i. +1.6B.8H. +1.7g.8r. +1.98.67. +2.7v.8h. +8.6z.8J. +9.7A.89. +1.6g.92. +1.6N.8D. +1.7g.8s. +1.8z.6/. +2.98.68. +8.6z.8K. +9.7A.8a. +9.7t.8j. +1.5s.9A. +1.6g.93. +1.8z.70. +9.7A.8b. +1.6g.94. +1.7t.8k. +1.8z.71. +3.6R.8C. +9.7A.8c. +d.7v.8i. +1.6v.8L. +1.7d.8t. +1.7t.8l. +1.8z.72. +1.98.69. +2.4G.9R. +9.7A.8d. +1.6v.8M. +1.8z.73. +1.98.6a. +9.7A.8e. +d.7v.8j. +1.6v.8N. +1.7d.8u. +1.8z.74. +1.9i.5T. +9.7A.8f. +1.7d.8v. +1.7g.8t. +1.7v.8k. +1.8z.75. +1.98.6b. +2.6L.8E. +2.7A.8g. +5.6P.8D. +9.7t.8m. +1.6g.95. +1.7d.8w. +1.7v.8l. +1.8z.76. +2.7A.8h. +9.7t.8n. +1.7g.8u. +1.7t.8o. +1.8z.77. +1.5s.9B. +1.7g.8v. +1.7t.8p. +1.8z.78. +1.5s.9C. +1.6v.8O. +1.7d.8x. +1.7g.8w. +1.8z.79. +3.6V.8C. +9.7A.8i. +d.7v.8m. +1.5s.9D. +1.6g.96. +1.6v.8P. +1.7t.8q. +1.8z.7a. +d.7v.8n. +1.5s.9E. +1.7d.8y. +1.7v.8o. +1.8z.7b. +2.6L.8F. +2.6N.8E. +9.7A.8j. +1.6v.8Q. +1.6z.8L. +1.7g.8x. +1.7v.8p. +1.8z.7c. +1.5s.9F. +1.6z.8M. +1.7A.8k. +1.7d.8z. +1.98.6c. +1.9i.5U. +1.6L.8G. +1.6z.8N. +1.7A.8l. +1.7g.8y. +1.7v.8q. +1.8S.6t. +1.8z.7e. +6.6v.8R. +9.6R.8D. +1.6L.8H. +1.8z.7f. +1.9i.5V. +2.6J.8I. +9.7t.8r. +1.8z.7g. +9.6v.8S. +9.7t.8s. +b.6+.8C. +1.5s.9G. +1.6L.8I. +1.8z.7h. +2.6N.8F. +2.6P.8E. +9.7A.8m. +f.7V.7V. +1.5s.9H. +1.6L.8J. +7.6x.8R. +9.7A.8n. +1.6g.97. +1.6L.8K. +1.6z.8O. +1.7A.8o. +1.8z.7i. +2.9i.5W. +6.6v.8T. +d.71.8C. +d.7v.8r. +1.6N.8G. +1.6z.8P. +1.7A.8p. +2.8z.7j. +2.9i.5X. +9.6x.8S. +d.7v.8s. +1.5s.9I. +1.6N.8H. +1.8S.6y. +1.8z.7k. +2.98.6d. +2.9i.5Y. +1.5s.9J. +1.6z.8Q. +1.7A.8q. +1.8z.7l. +1.9i.5Z. +2.98.6e. +4.7t.8t. +1.6N.8I. +1.8z.7m. +2.6P.8F. +2.9i.5+. +7.6x.8T. +1.6N.8J. +1.8z.7n. +1.98.6f. +7.6v.8U. +8.6z.8R. +9.7t.8u. +1.6B.8Q. +1.6N.8K. +1.7d.8B. +1.8z.7o. +1.98.6g. +9.7t.8v. +1.6g.99. +2.6R.8E. +5.6P.8G. +8.6z.8S. +9.7t.8w. +d.7v.8t. +5.6P.8H. +9.7A.8r. +1.7g.8B. +1.8z.7p. +2.9i.5/. +7.6x.8U. +9.7A.8s. +d.7v.8u. +f.7V.7Z. +1.6g.9a. +1.6v.8V. +1.7t.8x. +1.8z.7q. +1.98.6h. +2.9i.60. +5.6P.8I. +8.6z.8T. +d.7v.8v. +1.5s.9K. +1.8z.7r. +1.9i.61. +2.6g.9b. +5.6P.8J. +d.7v.8w. +1.9i.62. +4.7t.8y. +5.6P.8K. +9.6v.8W. +1.6L.8L. +1.8z.7s. +1.98.6i. +2.6R.8F. +1.6g.9c. +1.6L.8M. +1.7v.8x. +1.98.6j. +4.7t.8z. +1.6L.8N. +2.98.6k. +2.9i.63. +8.6z.8U. +9.7A.8t. +1.6g.9d. +1.8S.6F. +1.8z.7u. +1.98.6l. +6.6R.8G. +6.6v.8X. +9.6x.8W. +d.7v.8y. +1.5s.9L. +6.6R.8H. +6.6v.8Y. +9.7A.8u. +2.9i.64. +9.7A.8v. +d.7v.8z. +1.6N.8L. +1.8S.6G. +1.8z.7w. +2.98.6m. +9.6R.8I. +9.7A.8w. +1.6L.8O. +1.6N.8M. +1.6z.8V. +1.8z.7x. +1.9i.65. +2.6g.9e. +7.6x.8X. +9.6R.8J. +f.7Z.7Z. +1.6L.8P. +1.6N.8N. +7.6x.8Y. +9.6R.8K. +1.5s.9M. +1.7A.8x. +2.9i.66. +8.6z.8W. +1.5s.9N. +1.6L.8Q. +1.8z.7y. +1.9i.67. +2.6v.8Z. +1.8S.6I. +2.9i.68. +9.7A.8y. +1.6J.8S. +1.6L.8R. +1.6P.8L. +1.7d.8D. +1.8z.7z. +7.6v.8+. +1.6N.8O. +1.6P.8M. +1.8S.6K. +8.6z.8X. +9.7A.8z. +9.7t.8B. +1.6g.9f. +1.6L.8S. +1.6N.8P. +1.6P.8N. +1.9i.69. +8.6z.8Y. +1.5s.9O. +1.7g.8D. +1.9i.6a. +1.5s.9P. +1.6N.8Q. +2.6g.9g. +6.6v.8/. +7.6x.8+. +1.6L.8T. +1.8z.7B. +1.9i.6b. +6.85.7V. +d.7v.8B. +1.5s.9Q. +1.6N.8R. +1.8z.7C. +0.7t.8C. +1.6P.8O. +1.6v.90. +1.8z.7D. +1.6P.8P. +1.8z.7E. +2.6z.8Z. +2.8S.6N. +7.6x.8/. +k.6R.8L. +1.6L.8U. +1.6P.8Q. +8.6z.8+. +k.6R.8M. +1.6g.9h. +1.6N.8T. +1.8z.7F. +2.6B.8Z. +k.6R.8N. +1.6v.91. +5.6P.8R. +1.6B.8+. +1.8z.7G. +1.9i.6c. +9.7A.8B. +1.6v.92. +1.8z.7H. +6.6P.8S. +8.6z.8/. +1.6L.8V. +1.6v.93. +1.8z.7I. +2.6J.8W. +2.7g.8E. +1.6N.8U. +1.6v.94. +1.8z.7J. +6.85.7Z. +k.6R.8O. +1.6L.8W. +1.6z.90. +5.6P.8T. +k.6R.8P. +0.7A.8C. +1.6R.8Q. +2.6J.8Y. +9.7t.8D. +1.6L.8X. +1.6N.8V. +1.7d.8G. +2.7g.8F. +2.9i.6d. +6.6v.95. +9.6R.8R. +1.6L.8Y. +1.6z.91. +1.7d.8H. +2.9i.6e. +5.6P.8U. +1.6N.8W. +9.6R.8S. +1.6z.92. +1.7d.8I. +1.7g.8G. +1.9i.6f. +d.7v.8D. +1.6g.9i. +1.6v.96. +1.6z.93. +1.7d.8J. +1.7g.8H. +7.6x.95. +1.6z.94. +1.7d.8K. +2.5s.9R. +6.6R.8T. +1.6N.8X. +1.6P.8V. +1.7g.8I. +2.8z.7K. +1.6N.8Y. +1.7g.8J. +2.6L.8Z. +1.7g.8K. +1.9i.6h. +6.6P.8W. +1.6L.8+. +1.98.6n. +6.6R.8U. +8.6z.95. +1.8S.6W. +1.8z.7L. +1.9i.6i. +2.7t.8E. +9.7A.8D. +1.5s.9S. +1.9i.6j. +2.8z.7M. +5.6P.8X. +1.5s.9T. +1.6L.8/. +1.6v.97. +1.98.6q. +2.6N.8Z. +2.9i.6k. +5.6P.8Y. +1.6z.96. +1.9i.6l. +1.6N.8+. +1.6R.8V. +1.98.6r. +2.6g.9j. +2.7v.8E. +1.6L.90. +2.6g.9k. +6.85.85. +1.5s.9U. +1.98.6t. +2.7t.8F. +2.9i.6m. +9.6R.8W. +1.7d.8L. +e.8S.6/. +1.6N.8/. +1.7d.8M. +2.6P.8Z. +e.8S.70. +k.98.6v. +1.5s.9V. +1.6v.99. +1.7d.8N. +9.7t.8G. +e.8S.71. +1.6L.91. +1.7g.8L. +2.7v.8F. +5.6P.8+. +9.6R.8X. +9.7t.8H. +e.8S.72. +1.5s.9W. +1.6N.90. +1.7g.8M. +1.98.6w. +9.6R.8Y. +e.8S.73. +1.6L.92. +1.6v.9a. +1.6z.97. +1.7g.8N. +1.8S.74. +2.6J.94. +2.7A.8E. +2.8z.7O. +9.7t.8I. +1.5s.9X. +1.6L.93. +1.98.6y. +2.6v.9b. +9.7t.8J. +d.7v.8G. +e.8S.75. +1.6L.94. +1.7d.8O. +2.8z.7P. +4.7t.8K. +5.6P.8/. +d.7v.8H. +1.7d.8P. +1.8S.77. +1.6N.91. +2.8S.78. +9.6v.9c. +d.7v.8I. +1.6P.90. +1.7d.8Q. +1.7g.8O. +2.6R.8Z. +d.7v.8J. +1.6N.92. +1.7g.8P. +1.98.6z. +2.7A.8F. +2.8z.7Q. +6.6v.9d. +d.7v.8K. +1.6L.95. +1.6N.93. +1.6z.99. +1.7d.8R. +2.8S.7b. +6.6R.8+. +1.6N.94. +1.7g.8Q. +1.8S.7c. +1.98.6A. +9.6x.9c. +1.8z.7S. +9.7A.8G. +1.6P.91. +1.6z.9a. +1.7g.8R. +2.6v.9e. +7.6x.9d. +9.7A.8H. +1.6L.96. +1.8z.7T. +1.98.6C. +2.6z.9b. +6.6R.8/. +1.6P.92. +1.7d.8T. +1.98.6D. +2.8S.7g. +9.7A.8I. +1.6N.95. +1.6P.93. +1.8z.7U. +9.7A.8J. +e.8S.7h. +1.6P.94. +2.6B.9b. +8.6z.9c. +9.7A.8K. +k.6R.90. +1.7g.8T. +1.7t.8L. +1.8S.7i. +1.98.6F. +1.7t.8M. +2.8S.7j. +8.6z.9d. +1.6N.96. +1.7d.8U. +1.7t.8N. +1.8z.7X. +9.6v.9f. +1.8S.7l. +1.98.6G. +1.7v.8L. +2.6v.9g. +5.6P.95. +k.6R.91. +1.6L.97. +1.7g.8U. +1.7v.8M. +1.8S.7n. +2.6z.9e. +1.7v.8N. +9.6x.9f. +k.6R.92. +1.6R.93. +1.7t.8O. +1.6P.96. +1.6R.94. +1.7t.8P. +1.98.6I. +2.6B.9e. +1.7d.8W. +1.8S.7p. +1.8z.7Y. +1.6g.9l. +1.7g.8V. +1.7t.8Q. +1.8S.7q. +1.98.6K. +1.6N.97. +1.7v.8O. +1.8S.7r. +1.98.6L. +7.6v.9h. +1.6L.99. +1.7A.8L. +1.7g.8W. +1.7v.8P. +8.6z.9f. +9.7t.8R. +1.7A.8M. +1.7d.8X. +1.8S.7s. +6.6R.95. +1.6g.9m. +1.7A.8N. +1.7d.8Y. +1.7v.8Q. +1.8z.7+. +2.6J.9b. +2.6z.9g. +9.7t.8S. +1.6L.9a. +7.6x.9h. +1.7g.8X. +1.9i.6n. +2.6L.9b. +d.7v.8R. +e.8S.7u. +0.7V.8C. +1.6P.97. +1.6R.96. +1.7g.8Y. +2.6B.9g. +2.98.6N. +4.7t.8T. +1.6N.99. +1.8z.7/. +e.8S.7v. +1.6L.9c. +1.7A.8O. +1.9i.6q. +e.8S.7w. +1.6g.9n. +1.7A.8P. +1.8z.80. +2.8S.7x. +1.6g.9o. +1.6L.9d. +1.6N.9a. +1.9i.6r. +8.6z.9h. +d.7v.8T. +1.6g.9p. +1.7A.8Q. +1.7d.8+. +2.6N.9b. +4.7t.8U. +1.6g.9q. +1.9i.6t. +2.7g.8Z. +e.8S.7y. +k.98.6P. +1.6g.9r. +1.6P.99. +9.7A.8R. +1.6g.9s. +1.6N.9c. +1.7g.8+. +1.8S.7z. +2.6L.9e. +9.6v.9i. +1.6g.9t. +1.6R.97. +9.7A.8S. +d.7v.8U. +1.6g.9u. +1.6N.9d. +1.6P.9a. +1.7t.8V. +1.8z.81. +0.7Z.8C. +1.6g.9v. +1.8z.82. +1.9i.6w. +2.6P.9b. +1.6g.9w. +1.7d.90. +1.8z.83. +4.7A.8T. +9.6x.9i. +9.7t.8W. +1.6g.9x. +1.8S.7B. +1.8z.84. +1.9i.6y. +1.6g.9y. +1.7v.8V. +1.8S.7C. +1.98.6Q. +2.6N.9e. +9.6P.9c. +1.6g.9z. +1.6L.9f. +1.7g.90. +1.8S.7D. +1.8z.86. +2.6J.9g. +k.6R.98. +1.8S.7E. +5.6P.9d. +9.7t.8X. +d.7v.8W. +k.6R.99. +1.7d.91. +1.98.6T. +2.6L.9g. +4.7A.8U. +4.7t.8Y. +1.8z.87. +8.6z.9i. +1.6R.9a. +1.7d.92. +1.8S.7F. +1.8z.88. +2.6v.9j. +1.7d.93. +1.7g.91. +1.8z.89. +1.9i.6A. +2.6P.9e. +2.6R.9b. +2.6v.9k. +d.7v.8X. +1.6N.9f. +1.7d.94. +1.8S.7G. +1.8z.8a. +d.7v.8Y. +1.7A.8V. +1.7g.92. +1.8z.8b. +1.98.6U. +1.7g.93. +1.8S.7I. +1.8z.8c. +1.9i.6C. +2.6N.9g. +2.7t.8Z. +9.6R.9c. +1.6L.9h. +1.7g.94. +1.8z.8d. +1.98.6W. +1.9i.6D. +9.7A.8W. +1.8z.8e. +1.98.6X. +6.6R.9d. +9.7t.8+. +1.7d.95. +1.8z.8f. +1.98.6Y. +1.98.6Z. +1.9i.6F. +2.7v.8Z. +2.8z.8g. +6.6P.9f. +2.8z.8h. +9.7A.8X. +1.7g.95. +2.6P.9g. +2.6R.9e. +2.6z.9j. +4.7t.8/. +9.7A.8Y. +d.7v.8+. +1.6N.9h. +1.7d.96. +1.9i.6G. +2.6z.9k. +1.8z.8i. +1.98.6/. +1.7t.90. +1.98.70. +2.6B.9j. +6.85.8C. +1.7g.96. +1.8z.8j. +1.98.71. +2.6B.9k. +d.7v.8/. +1.98.72. +1.8z.8k. +1.98.73. +1.9i.6I. +2.7A.8Z. +2.8S.7K. +1.7v.90. +1.8z.8l. +1.98.74. +5.6P.9h. +9.6R.9f. +1.7t.91. +1.98.75. +1.9i.6K. +9.7A.8+. +1.98.76. +1.9i.6L. +2.6R.9g. +1.7d.97. +1.7t.92. +1.8z.8m. +1.98.77. +1.7t.93. +1.8z.8n. +1.98.78. +1.7t.94. +1.7v.91. +1.8z.8o. +1.98.79. +2.8S.7M. +9.7A.8/. +1.7g.97. +1.8z.8p. +1.98.7a. +1.7v.92. +1.98.7b. +1.7A.90. +1.7v.93. +1.8z.8q. +1.98.7c. +2.9i.6N. +1.7v.94. +1.98.7d. +9.6R.9h. +1.7d.99. +1.98.7e. +9.7t.95. +1.98.7f. +1.98.7g. +1.7A.91. +1.7d.9a. +1.7g.99. +1.8z.8r. +1.98.7h. +2.6L.9j. +1.7t.96. +1.8z.8s. +2.6L.9k. +9.6P.9i. +d.7v.95. +1.6g.9B. +1.7A.92. +1.98.7i. +1.6g.9C. +1.7A.93. +1.7g.9a. +2.8S.7O. +2.98.7j. +7.6v.9l. +1.6g.9D. +1.7A.94. +1.7d.9c. +1.98.7k. +2.7g.9b. +1.6g.9E. +1.7v.96. +1.98.7l. +2.8S.7P. +1.7d.9d. +1.98.7m. +2.6N.9j. +1.6g.9F. +1.7g.9c. +1.8z.8t. +1.98.7n. +2.6N.9k. +7.6v.9m. +7.6x.9l. +1.98.7o. +1.7g.9d. +1.8z.8u. +1.9i.6Q. +2.8S.7Q. +9.7A.95. +1.7t.97. +1.8z.8v. +9.6R.9i. +1.6g.9G. +1.8z.8w. +1.98.7p. +7.6x.9m. +1.6g.9H. +1.98.7q. +1.9i.6T. +2.6P.9j. +1.7A.96. +1.98.7r. +2.6P.9k. +2.7g.9e. +7.6v.9n. +8.6z.9l. +1.7v.97. +1.8S.7T. +1.8z.8x. +7.6v.9o. +1.6g.9I. +1.98.7s. +7.6v.9p. +1.6B.9l. +1.6g.9J. +7.6v.9q. +1.7d.9f. +1.7t.99. +1.9i.6U. +7.6v.9r. +7.6x.9n. +8.6z.9m. +1.98.7u. +7.6v.9s. +7.6x.9o. +1.9i.6W. +7.6v.9t. +7.6x.9p. +1.6B.9m. +1.7g.9f. +1.7t.9a. +1.98.7v. +1.9i.6X. +2.8S.7X. +7.6v.9u. +7.6x.9q. +1.7v.99. +1.98.7w. +1.9i.6Y. +2.6R.9j. +2.7t.9b. +7.6v.9v. +7.6x.9r. +1.7A.97. +1.98.7x. +1.9i.6Z. +2.6R.9k. +2.7g.9g. +7.6v.9w. +7.6x.9s. +7.6v.9x. +7.6x.9t. +8.6z.9n. +1.6g.9K. +1.7v.9a. +7.6v.9y. +7.6x.9u. +8.6z.9o. +9.7t.9c. +1.98.7y. +2.7v.9b. +7.6v.9z. +7.6x.9v. +8.6z.9p. +1.6B.9n. +1.9i.6/. +4.7t.9d. +7.6x.9w. +8.6z.9q. +1.6B.9o. +1.98.7z. +1.9i.70. +2.8S.7Y. +7.6x.9x. +8.6z.9r. +1.6B.9p. +1.9i.71. +7.6x.9y. +8.6z.9s. +d.7v.9c. +1.6B.9q. +1.7A.99. +1.7g.9h. +1.9i.72. +7.6x.9z. +8.6z.9t. +1.6B.9r. +1.6g.9L. +1.8z.8B. +1.9i.73. +2.7t.9e. +8.6z.9u. +d.7v.9d. +1.6B.9s. +1.9i.74. +8.6z.9v. +1.6B.9t. +1.7A.9a. +1.98.7B. +1.9i.75. +8.6z.9w. +1.6B.9u. +1.6L.9l. +1.98.7C. +1.9i.76. +2.7A.9b. +8.6z.9x. +1.6B.9v. +1.98.7D. +1.9i.77. +2.7v.9e. +8.6z.9y. +1.6B.9w. +1.6g.9M. +1.98.7E. +1.9i.78. +8.6z.9z. +1.6B.9x. +1.6g.9N. +1.9i.79. +9.7A.9c. +e.8S.7/. +1.6B.9y. +1.6L.9m. +1.9i.7a. +9.7t.9f. +1.98.7F. +1.9i.7b. +9.7A.9d. +1.6N.9l. +1.9i.7c. +2.7t.9g. +1.7d.9i. +1.98.7G. +1.6g.9O. +1.98.7H. +1.9i.7e. +2.6J.9n. +d.7v.9f. +1.6g.9P. +1.98.7I. +1.9i.7f. +2.6J.9o. +2.7A.9e. +1.6L.9n. +1.6N.9m. +1.7g.9i. +1.98.7J. +2.6J.9p. +2.7v.9g. +1.6g.9Q. +1.6L.9o. +1.9i.7h. +2.6J.9q. +1.6L.9p. +2.6J.9r. +2.8S.81. +5.6P.9l. +1.6L.9q. +1.9i.7i. +2.6J.9s. +9.7t.9h. +1.6L.9r. +2.6J.9t. +2.9i.7j. +1.6L.9s. +1.9i.7k. +2.6J.9u. +e.8S.84. +1.6L.9t. +1.6N.9n. +1.8z.8D. +1.9i.7l. +2.6J.9v. +5.6P.9m. +9.7A.9f. +1.6L.9u. +1.6N.9o. +1.8S.86. +1.9i.7m. +2.6J.9w. +d.7v.9h. +1.6L.9v. +1.6N.9p. +1.9i.7n. +2.6J.9x. +2.7A.9g. +1.6L.9w. +1.6N.9q. +1.9i.7o. +2.6J.9y. +1.6L.9x. +1.6N.9r. +2.6J.9z. +2.8S.87. +7.6v.9A. +0.8C.8C. +1.6L.9y. +1.6N.9s. +1.8S.88. +2.7g.9j. +2.98.7K. +6.6R.9l. +1.6L.9z. +1.6N.9t. +1.9i.7p. +2.7g.9k. +5.6P.9n. +1.6N.9u. +1.8S.8a. +1.9i.7q. +5.6P.9o. +1.6N.9v. +1.9i.7r. +5.6P.9p. +7.6x.9A. +1.6N.9w. +4.7A.9h. +5.6P.9q. +6.6R.9m. +1.6N.9x. +1.8S.8d. +1.98.7L. +1.9i.7s. +5.6P.9r. +1.6N.9y. +2.98.7M. +5.6P.9s. +9.7t.9i. +1.6N.9z. +1.8S.8f. +2.8z.8E. +5.6P.9t. +7.6v.9B. +1.9i.7u. +2.8S.8g. +5.6P.9u. +7.6v.9C. +2.8S.8h. +5.6P.9v. +7.6v.9D. +8.6z.9A. +2.6g.9R. +5.6P.9w. +6.6R.9n. +7.6v.9E. +d.7v.9i. +1.9i.7w. +5.6P.9x. +6.6R.9o. +7.6x.9B. +1.8S.8i. +1.9i.7x. +5.6P.9y. +6.6R.9p. +7.6v.9F. +7.6x.9C. +2.8z.8F. +5.6P.9z. +6.6R.9q. +7.6x.9D. +1.8S.8j. +6.6R.9r. +7.6x.9E. +1.9i.7y. +6.6R.9s. +1.8S.8k. +1.8z.8G. +6.6R.9t. +7.6v.9G. +7.6x.9F. +1.6g.9S. +1.8S.8l. +1.8z.8H. +1.9i.7z. +2.7t.9j. +2.98.7O. +6.6R.9u. +7.6v.9H. +8.6z.9B. +1.6g.9T. +2.7t.9k. +6.6R.9v. +8.6z.9C. +9.7A.9i. +1.8z.8I. +2.98.7P. +6.6R.9w. +8.6z.9D. +1.6B.9B. +1.8z.8J. +6.6R.9x. +7.6v.9I. +7.6x.9G. +8.6z.9E. +1.6B.9C. +1.8S.8n. +1.8z.8K. +2.7v.9j. +6.6R.9y. +6.6v.9J. +7.6x.9H. +1.6B.9D. +1.6g.9U. +1.9i.7B. +2.7v.9k. +6.6R.9z. +8.6z.9F. +1.6B.9E. +1.8S.8p. +1.9i.7C. +2.98.7Q. +1.9i.7D. +7.6x.9I. +1.6B.9F. +1.6g.9V. +1.8S.8q. +1.9i.7E. +7.6x.9J. +1.98.7S. +8.6z.9G. +1.6g.9W. +8.6z.9H. +1.98.7T. +1.9i.7F. +1.6B.9G. +1.6g.9X. +1.6L.9A. +2.7A.9j. +7.6v.9K. +1.6B.9H. +1.7d.9l. +1.8S.8r. +1.9i.7G. +2.7A.9k. +8.6z.9I. +1.8S.8s. +1.9i.7H. +8.6z.9J. +1.9i.7I. +1.6B.9I. +1.7g.9l. +1.9i.7J. +7.6x.9K. +1.6B.9J. +1.7d.9m. +1.8z.8L. +1.98.7X. +1.6N.9A. +1.8z.8M. +7.6v.9L. +1.8z.8N. +1.6L.9B. +1.7g.9m. +1.6L.9C. +1.6L.9D. +1.8S.8u. +7.6x.9L. +8.6z.9K. +1.6L.9E. +1.7d.9n. +1.8S.8v. +7.6v.9M. +1.7d.9o. +1.8S.8w. +1.8z.8O. +1.98.7Y. +5.6P.9A. +7.6v.9N. +1.6B.9K. +1.6L.9F. +1.7d.9p. +1.8z.8P. +1.6N.9B. +1.7d.9q. +1.7g.9n. +1.6N.9C. +1.7d.9r. +1.7g.9o. +1.8S.8x. +1.8z.8Q. +2.9i.7K. +7.6x.9M. +1.6N.9D. +1.7d.9s. +1.7g.9p. +7.6x.9N. +8.6z.9L. +1.6L.9G. +1.6N.9E. +1.7d.9t. +1.7g.9q. +1.8z.8R. +1.98.7+. +5.6v.9O. +1.6L.9H. +1.7d.9u. +1.7g.9r. +5.6v.9P. +1.6B.9L. +1.6N.9F. +1.7d.9v. +1.7g.9s. +2.8S.8z. +1.7d.9w. +1.7g.9t. +1.9i.7L. +5.6P.9B. +7.6v.9Q. +1.6L.9I. +1.7d.9x. +1.7g.9u. +1.98.7/. +2.9i.7M. +4.6x.9O. +5.6P.9C. +8.6z.9M. +9.6R.9A. +9.7t.9l. +1.6L.9J. +1.7d.9y. +1.7g.9v. +1.8z.8T. +4.6x.9P. +5.6P.9D. +8.6z.9N. +1.6N.9G. +1.7d.9z. +1.7g.9w. +1.98.80. +5.6P.9E. +1.6B.9M. +1.6N.9H. +1.7g.9x. +7.6x.9Q. +1.6B.9N. +1.7g.9y. +5.6P.9F. +9.7t.9m. +d.7v.9l. +1.7g.9z. +1.6N.9I. +1.8z.8U. +8.6z.9O. +1.6N.9J. +8.6z.9P. +5.6P.9G. +9.6R.9B. +d.7v.9m. +1.6B.9O. +1.6L.9K. +1.98.81. +5.6P.9H. +8.6z.9Q. +9.6R.9C. +1.6B.9P. +1.98.82. +4.7t.9n. +9.6R.9D. +e.8S.8B. +1.8z.8V. +1.98.83. +2.9i.7O. +9.6R.9E. +9.7t.9o. +1.6B.9Q. +1.98.84. +4.7A.9l. +5.6P.9I. +9.7t.9p. +1.8z.8W. +2.9i.7P. +4.7t.9q. +5.6P.9J. +6.6R.9F. +1.98.86. +9.7t.9r. +d.7v.9n. +1.6L.9L. +1.6N.9K. +4.7t.9s. +d.7v.9o. +4.7A.9m. +4.7t.9t. +d.7v.9p. +1.8z.8X. +1.98.87. +2.9i.7Q. +6.6R.9G. +9.7t.9u. +d.7v.9q. +1.8z.8Y. +1.98.88. +6.6R.9H. +9.7t.9v. +d.7v.9r. +1.98.89. +2.6v.9R. +9.7t.9w. +d.7v.9s. +1.6L.9M. +1.98.8a. +1.9i.7S. +9.7t.9x. +d.7v.9t. +1.6L.9N. +1.6N.9L. +1.98.8b. +5.6P.9K. +6.6R.9I. +9.7t.9y. +d.7v.9u. +1.98.8c. +1.9i.7T. +4.7A.9n. +9.6R.9J. +9.7t.9z. +d.7v.9v. +1.98.8d. +4.7A.9o. +d.7v.9w. +1.98.8e. +1.9i.7U. +2.8z.8Z. +4.7A.9p. +d.7v.9x. +1.98.8f. +4.7A.9q. +d.7v.9y. +1.6L.9O. +1.6N.9M. +1.8z.8+. +2.98.8g. +4.7A.9r. +7.6v.9S. +d.7v.9z. +1.6L.9P. +1.6N.9N. +2.98.8h. +4.7A.9s. +5.6P.9L. +7.6v.9T. +1.9i.7X. +4.7A.9t. +1.6L.9Q. +1.8S.8D. +2.6z.9R. +4.7A.9u. +1.7d.9A. +1.8z.8/. +1.98.8i. +4.7A.9v. +6.6R.9K. +7.6x.9S. +4.7A.9w. +7.6v.9U. +7.6x.9T. +1.6N.9O. +1.98.8j. +2.6B.9R. +4.7A.9x. +5.6P.9M. +1.6N.9P. +1.7g.9A. +1.8z.90. +4.7A.9y. +5.6P.9N. +1.98.8k. +4.7A.9z. +7.6v.9V. +1.6N.9Q. +1.98.8l. +1.9i.7Y. +7.6x.9U. +7.6v.9W. +8.6z.9S. +9.6R.9L. +8.6z.9T. +1.7d.9B. +1.8z.91. +1.98.8m. +5.6P.9O. +7.6v.9X. +7.6x.9V. +1.7d.9C. +1.98.8n. +5.6P.9P. +1.7d.9D. +1.8z.92. +1.98.8o. +1.9i.7+. +7.6x.9W. +1.7d.9E. +1.7g.9B. +1.8z.93. +1.98.8p. +2.8S.8E. +5.6P.9Q. +6.6R.9M. +8.6z.9U. +1.7g.9C. +1.8z.94. +6.6R.9N. +7.6x.9X. +1.7d.9F. +1.7g.9D. +1.98.8q. +1.7g.9E. +1.9i.7/. +8.6z.9V. +1.7g.9F. +1.9i.80. +8.6z.9W. +1.7d.9G. +1.8z.95. +2.6J.9R. +2.8S.8F. +6.6R.9O. +1.7d.9H. +1.98.8r. +6.6R.9P. +8.6z.9X. +1.98.8s. +2.6L.9R. +1.7g.9G. +1.8S.8G. +6.6R.9Q. +9.7t.9A. +1.7d.9I. +1.7g.9H. +1.8S.8H. +1.8z.96. +1.7d.9J. +1.9i.81. +1.7g.9I. +1.9i.82. +d.7v.9A. +1.7g.9J. +1.98.8t. +1.9i.83. +2.6N.9R. +1.6L.9S. +1.9i.84. +1.6L.9T. +1.98.8u. +1.98.8v. +1.9i.86. +9.7t.9B. +1.98.8w. +9.7t.9C. +1.7d.9K. +9.7t.9D. +1.6L.9U. +1.9i.87. +2.6P.9R. +9.7t.9E. +1.6N.9S. +1.98.8x. +1.9i.88. +9.7A.9A. +d.7v.9B. +1.6N.9T. +1.7g.9K. +1.9i.89. +9.7t.9F. +d.7v.9C. +1.6L.9V. +1.98.8y. +1.9i.8a. +d.7v.9D. +1.9i.8b. +d.7v.9E. +1.6L.9W. +1.7d.9L. +1.98.8z. +1.9i.8c. +1.6N.9U. +1.8z.99. +1.9i.8d. +9.7t.9G. +d.7v.9F. +1.6L.9X. +1.8S.8L. +1.9i.8e. +5.6P.9S. +9.7t.9H. +1.7g.9L. +1.8S.8M. +1.9i.8f. +5.6P.9T. +1.6N.9V. +1.8S.8N. +1.8z.9a. +2.6R.9R. +2.9i.8g. +9.7A.9B. +1.7d.9M. +2.8z.9b. +2.9i.8h. +4.7t.9I. +9.7A.9C. +d.7v.9G. +1.6N.9W. +1.7d.9N. +9.7A.9D. +9.7t.9J. +d.7v.9H. +5.6P.9U. +9.7A.9E. +1.6N.9X. +1.7g.9M. +1.8z.9c. +1.9i.8i. +1.7g.9N. +4.7A.9F. +d.7v.9I. +1.8z.9d. +1.9i.8j. +5.6P.9V. +d.7v.9J. +e.8S.8P. +1.7d.9O. +9.6R.9S. +1.7d.9P. +1.8S.8Q. +1.98.8B. +1.9i.8k. +5.6P.9W. +9.6R.9T. +1.9i.8l. +9.7A.9G. +1.7d.9Q. +1.7g.9O. +1.8S.8R. +2.8z.9e. +5.6P.9X. +9.7A.9H. +9.7t.9K. +1.7g.9P. +1.8S.8S. +1.9i.8m. +9.6R.9U. +1.7g.9Q. +1.9i.8n. +9.7A.9I. +1.9i.8o. +9.7A.9J. +d.7v.9K. +1.9i.8p. +9.6R.9V. +9.7t.9L. +1.8z.9f. +1.9i.8q. +9.6R.9W. +2.8z.9g. +9.6R.9X. +1.8S.8U. +d.7v.9L. +9.7t.9M. +1.9i.8r. +9.7A.9K. +9.7t.9N. +1.9i.8s. +2.8S.8V. +d.7v.9M. +d.7v.9N. +1.8S.8W. +1.98.8D. +4.7t.9O. +9.7A.9L. +9.7t.9P. +1.9i.8t. +9.7t.9Q. +1.9i.8u. +d.7v.9O. +e.8S.8X. +1.8S.8Y. +1.9i.8v. +d.7v.9P. +1.9i.8w. +2.7g.9R. +9.7A.9M. +9.7A.9N. +d.7v.9Q. +1.9i.8x. +1.7d.9S. +1.7d.9T. +2.8S.8Z. +2.98.8E. +9.7A.9O. +1.7g.9S. +1.8S.8+. +1.9i.8z. +9.7A.9P. +1.7g.9T. +1.7d.9U. +9.7A.9Q. +1.7d.9V. +1.7g.9U. +2.98.8F. +1.7d.9W. +1.7g.9V. +1.98.8G. +1.7d.9X. +1.98.8H. +1.7g.9W. +2.7t.9R. +1.98.8I. +2.8z.9j. +1.7g.9X. +1.98.8J. +1.9i.8B. +2.8z.9k. +1.98.8K. +2.7v.9R. +9.7t.9S. +9.7t.9T. +d.7v.9S. +e.8S.95. +2.7A.9R. +9.7t.9U. +d.7v.9T. +1.8S.96. +4.7t.9V. +1.98.8L. +d.7v.9U. +1.98.8M. +9.7t.9W. +1.98.8N. +4.7A.9S. +9.7t.9X. +d.7v.9V. +4.7A.9T. +1.9i.8D. +d.7v.9W. +1.98.8O. +d.7v.9X. +1.8S.97. +1.98.8P. +4.7A.9U. +1.98.8Q. +4.7A.9V. +1.98.8R. +4.7A.9W. +1.98.8S. +1.8S.99. +4.7A.9X. +1.98.8T. +2.9i.8E. +1.8z.9l. +2.8S.9b. +1.8z.9m. +1.98.8U. +2.9i.8F. +1.8S.9d. +1.9i.8G. +1.98.8V. +1.9i.8H. +1.8z.9n. +2.8S.9e. +1.8z.9o. +1.98.8W. +1.9i.8I. +1.8z.9p. +1.9i.8J. +1.8z.9q. +1.9i.8K. +1.8z.9r. +1.8z.9s. +1.98.8X. +1.8z.9t. +1.98.8Y. +1.8z.9u. +1.8z.9v. +1.8z.9w. +2.8S.9g. +1.8z.9x. +1.8z.9y. +1.8z.9z. +2.98.8Z. +1.98.8+. +1.9i.8L. +e.8S.9h. +1.9i.8M. +1.98.8/. +1.9i.8N. +1.98.90. +1.9i.8O. +1.9i.8P. +1.98.91. +1.9i.8Q. +1.98.92. +1.9i.8R. +1.98.93. +1.8S.9i. +1.98.94. +1.9i.8T. +1.98.95. +1.9i.8U. +1.98.96. +1.8z.9A. +2.8S.9j. +2.8S.9k. +1.9i.8V. +1.9i.8W. +1.8z.9B. +1.98.97. +1.8z.9C. +1.9i.8X. +1.8z.9D. +1.9i.8Y. +1.8z.9E. +1.8z.9F. +1.98.99. +2.9i.8Z. +1.8z.9G. +1.8z.9H. +1.98.9a. +1.9i.8+. +2.98.9b. +1.8z.9I. +1.8z.9J. +1.98.9c. +1.9i.8/. +1.98.9d. +1.9i.90. +2.98.9e. +1.8z.9K. +1.9i.91. +1.9i.92. +1.9i.93. +1.8S.9l. +1.9i.94. +1.98.9f. +1.8z.9L. +2.98.9g. +1.8S.9m. +1.9i.95. +1.8z.9M. +1.8z.9N. +1.9i.96. +1.8S.9n. +1.98.9h. +1.8S.9o. +1.8S.9p. +1.8S.9q. +1.8z.9O. +1.8S.9r. +1.8z.9P. +1.8S.9s. +1.8S.9t. +1.8z.9Q. +1.8S.9u. +1.8S.9v. +1.9i.97. +1.8S.9w. +1.8S.9x. +1.8S.9y. +1.8S.9z. +1.98.9i. +1.9i.99. +1.9i.9a. +2.9i.9b. +1.9i.9c. +1.9i.9d. +2.98.9j. +2.8z.9R. +2.98.9k. +2.9i.9e. +1.8z.9S. +1.8z.9T. +1.9i.9f. +2.9i.9g. +1.8z.9U. +1.8S.9A. +1.8z.9V. +1.8z.9W. +1.8z.9X. +1.8S.9B. +1.8S.9C. +1.8S.9D. +1.8S.9E. +1.8S.9F. +1.8S.9G. +1.8S.9H. +1.98.9l. +1.9i.9i. +1.8S.9I. +1.8S.9J. +1.98.9m. +1.98.9n. +1.98.9o. +2.9i.9j. +1.8S.9K. +1.98.9p. +2.9i.9k. +1.98.9q. +1.98.9r. +1.98.9s. +1.98.9t. +1.98.9u. +1.8S.9L. +1.98.9v. +1.98.9w. +1.98.9x. +1.98.9y. +1.98.9z. +1.8S.9M. +1.8S.9N. +e.8S.9O. +e.8S.9P. +e.8S.9Q. +1.9i.9l. +1.9i.9m. +1.98.9A. +2.8S.9R. +1.9i.9n. +1.9i.9o. +1.9i.9p. +1.9i.9q. +1.98.9B. +1.9i.9r. +1.98.9C. +1.9i.9s. +2.8S.9S. +1.98.9D. +1.9i.9t. +2.8S.9T. +1.98.9E. +1.9i.9u. +1.9i.9v. +1.98.9F. +1.9i.9w. +1.9i.9x. +2.8S.9U. +1.9i.9y. +1.9i.9z. +1.98.9G. +2.8S.9V. +1.98.9H. +2.8S.9W. +1.98.9I. +2.8S.9X. +1.98.9J. +1.98.9K. +1.98.9L. +1.98.9M. +1.98.9N. +1.9i.9A. +1.98.9O. +1.98.9P. +1.98.9Q. +1.9i.9B. +1.9i.9C. +1.9i.9D. +1.9i.9E. +1.9i.9F. +1.9i.9G. +1.9i.9H. +1.9i.9I. +1.9i.9J. +2.98.9R. +1.9i.9K. +1.98.9S. +1.98.9T. +1.9i.9L. +1.98.9U. +1.98.9V. +1.9i.9M. +1.9i.9N. +1.98.9W. +1.98.9X. +1.9i.9O. +1.9i.9P. +1.9i.9Q. +2.9i.9R. +1.9i.9S. +1.9i.9T. +1.9i.9U. +1.9i.9V. +1.9i.9W. +1.9i.9X. +2.9Y.0. +2.9Y.1. +2.9Y.2. +2.9Y.3. +2.9Y.4. +2.9Y.5. +2.9Y.6. +2.9Y.7. +2.9Y.8. +2.9Y.9. +2.9Y.a. +2.9Y.b. +2.9Y.c. +2.9Y.d. +2.9Y.e. +2.9Y.f. +2.9Y.g. +2.9Y.h. +2.9Y.i. +2.9Y.j. +2.9Y.k. +2.9Y.l. +2.9Y.m. +2.9Y.n. +2.9Y.o. +2.9Y.p. +2.9Y.q. +2.9Y.r. +2.9Y.s. +2.9Y.t. +2.9Y.u. +2.9Y.v. +2.9Y.w. +2.9Y.x. +2.9Y.y. +2.9Y.z. +2.9Y.A. +2.9Y.B. +2.9Y.C. +2.9Y.D. +2.9Y.E. +2.9Y.F. +2.9Y.G. +2.9Y.H. +2.9Y.I. +2.9Y.J. +2.9Y.K. +2.9Y.L. +2.9Y.M. +2.9Y.N. +2.9Y.O. +2.9Y.P. +2.9Y.Q. +2.9Y.R. +2.9Y.S. +2.9Y.T. +2.9Y.U. +2.9Y.V. +2.9Y.W. +2.9Y.X. +2.9Y.Y. +2.9Y.Z. +2.9Y.+. +2.9Y./. +2.9Y.10. +2.9Y.11. +2.9Y.12. +2.9Y.13. +2.9Y.14. +2.9Y.15. +2.9Y.16. +2.9Y.17. +2.9Y.18. +2.9Y.19. +2.9Y.1a. +2.9Y.1b. +2.9Y.1c. +2.9Y.1d. +2.9Y.1e. +2.9Y.1f. +2.9Y.1g. +2.9Y.1h. +2.9Y.1i. +2.9Y.1j. +2.9Y.1k. +2.9Y.1l. +2.9Y.1m. +2.9Y.1n. +2.9Y.1o. +2.9Y.1p. +2.9Y.1q. +2.9Y.1r. +2.9Y.1s. +2.9Y.1t. +2.9Y.1u. +2.9Y.1v. +2.9Y.1w. +2.9Y.1x. +2.9Y.1y. +2.9Y.1z. +2.9Y.1A. +2.9Y.1B. +2.9Y.1C. +2.9Y.1D. +2.9Y.1E. +2.9Y.1F. +2.9Y.1G. +2.9Y.1H. +2.9Y.1I. +2.9Y.1J. +2.9Y.1K. +2.9Y.1L. +2.9Y.1M. +2.9Y.1N. +2.9Y.1O. +2.9Y.1P. +2.9Y.1Q. +2.9Y.1R. +2.9Y.1S. +2.9Y.1V. +2.9Y.1W. +2.9Y.1X. +2.9Y.1Y. +2.9Y.1Z. +2.9Y.1+. +2.9Y.1/. +2.9Y.20. +2.9Y.21. +2.9Y.22. +2.9Y.23. +2.9Y.24. +2.9Y.25. +2.9Y.26. +2.9Y.27. +2.9Y.28. +2.9Y.29. +2.9Y.2a. +2.9Y.2c. +2.9Y.2d. +2.9Y.2g. +2.9Y.2m. +2.9Y.2n. +2.9Y.2p. +2.9Y.2q. +2.9Y.2w. +2.9Y.2x. +2.9Y.2y. +2.9Y.2z. +2.9Y.2A. +2.9Y.2B. +2.9Y.2C. +2.9Y.2D. +2.9Y.2E. +2.9Y.2F. +2.9Y.2G. +2.9Y.2H. +2.9Y.2I. +2.9Y.2J. +2.9Y.2K. +2.9Y.2L. +2.9Y.2M. +2.9Y.2N. +2.9Y.2O. +2.9Y.2P. +2.9Y.2Q. +2.9Y.2R. +2.9Y.2S. +2.9Y.2U. +2.9Y.2V. +2.9Y.2W. +2.9Y.2Y. +2.9Y.2+. +2.9Y.2/. +2.9Y.30. +2.9Y.31. +2.9Y.32. +2.9Y.33. +2.9Y.34. +2.9Y.35. +2.9Y.37. +2.9Y.39. +2.9Y.3e. +2.9Y.3f. +2.9Y.3g. +2.9Y.3h. +2.9Y.3i. +2.9Y.3m. +2.9Y.3q. +2.9Y.3r. +2.9Y.3t. +2.9Y.3v. +2.9Y.3x. +2.9Y.3y. +2.9Y.3C. +2.9Y.3G. +2.9Y.3J. +2.9Y.3K. +2.9Y.3L. +2.9Y.3M. +2.9Y.3N. +2.9Y.3R. +2.9Y.3S. +2.9Y.3Y. +2.9Y.40. +2.9Y.4c. +2.9Y.4j. +2.9Y.4q. +2.9Y.53. +2.9Y.55. +2.9Y.58. +2.9Y.5e. +2.9Y.5s. +2.9Y.5u. +2.9Y.5F. +2.9Y.5G. +2.9Y.5L. +2.9Y.6o. +2.9Y.6p. +2.9Y.6s. +2.9Y.6u. +2.9Y.6v. +2.9Y.6x. +2.9Y.6z. +2.9Y.6B. +2.9Y.6E. +2.9Y.6H. +2.9Y.6J. +2.9Y.6L. +2.9Y.6M. +2.9Y.6O. +2.9Y.6P. +2.9Y.6R. +2.9Y.6V. +2.9Y.6+. +2.9Y.71. +2.9Y.7t. +2.9Y.7A. +2.9Y.7V. +2.9Y.7Z. +2.9Y.85. +2.9Y.8C. +2.9Y.9Y. +`; diff --git a/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts b/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts index 89ade45b24..a573ec56bd 100644 --- a/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts +++ b/packages/frontend/src/scripts/emojiKitchen/emojiMixer.ts @@ -1,4 +1,4 @@ -import * as data from './emojiData'; +import * as data from './emojiData.js'; const mixEmojiUrl = (r, c) => { let padZeros = r < 20220500; // Revisions before 0522 had preceding zeros @@ -57,11 +57,28 @@ const pairsMatchingMap = match => { return mixEmojiUrl(d, [c1, c2]); }; +const fixedEncodeIndex = (emoji) => { + const e = hexEncodeEmoji(emoji); + let ei = data.points.indexOf(e); + if (ei === -1) { + ei = data.points.indexOf(e + "-fe0f"); + if (ei === -1) throw new Error('no match emoji'); + } + + return ei; +} + export const mixEmoji = (emoji1, emoji2) => { - const encordedEmoji1 = convertBase(data.points.indexOf(hexEncodeEmoji(emoji1)), 10, 64); - const encordedEmoji2 = convertBase(data.points.indexOf(hexEncodeEmoji(emoji2)), 10, 64); - return [ - ...data.pairs.matchAll(new RegExp("^.*\\." + encordedEmoji1 + "\\." + encordedEmoji2 + "\\.$", "gm")), - ...data.pairs.matchAll(new RegExp("^.*\\." + encordedEmoji2 + "\\." + encordedEmoji1 + "\\.$", "gm")) - ].map(pairsMatchingMap).pop(); + try { + const encordedEmoji1 = convertBase(fixedEncodeIndex(emoji1), 10, 64); + const encordedEmoji2 = convertBase(fixedEncodeIndex(emoji2), 10, 64); + return [ + ...data.pairs.matchAll(new RegExp("^.*\\." + encordedEmoji1 + "\\." + encordedEmoji2 + "\\.$", "gm")), + ...data.pairs.matchAll(new RegExp("^.*\\." + encordedEmoji2 + "\\." + encordedEmoji1 + "\\.$", "gm")) + ].map(pairsMatchingMap).pop(); + } + catch { + console.error('convert failed.', hexEncodeEmoji(emoji1), hexEncodeEmoji(emoji2)); + return; + } }; From db20241627fb25b16a3fa92a2acf600d636c3400 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 30 Sep 2023 11:31:59 +0900 Subject: [PATCH 095/501] =?UTF-8?q?fix=20=E8=99=9A=E7=84=A1=E3=83=9C?= =?UTF-8?q?=E3=82=BF=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index bca14d1f3b..c23cdb5bb6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -13,6 +13,7 @@ export interface Locale { "password": string; "forgotPassword": string; "fetchingAsApObject": string; + "fileAttachedOnly": string; "ok": string; "gotIt": string; "cancel": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 6fd6a61090..dada9d8869 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -673,6 +673,7 @@ reporterOrigin: "通報元" forwardReport: "リモートサーバーに通報を転送する" forwardReportIsAnonymous: "リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。" send: "送信" +fileAttachedOnly: "ファイル付きのみ" abuseMarkAsResolved: "対応済みにする" openInNewTab: "新しいタブで開く" openInSideView: "サイドビューで開く" From 2d815c72cbc81393d4d7d9aef818b1b2fd804951 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 30 Sep 2023 11:34:04 +0900 Subject: [PATCH 096/501] 2023.9.3-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2259ede6d5..9dc861ace3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.3", + "version": "2023.9.3-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From ac1367a0b9ad5da2870af540fce50ab324a3a9f1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 30 Sep 2023 19:02:17 +0900 Subject: [PATCH 097/501] =?UTF-8?q?TL=E3=81=AE=E7=B5=9E=E3=82=8A=E8=BE=BC?= =?UTF-8?q?=E3=81=BF=E4=BF=9D=E5=AD=98=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 +++- locales/ja-JP.yml | 2 ++ packages/frontend/src/pages/settings/general.vue | 4 +++- packages/frontend/src/pages/timeline.vue | 8 ++++---- packages/frontend/src/store.ts | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index c23cdb5bb6..47e542d4e3 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -13,7 +13,6 @@ export interface Locale { "password": string; "forgotPassword": string; "fetchingAsApObject": string; - "fileAttachedOnly": string; "ok": string; "gotIt": string; "cancel": string; @@ -677,6 +676,7 @@ export interface Locale { "forwardReport": string; "forwardReportIsAnonymous": string; "send": string; + "fileAttachedOnly": string; "abuseMarkAsResolved": string; "openInNewTab": string; "openInSideView": string; @@ -709,6 +709,8 @@ export interface Locale { "sentReactionsCount": string; "receivedReactionsCount": string; "pollVotesCount": string; + "onlyAndWithSave": string; + "onlyAndWithSaveInfo": string; "pollVotedCount": string; "yes": string; "no": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index dada9d8869..ed24aa9e85 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -706,6 +706,8 @@ followersCount: "フォロワー数" sentReactionsCount: "リアクションした数" receivedReactionsCount: "リアクションされた数" pollVotesCount: "アンケートに投票した数" +onlyAndWithSave: "タイムラインの絞り込みを保存する" +onlyAndWithSaveInfo: "ファイルのみ や リプライのみ などが保存されるようになります" pollVotedCount: "アンケートに投票された数" yes: "はい" no: "いいえ" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index e808e9b6af..d9279d8bb8 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -139,7 +139,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="enableUltimateDataSaverMode">{{ i18n.ts.UltimateDataSaver }}</MkSwitch> <MkSwitch v-model="enableCellularWithUltimateDataSaver">{{ i18n.ts.cellularWithUltimateDataSaver }}</MkSwitch> <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> - + <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave}}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> </div> <div> <MkRadios v-model="emojiStyle"> @@ -283,6 +283,7 @@ const notificationPosition = computed(defaultStore.makeGetterSetter('notificatio const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificationStackAxis')); const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWithSave')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) watch(lang, () => { @@ -345,6 +346,7 @@ watch([ keepScreenOn, showMediaTimeline, showVisibilityColor, + enableonlyAndWithSave, ], async () => { await reloadAsk(); }); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 3523603b3d..e7a6f82927 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -63,10 +63,10 @@ const rootEl = $shallowRef<HTMLElement>(); let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); -const withRenotes = $ref(true); -const withReplies = $ref(false); -const onlyFiles = $ref(false); - +const withRenotes = $ref(defaultStore.state.onlyAndWithSave ? computed(defaultStore.makeGetterSetter('withRenotes')) : true); +const withReplies = $ref(defaultStore.state.onlyAndWithSave ? computed(defaultStore.makeGetterSetter('withReplies')) : false); +const onlyFiles = $ref(defaultStore.state.onlyAndWithSave ? computed(defaultStore.makeGetterSetter('onlyFiles')) : false); +console.log(defaultStore.state.onlyAndWithSave) watch($$(src), () => queue = 0); function queueUpdated(q: number): void { diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 2ab8fb3593..e086d58b00 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -364,6 +364,22 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 44, }, + onlyAndWithSave:{ + where: 'device', + default: true, + }, + onlyFiles:{ + where: 'device', + default: false, + }, + withReplies:{ + where: 'device', + default: false, + }, + withRenotes:{ + where: 'device', + default: true, + }, showNoteActionsOnlyHover: { where: 'device', default: false, From f4ec97ce7e091f35191b69fb771170b971867c2a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 30 Sep 2023 19:02:34 +0900 Subject: [PATCH 098/501] 2023.9.3-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9dc861ace3..af11e8e66e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.3-prismisskey.1", + "version": "2023.9.3-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From 3ac7a34b63950cd76412159959a3ba58ef986d92 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 1 Oct 2023 05:22:34 +0900 Subject: [PATCH 099/501] =?UTF-8?q?list=E3=81=AE=E3=82=84=E3=81=A4?= =?UTF-8?q?=E3=81=BF=E3=82=8C=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/timeline.vue | 63 +++++++++++------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index e7a6f82927..e86ce3fad6 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -4,29 +4,30 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkStickyContainer> - <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> - <MkSpacer :contentMax="800"> - <div ref="rootEl" v-hotkey.global="keymap"> - <XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/> - <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <MkStickyContainer> + <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> + <MkSpacer :contentMax="800"> + <div ref="rootEl" v-hotkey.global="keymap"> + <XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/> + <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> - <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <div :class="$style.tl"> - <MkTimeline - ref="tlComponent" - :key="src + withRenotes + withReplies + onlyFiles" - :src="src" - :withRenotes="withRenotes" - :withReplies="withReplies" - :onlyFiles="onlyFiles" - :sound="true" - @queue="queueUpdated" - /> + <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> + <div :class="$style.tl"> + <MkTimeline + ref="tlComponent" + :key="src + withRenotes + withReplies + onlyFiles" + :src="src.split(':')[0]" + :list="src.split(':')[1]" + :withRenotes="withRenotes" + :withReplies="withReplies" + :onlyFiles="onlyFiles" + :sound="true" + @queue="queueUpdated" + /> + </div> </div> - </div> - </MkSpacer> -</MkStickyContainer> + </MkSpacer> + </MkStickyContainer> </template> <script lang="ts" setup> @@ -50,9 +51,6 @@ const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); -const isShowMediaTimeline = defaultStore.state.showMediaTimeline; -console.log(isShowMediaTimeline) - const keymap = { 't': focus, }; @@ -63,10 +61,12 @@ const rootEl = $shallowRef<HTMLElement>(); let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); -const withRenotes = $ref(defaultStore.state.onlyAndWithSave ? computed(defaultStore.makeGetterSetter('withRenotes')) : true); -const withReplies = $ref(defaultStore.state.onlyAndWithSave ? computed(defaultStore.makeGetterSetter('withReplies')) : false); -const onlyFiles = $ref(defaultStore.state.onlyAndWithSave ? computed(defaultStore.makeGetterSetter('onlyFiles')) : false); -console.log(defaultStore.state.onlyAndWithSave) +const withReplies_store = computed(defaultStore.makeGetterSetter('withRenotes')) +const withRenotes_store = computed(defaultStore.makeGetterSetter('withReplies')) +const onlyFiles_store = computed(defaultStore.makeGetterSetter('onlyFiles')) +const withRenotes = $ref(defaultStore.state.onlyAndWithSave ? withRenotes_store : true); +const withReplies = $ref(defaultStore.state.onlyAndWithSave ? withReplies_store : false); +const onlyFiles = $ref(defaultStore.state.onlyAndWithSave ? onlyFiles_store : false); watch($$(src), () => queue = 0); function queueUpdated(q: number): void { @@ -175,12 +175,7 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis title: i18n.ts._timelines.local, icon: 'ti ti-planet', iconOnly: true, -}, ...(isShowMediaTimeline ? [{ - key: 'media', - title: i18n.ts._timelines.media, - icon: 'ti ti-photo', - iconOnly: true, -}] : []), { +}, { key: 'social', title: i18n.ts._timelines.social, icon: 'ti ti-rocket', From a328a03c51732c1e7bc9776695f8a33c92674d0e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 1 Oct 2023 18:18:08 +0900 Subject: [PATCH 100/501] =?UTF-8?q?list=E3=81=AE=E3=82=84=E3=81=A4?= =?UTF-8?q?=E3=81=BF=E3=82=8C=E3=81=AA=E3=81=84=E3=81=AE=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/timeline.vue | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index e86ce3fad6..1fd0d44c8b 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -67,6 +67,7 @@ const onlyFiles_store = computed(defaultStore.makeGetterSetter('onlyFiles')) const withRenotes = $ref(defaultStore.state.onlyAndWithSave ? withRenotes_store : true); const withReplies = $ref(defaultStore.state.onlyAndWithSave ? withReplies_store : false); const onlyFiles = $ref(defaultStore.state.onlyAndWithSave ? onlyFiles_store : false); +const isShowMediaTimeline = $ref(defaultStore.state.showMediaTimeline) watch($$(src), () => queue = 0); function queueUpdated(q: number): void { @@ -175,7 +176,12 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis title: i18n.ts._timelines.local, icon: 'ti ti-planet', iconOnly: true, -}, { +}, ...(isShowMediaTimeline ? [{ + key: 'media', + title: i18n.ts._timelines.media, + icon: 'ti ti-photo', + iconOnly: true, +}] : []),{ key: 'social', title: i18n.ts._timelines.social, icon: 'ti ti-rocket', From a0927c53533d29628b6d242a9afc8b60eed44acc Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 4 Oct 2023 01:00:59 +0900 Subject: [PATCH 101/501] =?UTF-8?q?=E3=83=89=E3=83=A9=E3=82=A4=E3=83=96?= =?UTF-8?q?=E8=A4=87=E6=95=B0=E9=81=B8=E6=8A=9E=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E6=80=A7=E3=81=8C=E3=81=82=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/package.json | 2 + .../frontend/src/components/MkDrive.file.vue | 387 ++++++++++-------- .../src/components/MkDrive.folder.vue | 157 +++++-- .../src/components/MkDrive.navFolder.vue | 21 +- packages/frontend/src/components/MkDrive.vue | 50 ++- .../src/scripts/get-drive-file-menu.ts | 49 ++- pnpm-lock.yaml | 130 +++--- 9 files changed, 524 insertions(+), 274 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 47e542d4e3..af30a68f8b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -79,6 +79,7 @@ export interface Locale { "files": string; "download": string; "driveFileDeleteConfirm": string; + "driveFolderDeleteConfirm": string; "unfollowConfirm": string; "exportRequested": string; "importRequested": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ed24aa9e85..9f51fa5505 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -76,6 +76,7 @@ export: "エクスポート" files: "ファイル" download: "ダウンロード" driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。" +driveFolderDeleteConfirm: "フォルダ「{name}」を削除しますか?このフォルダの中に存在するファイルを使用した一部のコンテンツも削除されます。" unfollowConfirm: "{name}のフォローを解除しますか?" exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。" importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。" diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 1f704cd258..9204bda32d 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -27,6 +27,7 @@ "@vitejs/plugin-vue": "4.3.4", "@vue-macros/reactivity-transform": "0.3.23", "@vue/compiler-sfc": "3.3.4", + "@vueuse/core": "^10.4.1", "astring": "1.8.6", "autosize": "6.0.1", "broadcast-channel": "5.3.0", @@ -74,6 +75,7 @@ "vanilla-tilt": "1.8.1", "vite": "4.4.9", "vue": "3.3.4", + "vue-multiselect": "^2.1.7", "vue-prism-editor": "2.0.0-alpha.2", "vuedraggable": "next" }, diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index e3f96724d9..6cc18eae34 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -4,239 +4,290 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div - :class="[$style.root, { [$style.isSelected]: isSelected }]" - draggable="true" - :title="title" - @click="onClick" - @contextmenu.stop="onContextmenu" - @dragstart="onDragstart" - @dragend="onDragend" -> - <div style="pointer-events: none;"> - <div v-if="$i?.avatarId == file.id" :class="[$style.label]"> - <img :class="$style.labelImg" src="/client-assets/label.svg"/> - <p :class="$style.labelText">{{ i18n.ts.avatar }}</p> - </div> - <div v-if="$i?.bannerId == file.id" :class="[$style.label]"> - <img :class="$style.labelImg" src="/client-assets/label.svg"/> - <p :class="$style.labelText">{{ i18n.ts.banner }}</p> - </div> - <div v-if="file.isSensitive" :class="[$style.label, $style.red]"> - <img :class="$style.labelImg" src="/client-assets/label-red.svg"/> - <p :class="$style.labelText">{{ i18n.ts.sensitive }}</p> - </div> + <div + :class="[$style.root, { [$style.isSelected]: isSelected },{[$style.isSelected]: isMultiSelect}]" + draggable="true" + :title="title" + @click="onClick" + @pointerdown="startLongPress(file)" + @pointerup="endLongPress" + @contextmenu.stop="onContextmenu" + @dragstart="onDragstart" + @dragend="onDragend" + > + <div style="pointer-events: none;"> + <div v-if="$i?.avatarId == file.id" :class="[$style.label]"> + <img :class="$style.labelImg" src="/client-assets/label.svg"/> + <p :class="$style.labelText">{{ i18n.ts.avatar }}</p> + </div> + <div v-if="$i?.bannerId == file.id" :class="[$style.label]"> + <img :class="$style.labelImg" src="/client-assets/label.svg"/> + <p :class="$style.labelText">{{ i18n.ts.banner }}</p> + </div> + <div v-if="file.isSensitive" :class="[$style.label, $style.red]"> + <img :class="$style.labelImg" src="/client-assets/label-red.svg"/> + <p :class="$style.labelText">{{ i18n.ts.sensitive }}</p> + </div> - <MkDriveFileThumbnail :class="$style.thumbnail" :file="file" fit="contain"/> + <MkDriveFileThumbnail :class="$style.thumbnail" :file="file" fit="contain"/> - <p :class="$style.name"> - <span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substring(0, file.name.lastIndexOf('.')) : file.name }}</span> - <span v-if="file.name.lastIndexOf('.') != -1" style="opacity: 0.5;">{{ file.name.substring(file.name.lastIndexOf('.')) }}</span> - </p> - </div> -</div> + <p :class="$style.name"> + <span>{{ + file.name.lastIndexOf('.') != -1 ? file.name.substring(0, file.name.lastIndexOf('.')) : file.name + }}</span> + <span v-if="file.name.lastIndexOf('.') != -1" + style="opacity: 0.5;">{{ file.name.substring(file.name.lastIndexOf('.')) }}</span> + </p> + </div> + </div> </template> <script lang="ts" setup> -import { computed, ref } from 'vue'; +import {computed, ref, watch} from 'vue'; import * as Misskey from 'misskey-js'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import bytes from '@/filters/bytes.js'; import * as os from '@/os.js'; -import { i18n } from '@/i18n.js'; -import { $i } from '@/account.js'; -import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js'; +import {i18n} from '@/i18n.js'; +import {$i} from '@/account.js'; +import {getDriveFileMenu, getDriveFileMultiMenu} from '@/scripts/get-drive-file-menu.js'; +const isLongPressing = ref(false); +let longPressTimeout = null; +import {defaultStore} from '@/store.js' +function startLongPress() { + isLongPressing.value = true; + longPressTimeout = setTimeout(() => { + if (isLongPressing) { + } + }, 800); // 長押しと判断する時間(1秒)を設定 +} +function endLongPress() { + isLongPressing.value = false; + clearTimeout(longPressTimeout); +} + +const isMultiSelect = ref(false); const props = withDefaults(defineProps<{ - file: Misskey.entities.DriveFile; - folder: Misskey.entities.DriveFolder | null; - isSelected?: boolean; - selectMode?: boolean; + file: Misskey.entities.DriveFile; + folder: Misskey.entities.DriveFolder | null; + isSelected?: boolean; + selectMode?: boolean; + multipleselect?; + isLongPressing?; }>(), { - isSelected: false, - selectMode: false, + isSelected: false, + selectMode: false, }); const emit = defineEmits<{ - (ev: 'chosen', r: Misskey.entities.DriveFile): void; - (ev: 'dragstart'): void; - (ev: 'dragend'): void; + (ev: 'chosen', r: Misskey.entities.DriveFile): void; + (ev: 'dragstart'): void; + (ev: 'dragend'): void; }>(); const isDragging = ref(false); const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`); - +watch(props.multipleselect, () => { + isMultiSelect.value = !!props.multipleselect.some(item => item.id === props.file.id); +}); function onClick(ev: MouseEvent) { - if (props.selectMode) { - emit('chosen', props.file); - } else { - os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); - } -} + if (props.selectMode) { + emit('chosen', props.file); + } else if (ev.shiftKey && ev.button === 2 || isMultiSelect.value ) { + os.popupMenu(getDriveFileMultiMenu(props.multipleselect, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + + } + else if (!ev.shiftKey) { + os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + + } +} +function device() { + var ua = navigator.userAgent; + if(ua.indexOf('iPhone') > 0 || ua.indexOf('iPod') > 0 || ua.indexOf('Android') > 0 && ua.indexOf('Mobile') > 0){ + return 'mobile'; + }else if(ua.indexOf('iPad') > 0 || ua.indexOf('Android') > 0){ + return 'tablet'; + }else{ + return 'desktop'; + } +} function onContextmenu(ev: MouseEvent) { - os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); + + if (!ev.shiftKey && !isMultiSelect.value) { + os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); + } else if (ev.shiftKey && props.multipleselect.length > 0 && ev.button === 2) { + os.contextMenu(getDriveFileMultiMenu(props.multipleselect, props.folder), ev); + } else if(device()=== "desktop"){ + os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); + + } + if (props.isLongPressing){ + isMultiSelect.value = !isMultiSelect.value + } } function onDragstart(ev: DragEvent) { - if (ev.dataTransfer) { - ev.dataTransfer.effectAllowed = 'move'; - ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file)); - } - isDragging.value = true; + if (ev.dataTransfer) { + ev.dataTransfer.effectAllowed = 'move'; + ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file)); + } + isDragging.value = true; - emit('dragstart'); + emit('dragstart'); } function onDragend() { - isDragging.value = false; - emit('dragend'); + isDragging.value = false; + emit('dragend'); } </script> <style lang="scss" module> .root { - position: relative; - padding: 8px 0 0 0; - min-height: 180px; - border-radius: 8px; - cursor: pointer; + position: relative; + padding: 8px 0 0 0; + min-height: 180px; + border-radius: 8px; + cursor: pointer; - &:hover { - background: rgba(#000, 0.05); + &:hover { + background: rgba(#000, 0.05); - > .label { - &:before, - &:after { - background: #0b65a5; - } + > .label { + &:before, + &:after { + background: #0b65a5; + } - &.red { - &:before, - &:after { - background: #c12113; - } - } - } - } + &.red { + &:before, + &:after { + background: #c12113; + } + } + } + } - &:active { - background: rgba(#000, 0.1); + &:active { + background: rgba(#000, 0.1); - > .label { - &:before, - &:after { - background: #0b588c; - } + > .label { + &:before, + &:after { + background: #0b588c; + } - &.red { - &:before, - &:after { - background: #ce2212; - } - } - } - } + &.red { + &:before, + &:after { + background: #ce2212; + } + } + } + } - &.isSelected { - background: var(--accent); + &.isSelected { + background: var(--accent); - &:hover { - background: var(--accentLighten); - } + &:hover { + background: var(--accentLighten); + } - &:active { - background: var(--accentDarken); - } + &:active { + background: var(--accentDarken); + } - > .label { - &:before, - &:after { - display: none; - } - } + > .label { + &:before, + &:after { + display: none; + } + } - > .name { - color: #fff; - } + > .name { + color: #fff; + } - > .thumbnail { - color: #fff; - } - } + > .thumbnail { + color: #fff; + } + } } .label { - position: absolute; - top: 0; - left: 0; - pointer-events: none; + position: absolute; + top: 0; + left: 0; + pointer-events: none; - &:before, - &:after { - content: ""; - display: block; - position: absolute; - z-index: 1; - background: #0c7ac9; - } + &:before, + &:after { + content: ""; + display: block; + position: absolute; + z-index: 1; + background: #0c7ac9; + } - &:before { - top: 0; - left: 57px; - width: 28px; - height: 8px; - } + &:before { + top: 0; + left: 57px; + width: 28px; + height: 8px; + } - &:after { - top: 57px; - left: 0; - width: 8px; - height: 28px; - } + &:after { + top: 57px; + left: 0; + width: 8px; + height: 28px; + } - &.red { - &:before, - &:after { - background: #c12113; - } - } + &.red { + &:before, + &:after { + background: #c12113; + } + } } .labelImg { - position: absolute; - z-index: 2; - top: 0; - left: 0; + position: absolute; + z-index: 2; + top: 0; + left: 0; } .labelText { - position: absolute; - z-index: 3; - top: 19px; - left: -28px; - width: 120px; - margin: 0; - text-align: center; - line-height: 28px; - color: #fff; - transform: rotate(-45deg); + position: absolute; + z-index: 3; + top: 19px; + left: -28px; + width: 120px; + margin: 0; + text-align: center; + line-height: 28px; + color: #fff; + transform: rotate(-45deg); } .thumbnail { - width: 110px; - height: 110px; - margin: auto; + width: 110px; + height: 110px; + margin: auto; } .name { - display: block; - margin: 4px 0 0 0; - font-size: 0.8em; - text-align: center; - word-break: break-all; - color: var(--fg); - overflow: hidden; + display: block; + margin: 4px 0 0 0; + font-size: 0.8em; + text-align: center; + word-break: break-all; + color: var(--fg); + overflow: hidden; } </style> diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 5322664664..07fa06fb0e 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -18,6 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only @drop.prevent.stop="onDrop" @dragstart="onDragstart" @dragend="onDragend" + > <p :class="$style.name"> <template v-if="hover"><i :class="$style.icon" class="ti ti-folder ti-fw"></i></template> @@ -44,6 +45,7 @@ const props = withDefaults(defineProps<{ folder: Misskey.entities.DriveFolder; isSelected?: boolean; selectMode?: boolean; + multipleselect?; }>(), { isSelected: false, selectMode: false, @@ -143,11 +145,22 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { - fileId: file.id, - folderId: props.folder.id, - }); - } + if (props.multipleselect.length > 0) { + props.multipleselect.forEach((e)=>{ + os.api('drive/files/update', { + fileId: e.id, + folderId: props.folder.id, + }); + }) + }else{ + os.api('drive/files/update', { + fileId: file.id, + folderId: props.folder.id, + }); + } + + + } //#endregion //#region ドライブのフォルダ @@ -221,28 +234,118 @@ function rename() { } function deleteFolder() { - os.api('drive/folders/delete', { - folderId: props.folder.id, - }).then(() => { - if (defaultStore.state.uploadFolder === props.folder.id) { - defaultStore.set('uploadFolder', null); - } - }).catch(err => { - switch (err.id) { - case 'b0fc8a17-963c-405d-bfbc-859a487295e1': - os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders, - }); - break; - default: - os.alert({ - type: 'error', - text: i18n.ts.unableToDelete, - }); - } - }); + os.api('drive/folders/show', { + folderId: props.folder.id, + }).then(async (r) => { + + if (r.foldersCount > 0) { + await os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。', + }); + } + + if (r.filesCount > 0) { + + const {canceled} = await os.confirm({ + type: 'warning', + text: i18n.t('driveFolderDeleteConfirm', {name: props.folder.name}), + }); + + if (canceled) return; + + let allResults = []; + let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); + allResults = allResults.concat(Result) + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); // pushをconcatに変更 + } + allResults.forEach((r,i)=>{ + os.api('drive/files/delete',{fileId: r.id}) + }) + + + os.api('drive/folders/show', { + folderId: props.folder.id, + }).then(async (r) =>{ + if (r.filesCount > 0) { + + let allResults = []; + let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); + allResults = allResults.concat(Result) + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); // pushをconcatに変更 + } + allResults.forEach((r,i)=>{ + console.log(r) + os.api('drive/files/delete',{fileId: r.id}) + }) + + os.api('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); + + os.api('drive/folders/delete', { + folderId: props.folder.id, + }) + }else{ + os.api('drive/folders/delete', { + folderId: props.folder.id, + }) + } + }) + + } else { + + await os.api('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); + } + }) + } function setAsUploadFolder() { diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index 59458ad568..d960d86209 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -26,6 +26,7 @@ import { i18n } from '@/i18n.js'; const props = defineProps<{ folder?: Misskey.entities.DriveFolder; parentFolder: Misskey.entities.DriveFolder | null; + multipleselect?; }>(); const emit = defineEmits<{ @@ -112,10 +113,22 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { - fileId: file.id, - folderId: props.folder ? props.folder.id : null, - }); + + if (props.multipleselect.length > 0) { + + props.multipleselect.forEach((e)=>{ + os.api('drive/files/update', { + fileId: e.id, + folderId: props.folder ? props.folder.id : null, + }); + }) + + }else{ + os.api('drive/files/update', { + fileId: file.id, + folderId: props.folder ? props.folder.id : null, + }); + } } //#endregion diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 648e4c4e3d..d2dea8f9f1 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only @upload="upload" @removeFile="removeFile" @removeFolder="removeFolder" + :multipleselect="multipleselect" /> <template v-for="f in hierarchyFolders"> <span :class="[$style.navPathItem, $style.navSeparator]"><i class="ti ti-chevron-right"></i></span> @@ -25,6 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only @upload="upload" @removeFile="removeFile" @removeFolder="removeFolder" + :multipleselect="multipleselect" /> </template> <span v-if="folder != null" :class="[$style.navPathItem, $style.navSeparator]"><i class="ti ti-chevron-right"></i></span> @@ -51,6 +53,7 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="f" :selectMode="select === 'folder'" :isSelected="selectedFolders.some(x => x.id === f.id)" + :multipleselect="multipleselect" @chosen="chooseFolder" @move="move" @upload="upload" @@ -76,6 +79,12 @@ SPDX-License-Identifier: AGPL-3.0-only @chosen="chooseFile" @dragstart="isDragSource = true" @dragend="isDragSource = false" + @pointerdown="startLongPress(file)" + @pointerup="endLongPress" + @click.shift.left.exact="selectClick(file)" + @click.ctrl.left.exact="selectClick(file)" + :multipleselect="multipleselect" + :isLongPressing="isLongPressing" /> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <div v-for="(n, i) in 16" :key="i" :class="$style.padding"></div> @@ -118,6 +127,24 @@ const props = withDefaults(defineProps<{ select: null, }); +const isLongPressing = ref(false); +let longPressTimeout = null; + +function startLongPress(file) { + isLongPressing.value = true; + longPressTimeout = setTimeout(() => { + if (isLongPressing) { + selectClick(file) + } + }, 800); // 長押しと判断する時間(1秒)を設定 +} + +function endLongPress() { + isLongPressing.value = false; + clearTimeout(longPressTimeout); +} + + const emit = defineEmits<{ (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void; (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; @@ -140,7 +167,7 @@ const selectedFolders = ref<Misskey.entities.DriveFolder[]>([]); const uploadings = uploads; const connection = useStream().useChannel('drive'); const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい - +let multipleselect = ref([]); // ドロップされようとしているか const draghover = ref(false); @@ -168,7 +195,18 @@ function onStreamDriveFileUpdated(file: Misskey.entities.DriveFile) { addFile(file, true); } } +function selectClick(file) { + const index = multipleselect.value.findIndex(item => item.id === file.id); + if (index !== -1) { + // File is already selected, so remove it + multipleselect.value.splice(index, 1); + } else { + // File is not selected, so add it + multipleselect.value.push(file); + } + +} function onStreamDriveFileDeleted(fileId: string) { removeFile(fileId); } @@ -405,6 +443,7 @@ function chooseFile(file: Misskey.entities.DriveFile) { emit('change-selection', [file]); } } + } function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { @@ -427,6 +466,7 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { } function move(target?: Misskey.entities.DriveFolder) { + multipleselect.value = [] if (!target) { goRoot(); return; @@ -491,11 +531,17 @@ function addFile(fileToAdd: Misskey.entities.DriveFile, unshift = false) { function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) { const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove.id : folderToRemove; folders.value = folders.value.filter(f => f.id !== folderIdToRemove); + const index = multipleselect.value.findIndex(item => item.id === file.id); + if (index !== -1) { + // File is already selected, so remove it + multipleselect.value.splice(index, 1); + } } function removeFile(file: Misskey.entities.DriveFile | string) { const fileId = typeof file === 'object' ? file.id : file; files.value = files.value.filter(f => f.id !== fileId); + multipleselect.value = multipleselect.value.filter(a => a.id !== fileId) } function appendFile(file: Misskey.entities.DriveFile) { @@ -645,10 +691,12 @@ function getMenu() { } function showMenu(ev: MouseEvent) { + multipleselect.value = [] os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); } function onContextmenu(ev: MouseEvent) { + multipleselect.value = [] os.contextMenu(getMenu(), ev); } diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index 0964108249..15f822acc6 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -39,10 +39,10 @@ function describe(file: Misskey.entities.DriveFile) { }, 'closed'); } -function toggleSensitive(file: Misskey.entities.DriveFile) { +function toggleSensitive(file: Misskey.entities.DriveFile , isSensitive?) { os.api('drive/files/update', { fileId: file.id, - isSensitive: !file.isSensitive, + isSensitive: isSensitive !== null ? isSensitive : !file.isSensitive }).catch(err => { os.alert({ type: 'error', @@ -72,7 +72,20 @@ async function deleteFile(file: Misskey.entities.DriveFile) { fileId: file.id, }); } +async function deleteSelectFile(files) { + const { canceled } = await os.confirm({ + type: 'warning', + text: files.length+'つのファイルをまとめて削除しますか?', + }); + if (canceled) return; + files.forEach((e) => { + os.api('drive/files/delete', { + fileId: e.id, + }); + }) + +} export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Misskey.entities.DriveFolder | null): MenuItem[] { const isImage = file.type.startsWith('image/'); let menu; @@ -131,3 +144,35 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss return menu; } + +export function getDriveFileMultiMenu(file, folder?: Misskey.entities.DriveFolder | null): MenuItem[] { + + let menu; + menu = [{ + text: i18n.ts.unmarkAsSensitive, + icon: 'ti ti-eye', + action: () => {file.forEach((e) => toggleSensitive(e,false))} + },{ + text: i18n.ts.markAsSensitive, + icon: 'ti ti-eye-exclamation', + action: () => {file.forEach((e) => toggleSensitive(e,true))} + , + },{ + text: i18n.ts.delete, + icon: 'ti ti-trash', + danger: true, + action: () => deleteSelectFile(file), + }]; + + if (defaultStore.state.devMode) { + menu = menu.concat([null, { + icon: 'ti ti-id', + text: i18n.ts.copyFileId, + action: () => { + copyToClipboard(file.id); + }, + }]); + } + + return menu; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2ce08699c0..cf7a537469 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -673,6 +673,9 @@ importers: '@vue/compiler-sfc': specifier: 3.3.4 version: 3.3.4 + '@vueuse/core': + specifier: ^10.4.1 + version: 10.4.1(vue@3.3.4) astring: specifier: 1.8.6 version: 1.8.6 @@ -814,6 +817,9 @@ importers: vue: specifier: 3.3.4 version: 3.3.4 + vue-multiselect: + specifier: ^2.1.7 + version: 2.1.7 vue-prism-editor: specifier: 2.0.0-alpha.2 version: 2.0.0-alpha.2(vue@3.3.4) @@ -979,7 +985,7 @@ importers: version: 7.4.5 storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.5)(@storybook/components@7.4.4)(@storybook/core-events@7.4.5)(@storybook/manager-api@7.4.5)(@storybook/preview-api@7.4.5)(@storybook/theming@7.4.5)(@storybook/types@7.4.5)(react-dom@18.2.0)(react@18.2.0) + version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.5)(@storybook/components@7.4.5)(@storybook/core-events@7.4.5)(@storybook/manager-api@7.4.5)(@storybook/preview-api@7.4.5)(@storybook/theming@7.4.5)(@storybook/types@7.4.5)(react-dom@18.2.0)(react@18.2.0) summaly: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 @@ -6276,17 +6282,6 @@ packages: - supports-color dev: true - /@storybook/channels@7.4.4: - resolution: {integrity: sha512-YA2T3hClL95nFBBelm8wMOyWFDzfxKvyHAPQi+8YeYpZcPivwg/P9YnRhTTMbiZNkfoWKq4ZRuc79UP1iNLi3g==} - dependencies: - '@storybook/client-logger': 7.4.4 - '@storybook/core-events': 7.4.4 - '@storybook/global': 5.0.0 - qs: 6.11.1 - telejson: 7.2.0 - tiny-invariant: 1.3.1 - dev: true - /@storybook/channels@7.4.5: resolution: {integrity: sha512-zWPZn4CxPFXsrrSRQ9JD8GmTeWeFYgr3sTBpe23hnhYookCXVNJ6AcaXogrT9b2ALfbB6MiFDbZIHHTgIgbWpg==} dependencies: @@ -6350,12 +6345,6 @@ packages: - utf-8-validate dev: true - /@storybook/client-logger@7.4.4: - resolution: {integrity: sha512-rC/GcCy3DLtTI+oOHLBc6rq/c3oGF/mvdeWrhMM+berQplHJrOCI2pcldjVw8Fc25gLPK0LUlaOp1dfgt2Ri3Q==} - dependencies: - '@storybook/global': 5.0.0 - dev: true - /@storybook/client-logger@7.4.5: resolution: {integrity: sha512-Bn6eTAjhPDUfLpvuxhKkpDpOtkadfkSmkBNBZRu3r0Dzk2J1nNyKV5K6D8dOU4PFVof4z/gXYj5bktT29jKsmw==} dependencies: @@ -6383,29 +6372,6 @@ packages: - supports-color dev: true - /@storybook/components@7.4.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-tFOSu3IoAab/0aY2TY66Go0Nba7AB/+ZB9GFet+dxWypIKGLcPjyX2POIumJU4swzK+4IA8GxgDQ2itS6EOISQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.4.4 - '@storybook/csf': 0.1.0 - '@storybook/global': 5.0.0 - '@storybook/theming': 7.4.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.4 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - dev: true - /@storybook/components@7.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-boskkfvMBB8CFYY9+1ofFNyKrdWXTY/ghzt7oK80dz6f2Eseo/WXK3OsCdCq5vWbLRCdbgJ8zXG8pAFi4yBsxA==} peerDependencies: @@ -6467,12 +6433,6 @@ packages: - supports-color dev: true - /@storybook/core-events@7.4.4: - resolution: {integrity: sha512-kOf4I/a1XC9CaGFwJG5WR2KnkwrOkWX68TLh7OlelKxdl/WjxA4zfzaFPC/8zyCSLdGFLPKNqr1w+ezkb+9Irw==} - dependencies: - ts-dedent: 2.2.0 - dev: true - /@storybook/core-events@7.4.5: resolution: {integrity: sha512-Jzy/adSC95saYCZlgXE5j7jmiMLAXYpnBFBxEtBdXwSWEBb0zt21n1nyWBEAv9s/k2gqDXlPHKHeL5Mn6y40zA==} dependencies: @@ -6807,20 +6767,6 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/theming@7.4.4(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-ABIwLRUj2IZKMGxKq+fCCFcY7w52P1a+q8j7qrlELaTe4M74K6rwTgRF0/AFgWeiGRkNuA7z8DjQ73xQLoLqUg==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) - '@storybook/client-logger': 7.4.4 - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@storybook/theming@7.4.5(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-QSIJDIMzOegzlhubIBaYIovf4mlf+AVL0SmQOskPS8GZ6s9t77yUUI6gZTEjO+S4eB3djXRsfTTijQ8+z4XmRA==} peerDependencies: @@ -6835,15 +6781,6 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/types@7.4.4: - resolution: {integrity: sha512-B0VdgGb1XGEb9g3UuEd9xANCIhR3anvA3w0uYSG+7uMOflnEawwZksTSxvvoGM2hx9vC4pNT4Fci9sEC903UkA==} - dependencies: - '@storybook/channels': 7.4.4 - '@types/babel__core': 7.20.0 - '@types/express': 4.17.17 - file-system-cache: 2.3.0 - dev: true - /@storybook/types@7.4.5: resolution: {integrity: sha512-DTWFNjfRTpncjufDoUs0QnNkgHG2qThGKWL1D6sO18cYI02zWPyHWD8/cbqlvtT7XIGe3s1iUEfCTdU5GcwWBA==} dependencies: @@ -7965,6 +7902,10 @@ packages: '@types/node': 20.7.1 dev: true + /@types/web-bluetooth@0.0.17: + resolution: {integrity: sha512-4p9vcSmxAayx72yn70joFoL44c9MO/0+iVEBIQXe3v2h2SiAsEIo/G5v6ObFWvNKRFjbrVadNf9LqEEZeQPzdA==} + dev: false + /@types/web-push@3.6.1: resolution: {integrity: sha512-Zu6Iju7c4IlE8I8eEeFLYRb7XFqvHFmWWAYr1cmug9EX3c6CDarxIXWN/GO0sxjbJLkHPwozUzp6cLdXsrq7Ew==} dependencies: @@ -8396,6 +8337,31 @@ packages: - typescript dev: true + /@vueuse/core@10.4.1(vue@3.3.4): + resolution: {integrity: sha512-DkHIfMIoSIBjMgRRvdIvxsyboRZQmImofLyOHADqiVbQVilP8VVHDhBX2ZqoItOgu7dWa8oXiNnScOdPLhdEXg==} + dependencies: + '@types/web-bluetooth': 0.0.17 + '@vueuse/metadata': 10.4.1 + '@vueuse/shared': 10.4.1(vue@3.3.4) + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@vueuse/metadata@10.4.1: + resolution: {integrity: sha512-2Sc8X+iVzeuMGHr6O2j4gv/zxvQGGOYETYXEc41h0iZXIRnRbJZGmY/QP8dvzqUelf8vg0p/yEA5VpCEu+WpZg==} + dev: false + + /@vueuse/shared@10.4.1(vue@3.3.4): + resolution: {integrity: sha512-vz5hbAM4qA0lDKmcr2y3pPdU+2EVw/yzfRsBdu+6+USGa4PxqSQRYIUC9/NcT06y+ZgaTsyURw2I9qOFaaXHAg==} + dependencies: + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + /@webgpu/types@0.1.30: resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==} requiresBuild: true @@ -19184,6 +19150,21 @@ packages: vue: 3.3.4 dev: false + /vue-demi@0.14.6(vue@3.3.4): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + dev: false + /vue-docgen-api@4.64.1(vue@3.3.4): resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==} dependencies: @@ -19228,6 +19209,11 @@ packages: vue: 3.3.4 dev: true + /vue-multiselect@2.1.7: + resolution: {integrity: sha512-KIegcN+Ntwg3cbkY/jhw2s/+XJUM0Lpi/LcKFYCS8PrZHcWBl2iKCVze7ZCnRj3w8H7/lUJ9v7rj9KQiNxApBw==} + engines: {node: '>= 4.0.0', npm: '>= 3.0.0'} + dev: false + /vue-prism-editor@2.0.0-alpha.2(vue@3.3.4): resolution: {integrity: sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==} engines: {node: '>=10'} @@ -19707,7 +19693,7 @@ packages: sharp: 0.31.3 dev: false - github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.5)(@storybook/components@7.4.4)(@storybook/core-events@7.4.5)(@storybook/manager-api@7.4.5)(@storybook/preview-api@7.4.5)(@storybook/theming@7.4.5)(@storybook/types@7.4.5)(react-dom@18.2.0)(react@18.2.0): + github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.4.5)(@storybook/components@7.4.5)(@storybook/core-events@7.4.5)(@storybook/manager-api@7.4.5)(@storybook/preview-api@7.4.5)(@storybook/theming@7.4.5)(@storybook/types@7.4.5)(react-dom@18.2.0)(react@18.2.0): resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640 name: storybook-addon-misskey-theme @@ -19729,7 +19715,7 @@ packages: optional: true dependencies: '@storybook/blocks': 7.4.5(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 7.4.4(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 7.4.5(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': 7.4.5 '@storybook/manager-api': 7.4.5(react-dom@18.2.0)(react@18.2.0) '@storybook/preview-api': 7.4.5 From c3453833747bfe79eb5462ea6c06683c6965f982 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 6 Oct 2023 08:01:42 +0900 Subject: [PATCH 102/501] =?UTF-8?q?=E3=83=89=E3=83=A9=E3=82=A4=E3=83=96?= =?UTF-8?q?=E3=81=A7=E8=A4=87=E6=95=B0=E9=81=B8=E6=8A=9E=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 + locales/ja-JP.yml | 4 + package.json | 3 +- .../src/components/MkDrive.folder.vue | 152 +++++++-- .../src/components/MkDrive.navFolder.vue | 18 +- packages/frontend/src/components/MkDrive.vue | 312 ++++++++++++------ 6 files changed, 370 insertions(+), 123 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 47e542d4e3..e254f2e9f2 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -79,6 +79,10 @@ export interface Locale { "files": string; "download": string; "driveFileDeleteConfirm": string; + "driveFilesDeleteConfirm": string; + "driveFilesSensitiveonConfirm": string; + "driveFilesSensitiveoffConfirm": string; + "driveFolderDeleteConfirm": string; "unfollowConfirm": string; "exportRequested": string; "importRequested": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ed24aa9e85..c332fd9bf6 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -76,6 +76,10 @@ export: "エクスポート" files: "ファイル" download: "ダウンロード" driveFileDeleteConfirm: "ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。" +driveFilesDeleteConfirm: "{name}つのファイルを削除しますか?このファイルを使用した一部のコンテンツも削除されます。" +driveFilesSensitiveonConfirm: "{name}つのファイルをセンシティブにしますか?" +driveFilesSensitiveoffConfirm: "{name}つのファイルのセンシティブを解除しますか?" +driveFolderDeleteConfirm: "フォルダ「{name}」を削除しますか?このフォルダの中に存在するファイルを使用した一部のコンテンツも削除されます。" unfollowConfirm: "{name}のフォローを解除しますか?" exportRequested: "エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。" importRequested: "インポートをリクエストしました。これには時間がかかる場合があります。" diff --git a/package.json b/package.json index af11e8e66e..0a7efa9f5e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.9.3-prismisskey.2", + "version": "2023.9.3-prismisskey.3", "codename": "nasubi", "repository": { "type": "git", @@ -18,6 +18,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", + "build-and-start": "pnpm build && pnpm start", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index 5322664664..f13cdb61a8 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -44,6 +44,7 @@ const props = withDefaults(defineProps<{ folder: Misskey.entities.DriveFolder; isSelected?: boolean; selectMode?: boolean; + selectedFiles?: string[]; }>(), { isSelected: false, selectMode: false, @@ -143,10 +144,19 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { - fileId: file.id, - folderId: props.folder.id, - }); + if (props.selectedFiles.length > 0) { + props.selectedFiles.forEach((e)=>{ + os.api('drive/files/update', { + fileId: e.id, + folderId: props.folder.id, + }); + }) + }else{ + os.api('drive/files/update', { + fileId: file.id, + folderId: props.folder.id, + }); + } } //#endregion @@ -221,30 +231,120 @@ function rename() { } function deleteFolder() { - os.api('drive/folders/delete', { - folderId: props.folder.id, - }).then(() => { - if (defaultStore.state.uploadFolder === props.folder.id) { - defaultStore.set('uploadFolder', null); - } - }).catch(err => { - switch (err.id) { - case 'b0fc8a17-963c-405d-bfbc-859a487295e1': - os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders, - }); - break; - default: - os.alert({ - type: 'error', - text: i18n.ts.unableToDelete, - }); - } - }); + os.api('drive/folders/show', { + folderId: props.folder.id, + }).then(async (r) => { + + if (r.foldersCount > 0) { + await os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。', + }); + } + + if (r.filesCount > 0) { + + const {canceled} = await os.confirm({ + type: 'warning', + text: i18n.t('driveFolderDeleteConfirm', {name: props.folder.name}), + }); + + if (canceled) return; + + let allResults = []; + let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); + allResults = allResults.concat(Result) + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); // pushをconcatに変更 + } + allResults.forEach((r,i)=>{ + os.api('drive/files/delete',{fileId: r.id}) + }) + + + os.api('drive/folders/show', { + folderId: props.folder.id, + }).then(async (r) =>{ + if (r.filesCount > 0) { + + let allResults = []; + let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); + allResults = allResults.concat(Result) + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); + } + allResults.forEach((r,i)=>{ + os.api('drive/files/delete',{fileId: r.id}) + }) + + os.api('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); + + os.api('drive/folders/delete', { + folderId: props.folder.id, + }) + }else{ + os.api('drive/folders/delete', { + folderId: props.folder.id, + }) + } + }) + + } else { + + await os.api('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); + } + }) + } + function setAsUploadFolder() { defaultStore.set('uploadFolder', props.folder.id); } diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue index 59458ad568..f3c4374997 100644 --- a/packages/frontend/src/components/MkDrive.navFolder.vue +++ b/packages/frontend/src/components/MkDrive.navFolder.vue @@ -26,6 +26,7 @@ import { i18n } from '@/i18n.js'; const props = defineProps<{ folder?: Misskey.entities.DriveFolder; parentFolder: Misskey.entities.DriveFolder | null; + selectedFiles: string[]; }>(); const emit = defineEmits<{ @@ -112,10 +113,19 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - os.api('drive/files/update', { - fileId: file.id, - folderId: props.folder ? props.folder.id : null, - }); + if (props.selectedFiles.length > 0) { + props.selectedFiles.forEach((e) => { + os.api('drive/files/update', { + fileId: e.id, + folderId: props.folder ? props.folder.id : null, + }); + }); + } else { + os.api('drive/files/update', { + fileId: file.id, + folderId: props.folder ? props.folder.id : null, + }); + } } //#endregion diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 648e4c4e3d..6d15142d3d 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XNavFolder :class="[$style.navPathItem, { [$style.navCurrent]: folder == null }]" :parentFolder="folder" + :selectedFiles="selectedFiles" @move="move" @upload="upload" @removeFile="removeFile" @@ -27,10 +28,23 @@ SPDX-License-Identifier: AGPL-3.0-only @removeFolder="removeFolder" /> </template> - <span v-if="folder != null" :class="[$style.navPathItem, $style.navSeparator]"><i class="ti ti-chevron-right"></i></span> + <span v-if="folder != null" :class="[$style.navPathItem, $style.navSeparator]"><i + class="ti ti-chevron-right" + ></i></span> <span v-if="folder != null" :class="[$style.navPathItem, $style.navCurrent]">{{ folder.name }}</span> </div> - <button class="_button" :class="$style.navMenu" @click="showMenu"><i class="ti ti-dots"></i></button> + <button v-if="!multiple" class="_button" :class="$style.navMenu" @click="filesSelect">複数選択モード</button> + <span v-if="multiple && selectedFiles.length > 0" style="padding-right: 12px; margin-top: auto; margin-bottom: auto;opacity: 0.5;"> + ({{ number(selectedFiles.length) }}) + </span> + <button v-if="multiple" class="_button" :class="$style.navMenu" @click="filesSelect">複数選択モード解除</button> + <button v-if="multiple && selectedFiles.length === 0" style="padding-right: 12px;" class="_button" @click="filesAllSelect"> + 全選択 + </button> + <button v-if="multiple && selectedFiles.length !== 0" style="padding-right: 12px;" class="_button" @click="filesAllSelect"> + 全選択解除 + </button> + <button class="_button" @click="showMenu"><i class="ti ti-dots"></i></button> </nav> <div ref="main" @@ -51,6 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="f" :selectMode="select === 'folder'" :isSelected="selectedFolders.some(x => x.id === f.id)" + :selectedFiles="selectedFiles" @chosen="chooseFolder" @move="move" @upload="upload" @@ -73,6 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="folder" :selectMode="select === 'file'" :isSelected="selectedFiles.some(x => x.id === file.id)" + @click.shift.left.exact="filesSelect" @chosen="chooseFile" @dragstart="isDragSource = true" @dragend="isDragSource = false" @@ -83,14 +99,21 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-if="files.length == 0 && folders.length == 0 && !fetching" :class="$style.empty"> <div v-if="draghover">{{ i18n.t('empty-draghover') }}</div> - <div v-if="!draghover && folder == null"><strong>{{ i18n.ts.emptyDrive }}</strong><br/>{{ i18n.t('empty-drive-description') }}</div> + <div v-if="!draghover && folder == null"> + <strong>{{ + i18n.ts.emptyDrive + }}</strong><br/>{{ i18n.t('empty-drive-description') }} + </div> <div v-if="!draghover && folder != null">{{ i18n.ts.emptyFolder }}</div> </div> </div> <MkLoading v-if="fetching"/> </div> <div v-if="draghover" :class="$style.dropzone"></div> - <input ref="fileInput" style="display: none;" type="file" accept="*/*" multiple tabindex="-1" @change="onChangeFileInput"/> + <input + ref="fileInput" style="display: none;" type="file" accept="*/*" multiple tabindex="-1" + @change="onChangeFileInput" + /> </div> </template> @@ -107,23 +130,24 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { uploadFile, uploads } from '@/scripts/upload.js'; import { claimAchievement } from '@/scripts/achievements.js'; +import number from "@/filters/number.js"; const props = withDefaults(defineProps<{ - initialFolder?: Misskey.entities.DriveFolder; - type?: string; - multiple?: boolean; - select?: 'file' | 'folder' | null; + initialFolder?: Misskey.entities.DriveFolder; + type?: string; + multiple?: boolean; + select?: 'file' | 'folder' | null; }>(), { multiple: false, select: null, }); const emit = defineEmits<{ - (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void; - (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; - (ev: 'move-root'): void; - (ev: 'cd', v: Misskey.entities.DriveFolder | null): void; - (ev: 'open-folder', v: Misskey.entities.DriveFolder): void; + (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void; + (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; + (ev: 'move-root'): void; + (ev: 'cd', v: Misskey.entities.DriveFolder | null): void; + (ev: 'open-folder', v: Misskey.entities.DriveFolder): void; }>(); const loadMoreFiles = shallowRef<InstanceType<typeof MkButton>>(); @@ -140,6 +164,8 @@ const selectedFolders = ref<Misskey.entities.DriveFolder[]>([]); const uploadings = uploads; const connection = useStream().useChannel('drive'); const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい +const multiple = ref(props.multiple || false); +const select = ref(props.select || null); // ドロップされようとしているか const draghover = ref(false); @@ -390,7 +416,7 @@ function upload(file: File, folderToUpload?: Misskey.entities.DriveFolder | null function chooseFile(file: Misskey.entities.DriveFile) { const isAlreadySelected = selectedFiles.value.some(f => f.id === file.id); - if (props.multiple) { + if (multiple.value) { if (isAlreadySelected) { selectedFiles.value = selectedFiles.value.filter(f => f.id !== file.id); } else { @@ -409,7 +435,7 @@ function chooseFile(file: Misskey.entities.DriveFile) { function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { const isAlreadySelected = selectedFolders.value.some(f => f.id === folderToChoose.id); - if (props.multiple) { + if (multiple.value) { if (isAlreadySelected) { selectedFolders.value = selectedFolders.value.filter(f => f.id !== folderToChoose.id); } else { @@ -496,6 +522,7 @@ function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) { function removeFile(file: Misskey.entities.DriveFile | string) { const fileId = typeof file === 'object' ? file.id : file; files.value = files.value.filter(f => f.id !== fileId); + selectedFiles.value = selectedFiles.value.filter(f => f.id !== fileId); } function appendFile(file: Misskey.entities.DriveFile) { @@ -505,6 +532,7 @@ function appendFile(file: Misskey.entities.DriveFile) { function appendFolder(folderToAppend: Misskey.entities.DriveFolder) { addFolder(folderToAppend); } + /* function prependFile(file: Misskey.entities.DriveFile) { addFile(file, true); @@ -621,31 +649,128 @@ function getMenu() { }, { text: i18n.ts.upload, icon: 'ti ti-upload', - action: () => { selectLocalFile(); }, + action: () => { + selectLocalFile(); + }, }, { text: i18n.ts.fromUrl, icon: 'ti ti-link', - action: () => { urlUpload(); }, + action: () => { + urlUpload(); + }, }, null, { text: folder.value ? folder.value.name : i18n.ts.drive, type: 'label', }, folder.value ? { text: i18n.ts.renameFolder, icon: 'ti ti-forms', - action: () => { renameFolder(folder.value); }, + action: () => { + renameFolder(folder.value); + }, } : undefined, folder.value ? { text: i18n.ts.deleteFolder, icon: 'ti ti-trash', - action: () => { deleteFolder(folder.value as Misskey.entities.DriveFolder); }, + action: () => { + deleteFolder(folder.value as Misskey.entities.DriveFolder); + }, } : undefined, { text: i18n.ts.createFolder, icon: 'ti ti-folder-plus', - action: () => { createFolder(); }, + action: () => { + createFolder(); + }, }]; } +async function isSensitive(Files, isSensitive: boolean) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t(isSensitive ? 'driveFilesSensitiveonConfirm' : 'driveFilesSensitiveoffConfirm', { name: Files.length }), + }); + + if (canceled) return; + Files.forEach((file) => { + os.api('drive/files/update', { + fileId: file.id, + isSensitive, + }); + }); +} + +async function fileDelete(Files) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('driveFilesDeleteConfirm', { name: Files.length }), + }); + + if (canceled) return; + Files.forEach((file) => { + os.api('drive/files/delete', { + fileId: file.id, + }); + }); +} + +function getFilesMenu(Files) { + return [{ + text: i18n.ts.createNoteFromTheFile, + icon: 'ti ti-pencil', + action: () => { + if (Files.length >= 16) { + os.confirm({ + type: 'warning', + text: '16ファイル以上添付しようとしています', + }); + return; + } else { + os.post({ + initialFiles: [...Files], + }); + } + }, + }, { + text: i18n.ts.unmarkAsSensitive, + icon: 'ti ti-eye', + action: () => { + isSensitive(Files, false); + }, + }, { + text: i18n.ts.markAsSensitive, + icon: 'ti ti-eye-exclamation', + action: () => { + isSensitive(Files, true); + }, + }, { + text: i18n.ts.delete, + icon: 'ti ti-trash', + danger: true, + action: () => { + fileDelete(Files); + }, + }]; +} + +function filesSelect() { + multiple.value = !multiple.value; + select.value = (select.value === null) ? 'file' : null; + selectedFiles.value = []; +} + +function filesAllSelect() { + if (selectedFiles.value.length === 0) { + selectedFiles.value = files.value; + } else { + selectedFiles.value = []; + } +} + function showMenu(ev: MouseEvent) { - os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + console.log(selectedFiles.value.length); + if (selectedFiles.value.length === 0) { + os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } else { + os.popupMenu(getFilesMenu(selectedFiles.value), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } } function onContextmenu(ev: MouseEvent) { @@ -689,114 +814,117 @@ onBeforeUnmount(() => { <style lang="scss" module> .root { - display: flex; - flex-direction: column; - height: 100%; + display: flex; + flex-direction: column; + height: 100%; } .nav { - display: flex; - z-index: 2; - width: 100%; - padding: 0 8px; - box-sizing: border-box; - overflow: auto; - font-size: 0.9em; - box-shadow: 0 1px 0 var(--divider); - user-select: none; + display: flex; + top: 0; + position: sticky; + z-index: 1000; + background-color: var(--bg); + width: 100%; + padding: 0 8px; + box-sizing: border-box; + overflow: auto; + font-size: 0.9em; + box-shadow: 0 1px 0 var(--divider); + user-select: none; } .navPath { - display: inline-block; - vertical-align: bottom; - line-height: 42px; - white-space: nowrap; + display: inline-block; + vertical-align: bottom; + line-height: 42px; + white-space: nowrap; } .navPathItem { - display: inline-block; - margin: 0; - padding: 0 8px; - line-height: 42px; - cursor: pointer; + display: inline-block; + margin: 0; + padding: 0 8px; + line-height: 42px; + cursor: pointer; - &:hover { - text-decoration: underline; - } + &:hover { + text-decoration: underline; + } - &.navCurrent { - font-weight: bold; - cursor: default; + &.navCurrent { + font-weight: bold; + cursor: default; - &:hover { - text-decoration: none; - } - } + &:hover { + text-decoration: none; + } + } - &.navSeparator { - margin: 0; - padding: 0; - opacity: 0.5; - cursor: default; - } + &.navSeparator { + margin: 0; + padding: 0; + opacity: 0.5; + cursor: default; + } } .navMenu { - margin-left: auto; - padding: 0 12px; + margin-left: auto; + padding: 0 12px; } .main { - flex: 1; - overflow: auto; - padding: var(--margin); - user-select: none; + flex: 1; + overflow: auto; + padding: var(--margin); + user-select: none; - &.fetching { - cursor: wait !important; - opacity: 0.5; - pointer-events: none; - } + &.fetching { + cursor: wait !important; + opacity: 0.5; + pointer-events: none; + } - &.uploading { - height: calc(100% - 38px - 100px); - } + &.uploading { + height: calc(100% - 38px - 100px); + } } .folders, .files { - display: flex; - flex-wrap: wrap; + display: flex; + flex-wrap: wrap; } .folder, .file { - flex-grow: 1; - width: 128px; - margin: 4px; - box-sizing: border-box; + flex-grow: 1; + width: 128px; + margin: 4px; + box-sizing: border-box; } .padding { - flex-grow: 1; - pointer-events: none; - width: 128px + 8px; + flex-grow: 1; + pointer-events: none; + width: 128px + 8px; } .empty { - padding: 16px; - text-align: center; - pointer-events: none; - opacity: 0.5; + padding: 16px; + text-align: center; + pointer-events: none; + opacity: 0.5; } .dropzone { - position: absolute; - left: 0; - top: 38px; - width: 100%; - height: calc(100% - 38px); - border: dashed 2px var(--focus); - pointer-events: none; + position: absolute; + left: 0; + top: 38px; + width: 100%; + height: calc(100% - 38px); + border: dashed 2px var(--focus); + pointer-events: none; } </style> From 12d56d69bbeebd8cf98dd76f4c5cc3b96de9c3cb Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 6 Oct 2023 00:13:37 +0900 Subject: [PATCH 103/501] test --- .../frontend/src/components/MkDrive.file.vue | 406 ++++++++---------- packages/frontend/src/components/MkDrive.vue | 117 ++--- .../src/scripts/get-drive-file-menu.ts | 51 +-- 3 files changed, 277 insertions(+), 297 deletions(-) diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index 6cc18eae34..9c3e66ddfb 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -4,290 +4,258 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div - :class="[$style.root, { [$style.isSelected]: isSelected },{[$style.isSelected]: isMultiSelect}]" - draggable="true" - :title="title" - @click="onClick" - @pointerdown="startLongPress(file)" - @pointerup="endLongPress" - @contextmenu.stop="onContextmenu" - @dragstart="onDragstart" - @dragend="onDragend" - > - <div style="pointer-events: none;"> - <div v-if="$i?.avatarId == file.id" :class="[$style.label]"> - <img :class="$style.labelImg" src="/client-assets/label.svg"/> - <p :class="$style.labelText">{{ i18n.ts.avatar }}</p> - </div> - <div v-if="$i?.bannerId == file.id" :class="[$style.label]"> - <img :class="$style.labelImg" src="/client-assets/label.svg"/> - <p :class="$style.labelText">{{ i18n.ts.banner }}</p> - </div> - <div v-if="file.isSensitive" :class="[$style.label, $style.red]"> - <img :class="$style.labelImg" src="/client-assets/label-red.svg"/> - <p :class="$style.labelText">{{ i18n.ts.sensitive }}</p> - </div> +<div + :class="[$style.root, { [$style.isSelected]: isSelected || isSelectedFile }]" + draggable="true" + :title="title" + @click="onClick" + @contextmenu.stop="onContextmenu" + @dragstart="onDragstart" + @dragend="onDragend" +> + <div style="pointer-events: none;"> + <div v-if="$i?.avatarId == file.id" :class="[$style.label]"> + <img :class="$style.labelImg" src="/client-assets/label.svg"/> + <p :class="$style.labelText">{{ i18n.ts.avatar }}</p> + </div> + <div v-if="$i?.bannerId == file.id" :class="[$style.label]"> + <img :class="$style.labelImg" src="/client-assets/label.svg"/> + <p :class="$style.labelText">{{ i18n.ts.banner }}</p> + </div> + <div v-if="file.isSensitive" :class="[$style.label, $style.red]"> + <img :class="$style.labelImg" src="/client-assets/label-red.svg"/> + <p :class="$style.labelText">{{ i18n.ts.sensitive }}</p> + </div> - <MkDriveFileThumbnail :class="$style.thumbnail" :file="file" fit="contain"/> + <MkDriveFileThumbnail :class="$style.thumbnail" :file="file" fit="contain"/> - <p :class="$style.name"> - <span>{{ - file.name.lastIndexOf('.') != -1 ? file.name.substring(0, file.name.lastIndexOf('.')) : file.name - }}</span> - <span v-if="file.name.lastIndexOf('.') != -1" - style="opacity: 0.5;">{{ file.name.substring(file.name.lastIndexOf('.')) }}</span> - </p> - </div> - </div> + <p :class="$style.name"> + <span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substring(0, file.name.lastIndexOf('.')) : file.name }}</span> + <span v-if="file.name.lastIndexOf('.') != -1" style="opacity: 0.5;">{{ file.name.substring(file.name.lastIndexOf('.')) }}</span> + </p> + </div> +</div> </template> <script lang="ts" setup> -import {computed, ref, watch} from 'vue'; +import { computed, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import bytes from '@/filters/bytes.js'; import * as os from '@/os.js'; -import {i18n} from '@/i18n.js'; -import {$i} from '@/account.js'; -import {getDriveFileMenu, getDriveFileMultiMenu} from '@/scripts/get-drive-file-menu.js'; -const isLongPressing = ref(false); -let longPressTimeout = null; -import {defaultStore} from '@/store.js' -function startLongPress() { - isLongPressing.value = true; - longPressTimeout = setTimeout(() => { - if (isLongPressing) { - } - }, 800); // 長押しと判断する時間(1秒)を設定 -} +import { i18n } from '@/i18n.js'; +import { $i } from '@/account.js'; +import { getDriveFileMenu, getDriveMultiFileMenu } from '@/scripts/get-drive-file-menu.js'; +import { isTouchUsing } from '@/scripts/touch.js'; -function endLongPress() { - isLongPressing.value = false; - clearTimeout(longPressTimeout); -} - -const isMultiSelect = ref(false); const props = withDefaults(defineProps<{ - file: Misskey.entities.DriveFile; - folder: Misskey.entities.DriveFolder | null; - isSelected?: boolean; - selectMode?: boolean; - multipleselect?; - isLongPressing?; + file: Misskey.entities.DriveFile; + folder: Misskey.entities.DriveFolder | null; + isSelected?: boolean; + selectMode?: boolean; + SelectFiles?: string[]; }>(), { - isSelected: false, - selectMode: false, + isSelected: false, + selectMode: false, }); const emit = defineEmits<{ - (ev: 'chosen', r: Misskey.entities.DriveFile): void; - (ev: 'dragstart'): void; - (ev: 'dragend'): void; + (ev: 'chosen', r: Misskey.entities.DriveFile): void; + (ev: 'dragstart'): void; + (ev: 'dragend'): void; }>(); const isDragging = ref(false); - +const isSelectedFile = ref(false); const title = computed(() => `${props.file.name}\n${props.file.type} ${bytes(props.file.size)}`); -watch(props.multipleselect, () => { - isMultiSelect.value = !!props.multipleselect.some(item => item.id === props.file.id); + +watch(props.SelectFiles, () => { + const index = props.SelectFiles.findIndex(item => item.id === props.file.id); + isSelectedFile.value = index !== -1; }); + function onClick(ev: MouseEvent) { - - if (props.selectMode) { - emit('chosen', props.file); - } else if (ev.shiftKey && ev.button === 2 || isMultiSelect.value ) { - os.popupMenu(getDriveFileMultiMenu(props.multipleselect, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); - - } - else if (!ev.shiftKey) { + console.log('onclick'); + if (props.selectMode) { + emit('chosen', props.file); + } else if (!ev.shiftKey && !isTouchUsing && !isSelectedFile.value) { + os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } else if (!ev.shiftKey && isSelectedFile.value && props.SelectFiles.length === 0) { + os.popupMenu(getDriveMultiFileMenu(props.SelectFiles), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } else if (isTouchUsing && !isSelectedFile.value && props.SelectFiles.length === 0) { os.popupMenu(getDriveFileMenu(props.file, props.folder), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); + } +} - } -} -function device() { - var ua = navigator.userAgent; - if(ua.indexOf('iPhone') > 0 || ua.indexOf('iPod') > 0 || ua.indexOf('Android') > 0 && ua.indexOf('Mobile') > 0){ - return 'mobile'; - }else if(ua.indexOf('iPad') > 0 || ua.indexOf('Android') > 0){ - return 'tablet'; - }else{ - return 'desktop'; - } -} function onContextmenu(ev: MouseEvent) { - - if (!ev.shiftKey && !isMultiSelect.value) { - os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); - } else if (ev.shiftKey && props.multipleselect.length > 0 && ev.button === 2) { - os.contextMenu(getDriveFileMultiMenu(props.multipleselect, props.folder), ev); - } else if(device()=== "desktop"){ - os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); - - } - if (props.isLongPressing){ - isMultiSelect.value = !isMultiSelect.value - } + console.log('oncontext'); + if (!isTouchUsing) { + if (!ev.shiftKey && !isSelectedFile.value) { + os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); + } else if (isSelectedFile.value) { + os.contextMenu(getDriveMultiFileMenu(props.SelectFiles), ev); + } + } } function onDragstart(ev: DragEvent) { - if (ev.dataTransfer) { - ev.dataTransfer.effectAllowed = 'move'; - ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file)); - } - isDragging.value = true; - - emit('dragstart'); + if (ev.dataTransfer) { + ev.dataTransfer.effectAllowed = 'move'; + ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file)); + } + isDragging.value = true; + console.log(isDragging.value) + emit('dragstart'); } function onDragend() { - isDragging.value = false; - emit('dragend'); + isDragging.value = false; + emit('dragend'); } </script> <style lang="scss" module> .root { - position: relative; - padding: 8px 0 0 0; - min-height: 180px; - border-radius: 8px; - cursor: pointer; + position: relative; + padding: 8px 0 0 0; + min-height: 180px; + border-radius: 8px; + cursor: pointer; - &:hover { - background: rgba(#000, 0.05); + &:hover { + background: rgba(#000, 0.05); - > .label { - &:before, - &:after { - background: #0b65a5; - } + > .label { + &:before, + &:after { + background: #0b65a5; + } - &.red { - &:before, - &:after { - background: #c12113; - } - } - } - } + &.red { + &:before, + &:after { + background: #c12113; + } + } + } + } - &:active { - background: rgba(#000, 0.1); + &:active { + background: rgba(#000, 0.1); - > .label { - &:before, - &:after { - background: #0b588c; - } + > .label { + &:before, + &:after { + background: #0b588c; + } - &.red { - &:before, - &:after { - background: #ce2212; - } - } - } - } + &.red { + &:before, + &:after { + background: #ce2212; + } + } + } + } - &.isSelected { - background: var(--accent); + &.isSelected { + background: var(--accent); - &:hover { - background: var(--accentLighten); - } + &:hover { + background: var(--accentLighten); + } - &:active { - background: var(--accentDarken); - } + &:active { + background: var(--accentDarken); + } - > .label { - &:before, - &:after { - display: none; - } - } + > .label { + &:before, + &:after { + display: none; + } + } - > .name { - color: #fff; - } + > .name { + color: #fff; + } - > .thumbnail { - color: #fff; - } - } + > .thumbnail { + color: #fff; + } + } } .label { - position: absolute; - top: 0; - left: 0; - pointer-events: none; + position: absolute; + top: 0; + left: 0; + pointer-events: none; - &:before, - &:after { - content: ""; - display: block; - position: absolute; - z-index: 1; - background: #0c7ac9; - } + &:before, + &:after { + content: ""; + display: block; + position: absolute; + z-index: 1; + background: #0c7ac9; + } - &:before { - top: 0; - left: 57px; - width: 28px; - height: 8px; - } + &:before { + top: 0; + left: 57px; + width: 28px; + height: 8px; + } - &:after { - top: 57px; - left: 0; - width: 8px; - height: 28px; - } + &:after { + top: 57px; + left: 0; + width: 8px; + height: 28px; + } - &.red { - &:before, - &:after { - background: #c12113; - } - } + &.red { + &:before, + &:after { + background: #c12113; + } + } } .labelImg { - position: absolute; - z-index: 2; - top: 0; - left: 0; + position: absolute; + z-index: 2; + top: 0; + left: 0; } .labelText { - position: absolute; - z-index: 3; - top: 19px; - left: -28px; - width: 120px; - margin: 0; - text-align: center; - line-height: 28px; - color: #fff; - transform: rotate(-45deg); + position: absolute; + z-index: 3; + top: 19px; + left: -28px; + width: 120px; + margin: 0; + text-align: center; + line-height: 28px; + color: #fff; + transform: rotate(-45deg); } .thumbnail { - width: 110px; - height: 110px; - margin: auto; + width: 110px; + height: 110px; + margin: auto; } .name { - display: block; - margin: 4px 0 0 0; - font-size: 0.8em; - text-align: center; - word-break: break-all; - color: var(--fg); - overflow: hidden; + display: block; + margin: 4px 0 0 0; + font-size: 0.8em; + text-align: center; + word-break: break-all; + color: var(--fg); + overflow: hidden; } </style> diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index d2dea8f9f1..55f76a968f 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="$style.root"> +<div :class="$style.root" @scroll="handleScroll"> <nav :class="$style.nav"> <div :class="$style.navPath" @contextmenu.prevent.stop="() => {}"> <XNavFolder @@ -14,7 +14,6 @@ SPDX-License-Identifier: AGPL-3.0-only @upload="upload" @removeFile="removeFile" @removeFolder="removeFolder" - :multipleselect="multipleselect" /> <template v-for="f in hierarchyFolders"> <span :class="[$style.navPathItem, $style.navSeparator]"><i class="ti ti-chevron-right"></i></span> @@ -26,7 +25,6 @@ SPDX-License-Identifier: AGPL-3.0-only @upload="upload" @removeFile="removeFile" @removeFolder="removeFolder" - :multipleselect="multipleselect" /> </template> <span v-if="folder != null" :class="[$style.navPathItem, $style.navSeparator]"><i class="ti ti-chevron-right"></i></span> @@ -53,7 +51,6 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="f" :selectMode="select === 'folder'" :isSelected="selectedFolders.some(x => x.id === f.id)" - :multipleselect="multipleselect" @chosen="chooseFolder" @move="move" @upload="upload" @@ -76,15 +73,15 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="folder" :selectMode="select === 'file'" :isSelected="selectedFiles.some(x => x.id === file.id)" + :SelectFiles="SelectFiles" @chosen="chooseFile" @dragstart="isDragSource = true" @dragend="isDragSource = false" - @pointerdown="startLongPress(file)" - @pointerup="endLongPress" - @click.shift.left.exact="selectClick(file)" - @click.ctrl.left.exact="selectClick(file)" - :multipleselect="multipleselect" - :isLongPressing="isLongPressing" + @pointerdown="startLongPress(file)" + @pointerup="endLongPress" + @pointermove="debounceEvent" + @pointercancel="debounceEvent" + @click.shift.left.exact="multipleSelect(file)" /> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <div v-for="(n, i) in 16" :key="i" :class="$style.padding"></div> @@ -116,7 +113,7 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { uploadFile, uploads } from '@/scripts/upload.js'; import { claimAchievement } from '@/scripts/achievements.js'; - +import { isTouchUsing } from '@/scripts/touch.js'; const props = withDefaults(defineProps<{ initialFolder?: Misskey.entities.DriveFolder; type?: string; @@ -127,24 +124,6 @@ const props = withDefaults(defineProps<{ select: null, }); -const isLongPressing = ref(false); -let longPressTimeout = null; - -function startLongPress(file) { - isLongPressing.value = true; - longPressTimeout = setTimeout(() => { - if (isLongPressing) { - selectClick(file) - } - }, 800); // 長押しと判断する時間(1秒)を設定 -} - -function endLongPress() { - isLongPressing.value = false; - clearTimeout(longPressTimeout); -} - - const emit = defineEmits<{ (ev: 'selected', v: Misskey.entities.DriveFile | Misskey.entities.DriveFolder): void; (ev: 'change-selection', v: Misskey.entities.DriveFile[] | Misskey.entities.DriveFolder[]): void; @@ -167,7 +146,11 @@ const selectedFolders = ref<Misskey.entities.DriveFolder[]>([]); const uploadings = uploads; const connection = useStream().useChannel('drive'); const keepOriginal = ref<boolean>(defaultStore.state.keepOriginalUploading); // 外部渡しが多いので$refは使わないほうがよい -let multipleselect = ref([]); +const SelectFiles = ref([]); +const isLongPressing = ref(false); +let longPressTimeout = null; +const isScrolling = ref(false); // スクロール中でない状態で初期化 + // ドロップされようとしているか const draghover = ref(false); @@ -186,6 +169,47 @@ watch(folder, () => emit('cd', folder.value)); function onStreamDriveFileCreated(file: Misskey.entities.DriveFile) { addFile(file, true); } +let timeout; +const debounceEvent = (event) => { + if (isTouchUsing && SelectFiles.value.length === 0) { + clearTimeout(timeout); + timeout = setTimeout(() => { + isLongPressing.value = false; + }, 900); + }else{ + clearTimeout(timeout); + timeout = setTimeout(() => { + isLongPressing.value = false; + }, 80); + } +}; + +function startLongPress(file) { + console.log(SelectFiles.value.length) + if (isTouchUsing && SelectFiles.value.length === 0) { + isLongPressing.value = true; + longPressTimeout = setTimeout(() => { + if (isLongPressing.value) { + multipleSelect(file); + } + }, 1000); + }else{ + isLongPressing.value = true; + longPressTimeout = setTimeout(() => { + if (isLongPressing.value) { + multipleSelect(file); + } + }, 100); + } +} + + +function endLongPress() { + if (isTouchUsing) { + isLongPressing.value = false; + clearTimeout(longPressTimeout); + } +} function onStreamDriveFileUpdated(file: Misskey.entities.DriveFile) { const current = folder.value ? folder.value.id : null; @@ -195,18 +219,7 @@ function onStreamDriveFileUpdated(file: Misskey.entities.DriveFile) { addFile(file, true); } } -function selectClick(file) { - const index = multipleselect.value.findIndex(item => item.id === file.id); - if (index !== -1) { - // File is already selected, so remove it - multipleselect.value.splice(index, 1); - } else { - // File is not selected, so add it - multipleselect.value.push(file); - } - -} function onStreamDriveFileDeleted(fileId: string) { removeFile(fileId); } @@ -214,7 +227,14 @@ function onStreamDriveFileDeleted(fileId: string) { function onStreamDriveFolderCreated(createdFolder: Misskey.entities.DriveFolder) { addFolder(createdFolder, true); } - +function multipleSelect(file) { + const index = SelectFiles.value.findIndex(item => item.id === file.id); + if (index !== -1) { + SelectFiles.value.splice(index, 1); + } else { + SelectFiles.value.push(file); + } +} function onStreamDriveFolderUpdated(updatedFolder: Misskey.entities.DriveFolder) { const current = folder.value ? folder.value.id : null; if (current !== updatedFolder.parentId) { @@ -443,7 +463,6 @@ function chooseFile(file: Misskey.entities.DriveFile) { emit('change-selection', [file]); } } - } function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { @@ -466,7 +485,6 @@ function chooseFolder(folderToChoose: Misskey.entities.DriveFolder) { } function move(target?: Misskey.entities.DriveFolder) { - multipleselect.value = [] if (!target) { goRoot(); return; @@ -531,17 +549,11 @@ function addFile(fileToAdd: Misskey.entities.DriveFile, unshift = false) { function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) { const folderIdToRemove = typeof folderToRemove === 'object' ? folderToRemove.id : folderToRemove; folders.value = folders.value.filter(f => f.id !== folderIdToRemove); - const index = multipleselect.value.findIndex(item => item.id === file.id); - if (index !== -1) { - // File is already selected, so remove it - multipleselect.value.splice(index, 1); - } } function removeFile(file: Misskey.entities.DriveFile | string) { const fileId = typeof file === 'object' ? file.id : file; files.value = files.value.filter(f => f.id !== fileId); - multipleselect.value = multipleselect.value.filter(a => a.id !== fileId) } function appendFile(file: Misskey.entities.DriveFile) { @@ -691,12 +703,10 @@ function getMenu() { } function showMenu(ev: MouseEvent) { - multipleselect.value = [] os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); } - +const contents = ref(null); function onContextmenu(ev: MouseEvent) { - multipleselect.value = [] os.contextMenu(getMenu(), ev); } @@ -733,6 +743,7 @@ onBeforeUnmount(() => { connection.dispose(); ilFilesObserver.disconnect(); }); + </script> <style lang="scss" module> diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index 15f822acc6..76be8ee9c6 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -39,10 +39,10 @@ function describe(file: Misskey.entities.DriveFile) { }, 'closed'); } -function toggleSensitive(file: Misskey.entities.DriveFile , isSensitive?) { +function toggleSensitive(file: Misskey.entities.DriveFile) { os.api('drive/files/update', { fileId: file.id, - isSensitive: isSensitive !== null ? isSensitive : !file.isSensitive + isSensitive: !file.isSensitive, }).catch(err => { os.alert({ type: 'error', @@ -72,16 +72,30 @@ async function deleteFile(file: Misskey.entities.DriveFile) { fileId: file.id, }); } -async function deleteSelectFile(files) { +async function MultideleteFile(files: Misskey.entities.DriveFile[] | null) { const { canceled } = await os.confirm({ type: 'warning', - text: files.length+'つのファイルをまとめて削除しますか?', + text: i18n.t('driveMultiFileDeleteConfirm', { name: files.length }), }); if (canceled) return; - files.forEach((e) => { + files.forEach((e)=>{ os.api('drive/files/delete', { fileId: e.id, + }); + }) +} +function isSensitive(files: Misskey.entities.DriveFile[] | null ,sensitive:boolean) { + files.forEach((e)=>{ + os.api('drive/files/update', { + fileId: e.id, + isSensitive: sensitive, + }).catch(err => { + os.alert({ + type: 'error', + title: i18n.ts.error, + text: err.message, + }); }); }) @@ -144,35 +158,22 @@ export function getDriveFileMenu(file: Misskey.entities.DriveFile, folder?: Miss return menu; } - -export function getDriveFileMultiMenu(file, folder?: Misskey.entities.DriveFolder | null): MenuItem[] { - +export function getDriveMultiFileMenu(files: string[] & boolean): MenuItem[] { let menu; menu = [{ - text: i18n.ts.unmarkAsSensitive, + text: i18n.ts.unmarkAsSensitive, icon: 'ti ti-eye', - action: () => {file.forEach((e) => toggleSensitive(e,false))} + action: () => isSensitive(files,false), },{ text: i18n.ts.markAsSensitive, - icon: 'ti ti-eye-exclamation', - action: () => {file.forEach((e) => toggleSensitive(e,true))} - , - },{ + icon: 'ti ti-eye-exclamation', + action: () => isSensitive(files,true), + }, { text: i18n.ts.delete, icon: 'ti ti-trash', danger: true, - action: () => deleteSelectFile(file), + action: () => MultideleteFile(files), }]; - if (defaultStore.state.devMode) { - menu = menu.concat([null, { - icon: 'ti ti-id', - text: i18n.ts.copyFileId, - action: () => { - copyToClipboard(file.id); - }, - }]); - } - return menu; } From 449c87262bc7cd116842b6d9fa94f373fcb293ad Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 6 Oct 2023 08:17:19 +0900 Subject: [PATCH 104/501] =?UTF-8?q?console.log=E6=B6=88=E3=81=97=E3=82=8F?= =?UTF-8?q?=E3=81=95=E5=A3=B2=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkDrive.file.vue | 6 +++--- packages/frontend/src/components/MkDrive.vue | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index 9c3e66ddfb..717e8f96c8 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -75,7 +75,7 @@ watch(props.SelectFiles, () => { }); function onClick(ev: MouseEvent) { - console.log('onclick'); + ('onclick'); if (props.selectMode) { emit('chosen', props.file); } else if (!ev.shiftKey && !isTouchUsing && !isSelectedFile.value) { @@ -88,7 +88,7 @@ function onClick(ev: MouseEvent) { } function onContextmenu(ev: MouseEvent) { - console.log('oncontext'); + ('oncontext'); if (!isTouchUsing) { if (!ev.shiftKey && !isSelectedFile.value) { os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); @@ -104,7 +104,7 @@ function onDragstart(ev: DragEvent) { ev.dataTransfer.setData(_DATA_TRANSFER_DRIVE_FILE_, JSON.stringify(props.file)); } isDragging.value = true; - console.log(isDragging.value) + (isDragging.value) emit('dragstart'); } diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 6d15142d3d..875c450201 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -765,7 +765,6 @@ function filesAllSelect() { } function showMenu(ev: MouseEvent) { - console.log(selectedFiles.value.length); if (selectedFiles.value.length === 0) { os.popupMenu(getMenu(), (ev.currentTarget ?? ev.target ?? undefined) as HTMLElement | undefined); } else { From 696417140fedcaaea2e8bca112fe034ec2833019 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 6 Oct 2023 08:35:41 +0900 Subject: [PATCH 105/501] =?UTF-8?q?=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E3=81=AE=E6=83=85=E5=A0=B1?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=B0=A1=E5=8D=98=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../src/components/MkRemoteInfoUpdate.vue | 39 +++++++++++++++++++ packages/frontend/src/pages/user/home.vue | 3 +- 4 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 packages/frontend/src/components/MkRemoteInfoUpdate.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index e254f2e9f2..feb74dec35 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -306,6 +306,7 @@ export interface Locale { "light": string; "dark": string; "lightThemes": string; + "remoteUserInfoUpdate": string; "darkThemes": string; "syncDeviceDarkMode": string; "drive": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c332fd9bf6..ccf1a3f81d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -303,6 +303,7 @@ gamingModeInfo: "ボタンなどの装飾をいい感じのグラデーション light: "ライト" dark: "ダーク" lightThemes: "明るいテーマ" +remoteUserInfoUpdate: "アイコンなどが正常に表示されない場合にここをクリックしてください。" darkThemes: "暗いテーマ" syncDeviceDarkMode: "デバイスのダークモードと同期する" drive: "ドライブ" diff --git a/packages/frontend/src/components/MkRemoteInfoUpdate.vue b/packages/frontend/src/components/MkRemoteInfoUpdate.vue new file mode 100644 index 0000000000..e8f8aec99a --- /dev/null +++ b/packages/frontend/src/components/MkRemoteInfoUpdate.vue @@ -0,0 +1,39 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<a :class="$style.root" @click="UserInfoUpdate" ><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserInfoUpdate }}</a> +</template> + +<script lang="ts" setup> +import { i18n } from '@/i18n.js'; +import { api } from '@/os.js'; +const props = withDefaults(defineProps<{ + UserId: string; +}>(), { + UserId: null, +}); + +function UserInfoUpdate() { + api('federation/update-remote-user',{userId: props.UserId}) +} + +</script> + +<style lang="scss" module> +.root { + font-size: 0.8em; + padding: 16px; + background: var(--infoWarnBg); + color: var(--infoWarnFg); + border-radius: var(--radius); + overflow: clip; +} + +.link { + margin-left: 4px; + color: var(--accent); +} +</style> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index db4eba4e72..1c1e1499fa 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="profile _gaps"> <MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/> <MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/> - + <MkRemoteInfoUpdate v-if="user.host != null" :UserId="user.id" class="warn"/> <div :key="user.id" class="main _panel"> <div class="banner-container" :style="style"> <div ref="bannerEl" class="banner" :style="style"></div> @@ -168,6 +168,7 @@ import MkNotes from '@/components/MkNotes.vue'; import { api } from '@/os.js'; import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; import MkNotifyButton from "@/components/MkNotifyButton.vue"; +import MkRemoteInfoUpdate from "@/components/MkRemoteInfoUpdate.vue"; function calcAge(birthdate: string): number { const date = new Date(birthdate); From 2bb86104f6d6e7c5069607f7d733401712f818b0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 07:15:13 +0900 Subject: [PATCH 106/501] =?UTF-8?q?=E3=81=AA=E3=82=93=E3=81=A8=E3=81=AA?= =?UTF-8?q?=E3=81=8FTL=E3=82=92=E8=A6=8B=E3=82=8C=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 - locales/ja-JP.yml | 1 - packages/backend/src/core/QueryService.ts | 47 +++++++++++++ .../api/endpoints/notes/global-timeline.ts | 44 +++++++++++- .../api/endpoints/notes/hybrid-timeline.ts | 67 +++++++++++++++++++ .../api/endpoints/notes/local-timeline.ts | 40 ++++++++++- .../server/api/endpoints/notes/timeline.ts | 22 +++++- .../src/server/api/endpoints/users/notes.ts | 62 +++++++++++++++++ 8 files changed, 278 insertions(+), 6 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 014a98b7b3..e0af67d622 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1147,7 +1147,6 @@ export interface Locale { "edited": string; "notificationRecieveConfig": string; "mutualFollow": string; - "fileAttachedOnly": string; "showRepliesToOthersInTimeline": string; "hideRepliesToOthersInTimeline": string; "externalServices": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c6f25764dd..fb195b862c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1144,7 +1144,6 @@ showRenotes: "リノートを表示" edited: "編集済み" notificationRecieveConfig: "通知の受信設定" mutualFollow: "相互フォロー" -fileAttachedOnly: "ファイル付きのみ" showRepliesToOthersInTimeline: "TLに他の人への返信を含める" hideRepliesToOthersInTimeline: "TLに他の人への返信を含めない" externalServices: "外部サービス" diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 18bd49286e..a80fa5bdf2 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -235,4 +235,51 @@ export class QueryService { q.setParameters(mutingQuery.getParameters()); } + @bindThis + public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void { + if (me == null) { + q.andWhere('note.channelId IS NULL'); + } else { + q.leftJoinAndSelect('note.channel', 'channel'); + + const channelFollowingQuery = this.channelFollowingsRepository.createQueryBuilder('channelFollowing') + .select('channelFollowing.followeeId') + .where('channelFollowing.followerId = :followerId', { followerId: me.id }); + + q.andWhere(new Brackets(qb => { qb + // チャンネルのノートではない + .where('note.channelId IS NULL') + // または自分がフォローしているチャンネルのノート + .orWhere(`note.channelId IN (${ channelFollowingQuery.getQuery() })`); + })); + + q.setParameters(channelFollowingQuery.getParameters()); + } + } + @bindThis + public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<MiUser, 'id'> | null): void { + if (me == null) { + q.andWhere(new Brackets(qb => { qb + .where('note.replyId IS NULL') // 返信ではない + .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.replyUserId = note.userId'); + })); + })); + } else if (!withReplies) { + q.andWhere(new Brackets(qb => { qb + .where('note.replyId IS NULL') // 返信ではない + .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 + .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.userId = :meId', { meId: me.id }); + })) + .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.replyUserId = note.userId'); + })); + })); + } + } + } diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index e5a86905d6..c1949caf67 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -50,6 +50,7 @@ export const paramDef = { required: [], } as const; + @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( @@ -67,8 +68,47 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.gtlDisabled); } - // TODO? - return []; + //#region Construct query + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.visibility = \'public\'') + .andWhere('note.channelId IS NULL') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); + + if (me) { + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + //#endregion + + const timeline = await query.limit(ps.limit).getMany(); + + process.nextTick(() => { + if (me) { + this.activeUsersChart.read(me); + } + }); + + return await this.noteEntityService.packMany(timeline, me); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 8e7f2a2a98..25aa03ff12 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -16,6 +16,7 @@ import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; import { ApiError } from '../../error.js'; +import {QueryService} from "@/core/QueryService.js"; export const meta = { tags: ['notes'], @@ -67,7 +68,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + private noteEntityService: NoteEntityService, + private queryService: QueryService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, private idService: IdService, @@ -115,7 +120,69 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); + if (noteIds.length < limit) { + const followingQuery = this.followingsRepository.createQueryBuilder('following') + .select('following.followeeId') + .where('following.followerId = :followerId', { followerId: me.id }); + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで + .andWhere(new Brackets(qb => { + qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) + .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); + })) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser') + .setParameters(followingQuery.getParameters()); + this.queryService.generateChannelQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :meId', { meId: me.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeRenotedMyNotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.includeLocalRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteUserHost IS NOT NULL'); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + //#endregion + const ids = await query.limit(limit - noteIds.length).getMany(); + noteIds = noteIds.concat(ids.map(note => note.id)); + + } + if (noteIds.length === 0) { return []; } diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index ac8f8d610e..c5b2059176 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -16,6 +16,7 @@ import { IdService } from '@/core/IdService.js'; import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { ApiError } from '../../error.js'; +import {QueryService} from "@/core/QueryService.js"; export const meta = { tags: ['notes'], @@ -65,6 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private noteEntityService: NoteEntityService, private roleService: RoleService, + private queryService: QueryService, private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, @@ -98,7 +100,43 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- 'COUNT', limit); } - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + let noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + + if (noteIds.length < limit) { + //#region Construct query + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで + .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); + + this.queryService.generateChannelQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + const ids = await query.limit(limit - noteIds.length).getMany(); + noteIds = noteIds.concat(ids.map(note => note.id)); + } if (noteIds.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 7442356978..c26a378c9e 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -59,6 +59,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, + private queryService: QueryService, private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, @@ -89,7 +90,26 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- 'COUNT', limit); } - const noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + let noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); + if (noteIds.length < limit) { + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); + const followingIds = Object.keys(followings); + if (followingIds.length > 0) { + const meOrFolloweeIds = [me.id, ...followingIds]; + query.andWhere('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds }); + } else { + query.andWhere('note.userId = :meId', { meId: me.id }); + } + + this.queryService.generateChannelQuery(query, me); + this.queryService.generateVisibilityQuery(query, me); + this.queryService.generateMutedUserQuery(query, me); + this.queryService.generateBlockedUserQuery(query, me); + this.queryService.generateMutedUserRenotesQueryForNotes(query, me); + const ids = await query.limit(limit - noteIds.length).getMany(); + noteIds = noteIds.concat(ids.map(note => note.id)); + } if (noteIds.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 23c2811fb9..e9a67eb2e2 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -15,6 +15,7 @@ import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { ApiError } from '../../error.js'; +import {QueryService} from "@/core/QueryService.js"; export const meta = { tags: ['users', 'notes'], @@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + private queryService: QueryService, private noteEntityService: NoteEntityService, private getterService: GetterService, private cacheService: CacheService, @@ -117,6 +119,66 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); + if (noteIds.length < limit) { + // Lookup user + const user = await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; + }); + + //#region Construct query + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) + .andWhere('note.userId = :userId', { userId: user.id }) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('note.channel', 'channel') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); + + query.andWhere(new Brackets(qb => { + qb.orWhere('note.channelId IS NULL'); + qb.orWhere('channel.isSensitive = false'); + })); + + this.queryService.generateVisibilityQuery(query, me); + if (me) { + this.queryService.generateMutedUserQuery(query, me, user); + this.queryService.generateBlockedUserQuery(query, me); + } + + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); + } + + if (!ps.withReplies) { + query.andWhere('note.replyId IS NULL'); + } + + if (ps.withRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere(new Brackets(qb => { + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + })); + })); + } + + if (ps.includeMyRenotes === false) { + query.andWhere(new Brackets(qb => { + qb.orWhere('note.userId != :userId', { userId: user.id }); + qb.orWhere('note.renoteId IS NULL'); + qb.orWhere('note.text IS NOT NULL'); + qb.orWhere('note.fileIds != \'{}\''); + qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); + })); + } + + const ids = await query.limit(limit - noteIds.length).getMany(); + noteIds = noteIds.concat(ids.map(note => note.id)); + } + if (noteIds.length === 0) { return []; } From 89550c5bef70eacf5cb116020b70adc25cf106ff Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 07:17:56 +0900 Subject: [PATCH 107/501] fix --- packages/frontend/src/pages/about-misskey.vue | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index f6ab63f0ee..234de7e29e 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -34,11 +34,6 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts._aboutMisskey.source }} <template #suffix>GitHub</template> </FormLink> - <FormLink to="https://github.com/prismisskey/misskey" external> - <template #icon><i class="ti ti-code"></i></template> - {{ i18n.ts._aboutMisskey.forksource }} - <template #suffix>GitHub</template> - </FormLink> <FormLink to="https://crowdin.com/project/misskey" external> <template #icon><i class="ti ti-language-hiragana"></i></template> {{ i18n.ts._aboutMisskey.translation }} From bef828393f5eb3366c4930db7e0e675622a0aec4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 07:24:00 +0900 Subject: [PATCH 108/501] 2023.10.0-beta.5-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d6b9f610b9..4b9c321b70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.5", + "version": "2023.10.0-beta.5-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 722f10b7fa52a9c9e1c22c30b8cb27d7e914580a Mon Sep 17 00:00:00 2001 From: riku6460 <17585784+riku6460@users.noreply.github.com> Date: Fri, 6 Oct 2023 21:57:37 +0900 Subject: [PATCH 109/501] =?UTF-8?q?=E3=82=B5=E3=83=BC=E3=83=90=E3=83=BC?= =?UTF-8?q?=E3=81=8B=E3=82=89=E5=88=87=E6=96=AD=E3=81=95=E3=82=8C=E3=81=9F?= =?UTF-8?q?=E5=BE=8C=20stream=20indicator=20=E3=82=92=E3=81=99=E3=81=90?= =?UTF-8?q?=E3=81=AB=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B=20(MisskeyIO#172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit すぐに再接続されたら表示しないように --- packages/frontend/src/ui/_common_/stream-indicator.vue | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue index b09221f5d2..3752a6df75 100644 --- a/packages/frontend/src/ui/_common_/stream-indicator.vue +++ b/packages/frontend/src/ui/_common_/stream-indicator.vue @@ -24,12 +24,17 @@ import { defaultStore } from '@/store.js'; const zIndex = os.claimZIndex('high'); let hasDisconnected = $ref(false); +let timeoutId = $ref<number>(); function onDisconnected() { - hasDisconnected = true; + window.clearTimeout(timeoutId); + timeoutId = window.setTimeout(() => { + hasDisconnected = true; + }, 1000 * 10); } function resetDisconnected() { + window.clearTimeout(timeoutId); hasDisconnected = false; } @@ -37,9 +42,12 @@ function reload() { location.reload(); } +useStream().on('_connected_', resetDisconnected); useStream().on('_disconnected_', onDisconnected); onUnmounted(() => { + window.clearTimeout(timeoutId); + useStream().off('_connected_', resetDisconnected); useStream().off('_disconnected_', onDisconnected); }); </script> From c3ced9b75f15cb3891483147e7c90bad85879d23 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 07:42:27 +0900 Subject: [PATCH 110/501] Revert "revert: note editing" This reverts commit b4032988 --- CHANGELOG.md | 3 +- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../1696388600237-revert-note-edit.js | 16 ---- packages/backend/src/core/RoleService.ts | 3 + .../src/core/entities/NoteEntityService.ts | 1 + packages/backend/src/models/Note.ts | 5 ++ .../backend/src/models/json-schema/note.ts | 5 ++ .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/notes/update.ts | 89 +++++++++++++++++++ .../src/components/MkNoteDetailed.vue | 3 + .../frontend/src/components/MkNoteHeader.vue | 1 + .../frontend/src/components/MkPostForm.vue | 7 +- .../src/components/MkPostFormDialog.vue | 1 + packages/frontend/src/const.ts | 1 + .../frontend/src/pages/admin/roles.editor.vue | 20 +++++ packages/frontend/src/pages/admin/roles.vue | 8 ++ .../frontend/src/scripts/get-note-menu.ts | 9 ++ .../frontend/src/scripts/use-note-capture.ts | 7 ++ packages/misskey-js/etc/misskey-js.api.md | 3 +- packages/misskey-js/src/entities.ts | 1 + packages/misskey-js/src/streaming.types.ts | 7 ++ 23 files changed, 176 insertions(+), 22 deletions(-) delete mode 100644 packages/backend/migration/1696388600237-revert-note-edit.js create mode 100644 packages/backend/src/server/api/endpoints/notes/update.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f9ccaa9f1e..528c5ccb06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,9 +14,8 @@ ## 2023.10.0 ### NOTE +- muted_noteテーブルは使われなくなったため手動で削除を行ってください。 - 2023.9.2で導入されたノート編集機能はクオリティの高い実装が困難であることが判明したため撤回されました -- アップデート後、アップデートより前の時点にTLを遡ることはできません - - アップデート後であっても、今後のアップデートで2023.10.0以前のTLに遡れるようになる可能性はあります ### Changes - API: users/notes, notes/local-timeline で fileType 指定はできなくなりました diff --git a/locales/index.d.ts b/locales/index.d.ts index e0af67d622..ebc96ab652 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1563,6 +1563,7 @@ export interface Locale { "gtlAvailable": string; "ltlAvailable": string; "canPublicNote": string; + "canEditNote": string; "canInvite": string; "inviteLimit": string; "inviteLimitCycle": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fb195b862c..f9afdb54a0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1484,6 +1484,7 @@ _role: gtlAvailable: "グローバルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" + canEditNote: "ノートの編集" canInvite: "サーバー招待コードの発行" inviteLimit: "招待コードの作成可能数" inviteLimitCycle: "招待コードの発行間隔" diff --git a/packages/backend/migration/1696388600237-revert-note-edit.js b/packages/backend/migration/1696388600237-revert-note-edit.js deleted file mode 100644 index 83bc552c35..0000000000 --- a/packages/backend/migration/1696388600237-revert-note-edit.js +++ /dev/null @@ -1,16 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -export class RevertNoteEdit1696388600237 { - name = 'RevertNoteEdit1696388600237' - - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "updatedAt"`); - } - - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "note" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`); - } -} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index f2bd9de5ee..ad40fbaecd 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -26,6 +26,7 @@ export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + canEditNote: boolean; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -51,6 +52,7 @@ export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, + canEditNote: true, canInvite: false, inviteLimit: 0, inviteLimitCycle: 60 * 24 * 7, @@ -296,6 +298,7 @@ export class RoleService implements OnApplicationShutdown { gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + canEditNote: calc('canEditNote', vs => vs.some(v => v === true)), canInvite: calc('canInvite', vs => vs.some(v => v === true)), inviteLimit: calc('inviteLimit', vs => Math.max(...vs)), inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index e45a7992bb..95279d2e99 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -308,6 +308,7 @@ export class NoteEntityService implements OnModuleInit { const packed: Packed<'Note'> = await awaitAll({ id: note.id, createdAt: note.createdAt.toISOString(), + updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined, userId: note.userId, user: this.userEntityService.pack(note.user ?? note.userId, me, { detail: false, diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 3e2adf4d82..c376a75285 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -23,6 +23,11 @@ export class MiNote { }) public createdAt: Date; + @Column('timestamp with time zone', { + default: null, + }) + public updatedAt: Date | null; + @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 2caf0d0c3d..ad0cb3c45d 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -17,6 +17,11 @@ export const packedNoteSchema = { optional: false, nullable: false, format: 'date-time', }, + updatedAt: { + type: 'string', + optional: true, nullable: true, + format: 'date-time', + }, deletedAt: { type: 'string', optional: true, nullable: true, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 523bd0bf86..a21ef69dbf 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -259,6 +259,7 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_update from './endpoints/notes/update.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -612,6 +613,7 @@ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; +const $notes_update: Provider = { provide: 'ep:notes/update', useClass: ep___notes_update.default }; const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; @@ -969,6 +971,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_delete, + $notes_update, $notes_favorites_create, $notes_favorites_delete, $notes_featured, @@ -1320,6 +1323,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_delete, + $notes_update, $notes_favorites_create, $notes_favorites_delete, $notes_featured, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 7087c75ddf..1ad9cb3a3f 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -258,6 +258,7 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_update from './endpoints/notes/update.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -609,6 +610,7 @@ const eps = [ ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], ['notes/delete', ep___notes_delete], + ['notes/update', ep___notes_update], ['notes/favorites/create', ep___notes_favorites_create], ['notes/favorites/delete', ep___notes_favorites_delete], ['notes/featured', ep___notes_featured], diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts new file mode 100644 index 0000000000..cdf7f085e0 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -0,0 +1,89 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import type { UsersRepository, NotesRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteDeleteService } from '@/core/NoteDeleteService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + requireRolePolicy: 'canEditNote', + + kind: 'write:notes', + + limit: { + duration: ms('1hour'), + max: 10, + minInterval: ms('1sec'), + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + text: { + type: 'string', + minLength: 1, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: false, + }, + cw: { type: 'string', nullable: true, maxLength: 100 }, + }, + required: ['noteId', 'text', 'cw'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private getterService: GetterService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps, me) => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; + }); + + if (note.userId !== me.id) { + throw new ApiError(meta.errors.noSuchNote); + } + + await this.notesRepository.update({ id: note.id }, { + updatedAt: new Date(), + cw: ps.cw, + text: ps.text, + }); + + this.globalEventService.publishNoteStream(note.id, 'updated', { + cw: ps.cw, + text: ps.text, + }); + }); + } +} diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 18e1a45c91..eedd7a239c 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -93,6 +93,9 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <footer> <div :class="$style.noteFooterInfo"> + <div v-if="appearNote.updatedAt"> + {{ i18n.ts.edited }}: <MkTime :time="appearNote.updatedAt" mode="detail"/> + </div> <MkA :to="notePage(appearNote)"> <MkTime :time="appearNote.createdAt" mode="detail"/> </MkA> diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index dda7238d27..05f98c638e 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -14,6 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> </div> <div :class="$style.info"> + <span v-if="note.updatedAt" style="margin-right: 0.5em;" :title="i18n.ts.edited"><i class="ti ti-pencil"></i></span> <MkA :to="notePage(note)"> <MkTime :time="note.createdAt"/> </MkA> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 1e3b1f87be..d250304efb 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -174,7 +174,7 @@ const props = withDefaults(defineProps<{ fixed?: boolean; autofocus?: boolean; freezeAfterPosted?: boolean; - +updateMode?: boolean; }>(), { initialVisibleUsers: () => [], autofocus: true, @@ -742,7 +742,8 @@ async function post(ev?: MouseEvent) { visibleUserIds: visibility === 'specified' ? visibleUsers.map(u => u.id) : undefined, reactionAcceptance, - }; + noteId: props.updateMode ? props.initialNote?.id : undefined, + }; if (withHashtags && hashtags && hashtags.trim() !== '') { const hashtags_ = hashtags.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); @@ -764,7 +765,7 @@ async function post(ev?: MouseEvent) { } posting = true; - os.api('notes/create', postData, token).then(() => { + os.api(props.updateMode ? 'notes/update' : 'notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted = true; } else { diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index c07a166a83..f33d498f93 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -30,6 +30,7 @@ const props = defineProps<{ instant?: boolean; fixed?: boolean; autofocus?: boolean; + updateMode?: boolean; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 3998df9efe..f4ce0a096d 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -61,6 +61,7 @@ export const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', + 'canEditNote', 'canInvite', 'inviteLimit', 'inviteLimitCycle', diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index ead2250af2..8015bb7a7f 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])"> + <template #label>{{ i18n.ts._role._options.canEditNote }}</template> + <template #suffix> + <span v-if="role.policies.canEditNote.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.canEditNote.value ? i18n.ts.yes : i18n.ts.no }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canEditNote)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.canEditNote.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkSwitch v-model="role.policies.canEditNote.value" :disabled="role.policies.canEditNote.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + <MkRange v-model="role.policies.canEditNote.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 74de9f7396..001cf3490e 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -48,6 +48,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])"> + <template #label>{{ i18n.ts._role._options.canEditNote }}</template> + <template #suffix>{{ policies.canEditNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canEditNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index e399145fc9..62d9aab91b 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -172,6 +172,10 @@ export function getNoteMenu(props: { }); } + function edit(): void { + os.post({ initialNote: appearNote, renote: appearNote.renote, reply: appearNote.reply, channel: appearNote.channel, updateMode: true }); + } + function toggleFavorite(favorite: boolean): void { claimAchievement('noteFavorited1'); os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', { @@ -352,6 +356,11 @@ export function getNoteMenu(props: { ), ...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [ null, + appearNote.userId === $i.id && $i.policies.canEditNote ? { + icon: 'ti ti-edit', + text: i18n.ts.edit, + action: edit, + } : undefined, appearNote.userId === $i.id ? { icon: 'ti ti-edit', text: i18n.ts.deleteAndEdit, diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts index c618532570..a4c913749e 100644 --- a/packages/frontend/src/scripts/use-note-capture.ts +++ b/packages/frontend/src/scripts/use-note-capture.ts @@ -71,6 +71,13 @@ export function useNoteCapture(props: { break; } + case 'updated': { + note.value.updatedAt = new Date().toISOString(); + note.value.cw = body.cw; + note.value.text = body.text; + break; + } + case 'deleted': { props.isDeletedRef.value = true; break; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index fa09fd94e6..4df7e8332b 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2639,6 +2639,7 @@ export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; type Note = { id: ID; createdAt: DateString; + updatedAt?: DateString | null; text: string | null; cw: string | null; user: User; @@ -2978,7 +2979,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:630:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:594:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:595:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 8097618c33..e6bac2a5f4 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -177,6 +177,7 @@ export type GalleryPost = { export type Note = { id: ID; createdAt: DateString; + updatedAt?: DateString | null; text: string | null; cw: string | null; user: User; diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index 96ac7787e1..ce29a00032 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -133,6 +133,13 @@ export type NoteUpdatedEvent = { body: { deletedAt: string; }; +} | { + id: Note['id']; + type: 'updated'; + body: { + cw: string | null; + text: string; + }; } | { id: Note['id']; type: 'pollVoted'; From fc3337ad84ead85a6086e3f432a51db1281b73ad Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 07:57:23 +0900 Subject: [PATCH 111/501] note edit --- .../migration/1696388600237-revert-note-edit.js | 16 ++++++++++++++++ .../1696604429200-revert-revert-note-editting.js | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 packages/backend/migration/1696388600237-revert-note-edit.js create mode 100644 packages/backend/migration/1696604429200-revert-revert-note-editting.js diff --git a/packages/backend/migration/1696388600237-revert-note-edit.js b/packages/backend/migration/1696388600237-revert-note-edit.js new file mode 100644 index 0000000000..83bc552c35 --- /dev/null +++ b/packages/backend/migration/1696388600237-revert-note-edit.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class RevertNoteEdit1696388600237 { + name = 'RevertNoteEdit1696388600237' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "updatedAt"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`); + } +} diff --git a/packages/backend/migration/1696604429200-revert-revert-note-editting.js b/packages/backend/migration/1696604429200-revert-revert-note-editting.js new file mode 100644 index 0000000000..f723e68b44 --- /dev/null +++ b/packages/backend/migration/1696604429200-revert-revert-note-editting.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class revertrevertnoteeditting1696604429200 { + name = 'revertrevertnoteeditting1696604429200' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "updatedAt"`); + } +} From 69b156003640ac627b4b51091102a4eed857572e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 07:57:34 +0900 Subject: [PATCH 112/501] 2023.10.0-beta.5-prismisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 4b9c321b70..bd3b3a9703 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.5-prismisskey.1", + "version": "2023.10.0-beta.5-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From 8fb73c63d3d8b9778facb4a174d45e517cbb31ff Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 09:40:58 +0900 Subject: [PATCH 113/501] 2023.10.0-beta.5-prismisskey.2 --- packages/frontend/src/components/MkNote.vue | 10 ---------- packages/frontend/src/components/MkNoteDetailed.vue | 10 ---------- packages/frontend/src/components/MkNoteSub.vue | 11 +---------- 3 files changed, 1 insertion(+), 30 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index b8b52c3084..067f92b82a 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -5,7 +5,6 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div - v-if="!muted" v-show="!isDeleted" ref="el" v-hotkey="keymap" @@ -145,15 +144,6 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </article> </div> - <div v-else :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> - <template #name> - <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> - <MkUserName :user="appearNote.user"/> - </MkA> - </template> - </I18n> - </div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index eedd7a239c..0f6ae55962 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -5,7 +5,6 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div - v-if="!muted" v-show="!isDeleted" ref="el" v-hotkey="keymap" @@ -175,15 +174,6 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </div> -<div v-else class="_panel" :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> - <template #name> - <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> - <MkUserName :user="appearNote.user"/> - </MkA> - </template> - </I18n> -</div> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index bc52101f42..47048e73ef 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]"> +<div :class="[$style.root, { [$style.children]: depth > 1 }]"> <div :class="$style.main"> <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> <MkAvatar :class="$style.avatar" :user="note.user" link preview/> @@ -28,15 +28,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ti ti-chevron-double-right"></i></MkA> </div> </div> -<div v-else :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> - <template #name> - <MkA v-user-preview="note.userId" :to="userPage(note.user)"> - <MkUserName :user="note.user"/> - </MkA> - </template> - </I18n> -</div> </template> <script lang="ts" setup> From 4357c75a444d1382638eba800a000734626b623f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 09:50:02 +0900 Subject: [PATCH 114/501] =?UTF-8?q?=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88?= =?UTF-8?q?=E5=91=A8=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkNote.vue | 2 ++ packages/frontend/src/components/MkNoteDetailed.vue | 3 ++- packages/frontend/src/components/MkNoteSub.vue | 3 ++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 067f92b82a..a30e98bac0 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div + v-if="!muted" v-show="!isDeleted" ref="el" v-hotkey="keymap" @@ -144,6 +145,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </article> </div> + <div v-else /> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 0f6ae55962..c01601c630 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div + v-if="!muted" v-show="!isDeleted" ref="el" v-hotkey="keymap" @@ -173,7 +174,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> </div> -</div> +</div><div v-else /> </template> <script lang="ts" setup> diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue index 47048e73ef..2a84c14e73 100644 --- a/packages/frontend/src/components/MkNoteSub.vue +++ b/packages/frontend/src/components/MkNoteSub.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.children]: depth > 1 }]"> +<div v-if="!muted" :class="[$style.root, { [$style.children]: depth > 1 }]"> <div :class="$style.main"> <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> <MkAvatar :class="$style.avatar" :user="note.user" link preview/> @@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkA class="_link" :to="notePage(note)">{{ i18n.ts.continueThread }} <i class="ti ti-chevron-double-right"></i></MkA> </div> </div> + <div v-else /> </template> <script lang="ts" setup> From abb79a0b3656d5953df12967f6ec3b97bfaf2eba Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 10:40:08 +0900 Subject: [PATCH 115/501] =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC?= =?UTF-8?q?=E3=81=AE=E3=83=9A=E3=83=BC=E3=82=B8=E3=81=A7=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=81=8B?= =?UTF-8?q?=E3=83=8F=E3=82=A4=E3=83=A9=E3=82=A4=E3=83=88=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=99=E3=82=8B=E3=81=8B=E6=B1=BA=E3=82=81=E3=82=89?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 2 + locales/ja-JP.yml | 2 + .../frontend/src/pages/settings/general.vue | 3 + packages/frontend/src/pages/user/home.vue | 1066 +++++++++-------- packages/frontend/src/store.ts | 4 + 5 files changed, 557 insertions(+), 520 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index ebc96ab652..db41865f66 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -543,6 +543,8 @@ export interface Locale { "deleteAll": string; "showFixedPostForm": string; "showFixedPostFormInChannel": string; + "FeaturedOrNote": string; + "FeaturedOrNoteInfo": string; "newNoteRecived": string; "sounds": string; "sound": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f9afdb54a0..0c0179eaca 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -540,6 +540,8 @@ serverLogs: "サーバーログ" deleteAll: "全て削除" showFixedPostForm: "タイムライン上部に投稿フォームを表示する" showFixedPostFormInChannel: "タイムライン上部に投稿フォームを表示する(チャンネル)" +FeaturedOrNote: "ユーザーのページで最新のノートを表示する" +FeaturedOrNoteInfo: "ユーザーのページに行ったときにハイライトか最新のノートを表示するかを選択することができます。 オフでハイライト オンで最新のノート です" newNoteRecived: "新しいノートがあります" sounds: "サウンド" sound: "サウンド" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index d9279d8bb8..ba2e226f17 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -30,6 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch> <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> + <MkSwitch v-model="FeaturedOrNote">{{ i18n.ts.FeaturedOrNote}}<template #caption>{{ i18n.ts.FeaturedOrNoteInfo }} </template></MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> @@ -286,6 +287,7 @@ const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWithSave')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) +const FeaturedOrNote = computed(defaultStore.makeGetterSetter('FeaturedOrNote')) watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); @@ -347,6 +349,7 @@ watch([ showMediaTimeline, showVisibilityColor, enableonlyAndWithSave, + FeaturedOrNote, ], async () => { await reloadAsk(); }); diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 6a3687c85d..d9986ebd43 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -4,150 +4,168 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkSpacer :contentMax="narrow ? 800 : 1100"> - <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> - <div class="main _gaps"> - <!-- TODO --> - <!-- <div class="punished" v-if="user.isSuspended"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> --> - <!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> --> + <MkSpacer :contentMax="narrow ? 800 : 1100"> + <div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;"> + <div class="main _gaps"> + <!-- TODO --> + <!-- <div class="punished" v-if="user.isSuspended"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> --> + <!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> --> - <div class="profile _gaps"> - <MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/> - <MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/> - <MkRemoteInfoUpdate v-if="user.host != null" :UserId="user.id" class="warn"/> - <div :key="user.id" class="main _panel"> - <div class="banner-container" :style="style"> - <div ref="bannerEl" class="banner" :style="style"></div> - <div class="fade"></div> - <div class="title"> - <MkUserName class="name" :user="user" :nowrap="true"/> - <div class="bottom"> - <span class="username"><MkAcct :user="user" :detail="true"/></span> - <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="ti ti-shield"></i></span> - <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> - <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> - <button v-if="!isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea"> - <i class="ti ti-edit"/> {{ i18n.ts.addMemo }} - </button> - </div> - </div> - <span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> - <div v-if="$i" class="actions"> - <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> - <MkNotifyButton v-if="$i.id != user.id " :user="user"></MkNotifyButton> - <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> - </div> - </div> - <MkAvatar class="avatar" :user="user" indicator/> - <div class="title"> - <MkUserName :user="user" :nowrap="false" class="name"/> - <div class="bottom"> - <span class="username"><MkAcct :user="user" :detail="true"/></span> - <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i class="ti ti-shield"></i></span> - <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> - <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> - </div> - </div> - <div v-if="user.roles.length > 0" class="roles"> - <span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" :style="{ '--color': role.color }"> + <div class="profile _gaps"> + <MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/> + <MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/> + <MkRemoteInfoUpdate v-if="user.host != null" :UserId="user.id" class="warn"/> + <div :key="user.id" class="main _panel"> + <div class="banner-container" :style="style"> + <div ref="bannerEl" class="banner" :style="style"></div> + <div class="fade"></div> + <div class="title"> + <MkUserName class="name" :user="user" :nowrap="true"/> + <div class="bottom"> + <span class="username"><MkAcct :user="user" :detail="true"/></span> + <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i + class="ti ti-shield"></i></span> + <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> + <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> + <button v-if="!isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea"> + <i class="ti ti-edit"/> {{ i18n.ts.addMemo }} + </button> + </div> + </div> + <span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> + <div v-if="$i" class="actions"> + <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> + <MkNotifyButton v-if="$i.id != user.id " :user="user"></MkNotifyButton> + <MkFollowButton v-if="$i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" + class="koudoku"/> + </div> + </div> + <MkAvatar class="avatar" :user="user" indicator/> + <div class="title"> + <MkUserName :user="user" :nowrap="false" class="name"/> + <div class="bottom"> + <span class="username"><MkAcct :user="user" :detail="true"/></span> + <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i + class="ti ti-shield"></i></span> + <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> + <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> + </div> + </div> + <div v-if="user.roles.length > 0" class="roles"> + <span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" + :style="{ '--color': role.color }"> <MkA v-adaptive-bg :to="`/roles/${role.id}`"> <img v-if="role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="role.iconUrl"/> {{ role.name }} </MkA> </span> - </div> - <div v-if="iAmModerator" class="moderationNote"> - <MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave> - <template #label>{{ i18n.ts.moderationNote }}</template> - </MkTextarea> - <div v-else> - <MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton> - </div> - </div> - <div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}"> - <div class="heading" v-text="i18n.ts.memo"/> - <textarea - ref="memoTextareaEl" - v-model="memoDraft" - rows="1" - @focus="isEditingMemo = true" - @blur="updateMemo" - @input="adjustMemoTextarea" - /> - </div> - <div class="description"> - <MkOmit> - <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user" :i="$i"/> - <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> - </MkOmit> - </div> - <div class="fields system"> - <dl v-if="user.location" class="field"> - <dt class="name"><i class="ti ti-map-pin ti-fw"></i> {{ i18n.ts.location }}</dt> - <dd class="value">{{ user.location }}</dd> - </dl> - <dl v-if="user.birthday" class="field"> - <dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt> - <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ i18n.t('yearsOld', { age }) }})</dd> - </dl> - <dl class="field"> - <dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt> - <dd class="value">{{ dateString(user.createdAt) }} (<MkTime :time="user.createdAt"/>)</dd> - </dl> - </div> - <div v-if="user.fields.length > 0" class="fields"> - <dl v-for="(field, i) in user.fields" :key="i" class="field"> - <dt class="name"> - <Mfm :text="field.name" :plain="true" :colored="false"/> - </dt> - <dd class="value"> - <Mfm :text="field.value" :author="user" :i="$i" :colored="false"/> - <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" class="ti ti-circle-check" :class="$style.verifiedLink"></i> - </dd> - </dl> - </div> - <div class="status"> - <MkA :to="userPage(user)"> - <b>{{ number(user.notesCount) }}</b> - <span>{{ i18n.ts.notes }}</span> - </MkA> - <MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'following')"> - <b>{{ number(user.followingCount) }}</b> - <span>{{ i18n.ts.following }}</span> - </MkA> - <MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'followers')"> - <b>{{ number(user.followersCount) }}</b> - <span>{{ i18n.ts.followers }}</span> - </MkA> - </div> - </div> - </div> + </div> + <div v-if="iAmModerator" class="moderationNote"> + <MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" + v-model="moderationNote" manualSave> + <template #label>{{ i18n.ts.moderationNote }}</template> + </MkTextarea> + <div v-else> + <MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton> + </div> + </div> + <div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}"> + <div class="heading" v-text="i18n.ts.memo"/> + <textarea + ref="memoTextareaEl" + v-model="memoDraft" + rows="1" + @focus="isEditingMemo = true" + @blur="updateMemo" + @input="adjustMemoTextarea" + /> + </div> + <div class="description"> + <MkOmit> + <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user" :i="$i"/> + <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> + </MkOmit> + </div> + <div class="fields system"> + <dl v-if="user.location" class="field"> + <dt class="name"><i class="ti ti-map-pin ti-fw"></i> {{ i18n.ts.location }}</dt> + <dd class="value">{{ user.location }}</dd> + </dl> + <dl v-if="user.birthday" class="field"> + <dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt> + <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ + i18n.t('yearsOld', {age}) + }}) + </dd> + </dl> + <dl class="field"> + <dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt> + <dd class="value">{{ dateString(user.createdAt) }} ( + <MkTime :time="user.createdAt"/> + ) + </dd> + </dl> + </div> + <div v-if="user.fields.length > 0" class="fields"> + <dl v-for="(field, i) in user.fields" :key="i" class="field"> + <dt class="name"> + <Mfm :text="field.name" :plain="true" :colored="false"/> + </dt> + <dd class="value"> + <Mfm :text="field.value" :author="user" :i="$i" :colored="false"/> + <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" + class="ti ti-circle-check" :class="$style.verifiedLink"></i> + </dd> + </dl> + </div> + <div class="status"> + <MkA :to="userPage(user)"> + <b>{{ number(user.notesCount) }}</b> + <span>{{ i18n.ts.notes }}</span> + </MkA> + <MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'following')"> + <b>{{ number(user.followingCount) }}</b> + <span>{{ i18n.ts.following }}</span> + </MkA> + <MkA v-if="isFfVisibleForMe(user)" :to="userPage(user, 'followers')"> + <b>{{ number(user.followersCount) }}</b> + <span>{{ i18n.ts.followers }}</span> + </MkA> + </div> + </div> + </div> - <div class="contents _gaps"> - <div v-if="user.pinnedNotes.length > 0" class="_gaps"> - <MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> - </div> - <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> - <template v-if="narrow"> - <XFiles :key="user.id" :user="user"/> - <XActivity :key="user.id" :user="user"/> - </template> - <div v-if="!disableNotes"> - <div style="margin-bottom: 8px;">{{ i18n.ts.featured }}</div> - <MkNotes :class="$style.tl" :noGap="true" :pagination="pagination"/> - </div> - </div> - </div> - <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> - <XFiles :key="user.id" :user="user"/> - <XActivity :key="user.id" :user="user"/> - </div> - </div> -</MkSpacer> + <div class="contents _gaps"> + <div v-if="user.pinnedNotes.length > 0" class="_gaps"> + <MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> + </div> + <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> + <template v-if="narrow"> + <XFiles :key="user.id" :user="user"/> + <XActivity :key="user.id" :user="user"/> + </template> + <div v-if="!defaultStore.state.FeaturedOrNote"> + <div v-if="!disableNotes"> + <div style="margin-bottom: 8px;">{{ i18n.ts.featured }}</div> + <MkNotes :class="$style.tl" :noGap="true" :pagination="pagination"/> + </div> + </div> + <div v-else> + <div style="margin-bottom: 8px;">{{ i18n.ts._sfx.note }}</div> + <MkNotes :class="$style.tl" :noGap="true" :pagination="Notes"/> + </div> + </div> + </div> + <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> + <XFiles :key="user.id" :user="user"/> + <XActivity :key="user.id" :user="user"/> + </div> + </div> + </MkSpacer> </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch } from 'vue'; +import {defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch} from 'vue'; import * as Misskey from 'misskey-js'; import MkNote from '@/components/MkNote.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; @@ -157,46 +175,47 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkOmit from '@/components/MkOmit.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkButton from '@/components/MkButton.vue'; -import { getScrollPosition } from '@/scripts/scroll.js'; -import { getUserMenu } from '@/scripts/get-user-menu.js'; +import {getScrollPosition} from '@/scripts/scroll.js'; +import {getUserMenu} from '@/scripts/get-user-menu.js'; import number from '@/filters/number.js'; -import { userPage } from '@/filters/user.js'; +import {userPage} from '@/filters/user.js'; import * as os from '@/os.js'; -import { useRouter } from '@/router.js'; -import { i18n } from '@/i18n.js'; -import { $i, iAmModerator } from '@/account.js'; -import { dateString } from '@/filters/date.js'; -import { confetti } from '@/scripts/confetti.js'; +import {useRouter} from '@/router.js'; +import {i18n} from '@/i18n.js'; +import {$i, iAmModerator} from '@/account.js'; +import {dateString} from '@/filters/date.js'; +import {confetti} from '@/scripts/confetti.js'; import MkNotes from '@/components/MkNotes.vue'; -import { api } from '@/os.js'; -import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; +import {api} from '@/os.js'; +import {isFfVisibleForMe} from '@/scripts/isFfVisibleForMe.js'; import MkNotifyButton from "@/components/MkNotifyButton.vue"; import MkRemoteInfoUpdate from "@/components/MkRemoteInfoUpdate.vue"; +import {defaultStore} from '@/store.js'; function calcAge(birthdate: string): number { - const date = new Date(birthdate); - const now = new Date(); + const date = new Date(birthdate); + const now = new Date(); - let yearDiff = now.getFullYear() - date.getFullYear(); - const monthDiff = now.getMonth() - date.getMonth(); - const pastDate = now.getDate() < date.getDate(); + let yearDiff = now.getFullYear() - date.getFullYear(); + const monthDiff = now.getMonth() - date.getMonth(); + const pastDate = now.getDate() < date.getDate(); - if (monthDiff < 0 || (monthDiff === 0 && pastDate)) { - yearDiff--; - } + if (monthDiff < 0 || (monthDiff === 0 && pastDate)) { + yearDiff--; + } - return yearDiff; + return yearDiff; } const XFiles = defineAsyncComponent(() => import('./index.files.vue')); const XActivity = defineAsyncComponent(() => import('./index.activity.vue')); const props = withDefaults(defineProps<{ - user: Misskey.entities.UserDetailed; - /** Test only; MkNotes currently causes problems in vitest */ - disableNotes: boolean; + user: Misskey.entities.UserDetailed; + /** Test only; MkNotes currently causes problems in vitest */ + disableNotes: boolean; }>(), { - disableNotes: false, + disableNotes: false, }); const router = useRouter(); @@ -212,478 +231,485 @@ let moderationNote = $ref(props.user.moderationNote); let editModerationNote = $ref(false); watch($$(moderationNote), async () => { - await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote }); + await os.api('admin/update-user-note', {userId: props.user.id, text: moderationNote}); }); const pagination = { - endpoint: 'users/featured-notes' as const, - limit: 10, - params: computed(() => ({ - userId: props.user.id, - })), + endpoint: 'users/featured-notes' as const, + limit: 10, + params: computed(() => ({ + userId: props.user.id, + })), }; +const Notes ={ + endpoint: 'users/notes' as const, + limit: 10, + params: computed(() => ({ + userId: props.user.id, + })), +} const style = $computed(() => { - if (props.user.bannerUrl == null) return {}; - return { - backgroundImage: `url(${ props.user.bannerUrl })`, - }; + if (props.user.bannerUrl == null) return {}; + return { + backgroundImage: `url(${props.user.bannerUrl})`, + }; }); const age = $computed(() => { - return calcAge(props.user.birthday); + return calcAge(props.user.birthday); }); function menu(ev) { - const { menu, cleanup } = getUserMenu(props.user, router); - os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); + const {menu, cleanup} = getUserMenu(props.user, router); + os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); } function parallaxLoop() { - parallaxAnimationId = window.requestAnimationFrame(parallaxLoop); - parallax(); + parallaxAnimationId = window.requestAnimationFrame(parallaxLoop); + parallax(); } function parallax() { - const banner = bannerEl as any; - if (banner == null) return; + const banner = bannerEl as any; + if (banner == null) return; - const top = getScrollPosition(rootEl); + const top = getScrollPosition(rootEl); - if (top < 0) return; + if (top < 0) return; - const z = 1.75; // 奥行き(小さいほど奥) - const pos = -(top / z); - banner.style.backgroundPosition = `center calc(50% - ${pos}px)`; + const z = 1.75; // 奥行き(小さいほど奥) + const pos = -(top / z); + banner.style.backgroundPosition = `center calc(50% - ${pos}px)`; } function showMemoTextarea() { - isEditingMemo = true; - nextTick(() => { - memoTextareaEl?.focus(); - }); + isEditingMemo = true; + nextTick(() => { + memoTextareaEl?.focus(); + }); } function adjustMemoTextarea() { - if (!memoTextareaEl) return; - memoTextareaEl.style.height = '0px'; - memoTextareaEl.style.height = `${memoTextareaEl.scrollHeight}px`; + if (!memoTextareaEl) return; + memoTextareaEl.style.height = '0px'; + memoTextareaEl.style.height = `${memoTextareaEl.scrollHeight}px`; } async function updateMemo() { - await api('users/update-memo', { - memo: memoDraft, - userId: props.user.id, - }); - isEditingMemo = false; + await api('users/update-memo', { + memo: memoDraft, + userId: props.user.id, + }); + isEditingMemo = false; } watch([props.user], () => { - memoDraft = props.user.memo; + memoDraft = props.user.memo; }); onMounted(() => { - window.requestAnimationFrame(parallaxLoop); - narrow = rootEl!.clientWidth < 1000; + window.requestAnimationFrame(parallaxLoop); + narrow = rootEl!.clientWidth < 1000; - if (props.user.birthday) { - const m = new Date().getMonth() + 1; - const d = new Date().getDate(); - const bm = parseInt(props.user.birthday.split('-')[1]); - const bd = parseInt(props.user.birthday.split('-')[2]); - if (m === bm && d === bd) { - confetti({ - duration: 1000 * 4, - }); - } - } - nextTick(() => { - adjustMemoTextarea(); - }); + if (props.user.birthday) { + const m = new Date().getMonth() + 1; + const d = new Date().getDate(); + const bm = parseInt(props.user.birthday.split('-')[1]); + const bd = parseInt(props.user.birthday.split('-')[2]); + if (m === bm && d === bd) { + confetti({ + duration: 1000 * 4, + }); + } + } + nextTick(() => { + adjustMemoTextarea(); + }); }); onUnmounted(() => { - if (parallaxAnimationId) { - window.cancelAnimationFrame(parallaxAnimationId); - } + if (parallaxAnimationId) { + window.cancelAnimationFrame(parallaxAnimationId); + } }); </script> <style lang="scss" scoped> .ftskorzw { - > .main { + > .main { - > .punished { - font-size: 0.8em; - padding: 16px; - } + > .punished { + font-size: 0.8em; + padding: 16px; + } - > .profile { + > .profile { - > .main { - position: relative; - overflow: clip; + > .main { + position: relative; + overflow: clip; - > .banner-container { - position: relative; - height: 250px; - overflow: clip; - background-size: cover; - background-position: center; + > .banner-container { + position: relative; + height: 250px; + overflow: clip; + background-size: cover; + background-position: center; - > .banner { - height: 100%; - background-color: #4c5e6d; - background-size: cover; - background-position: center; - box-shadow: 0 0 128px rgba(0, 0, 0, 0.5) inset; - will-change: background-position; - } + > .banner { + height: 100%; + background-color: #4c5e6d; + background-size: cover; + background-position: center; + box-shadow: 0 0 128px rgba(0, 0, 0, 0.5) inset; + will-change: background-position; + } - > .fade { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - height: 78px; - background: linear-gradient(transparent, rgba(#000, 0.7)); - } + > .fade { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 78px; + background: linear-gradient(transparent, rgba(#000, 0.7)); + } - > .followed { - position: absolute; - top: 12px; - left: 12px; - padding: 4px 8px; - color: #fff; - background: rgba(0, 0, 0, 0.7); - font-size: 0.7em; - border-radius: 6px; - } + > .followed { + position: absolute; + top: 12px; + left: 12px; + padding: 4px 8px; + color: #fff; + background: rgba(0, 0, 0, 0.7); + font-size: 0.7em; + border-radius: 6px; + } - > .actions { - position: absolute; - top: 12px; - right: 12px; - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - background: rgba(0, 0, 0, 0.2); - padding: 8px; - border-radius: 24px; + > .actions { + position: absolute; + top: 12px; + right: 12px; + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + background: rgba(0, 0, 0, 0.2); + padding: 8px; + border-radius: 24px; - > .menu { - vertical-align: bottom; - height: 31px; - width: 31px; - color: #fff; - text-shadow: 0 0 8px #000; - font-size: 16px; - } + > .menu { + vertical-align: bottom; + height: 31px; + width: 31px; + color: #fff; + text-shadow: 0 0 8px #000; + font-size: 16px; + } - > .koudoku { - margin-left: 4px; - vertical-align: bottom; - } - } + > .koudoku { + margin-left: 4px; + vertical-align: bottom; + } + } - > .title { - position: absolute; - bottom: 0; - left: 0; - width: 100%; - padding: 0 0 8px 154px; - box-sizing: border-box; - color: #fff; + > .title { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + padding: 0 0 8px 154px; + box-sizing: border-box; + color: #fff; - > .name { - display: block; - margin: 0; - line-height: 32px; - font-weight: bold; - font-size: 1.8em; - text-shadow: 0 0 8px #000; - } + > .name { + display: block; + margin: 0; + line-height: 32px; + font-weight: bold; + font-size: 1.8em; + text-shadow: 0 0 8px #000; + } - > .bottom { - > * { - display: inline-block; - margin-right: 16px; - line-height: 20px; - opacity: 0.8; + > .bottom { + > * { + display: inline-block; + margin-right: 16px; + line-height: 20px; + opacity: 0.8; - &.username { - font-weight: bold; - } - } + &.username { + font-weight: bold; + } + } - > .add-note-button { - background: rgba(0, 0, 0, 0.2); - color: #fff; - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(8px)); - border-radius: 24px; - padding: 4px 8px; - font-size: 80%; - } - } - } - } + > .add-note-button { + background: rgba(0, 0, 0, 0.2); + color: #fff; + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(8px)); + border-radius: 24px; + padding: 4px 8px; + font-size: 80%; + } + } + } + } - > .title { - display: none; - text-align: center; - padding: 50px 8px 16px 8px; - font-weight: bold; - border-bottom: solid 0.5px var(--divider); + > .title { + display: none; + text-align: center; + padding: 50px 8px 16px 8px; + font-weight: bold; + border-bottom: solid 0.5px var(--divider); - > .bottom { - > * { - display: inline-block; - margin-right: 8px; - opacity: 0.8; - } - } - } + > .bottom { + > * { + display: inline-block; + margin-right: 8px; + opacity: 0.8; + } + } + } - > .avatar { - display: block; - position: absolute; - top: 170px; - left: 16px; - z-index: 2; - width: 120px; - height: 120px; - box-shadow: 1px 1px 3px rgba(#000, 0.2); - } + > .avatar { + display: block; + position: absolute; + top: 170px; + left: 16px; + z-index: 2; + width: 120px; + height: 120px; + box-shadow: 1px 1px 3px rgba(#000, 0.2); + } - > .roles { - padding: 24px 24px 0 154px; - font-size: 0.95em; - display: flex; - flex-wrap: wrap; - gap: 8px; + > .roles { + padding: 24px 24px 0 154px; + font-size: 0.95em; + display: flex; + flex-wrap: wrap; + gap: 8px; - > .role { - border: solid 1px var(--color, var(--divider)); - border-radius: 999px; - margin-right: 4px; - padding: 3px 8px; - } - } + > .role { + border: solid 1px var(--color, var(--divider)); + border-radius: 999px; + margin-right: 4px; + padding: 3px 8px; + } + } - > .moderationNote { - margin: 12px 24px 0 154px; - } + > .moderationNote { + margin: 12px 24px 0 154px; + } - > .memo { - margin: 12px 24px 0 154px; - background: transparent; - color: var(--fg); - border: 1px solid var(--divider); - border-radius: 8px; - padding: 8px; - line-height: 0; + > .memo { + margin: 12px 24px 0 154px; + background: transparent; + color: var(--fg); + border: 1px solid var(--divider); + border-radius: 8px; + padding: 8px; + line-height: 0; - > .heading { - text-align: left; - color: var(--fgTransparent); - line-height: 1.5; - font-size: 85%; - } + > .heading { + text-align: left; + color: var(--fgTransparent); + line-height: 1.5; + font-size: 85%; + } - textarea { - margin: 0; - padding: 0; - resize: none; - border: none; - outline: none; - width: 100%; - height: auto; - min-height: 0; - line-height: 1.5; - color: var(--fg); - overflow: hidden; - background: transparent; - font-family: inherit; - } - } + textarea { + margin: 0; + padding: 0; + resize: none; + border: none; + outline: none; + width: 100%; + height: auto; + min-height: 0; + line-height: 1.5; + color: var(--fg); + overflow: hidden; + background: transparent; + font-family: inherit; + } + } - > .description { - padding: 24px 24px 24px 154px; - font-size: 0.95em; + > .description { + padding: 24px 24px 24px 154px; + font-size: 0.95em; - > .empty { - margin: 0; - opacity: 0.5; - } - } + > .empty { + margin: 0; + opacity: 0.5; + } + } - > .fields { - padding: 24px; - font-size: 0.9em; - border-top: solid 0.5px var(--divider); + > .fields { + padding: 24px; + font-size: 0.9em; + border-top: solid 0.5px var(--divider); - > .field { - display: flex; - padding: 0; - margin: 0; - align-items: center; + > .field { + display: flex; + padding: 0; + margin: 0; + align-items: center; - &:not(:last-child) { - margin-bottom: 8px; - } + &:not(:last-child) { + margin-bottom: 8px; + } - > .name { - width: 30%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - font-weight: bold; - text-align: center; - } + > .name { + width: 30%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-weight: bold; + text-align: center; + } - > .value { - width: 70%; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - margin: 0; - } - } + > .value { + width: 70%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + margin: 0; + } + } - &.system > .field > .name { - } - } + &.system > .field > .name { + } + } - > .status { - display: flex; - padding: 24px; - border-top: solid 0.5px var(--divider); + > .status { + display: flex; + padding: 24px; + border-top: solid 0.5px var(--divider); - > a { - flex: 1; - text-align: center; + > a { + flex: 1; + text-align: center; - &.active { - color: var(--accent); - } + &.active { + color: var(--accent); + } - &:hover { - text-decoration: none; - } + &:hover { + text-decoration: none; + } - > b { - display: block; - line-height: 16px; - } + > b { + display: block; + line-height: 16px; + } - > span { - font-size: 70%; - } - } - } - } - } + > span { + font-size: 70%; + } + } + } + } + } - > .contents { - > .content { - margin-bottom: var(--margin); - } - } - } + > .contents { + > .content { + margin-bottom: var(--margin); + } + } + } - &.wide { - display: flex; - width: 100%; + &.wide { + display: flex; + width: 100%; - > .main { - width: 100%; - min-width: 0; - } + > .main { + width: 100%; + min-width: 0; + } - > .sub { - max-width: 350px; - min-width: 350px; - margin-left: var(--margin); - } - } + > .sub { + max-width: 350px; + min-width: 350px; + margin-left: var(--margin); + } + } } @container (max-width: 500px) { - .ftskorzw { - > .main { - > .profile > .main { - > .banner-container { - height: 140px; + .ftskorzw { + > .main { + > .profile > .main { + > .banner-container { + height: 140px; - > .fade { - display: none; - } + > .fade { + display: none; + } - > .title { - display: none; - } - } + > .title { + display: none; + } + } - > .title { - display: block; - } + > .title { + display: block; + } - > .avatar { - top: 90px; - left: 0; - right: 0; - width: 92px; - height: 92px; - margin: auto; - } + > .avatar { + top: 90px; + left: 0; + right: 0; + width: 92px; + height: 92px; + margin: auto; + } - > .roles { - padding: 16px 16px 0 16px; - justify-content: center; - } + > .roles { + padding: 16px 16px 0 16px; + justify-content: center; + } - > .moderationNote { - margin: 16px 16px 0 16px; - } + > .moderationNote { + margin: 16px 16px 0 16px; + } - > .memo { - margin: 16px 16px 0 16px; - } + > .memo { + margin: 16px 16px 0 16px; + } - > .description { - padding: 16px; - text-align: center; - } + > .description { + padding: 16px; + text-align: center; + } - > .fields { - padding: 16px; - } + > .fields { + padding: 16px; + } - > .status { - padding: 16px; - } - } + > .status { + padding: 16px; + } + } - > .contents { - > .nav { - font-size: 80%; - } - } - } - } + > .contents { + > .nav { + font-size: 80%; + } + } + } + } } </style> <style lang="scss" module> .tl { - background: var(--bg); - border-radius: var(--radius); - overflow: clip; + background: var(--bg); + border-radius: var(--radius); + overflow: clip; } .verifiedLink { - margin-left: 4px; - color: var(--success); + margin-left: 4px; + color: var(--success); } </style> diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 9c96b904b2..b2d3c610ab 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -432,6 +432,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + FeaturedOrNote: { + where: 'device', + default: false + } })); // TODO: 他のタブと永続化されたstateを同期 From a1094a3efca1c79d894aa3d49bb9b75e68ed0918 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 20:14:29 +0900 Subject: [PATCH 116/501] isSensitive --- packages/frontend/src/pages/user/index.files.vue | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue index 205da5071d..2bb277ef60 100644 --- a/packages/frontend/src/pages/user/index.files.vue +++ b/packages/frontend/src/pages/user/index.files.vue @@ -61,10 +61,12 @@ onMounted(() => { }).then(notes => { for (const note of notes) { for (const file of note.files) { - files.push({ - note, - file, - }); + if (file.isSensitive){ + files.push({ + note, + file, + }); + } } } fetching = false; From d4785c64200cfc3bcb79475e528058adaa2d07a6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 20:19:42 +0900 Subject: [PATCH 117/501] isSensitive --- packages/frontend/src/pages/user/index.files.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue index 2bb277ef60..4c0b0e3fad 100644 --- a/packages/frontend/src/pages/user/index.files.vue +++ b/packages/frontend/src/pages/user/index.files.vue @@ -61,7 +61,7 @@ onMounted(() => { }).then(notes => { for (const note of notes) { for (const file of note.files) { - if (file.isSensitive){ + if (!file.isSensitive){ files.push({ note, file, From 71f324997937f09dd888d84237aabb657eea54f5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 20:21:21 +0900 Subject: [PATCH 118/501] 2023.10.0-beta.7-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 13f015745a..1adac4764b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.7", + "version": "2023.10.0-beta.7-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 0a235a108cbbf990f5f5e5b2baa49ac0f95c3aa2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 20:37:29 +0900 Subject: [PATCH 119/501] bug fix --- packages/backend/src/server/api/endpoints/users/notes.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 3dc0f50e52..47e23d8d03 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -69,7 +69,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private queryService: QueryService, private noteEntityService: NoteEntityService, - private queryService: QueryService, private cacheService: CacheService, private idService: IdService, ) { From 652cb3b8c4ea30f9b6bbb4e922e85b43a337ec9d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 20:40:07 +0900 Subject: [PATCH 120/501] bug fix --- packages/backend/src/server/api/endpoints/users/notes.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 47e23d8d03..2b3ba492d7 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -15,7 +15,6 @@ import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { QueryService } from '@/core/QueryService.js'; import { ApiError } from '../../error.js'; -import {QueryService} from "@/core/QueryService.js"; export const meta = { tags: ['users', 'notes'], From 3bc93e519ee1b1e31cc7ee5147e6f158cf8272e6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 7 Oct 2023 20:56:26 +0900 Subject: [PATCH 121/501] bug fix --- packages/backend/src/server/api/endpoints/users/notes.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 2b3ba492d7..5ba0610f88 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -13,8 +13,9 @@ import { DI } from '@/di-symbols.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { QueryService } from '@/core/QueryService.js'; +import { QueryService } from '@/server/api/QueryService.js'; import { ApiError } from '../../error.js'; +import { GetterService } from '@/core/GetterService.js' export const meta = { tags: ['users', 'notes'], @@ -66,6 +67,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + private getterService: GetterService, private queryService: QueryService, private noteEntityService: NoteEntityService, private cacheService: CacheService, From 271d3483b2196a56b874db1ab391f088db499021 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 8 Oct 2023 00:25:33 +0900 Subject: [PATCH 122/501] bug fix --- package.json | 2 +- .../src/server/api/endpoints/users/notes.ts | 15 ++--- packages/frontend/src/pages/timeline.vue | 2 +- packages/frontend/src/store.ts | 4 +- pnpm-lock.yaml | 58 ++++++++++++++++++- 5 files changed, 64 insertions(+), 17 deletions(-) diff --git a/package.json b/package.json index 1adac4764b..94a7198d5d 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", - "build-and-start": "pnpm build && pnpm start", + "build-and-start": "pnpm i && pnpm build && pnpm start", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", diff --git a/packages/backend/src/server/api/endpoints/users/notes.ts b/packages/backend/src/server/api/endpoints/users/notes.ts index 5ba0610f88..73c066c6e8 100644 --- a/packages/backend/src/server/api/endpoints/users/notes.ts +++ b/packages/backend/src/server/api/endpoints/users/notes.ts @@ -13,9 +13,9 @@ import { DI } from '@/di-symbols.js'; import { CacheService } from '@/core/CacheService.js'; import { IdService } from '@/core/IdService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; -import { QueryService } from '@/server/api/QueryService.js'; +import { QueryService } from '@/core/QueryService.js'; import { ApiError } from '../../error.js'; -import { GetterService } from '@/core/GetterService.js' +import { GetterService } from '@/server/api/GetterService.js' export const meta = { tags: ['users', 'notes'], @@ -118,15 +118,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- noteIds = noteIds.slice(0, ps.limit); if (noteIds.length < limit) { - // Lookup user - const user = await this.getterService.getUser(ps.userId).catch(err => { - if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); - throw err; - }); //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.userId = :userId', { userId: user.id }) + .andWhere('note.userId = :userId', { userId: ps.userId }) .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') @@ -141,7 +136,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.queryService.generateVisibilityQuery(query, me); if (me) { - this.queryService.generateMutedUserQuery(query, me, user); + this.queryService.generateMutedUserQuery(query, me); this.queryService.generateBlockedUserQuery(query, me); } @@ -165,7 +160,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.includeMyRenotes === false) { query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :userId', { userId: user.id }); + qb.orWhere('note.userId != :userId', { userId: ps.userId }); qb.orWhere('note.renoteId IS NULL'); qb.orWhere('note.text IS NOT NULL'); qb.orWhere('note.fileIds != \'{}\''); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 179ab63f41..fbb61d7bb8 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -65,7 +65,7 @@ const withReplies_store = computed(defaultStore.makeGetterSetter('withRenotes') const withRenotes_store = computed(defaultStore.makeGetterSetter('withReplies')) const onlyFiles_store = computed(defaultStore.makeGetterSetter('onlyFiles')) const withRenotes = $ref(defaultStore.state.onlyAndWithSave ? withRenotes_store : true); -const withReplies = $ref(defaultStore.state.onlyAndWithSave ? withReplies_store : false); +const withReplies = $ref(defaultStore.state.onlyAndWithSave ? withReplies_store : true); const onlyFiles = $ref(defaultStore.state.onlyAndWithSave ? onlyFiles_store : false); const isShowMediaTimeline = $ref(defaultStore.state.showMediaTimeline) watch($$(src), () => queue = 0); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index b2d3c610ab..eb7def7762 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -362,7 +362,7 @@ export const defaultStore = markRaw(new Storage('base', { }, onlyAndWithSave:{ where: 'device', - default: true, + default: false, }, onlyFiles:{ where: 'device', @@ -370,7 +370,7 @@ export const defaultStore = markRaw(new Storage('base', { }, withReplies:{ where: 'device', - default: false, + default: true, }, withRenotes:{ where: 'device', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 49ac89d59c..0e5dbf8438 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -814,6 +814,9 @@ importers: vue: specifier: 3.3.4 version: 3.3.4 + vue-multiselect: + specifier: ^2.1.7 + version: 2.1.7 vue-prism-editor: specifier: 2.0.0-alpha.2 version: 2.0.0-alpha.2(vue@3.3.4) @@ -6906,7 +6909,7 @@ packages: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.3.4 - vue-component-type-helpers: 1.8.15 + vue-component-type-helpers: 1.8.16 transitivePeerDependencies: - encoding - supports-color @@ -7972,6 +7975,10 @@ packages: '@types/node': 20.8.2 dev: true + /@types/web-bluetooth@0.0.18: + resolution: {integrity: sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==} + dev: false + /@types/web-push@3.6.1: resolution: {integrity: sha512-Zu6Iju7c4IlE8I8eEeFLYRb7XFqvHFmWWAYr1cmug9EX3c6CDarxIXWN/GO0sxjbJLkHPwozUzp6cLdXsrq7Ew==} dependencies: @@ -8403,6 +8410,31 @@ packages: - typescript dev: true + /@vueuse/core@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==} + dependencies: + '@types/web-bluetooth': 0.0.18 + '@vueuse/metadata': 10.5.0 + '@vueuse/shared': 10.5.0(vue@3.3.4) + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@vueuse/metadata@10.5.0: + resolution: {integrity: sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==} + dev: false + + /@vueuse/shared@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==} + dependencies: + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + /@webgpu/types@0.1.30: resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==} requiresBuild: true @@ -19173,8 +19205,8 @@ packages: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} - /vue-component-type-helpers@1.8.15: - resolution: {integrity: sha512-RKiPRKW4BdwgmQ9vaNkHYKAThdTbgU4TOphVyyzqxRwsOJOoRIrb+vB49XLvs5CKPNrvxMXZMwPe5FyJCqFWyg==} + /vue-component-type-helpers@1.8.16: + resolution: {integrity: sha512-Pbm1/OyJ1m+8TnMApwHu9+WFqju756rdjdhd9TxldkbjmD5baVXVE3UnCqGa/qlEqUxqgi6zC7I5OKNUI8inGQ==} dev: true /vue-demi@0.13.11(vue@3.3.4): @@ -19192,6 +19224,21 @@ packages: vue: 3.3.4 dev: false + /vue-demi@0.14.6(vue@3.3.4): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + dev: false + /vue-docgen-api@4.64.1(vue@3.3.4): resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==} dependencies: @@ -19236,6 +19283,11 @@ packages: vue: 3.3.4 dev: true + /vue-multiselect@2.1.7: + resolution: {integrity: sha512-KIegcN+Ntwg3cbkY/jhw2s/+XJUM0Lpi/LcKFYCS8PrZHcWBl2iKCVze7ZCnRj3w8H7/lUJ9v7rj9KQiNxApBw==} + engines: {node: '>= 4.0.0', npm: '>= 3.0.0'} + dev: false + /vue-prism-editor@2.0.0-alpha.2(vue@3.3.4): resolution: {integrity: sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==} engines: {node: '>=10'} From 704a80860aa6825063b273bd570faa80eeae8db7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 8 Oct 2023 15:48:28 +0900 Subject: [PATCH 123/501] bug fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2255c1c372..a945dbb373 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.8", + "version": "2023.10.0-beta.8-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From fe7931b2505b3c81c8ab3acd933c683e80b0aa48 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 8 Oct 2023 21:57:36 +0900 Subject: [PATCH 124/501] 2023.10.0-beta.9-prismisskey.1 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 0e4fe4cc7f..9c5227ca4b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.9", + "version": "2023.10.0-beta.9-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", @@ -18,6 +18,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", + "build-and-start": "pnpm run init && pnpm build && pnpm start", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", From 2a6d676ca17432b7ff302158a7ef55e0b8bbc8a4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 9 Oct 2023 19:47:26 +0900 Subject: [PATCH 125/501] 2023.10.0-beta.12-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 862a1b2657..310882d0f9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.12", + "version": "2023.10.0-beta.12-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From dbe67738d88057a7d4203e88fdc3e39949de1113 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 9 Oct 2023 22:17:13 +0900 Subject: [PATCH 126/501] bug fix --- .../src/server/api/endpoints/notes/hybrid-timeline.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 0a3006efc8..3ec8281888 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -81,8 +81,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; const policies = await this.roleService.getUserPolicies(me.id); if (!policies.ltlAvailable) { @@ -107,7 +107,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); - if (noteIds.length < limit) { + if (noteIds.length < ps.limit) { const followingQuery = this.followingsRepository.createQueryBuilder('following') .select('following.followeeId') .where('following.followerId = :followerId', { followerId: me.id }); @@ -165,11 +165,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- query.andWhere('note.fileIds != \'{}\''); } //#endregion - const ids = await query.limit(limit - noteIds.length).getMany(); + const ids = await query.limit(ps.limit - noteIds.length).getMany(); noteIds = noteIds.concat(ids.map(note => note.id)); } - + if (noteIds.length === 0) { return []; } From 281e4100a75d30b87b6e299db6a419dfb9280784 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 9 Oct 2023 22:17:32 +0900 Subject: [PATCH 127/501] 2023.10.0-beta.12-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 270385425d..1ed5fff6d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.15", + "version": "2023.10.0-beta.15-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 838a624655a72d57dcfab59e883e51ba2e17b3aa Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 9 Oct 2023 22:32:56 +0900 Subject: [PATCH 128/501] TL bug fix --- .../backend/src/server/api/endpoints/notes/local-timeline.ts | 3 --- packages/backend/src/server/api/endpoints/notes/timeline.ts | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index bb7f830ced..2c873aecc3 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -74,9 +74,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); - const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index e9ee045e47..beebf26d02 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -67,8 +67,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); - const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; + const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; const [ followings, From 1dcc64915276d13d442ae7c02fd009adf14a4612 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 9 Oct 2023 22:33:12 +0900 Subject: [PATCH 129/501] 2023.10.0-beta.15-prismisskey.2 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ed5fff6d4..5c3463abbb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.15-prismisskey.1", + "version": "2023.10.0-beta.15-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", @@ -16,6 +16,7 @@ "scripts": { "build-pre": "node ./scripts/build-pre.js", "build-assets": "node ./scripts/build-assets.mjs", + "build-and-start": "pnpm build && pnpm start", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", From 8358ace249247e175becfdee163db41e777e3771 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 9 Oct 2023 22:44:35 +0900 Subject: [PATCH 130/501] 2023.10.0-beta.15-prismisskey.3 --- .../api/endpoints/notes/hybrid-timeline.ts | 71 +-------------- .../api/endpoints/notes/local-timeline.ts | 61 ++----------- .../server/api/endpoints/notes/timeline.ts | 42 ++------- .../src/server/api/endpoints/notes/update.ts | 90 ++++++++++++++++++- 4 files changed, 101 insertions(+), 163 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 3ec8281888..1b77285d47 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -17,7 +17,6 @@ import { isUserRelated } from '@/misc/is-user-related.js'; import { CacheService } from '@/core/CacheService.js'; import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; -import {QueryService} from "@/core/QueryService.js"; export const meta = { tags: ['notes'], @@ -69,11 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, - @Inject(DI.followingsRepository) - private followingsRepository: FollowingsRepository, - private noteEntityService: NoteEntityService, - private queryService: QueryService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, private idService: IdService, @@ -81,8 +76,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const policies = await this.roleService.getUserPolicies(me.id); if (!policies.ltlAvailable) { @@ -107,68 +102,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- let noteIds = Array.from(new Set([...htlNoteIds, ...ltlNoteIds])); noteIds.sort((a, b) => a > b ? -1 : 1); noteIds = noteIds.slice(0, ps.limit); - if (noteIds.length < ps.limit) { - const followingQuery = this.followingsRepository.createQueryBuilder('following') - .select('following.followeeId') - .where('following.followerId = :followerId', { followerId: me.id }); - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで - .andWhere(new Brackets(qb => { - qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) - .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); - })) - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser') - .setParameters(followingQuery.getParameters()); - - this.queryService.generateChannelQuery(query, me); - this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.includeMyRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.userId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeRenotedMyNotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserId != :meId', { meId: me.id }); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.includeLocalRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteUserHost IS NOT NULL'); - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - qb.orWhere('0 < (SELECT COUNT(*) FROM poll WHERE poll."noteId" = note.id)'); - })); - } - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - //#endregion - const ids = await query.limit(ps.limit - noteIds.length).getMany(); - noteIds = noteIds.concat(ids.map(note => note.id)); - - } if (noteIds.length === 0) { return []; diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 2c873aecc3..2357f32d5e 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -17,7 +17,6 @@ import { CacheService } from '@/core/CacheService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; -import {QueryService} from "@/core/QueryService.js"; export const meta = { tags: ['notes'], @@ -67,13 +66,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private noteEntityService: NoteEntityService, private roleService: RoleService, - private queryService: QueryService, private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); + const policies = await this.roleService.getUserPolicies(me ? me.id : null); if (!policies.ltlAvailable) { throw new ApiError(meta.errors.ltlDisabled); @@ -89,56 +90,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]) : [new Set<string>(), new Set<string>(), new Set<string>()]; - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - } - - let noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - - if (noteIds.length < limit) { - //#region Construct query - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで - .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); - - this.queryService.generateChannelQuery(query, me); - this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); - if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - - - if (ps.withRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere(new Brackets(qb => { - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - })); - })); - } - const ids = await query.limit(limit - noteIds.length).getMany(); - noteIds = noteIds.concat(ids.map(note => note.id)); - } + let noteIds = await this.redisTimelineService.get(ps.withFiles ? 'localTimelineWithFiles' : 'localTimeline', untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -153,7 +106,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (me && (note.userId === me.id)) { @@ -171,6 +124,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- return true; }); + // TODO: フィルタした結果件数が足りなかった場合の対応 + timeline.sort((a, b) => a.id > b.id ? -1 : 1); process.nextTick(() => { diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index beebf26d02..760d52c9db 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -60,15 +60,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private notesRepository: NotesRepository, private noteEntityService: NoteEntityService, - private queryService: QueryService, private activeUsersChart: ActiveUsersChart, private idService: IdService, private cacheService: CacheService, private redisTimelineService: RedisTimelineService, ) { super(meta, paramDef, async (ps, me) => { - const untilId = ps.untilId ?? ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null; - const sinceId = ps.sinceId ?? ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null; + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); const [ followings, @@ -82,39 +81,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.cacheService.userBlockedCache.fetch(me.id), ]); - let timeline: MiNote[] = []; - - const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdに指定したものも含まれるため+1 - let noteIdsRes: [string, string[]][] = []; - - if (!ps.sinceId && !ps.sinceDate) { - noteIdsRes = await this.redisForTimelines.xrevrange( - ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, - ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : ps.untilDate ?? '+', - ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : ps.sinceDate ?? '-', - 'COUNT', limit); - } - - let noteIds = noteIdsRes.map(x => x[1][1]).filter(x => x !== ps.untilId && x !== ps.sinceId); - if (noteIds.length < limit) { - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate); - const followingIds = Object.keys(followings); - if (followingIds.length > 0) { - const meOrFolloweeIds = [me.id, ...followingIds]; - query.andWhere('note.userId IN (:...meOrFolloweeIds)', { meOrFolloweeIds: meOrFolloweeIds }); - } else { - query.andWhere('note.userId = :meId', { meId: me.id }); - } - - this.queryService.generateChannelQuery(query, me); - this.queryService.generateVisibilityQuery(query, me); - this.queryService.generateMutedUserQuery(query, me); - this.queryService.generateBlockedUserQuery(query, me); - this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - const ids = await query.limit(limit - noteIds.length).getMany(); - noteIds = noteIds.concat(ids.map(note => note.id)); - } + let noteIds = await this.redisTimelineService.get(ps.withFiles ? `homeTimelineWithFiles:${me.id}` : `homeTimeline:${me.id}`, untilId, sinceId); + noteIds = noteIds.slice(0, ps.limit); if (noteIds.length === 0) { return []; @@ -129,7 +97,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - timeline = await query.getMany(); + let timeline = await query.getMany(); timeline = timeline.filter(note => { if (note.userId === me.id) { diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index cdf7f085e0..4f01cc2217 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -5,7 +5,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, NotesRepository } from '@/models/_.js'; +import type { UsersRepository, NotesRepository , DriveFilesRepository, MiDriveFile} from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; @@ -14,6 +14,7 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { ApiError } from '../../error.js'; + export const meta = { tags: ['notes'], @@ -34,6 +35,11 @@ export const meta = { code: 'NO_SUCH_NOTE', id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474', }, + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, }, } as const; @@ -41,15 +47,68 @@ export const paramDef = { type: 'object', properties: { noteId: { type: 'string', format: 'misskey:id' }, + visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, + visibleUserIds: { type: 'array', uniqueItems: true, items: { + type: 'string', format: 'misskey:id', + } }, + cw: { type: 'string', nullable: true, maxLength: 100 }, + localOnly: { type: 'boolean', default: false }, + reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, + noExtractMentions: { type: 'boolean', default: false }, + noExtractHashtags: { type: 'boolean', default: false }, + noExtractEmojis: { type: 'boolean', default: false }, + replyId: { type: 'string', format: 'misskey:id', nullable: true }, + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + channelId: { type: 'string', format: 'misskey:id', nullable: true }, + + // anyOf内にバリデーションを書いても最初の一つしかチェックされない + // See https://github.com/misskey-dev/misskey/pull/10082 text: { type: 'string', minLength: 1, maxLength: MAX_NOTE_TEXT_LENGTH, - nullable: false, + nullable: true, + }, + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + mediaIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 2, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], }, - cw: { type: 'string', nullable: true, maxLength: 100 }, }, - required: ['noteId', 'text', 'cw'], + // (re)note with text, files and poll are optional + anyOf: [ + { required: ['text'] }, + { required: ['renoteId'] }, + { required: ['fileIds'] }, + { required: ['mediaIds'] }, + { required: ['poll'] }, + ], } as const; @Injectable() @@ -61,6 +120,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + private getterService: GetterService, private globalEventService: GlobalEventService, ) { @@ -70,14 +132,34 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); + let files: MiDriveFile[] = []; + const fileIds = ps.fileIds ?? null; + + if (fileIds != null) { + files = await this.driveFilesRepository.createQueryBuilder('file') + .where('file.userId = :userId AND file.id IN (:...fileIds)', { + userId: me.id, + fileIds, + }) + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') + .setParameters({ fileIds }) + .getMany(); + + if (files.length !== fileIds.length) { + throw new ApiError(meta.errors.noSuchFile); + } + } + if (note.userId !== me.id) { throw new ApiError(meta.errors.noSuchNote); } + await this.notesRepository.update({ id: note.id }, { updatedAt: new Date(), cw: ps.cw, text: ps.text, + fileIds: files.length > 0 ? files.map(f => f.id) : undefined, }); this.globalEventService.publishNoteStream(note.id, 'updated', { From 140dfdaccc1ad405b2387e5e7b45b28d40c9596e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 10 Oct 2023 12:38:34 +0900 Subject: [PATCH 131/501] bug fix --- .../src/server/api/endpoints/channels/timeline.ts | 6 ++---- .../src/server/api/endpoints/notes/featured.ts | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/channels/timeline.ts b/packages/backend/src/server/api/endpoints/channels/timeline.ts index 4385c698eb..1efaef19a3 100644 --- a/packages/backend/src/server/api/endpoints/channels/timeline.ts +++ b/packages/backend/src/server/api/endpoints/channels/timeline.ts @@ -94,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ] = me ? await Promise.all([ this.cacheService.userMutingsCache.fetch(me.id), ]) : [new Set<string>()]; - + console.log(userIdsWhoMeMuting) let noteIds = await this.redisTimelineService.get(`channelTimeline:${channel.id}`, untilId, sinceId); noteIds = noteIds.slice(0, ps.limit); @@ -111,9 +111,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- let timeline = await query.getMany(); timeline = timeline.filter(note => { - if (me && isUserRelated(note, userIdsWhoMeMuting, true)) return false; - - return true; + return !isUserRelated(note, userIdsWhoMeMuting); }); // TODO: フィルタで件数が減った場合の埋め合わせ処理 diff --git a/packages/backend/src/server/api/endpoints/notes/featured.ts b/packages/backend/src/server/api/endpoints/notes/featured.ts index c456874309..84e2d4f39b 100644 --- a/packages/backend/src/server/api/endpoints/notes/featured.ts +++ b/packages/backend/src/server/api/endpoints/notes/featured.ts @@ -9,6 +9,9 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { FeaturedService } from '@/core/FeaturedService.js'; +import {isUserRelated} from "@/misc/is-user-related.js"; +import {CacheService} from "@/core/CacheService.js"; + export const meta = { tags: ['notes'], @@ -47,6 +50,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + private cacheService: CacheService, private noteEntityService: NoteEntityService, private featuredService: FeaturedService, ) { @@ -63,6 +67,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.globalNotesRankingCacheLastFetchedAt = Date.now(); } } + const [ + userIdsWhoMeMuting, + ] = me ? await Promise.all([ + this.cacheService.userMutingsCache.fetch(me.id), + ]) : [new Set<string>()]; if (noteIds.length === 0) { return []; @@ -83,9 +92,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- .leftJoinAndSelect('renote.user', 'renoteUser') .leftJoinAndSelect('note.channel', 'channel'); - const notes = await query.getMany(); + let notes = await query.getMany(); notes.sort((a, b) => a.id > b.id ? -1 : 1); - + notes = notes.filter(note => { + return !isUserRelated(note, userIdsWhoMeMuting); + }); // TODO: ミュート等考慮 return await this.noteEntityService.packMany(notes, me); From cde2f1152d9e713581950a13276662d61a1a4bb3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 10 Oct 2023 12:38:54 +0900 Subject: [PATCH 132/501] 2023.10.0-beta.15-prismisskey.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5c3463abbb..f709cff6d5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0-beta.15-prismisskey.2", + "version": "2023.10.0-beta.15-prismisskey.4", "codename": "nasubi", "repository": { "type": "git", From e0f65229b6aa3c8e601008450d1532c9bbf73b1f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 10 Oct 2023 18:33:36 +0900 Subject: [PATCH 133/501] 2023.10.0-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e94852616e..4b858e8208 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.0", + "version": "2023.10.0-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 6ad8fd3d4f3bb753ae64742e3461563abfb07802 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 11 Oct 2023 06:19:14 +0900 Subject: [PATCH 134/501] bugfix --- packages/frontend/src/components/MkDrive.file.vue | 4 ++-- packages/frontend/src/components/MkDrive.vue | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue index b3c5e57455..d5e9001189 100644 --- a/packages/frontend/src/components/MkDrive.file.vue +++ b/packages/frontend/src/components/MkDrive.file.vue @@ -78,7 +78,7 @@ watch(props.SelectFiles, () => { }); function onClick(ev: MouseEvent) { - ('onclick'); + if (props.selectMode) { emit('chosen', props.file); } else if (!ev.shiftKey && !isTouchUsing && !isSelectedFile.value) { @@ -93,7 +93,7 @@ function onClick(ev: MouseEvent) { } function onContextmenu(ev: MouseEvent) { - ('oncontext'); + if (!isTouchUsing) { if (!ev.shiftKey && !isSelectedFile.value) { os.contextMenu(getDriveFileMenu(props.file, props.folder), ev); diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 875c450201..20bda88078 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -22,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="f" :parentFolder="folder" :class="[$style.navPathItem]" + :selectedFiles="selectedFiles" @move="move" @upload="upload" @removeFile="removeFile" @@ -87,6 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only :file="file" :folder="folder" :selectMode="select === 'file'" + :SelectFiles="selectedFiles" :isSelected="selectedFiles.some(x => x.id === file.id)" @click.shift.left.exact="filesSelect" @chosen="chooseFile" From 3ce7ad76f83ba2ae0cc3baab0e676f870c4f48a6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 06:04:42 +0900 Subject: [PATCH 135/501] Role Timeline --- .../endpoints/notes/hybrid-all-timeline.ts | 126 ------------------ .../src/server/api/endpoints/roles/notes.ts | 20 ++- 2 files changed, 13 insertions(+), 133 deletions(-) delete mode 100644 packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts deleted file mode 100644 index d3d9fa346a..0000000000 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-all-timeline.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Brackets } from 'typeorm'; -import { Inject, Injectable } from '@nestjs/common'; -import type { NotesRepository } from '@/models/index.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { QueryService } from '@/core/QueryService.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import ActiveUsersChart from '@/core/chart/charts/active-users.js'; -import { DI } from '@/di-symbols.js'; -import { RoleService } from '@/core/RoleService.js'; -import { IdService } from '@/core/IdService.js'; -import { ApiError } from '../../error.js'; - -export const meta = { - tags: ['notes'], - - res: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'object', - optional: false, nullable: false, - ref: 'Note', - }, - }, - - errors: { - ltlDisabled: { - message: 'hybrid Local timeline has been disabled.', - code: 'LTL_DISABLED', - id: '45a6eb02-7695-4393-b023-dd3be9aaaefd', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - withFiles: { type: 'boolean', default: false }, - withReplies: { type: 'boolean', default: false }, - fileType: { type: 'array', items: { - type: 'string', - } }, - excludeNsfw: { type: 'boolean', default: false }, - limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, - sinceId: { type: 'string', format: 'misskey:id' }, - untilId: { type: 'string', format: 'misskey:id' }, - sinceDate: { type: 'integer' }, - untilDate: { type: 'integer' }, - }, - required: [], -} as const; - -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export - constructor( - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - private noteEntityService: NoteEntityService, - private queryService: QueryService, - private roleService: RoleService, - private activeUsersChart: ActiveUsersChart, - private idService: IdService, - ) { - super(meta, paramDef, async (ps, me) => { - const policies = await this.roleService.getUserPolicies(me ? me.id : null); - if (!policies.ltlAvailable) { - throw new ApiError(meta.errors.ltlDisabled); - } - - //#region Construct query - const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), - ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで - .andWhere('(note.visibility = \'home\') AND (note.userHost IS NULL)') - .innerJoinAndSelect('note.user', 'user') - .leftJoinAndSelect('note.reply', 'reply') - .leftJoinAndSelect('note.renote', 'renote') - .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); - - this.queryService.generateChannelQuery(query, me); - this.queryService.generateRepliesQuery(query, ps.withReplies, me); - this.queryService.generateVisibilityQuery(query, me); - if (me) this.queryService.generateMutedUserQuery(query, me); - if (me) this.queryService.generateMutedNoteQuery(query, me); - if (me) this.queryService.generateBlockedUserQuery(query, me); - if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - - if (ps.withFiles) { - query.andWhere('note.fileIds != \'{}\''); - } - - if (ps.fileType != null) { - query.andWhere('note.fileIds != \'{}\''); - query.andWhere(new Brackets(qb => { - for (const type of ps.fileType!) { - const i = ps.fileType!.indexOf(type); - qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type }); - } - })); - - if (ps.excludeNsfw) { - query.andWhere('note.cw IS NULL'); - query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)'); - } - } - //#endregion - - const timeline = await query.limit(ps.limit).getMany(); - - process.nextTick(() => { - if (me) { - this.activeUsersChart.read(me); - } - }); - - return await this.noteEntityService.packMany(timeline, me); - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/roles/notes.ts b/packages/backend/src/server/api/endpoints/roles/notes.ts index 0db51abc55..8a1cc8dd59 100644 --- a/packages/backend/src/server/api/endpoints/roles/notes.ts +++ b/packages/backend/src/server/api/endpoints/roles/notes.ts @@ -13,6 +13,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { IdService } from '@/core/IdService.js'; import { RedisTimelineService } from '@/core/RedisTimelineService.js'; import { ApiError } from '../../error.js'; +import {RoleService} from "@/core/RoleService.js"; export const meta = { tags: ['role', 'notes'], @@ -62,7 +63,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.rolesRepository) private rolesRepository: RolesRepository, - + private roleService: RoleService, private idService: IdService, private noteEntityService: NoteEntityService, private queryService: QueryService, @@ -71,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- super(meta, paramDef, async (ps, me) => { const untilId = ps.untilId ?? (ps.untilDate ? this.idService.genId(new Date(ps.untilDate!)) : null); const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.genId(new Date(ps.sinceDate!)) : null); - + const isModerator = await this.roleService.isModerator(me); const role = await this.rolesRepository.findOneBy({ id: ps.roleId, isPublic: true, @@ -90,15 +91,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (noteIds.length === 0) { return []; } - - const query = this.notesRepository.createQueryBuilder('note') + const query = isModerator ? this.notesRepository.createQueryBuilder('note') .where('note.id IN (:...noteIds)', { noteIds: noteIds }) - .andWhere('(note.visibility = \'public\')') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('note.reply', 'reply') .leftJoinAndSelect('note.renote', 'renote') .leftJoinAndSelect('reply.user', 'replyUser') - .leftJoinAndSelect('renote.user', 'renoteUser'); + .leftJoinAndSelect('renote.user', 'renoteUser') : + this.notesRepository.createQueryBuilder('note') + .where('note.id IN (:...noteIds)', { noteIds: noteIds }) + .andWhere('(note.visibility = \'public\')' ) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); this.queryService.generateVisibilityQuery(query, me); this.queryService.generateMutedUserQuery(query, me); @@ -106,7 +113,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const notes = await query.getMany(); notes.sort((a, b) => a.id > b.id ? -1 : 1); - return await this.noteEntityService.packMany(notes, me); }); } From 0900fcead4903c9ea1dacb7aa58eabbf1ac1dc32 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 06:04:48 +0900 Subject: [PATCH 136/501] build and run --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4b858e8208..47c8484f0a 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", + "build-and-start": "pnpm build && pnpm start", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", From bffe140ab0987fb9d83ec961d4f4e458abe2fa5e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 12:54:09 +0900 Subject: [PATCH 137/501] ruby --- .../global/MkMisskeyFlavoredMarkdown.ts | 27 ++++++++++++++++++- .../frontend/src/components/global/MkRuby.vue | 18 +++++++++++++ packages/frontend/src/scripts/mfm-tags.ts | 2 +- 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 packages/frontend/src/components/global/MkRuby.vue diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 25262030d6..57939e2a1d 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -14,6 +14,7 @@ import MkA from '@/components/global/MkA.vue'; import { host } from '@/config'; import { defaultStore } from '@/store'; import { mixEmoji } from '@/scripts/emojiKitchen/emojiMixer'; +import MkRuby from "@/components/global/MkRuby.vue"; const QUOTE_STYLE = ` display: block; @@ -327,6 +328,31 @@ export default function(props: { style = useAnim ? `--move-fromX: ${fromX}em; --move-fromY: ${fromY}em; --move-toX: ${toX}em; --move-toY: ${toY}em; animation: ${speed} ${ease} ${delay} infinite ${direction} mfm-move;` : ''; break; } + case 'ruby': { + if (token.children.length === 1 ){ + const base = token.children[0].props.text.split(/[ ]+/); + if (base.length !== 2 ){ + style = null; + break; + } + return h(MkRuby,{ + base:base[0], + text:base[1] + }); + }else if(token.children.length === 2){ + console.log(token.children) + const base = token.children[0].type !== 'unicodeEmoji' ? token.children[0].props.text : token.children[0].props.emoji; + const txt = token.children[1].type !== 'unicodeEmoji' ? token.children[1].props.text : token.children[1].props.emoji; + return h(MkRuby,{ + base:base, + basetype:token.children[0].type, + text:txt, + }); + }else{ + style = null; + break; + } + } case 'mix': { const ch = token.children; if (ch.length != 2 || ch.some(c => c.type !== 'unicodeEmoji')) { @@ -395,7 +421,6 @@ export default function(props: { username: token.props.username, })]; } - case 'hashtag': { return [h(MkA, { key: Math.random(), diff --git a/packages/frontend/src/components/global/MkRuby.vue b/packages/frontend/src/components/global/MkRuby.vue new file mode 100644 index 0000000000..0ea808f06d --- /dev/null +++ b/packages/frontend/src/components/global/MkRuby.vue @@ -0,0 +1,18 @@ +<script setup lang="ts"> +import { computed } from 'vue'; +import MkEmoji from './MkEmoji.vue'; +const props = defineProps<{ + base: string; + text: string; + basetype: string; +}>(); +</script> + +<template> + <ruby> + + <MkEmoji v-if="basetype === 'unicodeEmoji' " class="emoji" :emoji="base" :normal="true"/> + <p v-else >{{base}}</p> + <rt>{{text}}</rt> + </ruby> +</template> diff --git a/packages/frontend/src/scripts/mfm-tags.ts b/packages/frontend/src/scripts/mfm-tags.ts index 5968a56639..b1055a696b 100644 --- a/packages/frontend/src/scripts/mfm-tags.ts +++ b/packages/frontend/src/scripts/mfm-tags.ts @@ -1 +1 @@ -export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'skew', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'fgg', 'bgg', 'clip', 'move', 'mix']; +export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'skew', 'position', 'fg', 'bg', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'fgg', 'bgg', 'clip', 'move', 'mix','ruby']; From 805280874bd51309cb77c07cc1335f5ff33c5e69 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 12:55:02 +0900 Subject: [PATCH 138/501] 2023.10.1-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 66c94b56d4..1375652ae0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.1", + "version": "2023.10.1-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 592c6e60a9c070c186bd03ae0371a86d33015120 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 15:25:41 +0900 Subject: [PATCH 139/501] =?UTF-8?q?=E8=89=B2=E3=80=85fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/users/stats.ts | 228 ++++++++++++++++++ .../frontend/src/components/MkPostForm.vue | 4 + .../global/MkMisskeyFlavoredMarkdown.ts | 19 +- .../frontend/src/components/global/MkRuby.vue | 9 +- .../src/pages/settings/account-stats.vue | 119 +++++++++ .../frontend/src/pages/settings/other.vue | 1 + packages/frontend/src/router.ts | 6 +- 11 files changed, 388 insertions(+), 6 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/users/stats.ts create mode 100644 packages/frontend/src/pages/settings/account-stats.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index dfec540009..744e21607d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -14,6 +14,7 @@ export interface Locale { "forgotPassword": string; "fetchingAsApObject": string; "ok": string; + "ruby": string; "gotIt": string; "cancel": string; "noThankYou": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e066a36d5b..8f4b2e5ae0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -11,6 +11,7 @@ password: "パスワード" forgotPassword: "パスワードを忘れた" fetchingAsApObject: "連合に照会中" ok: "OK" +ruby: "ルビ" gotIt: "わかった" cancel: "キャンセル" noThankYou: "やめておく" diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index a21ef69dbf..b4cdc196f8 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -330,6 +330,7 @@ import * as ep___users_following from './endpoints/users/following.js'; import * as ep___users_gallery_posts from './endpoints/users/gallery/posts.js'; import * as ep___users_getFrequentlyRepliedUsers from './endpoints/users/get-frequently-replied-users.js'; import * as ep___users_featuredNotes from './endpoints/users/featured-notes.js'; +import * as ep___users_user_stats from './endpoints/users/stats.js'; import * as ep___users_lists_create from './endpoints/users/lists/create.js'; import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; import * as ep___users_lists_list from './endpoints/users/lists/list.js'; @@ -567,6 +568,7 @@ const $i_importMuting: Provider = { provide: 'ep:i/import-muting', useClass: ep_ const $i_importUserLists: Provider = { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }; const $i_importAntennas: Provider = { provide: 'ep:i/import-antennas', useClass: ep___i_importAntennas.default }; const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }; +const $i_userstats: Provider = { provide: 'ep:i/stats', useClass: ep___users_user_stats.default } const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }; const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default }; const $i_pin: Provider = { provide: 'ep:i/pin', useClass: ep___i_pin.default }; @@ -816,6 +818,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $channels_timeline, $channels_unfollow, $channels_update, + $i_userstats, $channels_favorite, $channels_unfavorite, $channels_myFavorites, @@ -1103,6 +1106,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_setisSensitiveBulk, $admin_emoji_update, $admin_federation_deleteAllFiles, + $i_userstats, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, $admin_federation_updateInstance, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 1ad9cb3a3f..3fac9552ad 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -237,6 +237,7 @@ import * as ep___i_webhooks_show from './endpoints/i/webhooks/show.js'; import * as ep___i_webhooks_list from './endpoints/i/webhooks/list.js'; import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; +import * as ep___i_user_stats from './endpoints/users/stats.js'; import * as ep___invite_create from './endpoints/invite/create.js'; import * as ep___invite_delete from './endpoints/invite/delete.js'; import * as ep___invite_list from './endpoints/invite/list.js'; @@ -589,6 +590,7 @@ const eps = [ ['i/webhooks/show', ep___i_webhooks_show], ['i/webhooks/update', ep___i_webhooks_update], ['i/webhooks/delete', ep___i_webhooks_delete], + ['i/stats', ep___i_user_stats], ['invite/create', ep___invite_create], ['invite/delete', ep___invite_delete], ['invite/list', ep___invite_list], diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts new file mode 100644 index 0000000000..f51da1baad --- /dev/null +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -0,0 +1,228 @@ +import { Inject, Injectable } from '@nestjs/common'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { DI } from '@/di-symbols.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, DriveFilesRepository, NoteReactionsRepository, PageLikesRepository, NoteFavoritesRepository, PollVotesRepository } from '@/models/index.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['users'], + + requireCredential: false, + + description: 'Show statistics about a user.', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '9e638e45-3b25-4ef7-8f95-07e8498f1819', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + notesCount: { + type: 'integer', + optional: false, nullable: false, + }, + repliesCount: { + type: 'integer', + optional: false, nullable: false, + }, + renotesCount: { + type: 'integer', + optional: false, nullable: false, + }, + repliedCount: { + type: 'integer', + optional: false, nullable: false, + }, + renotedCount: { + type: 'integer', + optional: false, nullable: false, + }, + pollVotesCount: { + type: 'integer', + optional: false, nullable: false, + }, + pollVotedCount: { + type: 'integer', + optional: false, nullable: false, + }, + localFollowingCount: { + type: 'integer', + optional: false, nullable: false, + }, + remoteFollowingCount: { + type: 'integer', + optional: false, nullable: false, + }, + localFollowersCount: { + type: 'integer', + optional: false, nullable: false, + }, + remoteFollowersCount: { + type: 'integer', + optional: false, nullable: false, + }, + followingCount: { + type: 'integer', + optional: false, nullable: false, + }, + followersCount: { + type: 'integer', + optional: false, nullable: false, + }, + sentReactionsCount: { + type: 'integer', + optional: false, nullable: false, + }, + receivedReactionsCount: { + type: 'integer', + optional: false, nullable: false, + }, + noteFavoritesCount: { + type: 'integer', + optional: false, nullable: false, + }, + pageLikesCount: { + type: 'integer', + optional: false, nullable: false, + }, + pageLikedCount: { + type: 'integer', + optional: false, nullable: false, + }, + driveFilesCount: { + type: 'integer', + optional: false, nullable: false, + }, + driveUsage: { + type: 'integer', + optional: false, nullable: false, + description: 'Drive usage in bytes', + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + }, + required: ['userId'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.followingsRepository) + private followingsRepository: FollowingsRepository, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + @Inject(DI.noteReactionsRepository) + private noteReactionsRepository: NoteReactionsRepository, + + @Inject(DI.pageLikesRepository) + private pageLikesRepository: PageLikesRepository, + + @Inject(DI.noteFavoritesRepository) + private noteFavoritesRepository: NoteFavoritesRepository, + + @Inject(DI.pollVotesRepository) + private pollVotesRepository: PollVotesRepository, + + private driveFileEntityService: DriveFileEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); + if (user == null) { + throw new ApiError(meta.errors.noSuchUser); + } + + const result = await awaitAll({ + notesCount: this.notesRepository.createQueryBuilder('note') + .where('note.userId = :userId', { userId: user.id }) + .getCount(), + repliesCount: this.notesRepository.createQueryBuilder('note') + .where('note.userId = :userId', { userId: user.id }) + .andWhere('note.replyId IS NOT NULL') + .getCount(), + renotesCount: this.notesRepository.createQueryBuilder('note') + .where('note.userId = :userId', { userId: user.id }) + .andWhere('note.renoteId IS NOT NULL') + .getCount(), + repliedCount: this.notesRepository.createQueryBuilder('note') + .where('note.replyUserId = :userId', { userId: user.id }) + .getCount(), + renotedCount: this.notesRepository.createQueryBuilder('note') + .where('note.renoteUserId = :userId', { userId: user.id }) + .getCount(), + pollVotesCount: this.pollVotesRepository.createQueryBuilder('vote') + .where('vote.userId = :userId', { userId: user.id }) + .getCount(), + pollVotedCount: this.pollVotesRepository.createQueryBuilder('vote') + .innerJoin('vote.note', 'note') + .where('note.userId = :userId', { userId: user.id }) + .getCount(), + localFollowingCount: this.followingsRepository.createQueryBuilder('following') + .where('following.followerId = :userId', { userId: user.id }) + .andWhere('following.followeeHost IS NULL') + .getCount(), + remoteFollowingCount: this.followingsRepository.createQueryBuilder('following') + .where('following.followerId = :userId', { userId: user.id }) + .andWhere('following.followeeHost IS NOT NULL') + .getCount(), + localFollowersCount: this.followingsRepository.createQueryBuilder('following') + .where('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.followerHost IS NULL') + .getCount(), + remoteFollowersCount: this.followingsRepository.createQueryBuilder('following') + .where('following.followeeId = :userId', { userId: user.id }) + .andWhere('following.followerHost IS NOT NULL') + .getCount(), + sentReactionsCount: this.noteReactionsRepository.createQueryBuilder('reaction') + .where('reaction.userId = :userId', { userId: user.id }) + .getCount(), + receivedReactionsCount: this.noteReactionsRepository.createQueryBuilder('reaction') + .innerJoin('reaction.note', 'note') + .where('note.userId = :userId', { userId: user.id }) + .getCount(), + noteFavoritesCount: this.noteFavoritesRepository.createQueryBuilder('favorite') + .where('favorite.userId = :userId', { userId: user.id }) + .getCount(), + pageLikesCount: this.pageLikesRepository.createQueryBuilder('like') + .where('like.userId = :userId', { userId: user.id }) + .getCount(), + pageLikedCount: this.pageLikesRepository.createQueryBuilder('like') + .innerJoin('like.page', 'page') + .where('page.userId = :userId', { userId: user.id }) + .getCount(), + driveFilesCount: this.driveFilesRepository.createQueryBuilder('file') + .where('file.userId = :userId', { userId: user.id }) + .getCount(), + driveUsage: this.driveFileEntityService.calcDriveUsageOf(user), + }); + + return { + ...result, + followingCount: result.localFollowingCount + result.remoteFollowingCount, + followersCount: result.localFollowersCount + result.remoteFollowersCount, + }; + }); + } +} diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index d250304efb..909ad9d666 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -86,6 +86,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugin" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> <button v-tooltip="i18n.ts.mfm" :class="['_button', $style.footerButton]" @click="insertMfm"><i class="ti ti-wand"></i></button> + <button v-tooltip="i18n.ts.ruby" :class="['_button', $style.footerButton]" @click="insertRuby"><i class="ti ti-abc"></i></button> </div> <div :class="$style.footerRight"> <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> @@ -849,6 +850,9 @@ async function insertEmoji(ev: MouseEvent) { function insertMfm(){ insertTextAtCursor(textareaEl, '$'); } +function insertRuby() { + insertTextAtCursor(textareaEl, '$[ruby 本文 上につくやつ]'); +} function showActions(ev) { os.popupMenu(postFormActions.map(action => ({ text: action.title, diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 57939e2a1d..069522c71f 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -340,9 +340,24 @@ export default function(props: { text:base[1] }); }else if(token.children.length === 2){ + let txt,base; console.log(token.children) - const base = token.children[0].type !== 'unicodeEmoji' ? token.children[0].props.text : token.children[0].props.emoji; - const txt = token.children[1].type !== 'unicodeEmoji' ? token.children[1].props.text : token.children[1].props.emoji; + if (token.children[1].type === 'emojiCode'){ + txt = token.children[1].props.name + }else if(token.children[1].type === 'unicodeEmoji'){ + txt = token.children[1].props.emoji + }else { + txt = token.children[1].props.text + } + + if (token.children[0].type === 'emojiCode'){ + base = token.children[0].props.name + }else if(token.children[0].type === 'unicodeEmoji'){ + base = token.children[0].props.emoji + }else { + base = token.children[0].props.text + } + return h(MkRuby,{ base:base, basetype:token.children[0].type, diff --git a/packages/frontend/src/components/global/MkRuby.vue b/packages/frontend/src/components/global/MkRuby.vue index 0ea808f06d..5036af70fb 100644 --- a/packages/frontend/src/components/global/MkRuby.vue +++ b/packages/frontend/src/components/global/MkRuby.vue @@ -1,6 +1,7 @@ <script setup lang="ts"> import { computed } from 'vue'; import MkEmoji from './MkEmoji.vue'; +import MkCustomEmoji from './MkCustomEmoji.vue'; const props = defineProps<{ base: string; text: string; @@ -10,9 +11,11 @@ const props = defineProps<{ <template> <ruby> - - <MkEmoji v-if="basetype === 'unicodeEmoji' " class="emoji" :emoji="base" :normal="true"/> - <p v-else >{{base}}</p> + <MkEmoji v-if="basetype === 'unicodeEmoji' " class="emoji" :emoji="base" :normal="true" /> + <MkCustomEmoji v-else-if="basetype === 'emojiCode' " :name="base"/> + <span style="white-space: pre-wrap;" v-else >{{base}}</span> <rt>{{text}}</rt> </ruby> </template> +<style> +</style> diff --git a/packages/frontend/src/pages/settings/account-stats.vue b/packages/frontend/src/pages/settings/account-stats.vue new file mode 100644 index 0000000000..4e591c648a --- /dev/null +++ b/packages/frontend/src/pages/settings/account-stats.vue @@ -0,0 +1,119 @@ +<template> + <div class="_gaps_m"> + <FormSection v-if="stats" first> + <template #label>{{ i18n.ts.statistics }}</template> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.notesCount }}</template> + <template #value>{{ number(stats.notesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.repliesCount }}</template> + <template #value>{{ number(stats.repliesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.renotesCount }}</template> + <template #value>{{ number(stats.renotesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.repliedCount }}</template> + <template #value>{{ number(stats.repliedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.renotedCount }}</template> + <template #value>{{ number(stats.renotedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pollVotesCount }}</template> + <template #value>{{ number(stats.pollVotesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pollVotedCount }}</template> + <template #value>{{ number(stats.pollVotedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.sentReactionsCount }}</template> + <template #value>{{ number(stats.sentReactionsCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.receivedReactionsCount }}</template> + <template #value>{{ number(stats.receivedReactionsCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.noteFavoritesCount }}</template> + <template #value>{{ number(stats.noteFavoritesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followingCount }}</template> + <template #value>{{ number(stats.followingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.local }})</template> + <template #value>{{ number(stats.localFollowingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.remote }})</template> + <template #value>{{ number(stats.remoteFollowingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followersCount }}</template> + <template #value>{{ number(stats.followersCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.local }})</template> + <template #value>{{ number(stats.localFollowersCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.remote }})</template> + <template #value>{{ number(stats.remoteFollowersCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pageLikesCount }}</template> + <template #value>{{ number(stats.pageLikesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pageLikedCount }}</template> + <template #value>{{ number(stats.pageLikedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.driveFilesCount }}</template> + <template #value>{{ number(stats.driveFilesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.driveUsage }}</template> + <template #value>{{ bytes(stats.driveUsage) }}</template> + </MkKeyValue> + </FormSection> + + </div> +</template> + +<script lang="ts" setup> +import { onMounted, ref ,computed } from 'vue'; +import FormSection from '@/components/form/section.vue'; +import MkKeyValue from '@/components/MkKeyValue.vue'; +import * as os from '@/os'; +import number from '@/filters/number'; +import bytes from '@/filters/bytes'; +import { $i } from '@/account'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +const stats = ref<any>({}); + +onMounted(() => { + os.api('i/stats', { + userId: $i!.id, + }).then(response => { + stats.value = response; + }); +}); + +const headerActions = computed(() => []); + +const headerTabs = computed(() => []); + +definePageMetadata({ + title: i18n.ts.accountInfo, + icon: 'ti ti-info-circle', +}); +</script> diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue index e2fc021099..695cc1a9e5 100644 --- a/packages/frontend/src/pages/settings/other.vue +++ b/packages/frontend/src/pages/settings/other.vue @@ -31,6 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #key>{{ i18n.ts.registeredDate }}</template> <template #value><MkTime :time="$i.createdAt" mode="detail"/></template> </MkKeyValue> + <FormLink to="/settings/account-stats"><template #icon><i class="ti ti-info-circle"></i></template>{{ i18n.ts.statistics }}</FormLink> </div> </MkFolder> diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 6c33d0d8ee..a8e645503c 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -174,7 +174,11 @@ export const routes = [{ path: '/other', name: 'other', component: page(() => import('./pages/settings/other.vue')), - }, { + },{ + path: '/account-stats', + name: 'other', + component: page(() => import('./pages/settings/account-stats.vue')), + },{ path: '/', component: page(() => import('./pages/_empty_.vue')), }], From 40469aa0ce60c5bac17c6205a6f62821da201cb4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 17:34:30 +0900 Subject: [PATCH 140/501] =?UTF-8?q?=E9=80=86=E5=BC=B5=E3=82=8A=E3=83=A2?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=AE=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 ++ locales/ja-JP.yml | 3 ++ package.json | 2 +- .../frontend/src/pages/settings/general.vue | 7 ++-- packages/frontend/src/store.ts | 4 ++ packages/frontend/src/ui/_common_/navbar.vue | 41 ++++++++++--------- .../frontend/src/widgets/WidgetGamingMode.vue | 4 +- .../src/widgets/WidgetGyakubariMode.vue | 41 +++++++++++++++++++ packages/frontend/src/widgets/index.ts | 2 + 9 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 packages/frontend/src/widgets/WidgetGyakubariMode.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index 744e21607d..a7182e6c1b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -14,6 +14,8 @@ export interface Locale { "forgotPassword": string; "fetchingAsApObject": string; "ok": string; + "hanntenn": string; + "hanntennInfo": string; "ruby": string; "gotIt": string; "cancel": string; @@ -1972,6 +1974,7 @@ export interface Locale { "memo": string; "notifications": string; "gamingMode": string; + "gyakubariMode": string; "timeline": string; "calendar": string; "trends": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8f4b2e5ae0..db45638a8e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -11,6 +11,8 @@ password: "パスワード" forgotPassword: "パスワードを忘れた" fetchingAsApObject: "連合に照会中" ok: "OK" +hanntenn: "アイコンとバナーを反転させる" +hanntennInfo: "ダークだったらライトのアイコンに、ライトだったらダークのアイコンに。" ruby: "ルビ" gotIt: "わかった" cancel: "キャンセル" @@ -1889,6 +1891,7 @@ _widgets: memo: "付箋" notifications: "通知" gamingMode: "ゲーミングモード" + gyakubariMode: "反転モード" timeline: "タイムライン" calendar: "カレンダー" trends: "トレンド" diff --git a/package.json b/package.json index 1375652ae0..003de97e6f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.1-prismisskey.1", + "version": "2023.10.1-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index ba2e226f17..c06d7540ce 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -134,14 +134,14 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch> <MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch> <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> - <MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> <MkSwitch v-model="enableUltimateDataSaverMode">{{ i18n.ts.UltimateDataSaver }}</MkSwitch> <MkSwitch v-model="enableCellularWithUltimateDataSaver">{{ i18n.ts.cellularWithUltimateDataSaver }}</MkSwitch> <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> - <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave}}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> - </div> + <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave}}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> + <MkSwitch v-model="enablehanntenn">{{ i18n.ts.hanntenn }}<template #caption>{{ i18n.ts.hanntennInfo }} </template></MkSwitch> + </div> <div> <MkRadios v-model="emojiStyle"> <template #label>{{ i18n.ts.emojiStyle }}</template> @@ -285,6 +285,7 @@ const notificationStackAxis = computed(defaultStore.makeGetterSetter('notificati const keepScreenOn = computed(defaultStore.makeGetterSetter('keepScreenOn')); const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWithSave')); +const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) const FeaturedOrNote = computed(defaultStore.makeGetterSetter('FeaturedOrNote')) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index eb7def7762..8f5b72cc6a 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -304,6 +304,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: [] as string[], }, + enablehanntenn:{ + where:'device', + default: false + }, recentlyUsedUsers: { where: 'device', default: [] as string[], diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 02d8d8fed9..8fa94c4653 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -94,6 +94,7 @@ import {$i, openAccountMenu as openAccountMenu_} from '@/account'; import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; import {i18n} from '@/i18n'; import {instance} from '@/instance'; + function hexToRgb(hex) { // 16進数のカラーコードから "#" を除去 hex = hex.replace(/^#/, ''); @@ -105,6 +106,7 @@ function hexToRgb(hex) { return `${r},${g},${b}`; } + document.documentElement.style.setProperty('--homeColor', hexToRgb(defaultStore.state.homeColor)); document.documentElement.style.setProperty("--followerColor",hexToRgb(defaultStore.state.followerColor)); document.documentElement.style.setProperty("--specifiedColor",hexToRgb(defaultStore.state.specifiedColor)) @@ -118,6 +120,7 @@ let gaming = ref(''); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +let gamingType = computed(defaultStore.state.gamingType); if (darkMode.value) { bannerUrl.value = bannerDark; @@ -137,7 +140,6 @@ watch(darkMode, () => { } }) -// gaming.valueに新しい値を代入する if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -167,17 +169,18 @@ watch(gamingMode, () => { } }) + const menu = computed(() => defaultStore.state.menu); const otherMenuItemIndicated = computed(() => { - for (const def in navbarItemDef) { - if (menu.value.includes(def)) continue; - if (navbarItemDef[def].indicated) return true; - } - return false; + for (const def in navbarItemDef) { + if (menu.value.includes(def)) continue; + if (navbarItemDef[def].indicated) return true; + } + return false; }); const calcViewState = () => { - iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon'); + iconOnly.value = (window.innerWidth <= 1279) || (defaultStore.state.menuDisplay === 'sideIcon'); }; calcViewState(); @@ -185,19 +188,19 @@ calcViewState(); window.addEventListener('resize', calcViewState); watch(defaultStore.reactiveState.menuDisplay, () => { - calcViewState(); + calcViewState(); }); function openAccountMenu(ev: MouseEvent) { - openAccountMenu_({ - withExtraOperation: true, - }, ev); + openAccountMenu_({ + withExtraOperation: true, + }, ev); } function more(ev: MouseEvent) { - os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { - src: ev.currentTarget ?? ev.target, - }, {}, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { + src: ev.currentTarget ?? ev.target, + }, {}, 'closed'); } </script> @@ -308,11 +311,11 @@ function more(ev: MouseEvent) { } &.gamingLight { - color: white !important; + color: white !important; } &.gamingDark { - color: black !important; + color: black !important; } &.gamingLight:before { @@ -442,11 +445,11 @@ function more(ev: MouseEvent) { color: var(--navFg); &.gamingDark { - color: var(--navFg); + color: var(--navFg); } &.gamingLight { - color: var(--navFg); + color: var(--navFg); } &:hover { @@ -775,7 +778,7 @@ function more(ev: MouseEvent) { text-align: center; &.gamingLight { - color: var(--fg); + color: var(--fg); } &:hover, &.active { diff --git a/packages/frontend/src/widgets/WidgetGamingMode.vue b/packages/frontend/src/widgets/WidgetGamingMode.vue index 966e223087..749527c546 100644 --- a/packages/frontend/src/widgets/WidgetGamingMode.vue +++ b/packages/frontend/src/widgets/WidgetGamingMode.vue @@ -10,10 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { Interpreter, Parser } from '@syuilo/aiscript'; -import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; import { GetFormResultType } from '@/scripts/form.js'; -import MkButton from '@/components/MkButton.vue'; import {i18n} from "@/i18n.js"; import MkSwitch from "@/components/MkSwitch.vue"; import {computed} from "vue"; diff --git a/packages/frontend/src/widgets/WidgetGyakubariMode.vue b/packages/frontend/src/widgets/WidgetGyakubariMode.vue new file mode 100644 index 0000000000..13592a5c6a --- /dev/null +++ b/packages/frontend/src/widgets/WidgetGyakubariMode.vue @@ -0,0 +1,41 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div> + <MkSwitch v-model="enablehanntenn">{{ i18n.ts.hanntenn }} <template #caption>{{ i18n.ts.hanntennInfo }} </template></MkSwitch> +</div> +</template> + +<script lang="ts" setup> +import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget'; +import { GetFormResultType } from '@/scripts/form.js'; +import {i18n} from "@/i18n.js"; +import MkSwitch from "@/components/MkSwitch.vue"; +import {computed} from "vue"; +import {defaultStore} from "@/store.js"; +const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); +const name = 'gyakubariMode'; + +const widgetPropsDef = { +}; + +type WidgetProps = GetFormResultType<typeof widgetPropsDef>; + +const props = defineProps<WidgetComponentProps<WidgetProps>>(); +const emit = defineEmits<WidgetComponentEmits<WidgetProps>>(); + +const { widgetProps, configure } = useWidgetPropsManager(name, + widgetPropsDef, + props, + emit, +); + +defineExpose<WidgetComponentExpose>({ + name, + configure, + id: props.widget ? props.widget.id : null, +}); +</script> diff --git a/packages/frontend/src/widgets/index.ts b/packages/frontend/src/widgets/index.ts index 655ba609ed..d3e873d0e6 100644 --- a/packages/frontend/src/widgets/index.ts +++ b/packages/frontend/src/widgets/index.ts @@ -11,6 +11,7 @@ export default function(app: App) { app.component('WidgetMemo', defineAsyncComponent(() => import('./WidgetMemo.vue'))); app.component('WidgetNotifications', defineAsyncComponent(() => import('./WidgetNotifications.vue'))); app.component('WidgetGamingMode', defineAsyncComponent(() => import('./WidgetGamingMode.vue'))); + app.component('WidgetGyakubariMode', defineAsyncComponent(() => import('./WidgetGyakubariMode.vue'))); app.component('WidgetTimeline', defineAsyncComponent(() => import('./WidgetTimeline.vue'))); app.component('WidgetCalendar', defineAsyncComponent(() => import('./WidgetCalendar.vue'))); app.component('WidgetRss', defineAsyncComponent(() => import('./WidgetRss.vue'))); @@ -42,6 +43,7 @@ export const widgets = [ 'memo', 'notifications', 'gamingMode', + 'gyakubariMode', 'timeline', 'calendar', 'rss', From 79876ab3acd5b1fbc1d32bff3a27ac04834dbaaf Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 12 Oct 2023 18:09:48 +0900 Subject: [PATCH 141/501] fix --- packages/frontend/src/ui/_common_/navbar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 8fa94c4653..3829d251f7 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -120,7 +120,7 @@ let gaming = ref(''); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -let gamingType = computed(defaultStore.state.gamingType); + if (darkMode.value) { bannerUrl.value = bannerDark; From 120efff6c156a204c3ef46a02ae8173b8369fa00 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 13 Oct 2023 07:26:27 +0900 Subject: [PATCH 142/501] difk --- .../frontend/src/components/MkClickerGame.vue | 48 +++++++++++++------ packages/frontend/src/pages/clicker.vue | 4 +- packages/frontend/src/store.ts | 5 ++ packages/frontend/src/ui/_common_/common.ts | 4 +- .../frontend/src/widgets/WidgetClicker.vue | 19 +++++++- 5 files changed, 59 insertions(+), 21 deletions(-) diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 1c3920962e..e126054296 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -4,29 +4,35 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <div v-if="game.ready" :class="$style.game"> - <div :class="$style.cps" class="">{{ number(cps) }}cps</div> - <div :class="$style.count" class=""><i class="ti ti-cookie" style="font-size: 70%;"></i> {{ number(cookies) }}</div> - <button v-click-anime class="_button" @click="onClick"> - <img src="/client-assets/cookie.png" :class="$style.img"> - </button> + <div> + <div v-if="game.ready" :class="$style.game"> + <div :class="$style.cps" class="">{{ number(cps) }}cps</div> + <div :class="$style.count" class=""><img :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" + src="https://media.discordapp.net/attachments/1153099592863334431/1162139796647448576/AfovawbDhjHYAAAAAElFTkSuQmCC.png"/> + {{ number(cookies) }} + </div> + <button v-click-anime class="_button" @click="onClick"> + <img src="https://cdn.discordapp.com/attachments/1153099592863334431/1160169965568143391/dihk_cossack.gif" + :class="$style.img"> + </button> + </div> + <div v-else> + <MkLoading/> + </div> </div> - <div v-else> - <MkLoading/> - </div> -</div> </template> <script lang="ts" setup> -import { computed, onMounted, onUnmounted } from 'vue'; +import {computed, onMounted, onUnmounted} from 'vue'; import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; import * as os from '@/os.js'; -import { useInterval } from '@/scripts/use-interval.js'; +import {useInterval} from '@/scripts/use-interval.js'; import * as game from '@/scripts/clicker-game.js'; import number from '@/filters/number.js'; -import { claimAchievement } from '@/scripts/achievements.js'; +import {claimAchievement} from '@/scripts/achievements.js'; +import {defaultStore} from "@/store.js"; +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const saveData = game.saveData; const cookies = computed(() => saveData.value?.cookies); let cps = $ref(0); @@ -35,7 +41,7 @@ let prevCookies = $ref(0); function onClick(ev: MouseEvent) { const x = ev.clientX; const y = ev.clientY; - os.popup(MkPlusOneEffect, { x, y }, {}, 'end'); + os.popup(MkPlusOneEffect, {x, y}, {}, 'end'); saveData.value!.cookies++; saveData.value!.totalCookies++; @@ -92,4 +98,16 @@ onUnmounted(() => { .img { max-width: 90px; } + +$color-scheme: var(--color-scheme); + + +.icon { + width: 1.3em; + vertical-align: -24%; +} + +.dark { + filter: invert(1); +} </style> diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue index 5b194881d1..ba19a8abfc 100644 --- a/packages/frontend/src/pages/clicker.vue +++ b/packages/frontend/src/pages/clicker.vue @@ -17,8 +17,8 @@ import MkClickerGame from '@/components/MkClickerGame.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; definePageMetadata({ - title: '🍪👈', - icon: 'ti ti-cookie', + title: '●👈', + icon: 'ti ti-circle', }); </script> diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 8f5b72cc6a..27b8ae6fc9 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -272,6 +272,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true, }, + gamingType: { + where: 'device', + default: 'dark', + }, bannerUrl:{ where: 'device', default: bannerDark @@ -442,6 +446,7 @@ export const defaultStore = markRaw(new Storage('base', { } })); + // TODO: 他のタブと永続化されたstateを同期 const PREFIX = 'miux:' as const; diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index e075e05db3..2d2888e132 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -60,8 +60,8 @@ export function openInstanceMenu(ev: MouseEvent) { }, { type: 'link', to: '/clicker', - text: '🍪👈', - icon: 'ti ti-cookie', + text: '●👈', + icon: 'ti ti-circle', }, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? { type: 'link', to: '/custom-emojis-manager', diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index 5e7464f3a4..8ab62d416e 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -5,7 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkContainer :showHeader="widgetProps.showHeader" class="mkw-clicker"> - <template #icon><i class="ti ti-cookie"></i></template> + <template #icon><img :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" + src="https://media.discordapp.net/attachments/1153099592863334431/1162139796647448576/AfovawbDhjHYAAAAAElFTkSuQmCC.png"/></template> <template #header>Clicker</template> <MkClickerGame/> </MkContainer> @@ -16,7 +17,9 @@ import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExp import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkClickerGame from '@/components/MkClickerGame.vue'; - +import {computed} from "vue"; +import {defaultStore} from "@/store.js"; +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const name = 'clicker'; const widgetPropsDef = { @@ -43,3 +46,15 @@ defineExpose<WidgetComponentExpose>({ id: props.widget ? props.widget.id : null, }); </script> + + +<style lang="scss" module> +.icon { + width: 1.3em; + vertical-align: -24%; +} + +.dark { + filter: invert(1); +} +</style> From ffa81260acfbaf94cc770bffe1a1fbb8fb220797 Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Wed, 17 May 2023 00:44:19 +0900 Subject: [PATCH 143/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE?= =?UTF-8?q?=E7=99=BB=E9=8C=B2=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 702a3e86878bc7f210d90f15c4f4417d542ba086) --- locales/index.d.ts | 3 + locales/ja-JP.yml | 3 + .../1684236161625-addEmojiDraftFlag.js | 11 ++ .../backend/src/core/CustomEmojiService.ts | 13 +- packages/backend/src/core/RoleService.ts | 3 + .../src/core/entities/EmojiEntityService.ts | 2 + packages/backend/src/models/Emoji.ts | 6 + .../backend/src/models/json-schema/emoji.ts | 8 + .../ImportCustomEmojisProcessorService.ts | 1 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/admin/emoji/add-draft.ts | 80 +++++++++ .../server/api/endpoints/admin/emoji/add.ts | 4 +- .../api/endpoints/admin/emoji/update.ts | 2 + .../src/components/MkAutocomplete.vue | 4 + .../frontend/src/components/MkEmojiPicker.vue | 4 +- packages/frontend/src/const.ts | 1 + packages/frontend/src/pages/about.emojis.vue | 26 ++- .../frontend/src/pages/admin/roles.editor.vue | 20 +++ packages/frontend/src/pages/admin/roles.vue | 8 + .../src/pages/custom-emojis-manager.vue | 35 +++- .../frontend/src/pages/emoji-edit-dialog.vue | 158 +++++++++++++----- packages/frontend/src/pages/emojis.emoji.vue | 16 +- packages/misskey-js/src/entities.ts | 1 + 24 files changed, 351 insertions(+), 64 deletions(-) create mode 100644 packages/backend/migration/1684236161625-addEmojiDraftFlag.js create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index 2494c1709b..504df64250 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -978,6 +978,7 @@ export interface Locale { "unassign": string; "color": string; "manageCustomEmojis": string; + "requestCustomEmojis": string; "youCannotCreateAnymore": string; "cannotPerformTemporary": string; "cannotPerformTemporaryDescription": string; @@ -1020,6 +1021,7 @@ export interface Locale { "sensitiveWordsDescription2": string; "notesSearchNotAvailable": string; "license": string; + "draft": string; "unfavoriteConfirm": string; "myClips": string; "drivecleaner": string; @@ -1556,6 +1558,7 @@ export interface Locale { "inviteLimitCycle": string; "inviteExpirationTime": string; "canManageCustomEmojis": string; + "canRequestCustomEmojis": string; "driveCapacity": string; "alwaysMarkNsfw": string; "pinMax": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9adc4381a7..db2420c40d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -975,6 +975,7 @@ assign: "アサイン" unassign: "アサインを解除" color: "色" manageCustomEmojis: "カスタム絵文字の管理" +requestCustomEmojis: "カスタム絵文字のリクエスト" youCannotCreateAnymore: "これ以上作成することはできません。" cannotPerformTemporary: "一時的に利用できません" cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。" @@ -1017,6 +1018,7 @@ sensitiveWordsDescription: "設定したワードが含まれるノートの公 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" +draft: "ドラフト" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" drivecleaner: "ドライブクリーナー" @@ -1477,6 +1479,7 @@ _role: inviteLimitCycle: "招待コードの発行間隔" inviteExpirationTime: "招待コードの有効期限" canManageCustomEmojis: "カスタム絵文字の管理" + canRequestCustomEmojis: "カスタム絵文字のリクエスト" driveCapacity: "ドライブ容量" alwaysMarkNsfw: "ファイルにNSFWを常に付与" pinMax: "ノートのピン留めの最大数" diff --git a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js new file mode 100644 index 0000000000..b0a13ea498 --- /dev/null +++ b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js @@ -0,0 +1,11 @@ +export class AddEmojiDraftFlag1684236161625 { + name = 'AddEmojiDraftFlag1684236161625' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" ADD "draft" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "draft"`); + } +} diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 145c224f67..332a909b8c 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -31,6 +31,12 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.redis) private redisClient: Redis.Redis, + @Inject(DI.config) + private config: Config, + + @Inject(DI.db) + private db: DataSource, + @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, @@ -66,6 +72,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; }, moderator?: MiUser): Promise<MiEmoji> { const emoji = await this.emojisRepository.insert({ @@ -82,6 +89,7 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: data.draft, }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); if (data.host == null) { @@ -111,6 +119,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license?: string | null; isSensitive?: boolean; localOnly?: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; }, moderator?: MiUser): Promise<void> { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); @@ -125,9 +134,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license: data.license, isSensitive: data.isSensitive, localOnly: data.localOnly, - originalUrl: data.driveFile != null ? data.driveFile.url : undefined, - publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, - type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + draft: data.draft, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, }); diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 2c3547e4ac..6aad82e74e 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -32,6 +32,7 @@ export type RolePolicies = { inviteLimitCycle: number; inviteExpirationTime: number; canManageCustomEmojis: boolean; + canRequestCustomEmojis: boolean; canSearchNotes: boolean; canUseTranslator: boolean; canHideAds: boolean; @@ -57,6 +58,7 @@ export const DEFAULT_POLICIES: RolePolicies = { inviteLimitCycle: 60 * 24 * 7, inviteExpirationTime: 0, canManageCustomEmojis: false, + canRequestCustomEmojis: false, canSearchNotes: false, canUseTranslator: true, canHideAds: false, @@ -303,6 +305,7 @@ export class RoleService implements OnApplicationShutdown { inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), inviteExpirationTime: calc('inviteExpirationTime', vs => Math.max(...vs)), canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)), + canRequestCustomEmojis: calc('canRequestCustomEmojis', vs => vs.some(v => v === true)), canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)), canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 5b97cfad5e..d50d7356ae 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -33,6 +33,7 @@ export class EmojiEntityService { url: emoji.publicUrl || emoji.originalUrl, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, + draft: emoji.draft, }; } @@ -61,6 +62,7 @@ export class EmojiEntityService { isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: emoji.draft, }; } diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index 563ac1d9d3..bd8d54ffc3 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -81,4 +81,10 @@ export class MiEmoji { array: true, length: 128, default: '{}', }) public roleIdsThatCanBeUsedThisEmojiAsReaction: string[]; + + @Column('boolean', { + default: false, + nullable: false, + }) + public draft: boolean; } diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 99a58f8773..90054cbc50 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -40,6 +40,10 @@ export const packedEmojiSimpleSchema = { format: 'id', }, }, + draft: { + type: 'boolean', + optional: false, nullable: true, + }, }, } as const; @@ -81,6 +85,10 @@ export const packedEmojiDetailedSchema = { type: 'string', optional: false, nullable: true, }, + draft: { + type: 'boolean', + optional: false, nullable: true, + }, isSensitive: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index a52af54a39..088596bba6 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -103,6 +103,7 @@ export class ImportCustomEmojisProcessorService { isSensitive: emojiInfo.isSensitive, localOnly: emojiInfo.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: [], + draft: false, }); } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index f834561456..35d44d3579 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -25,6 +25,7 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; @@ -375,6 +376,7 @@ const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; +const $admin_emoji_addDraft: Provider = { provide: 'ep:admin/emoji/add-draft', useClass: ep___admin_emoji_addDraft.default }; const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; @@ -729,6 +731,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, + $admin_emoji_addDraft, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, @@ -1077,6 +1080,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, + $admin_emoji_addDraft, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d12a035afa..202f449efc 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -25,6 +25,7 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; @@ -373,6 +374,7 @@ const eps = [ ['admin/drive/show-file', ep___admin_drive_showFile], ['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk], ['admin/emoji/add', ep___admin_emoji_add], + ['admin/emoji/add-draft', ep___admin_emoji_addDraft], ['admin/emoji/copy', ep___admin_emoji_copy], ['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk], ['admin/emoji/delete', ep___admin_emoji_delete], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts new file mode 100644 index 0000000000..ac5082091e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -0,0 +1,80 @@ +import { Inject, Injectable } from '@nestjs/common'; +import rndstr from 'rndstr'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canRequestCustomEmojis', + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, + category: { + type: 'string', + nullable: true, + description: 'Use `null` to reset the category.', + }, + aliases: { type: 'array', items: { + type: 'string', + } }, + license: { type: 'string', nullable: true }, + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['name', 'fileId'], +} as const; + +// TODO: ロジックをサービスに切り出す + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + private customEmojiService: CustomEmojiService, + + private moderationLogService: ModerationLogService, + ) { + super(meta, paramDef, async (ps, me) => { + const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + + const emoji = await this.customEmojiService.add({ + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + host: null, + draft: true, + }); + + this.moderationLogService.insertModerationLog(me, 'addEmoji', { + emojiId: emoji.id, + }); + + return { + id: emoji.id, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index faab8ee608..5e1b0e345c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -51,7 +51,7 @@ export const paramDef = { type: 'string', } }, }, - required: ['name', 'fileId'], + required: ['name','fileId', 'draft'], } as const; // TODO: ロジックをサービスに切り出す @@ -68,6 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.duplicateName); @@ -81,6 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, + draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 04226d8953..7519f2d33e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -55,6 +55,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, + draft: { type: 'boolean' }, }, required: ['id', 'name', 'aliases'], } as const; @@ -93,6 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: ps.draft, }, me); }); } diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 7c4f910559..f8b655e772 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -96,6 +96,10 @@ const emojiDb = computed(() => { const customEmojiDB: EmojiDef[] = []; for (const x of customEmojis.value) { + if (x.draft) { + continue; + } + customEmojiDB.push({ name: x.name, emoji: `:${x.name}:`, diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 7eff637482..25c4d028e6 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="category in customEmojiCategories" :key="`custom:${category}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(emoji => !emoji.draft).filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" @chosen="chosen" > {{ category || i18n.ts.other }} @@ -157,7 +157,7 @@ watch(q, () => { const searchCustom = () => { const max = 100; - const emojis = customEmojis.value; + const emojis = customEmojis.value.filter(emoji => !emoji.draft); const matches = new Set<Misskey.entities.CustomEmoji>(); const exactMatch = emojis.find(emoji => emoji.name === newQ); diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 3998df9efe..89bd092aa2 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -66,6 +66,7 @@ export const ROLE_POLICIES = [ 'inviteLimitCycle', 'inviteExpirationTime', 'canManageCustomEmojis', + 'canRequestCustomEmojis', 'canSearchNotes', 'canUseTranslator', 'canHideAds', diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index c8f56f1674..6fb691bc5e 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps"> <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> + <MkButton v-if="$i && (!$i.isModerator && !$i.policies.canManageCustomEmojis && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> <div class="query"> <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> @@ -22,21 +23,21 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFoldableSection v-if="searchEmojis"> <template #header>{{ i18n.ts.searchResult }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji"/> + <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> </MkFoldableSection> <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/> + <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> </MkFoldableSection> </div> </template> <script lang="ts" setup> -import { watch } from 'vue'; +import { watch, defineAsyncComponent } from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; @@ -44,6 +45,7 @@ import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js'; import { i18n } from '@/i18n.js'; +import * as os from '@/os'; import { $i } from '@/account.js'; const customEmojiTags = getCustomEmojiTags(); @@ -80,6 +82,24 @@ function toggleTag(tag) { } } +const edit = () => { + os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + emoji: { + name: '', + category: null, + aliases: [], + license: '', + url: '', + draft: true, + }, + isRequest: true, + }, { + done: result => { + window.location.reload(); + }, + }, 'closed'); +}; + watch($$(q), () => { search(); }); diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index ead2250af2..82658daf6a 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -259,6 +259,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix> + <span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 74de9f7396..e119c3b031 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -87,6 +87,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix>{{ policies.canRequestCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canRequestCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index bee73045b7..293a3f0f9c 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -30,13 +30,22 @@ SPDX-License-Identifier: AGPL-3.0-only <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> <div class="ldhfsamy"> - <button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> + <div v-for="emoji in items" :key="emoji.id"> + <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + </div> </div> </template> </MkPagination> @@ -57,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #default="{items}"> <div class="ldhfsamy"> <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" class="img" :alt="emoji.name"/> + <img :src="emoji.url" class="img" :alt="emoji.name"/> <div class="body"> <div class="name _monospace">{{ emoji.name }}</div> <div class="info">{{ emoji.host }}</div> @@ -148,6 +157,7 @@ const edit = (emoji) => { ...oldEmoji, ...result.updated, })); + emojisPaginationComponent.value.reload(); } else if (result.deleted) { emojisPaginationComponent.value.removeItem(emoji.id); } @@ -323,12 +333,13 @@ definePageMetadata(computed(() => ({ grid-gap: 12px; margin: var(--margin) 0; - > .emoji { + div > .emoji { display: flex; align-items: center; padding: 11px; text-align: left; border: solid 1px var(--panel); + width: 100%; &:hover { border-color: var(--inputBorderHover); @@ -410,4 +421,10 @@ definePageMetadata(computed(() => ({ } } } + +.emoji-draft { + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; +} </style> diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 2e6050490e..b241b6ed13 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" + :with-ok-button="true" @close="dialog.close()" @closed="$emit('closed')" > @@ -68,7 +69,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> <div :class="$style.footer"> <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> - </div> + <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> + {{ i18n.ts.draft }} + </MkSwitch> + <MkButton v-if="!isRequest" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> </div> </MkModalWindow> </template> @@ -87,9 +92,11 @@ import { customEmojiCategories } from '@/custom-emojis.js'; import MkSwitch from '@/components/MkSwitch.vue'; import { selectFile, selectFiles } from '@/scripts/select-file.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; +import { $i } from '@/account'; const props = defineProps<{ emoji?: any, + isRequest: boolean, }>(); let dialog = $ref(null); @@ -102,80 +109,141 @@ let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); +let chooseFile: DriveFile|null = $ref(null); +let draft = $ref(props.emoji.draft); +let isRequest = $ref(props.isRequest); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); +let draft = $ref(props.emoji.draft); +let isRequest = $ref(props.isRequest); const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, (ev: 'closed'): void }>(); -async function changeImage(ev) { - file = await selectFile(ev.currentTarget ?? ev.target, null); - const candidate = file.name.replace(/\.(.+)$/, ''); - if (candidate.match(/^[a-z0-9_]+$/)) { - name = candidate; +function ok() { + if (isRequest) { + if (chooseFile !== null && name.match(/^[a-zA-Z0-9_]+$/)) { + add(); + } + } else { + update(); } } +async function add() { + const ret = await os.api('admin/emoji/add-draft', { + name: name, + category: category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + fileId: chooseFile.id, + }); + + emit('done', { + updated: { + id: ret.id, + name, + category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + draft: true, + }, + }); + + dialog.close(); +} +async function changeImage(ev) { + file = await selectFile(ev.currentTarget ?? ev.target, null); + const candidate = file.name.replace(/\.(.+)$/, ''); + if (candidate.match(/^[a-z0-9_]+$/)) { + name = candidate; + } +} + async function addRole() { - const roles = await os.api('admin/roles/list'); - const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id); + const roles = await os.api('admin/roles/list'); + const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id); - const { canceled, result: role } = await os.select({ - items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), - }); - if (canceled) return; + const { canceled, result: role } = await os.select({ + items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), + }); + if (canceled) return; - rolesThatCanBeUsedThisEmojiAsReaction.push(role); + rolesThatCanBeUsedThisEmojiAsReaction.push(role); } async function removeRole(role, ev) { - rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); + rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); } - -async function done() { - const params = { +async function update() { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, name, - category: category === '' ? null : category, - aliases: aliases.split(' ').filter(x => x !== ''), + category, + aliases: aliases.split(' '), license: license === '' ? null : license, - isSensitive, - localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), - }; + fileId: chooseFile?.id, + draft: draft, + }); - if (file) { - params.fileId = file.id; - } - - if (props.emoji) { - await os.apiWithDialog('admin/emoji/update', { + emit('done', { + updated: { id: props.emoji.id, - ...params, - }); - - emit('done', { - updated: { - id: props.emoji.id, - ...params, - }, - }); + name, + category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + draft: draft, + }, + }); dialog.close(); - } else { - const created = await os.apiWithDialog('admin/emoji/add', params); - emit('done', { - created: created, - }); +} +async function done() { + const params = { + name, + category: category === '' ? null : category, + aliases: aliases.split(' ').filter(x => x !== ''), + license: license === '' ? null : license, + isSensitive, + localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), + }; - dialog.close(); - } + if (file) { + params.fileId = file.id; + } + + if (props.emoji) { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, + ...params, + }); + + emit('done', { + updated: { + id: props.emoji.id, + ...params, + }, + }); + + dialog.close(); + } else { + const created = await os.apiWithDialog('admin/emoji/add', params); + + emit('done', { + created: created, + }); + + dialog.close(); + } } async function del() { diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index 9aaa7890a9..fcc1fff104 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -4,7 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<button class="_button" :class="$style.root" @click="menu"> +<button v-if="emoji.draft" class="zuvgdzyu _button emoji-draft" @click="menu"> + <img :src="emoji.url" class="img" loading="lazy"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="info">{{ emoji.aliases.join(' ') }}</div> + </div> +</button> +<button v-else class="_button" :class="$style.root" @click="menu"> <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div :class="$style.body"> <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> @@ -25,6 +32,7 @@ const props = defineProps<{ aliases: string[]; category: string; url: string; + draft: boolean; }; }>(); @@ -91,4 +99,10 @@ function menu(ev) { text-overflow: ellipsis; overflow: hidden; } + +.emoji-draft { + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; +} </style> diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index aed242d8aa..89a5b795d8 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -311,6 +311,7 @@ export type CustomEmoji = { url: string; category: string; aliases: string[]; + draft: boolean; }; export type LiteInstanceMetadata = { From d607f343809fc98ad66627f1f5f8408929c32fed Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Fri, 19 May 2023 01:43:42 +0900 Subject: [PATCH 144/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=83=80?= =?UTF-8?q?=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E3=81=AE=E4=B8=8D=E5=85=B7?= =?UTF-8?q?=E5=90=88=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit c9d6c17353a663bf68cdea9bcef100f5de5edb68) --- locales/ja-JP.yml | 2 + .../api/endpoints/admin/emoji/add-draft.ts | 5 ++ packages/frontend/src/pages/about.emojis.vue | 8 --- .../frontend/src/pages/emoji-edit-dialog.vue | 58 ++++++++++++++----- 4 files changed, 50 insertions(+), 23 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index db2420c40d..367233c13c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -653,6 +653,8 @@ other: "その他" regenerateLoginToken: "ログイントークンを再生成" regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。" setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。" +emojiNameValidation: "名前には英数字と_が利用できます。" +isSensitive: "センシティブ" fileIdOrUrl: "ファイルIDまたはURL" behavior: "動作" sample: "サンプル" diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index ac5082091e..40b7a12e87 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -36,6 +36,8 @@ export const paramDef = { } }, license: { type: 'string', nullable: true }, fileId: { type: 'string', format: 'misskey:id' }, + isSensitive: { type: 'boolean' }, + localOnly: { type: 'boolean' }, }, required: ['name', 'fileId'], } as const; @@ -66,6 +68,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { license: ps.license ?? null, host: null, draft: true, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); this.moderationLogService.insertModerationLog(me, 'addEmoji', { diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 6fb691bc5e..a00d2e3219 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -84,14 +84,6 @@ function toggleTag(tag) { const edit = () => { os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { - emoji: { - name: '', - category: null, - aliases: [], - license: '', - url: '', - draft: true, - }, isRequest: true, }, { done: result => { diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index b241b6ed13..64be684db3 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -12,6 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="$emit('closed')" > <template v-if="emoji" #header>:{{ emoji.name }}:</template> + <template v-else-if="isRequest" #header>{{ i18n.ts.requestCustomEmojis }}</template> <template v-else #header>New emoji</template> <div> @@ -34,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton rounded style="margin: 0 auto;" @click="changeImage">{{ i18n.ts.selectFile }}</MkButton> <MkInput v-model="name" pattern="[a-z0-9_]"> <template #label>{{ i18n.ts.name }}</template> + <template #caption>{{ i18n.ts.emojiNameValidation }}</template> </MkInput> <MkInput v-model="category" :datalist="customEmojiCategories"> <template #label>{{ i18n.ts.category }}</template> @@ -45,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder> + <MkFolder v-if="!isRequest" > <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> @@ -62,13 +64,20 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo> </div> </MkFolder> - <MkSwitch v-model="isSensitive">isSensitive</MkSwitch> + <MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> + <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> + {{ i18n.ts.draft }} + </MkSwitch> <MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </MkSpacer> <div :class="$style.footer"> - <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> + <div :class="$style.footerButtons"> + <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-check"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-if="validation" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> + <MkButton v-else rounded style="margin: 0 auto;"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> + </div> <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> {{ i18n.ts.draft }} </MkSwitch> @@ -110,7 +119,7 @@ let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.rol let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); -let draft = $ref(props.emoji.draft); +let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { @@ -120,7 +129,9 @@ watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); let draft = $ref(props.emoji.draft); let isRequest = $ref(props.isRequest); - +const validation = computed(() => { + return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; +}) const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, (ev: 'closed'): void @@ -207,15 +218,16 @@ async function update() { } async function done() { - const params = { - name, - category: category === '' ? null : category, - aliases: aliases.split(' ').filter(x => x !== ''), - license: license === '' ? null : license, - isSensitive, - localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), - }; + const params = { + name, + category: category === '' ? null : category, + aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''), + license: license === '' ? null : license, + draft: draft, + isSensitive, + localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), + }; if (file) { params.fileId = file.id; @@ -236,7 +248,9 @@ async function done() { dialog.close(); } else { - const created = await os.apiWithDialog('admin/emoji/add', params); + const created = isRequest + ? await os.apiWithDialog('admin/emoji/add-draft', params) + : await os.apiWithDialog('admin/emoji/add', params); emit('done', { created: created, @@ -246,6 +260,13 @@ async function done() { } } +function chooseFileFrom(ev) { + selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { + chooseFile = files_[0]; + url = chooseFile.url; + }); +} + async function del() { const { canceled } = await os.confirm({ type: 'warning', @@ -308,4 +329,11 @@ async function del() { -webkit-backdrop-filter: var(--blur, blur(15px)); backdrop-filter: var(--blur, blur(15px)); } + +.footerButtons { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: center; +} </style> From b138752ccba157783052740c54be507d3b7e0014 Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Sun, 21 May 2023 08:15:36 +0900 Subject: [PATCH 145/501] =?UTF-8?q?fix:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F=E5=BE=8C=E3=81=AB?= =?UTF-8?q?=E7=94=BB=E5=83=8F=E3=81=AE=E3=83=AA=E3=83=B3=E3=82=AF=E3=81=8C?= =?UTF-8?q?=E3=82=A8=E3=83=A9=E3=83=BC=E3=81=AB=E3=81=AA=E3=82=8B=E4=B8=8D?= =?UTF-8?q?=E5=85=B7=E5=90=88=E3=81=AE=E9=9B=86=E8=83=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit d23cc54a11ef1a1fe1d06bda4cc1b15e29638ed7) --- packages/frontend/src/pages/custom-emojis-manager.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 293a3f0f9c..ecbdae9ed8 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -142,6 +142,7 @@ const add = async (ev: MouseEvent) => { done: result => { if (result.created) { emojisPaginationComponent.value.prepend(result.created); + emojisPaginationComponent.value.reload(); } }, }, 'closed'); From 51313e7ec2b4c551c02f1cfa640e32b522452cbb Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Sun, 21 May 2023 14:25:59 +0900 Subject: [PATCH 146/501] =?UTF-8?q?fix:=20=E3=83=89=E3=83=A9=E3=83=95?= =?UTF-8?q?=E3=83=88=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AF=E8=A9=B2?= =?UTF-8?q?=E5=BD=93=E3=81=99=E3=82=8B=E3=82=AD=E3=83=BC=E3=82=92=E5=85=A5?= =?UTF-8?q?=E5=8A=9B=E3=81=95=E3=82=8C=E3=81=A6=E3=82=82=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit f332fbc47eabc373e292076078ad673d72e6a5c9) --- packages/frontend/src/components/global/MkCustomEmoji.vue | 3 ++- packages/frontend/src/custom-emojis.ts | 7 ++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index 063b122f8b..c77fb4c098 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<span v-if="errored">:{{ customEmojiName }}:</span> +<span v-if="errored || isDraft">:{{ customEmojiName }}:</span> <img v-else :class="[$style.root, { [$style.normal]: normal, [$style.noStyle]: noStyle }]" :src="url" :alt="alt" :title="alt" decoding="async" @error="errored = true" @load="errored = false"/> </template> @@ -25,6 +25,7 @@ const props = defineProps<{ const customEmojiName = computed(() => (props.name[0] === ':' ? props.name.substring(1, props.name.length - 1) : props.name).replace('@.', '')); const isLocal = computed(() => !props.host && (customEmojiName.value.endsWith('@.') || !customEmojiName.value.includes('@'))); +const isDraft = computed(() => customEmojisNameMap.value.get(customEmojiName.value)?.draft ?? false); const rawUrl = computed(() => { if (props.url) { diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts index 8ecd1bd2eb..9b4b56b7de 100644 --- a/packages/frontend/src/custom-emojis.ts +++ b/packages/frontend/src/custom-emojis.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { shallowRef, computed, markRaw, watch } from 'vue'; +import { shallowRef, computed, markRaw, triggerRef,watch } from 'vue'; import * as Misskey from 'misskey-js'; import { api, apiGet } from '@/os.js'; import { useStream } from '@/stream.js'; @@ -11,6 +11,7 @@ import { get, set } from '@/scripts/idb-proxy.js'; const storageCache = await get('emojis'); export const customEmojis = shallowRef<Misskey.entities.CustomEmoji[]>(Array.isArray(storageCache) ? storageCache : []); +export const customEmojisNameMap = computed(() => new Map(customEmojis.value.map(item => [item.name, item]))); export const customEmojiCategories = computed<[ ...string[], null ]>(() => { const categories = new Set<string>(); for (const emoji of customEmojis.value) { @@ -34,16 +35,19 @@ const stream = useStream(); stream.on('emojiAdded', emojiData => { customEmojis.value = [emojiData.emoji, ...customEmojis.value]; + triggerRef(customEmojis); set('emojis', customEmojis.value); }); stream.on('emojiUpdated', emojiData => { customEmojis.value = customEmojis.value.map(item => emojiData.emojis.find(search => search.name === item.name) as Misskey.entities.CustomEmoji ?? item); + triggerRef(customEmojis); set('emojis', customEmojis.value); }); stream.on('emojiDeleted', emojiData => { customEmojis.value = customEmojis.value.filter(item => !emojiData.emojis.some(search => search.name === item.name)); + triggerRef(customEmojis); set('emojis', customEmojis.value); }); @@ -60,6 +64,7 @@ export async function fetchCustomEmojis(force = false) { } customEmojis.value = res.emojis; + triggerRef(customEmojis); set('emojis', res.emojis); set('lastEmojisFetchedAt', now); } From bf9089209d22fd4a18a652f3c50c44be78219db3 Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Fri, 11 Aug 2023 19:04:15 +0900 Subject: [PATCH 147/501] =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E4=B8=80=E8=A6=A7=E3=82=92=E4=B8=80?= =?UTF-8?q?=E8=A6=A7=E3=80=81=E6=96=B0=E7=9D=80=E3=80=81=E7=94=B3=E8=AB=8B?= =?UTF-8?q?=E4=B8=AD=E3=81=AB=E5=88=86=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 5c464cd1bc177f0276397a686160c5c77e774d10) --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/pages/about.emojis.vue | 87 ++++++++++++++------ packages/frontend/src/pages/about.vue | 4 +- 4 files changed, 65 insertions(+), 28 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 504df64250..7c440d9119 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -824,6 +824,7 @@ export interface Locale { "high": string; "middle": string; "low": string; + "list": string; "emailNotConfiguredWarning": string; "ratio": string; "previewNoteText": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 367233c13c..dcf413be13 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -823,6 +823,7 @@ priority: "優先度" high: "高" middle: "中" low: "低" +list: "一覧" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" previewNoteText: "本文をプレビュー" diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index a00d2e3219..805662065c 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -4,51 +4,88 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps"> - <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> - <MkButton v-if="$i && (!$i.isModerator && !$i.policies.canManageCustomEmojis && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> +<MkStickyContainer> + <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> + <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> + <MkButton v-if="$i && (!$i.isModerator && !$i.policies.canManageCustomEmojis && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> - <div class="query"> - <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> - <template #prefix><i class="ti ti-search"></i></template> - </MkInput> + <div class="query" style="margin-top: 10px;"> + <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> - <!-- たくさんあると邪魔 - <div class="tags"> - <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> + <!-- たくさんあると邪魔 + <div class="tags"> + <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> + </div> + --> </div> - --> - </div> - <MkFoldableSection v-if="searchEmojis"> - <template #header>{{ i18n.ts.searchResult }}</template> + <MkFoldableSection v-if="searchEmojis"> + <template #header>{{ i18n.ts.searchResult }}</template> + <div :class="$style.emojis"> + <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + </div> + </MkFoldableSection> + + <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> + <template #header>{{ category || i18n.ts.other }}</template> + <div :class="$style.emojis"> + <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + </div> + </MkFoldableSection> + </MkSpacer> + <MkSpacer v-if="tab === 'new'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> - <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in newEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> - </MkFoldableSection> - - <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> - <template #header>{{ category || i18n.ts.other }}</template> + </MkSpacer> + <MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> - <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> - </MkFoldableSection> -</div> + </MkSpacer> +</MkStickyContainer> </template> <script lang="ts" setup> -import { watch, defineAsyncComponent } from 'vue'; +import { watch, defineAsyncComponent, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js'; +import { customEmojis, customEmojiCategories } from '@/custom-emojis.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os'; import { $i } from '@/account.js'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +let tab = $ref('emojis'); +const headerActions = $computed(() => []); + +const headerTabs = $computed(() => [{ + key: 'emojis', + title: i18n.ts.list, +}, { + key: 'new', + title: i18n.ts.newEmojis, +}, { + key: 'draft', + title: i18n.ts.draftEmojis, +}]); + +definePageMetadata(ref({})); + +const pagination = { + endpoint: 'admin/emoji/list' as const, + limit: 30, + params: computed(() => ({ + query: (query.value && query.value !== '') ? query.value : null, + })), +}; -const customEmojiTags = getCustomEmojiTags(); let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index ee4043f9a5..d889d989e2 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -89,9 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> </div> </MkSpacer> - <MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> - <XEmojis/> - </MkSpacer> + <XEmojis v-else-if="tab === 'emojis'"/> <MkSpacer v-else-if="tab === 'federation'" :contentMax="1000" :marginMin="20"> <XFederation/> </MkSpacer> From 21bee2ffde1dc0e2512c9cb8179060caf7747d8d Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Fri, 11 Aug 2023 23:02:54 +0900 Subject: [PATCH 148/501] =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=83=AA=E3=82=AF=E3=82=A8?= =?UTF-8?q?=E3=82=B9=E3=83=88=E6=89=BF=E8=AA=8D=E7=94=A8=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 94451c380458b459791fc2bb6864a3255aca03c4) --- locales/index.d.ts | 2 + locales/ja-JP.yml | 2 + .../server/api/endpoints/admin/emoji/list.ts | 9 + .../src/pages/custom-emojis-manager.vue | 223 +++++++++++++++++- 4 files changed, 234 insertions(+), 2 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 7c440d9119..60b44e352b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -260,6 +260,7 @@ export interface Locale { "removed": string; "removeAreYouSure": string; "deleteAreYouSure": string; + "undraftAreYouSure": string; "resetAreYouSure": string; "saved": string; "messaging": string; @@ -1023,6 +1024,7 @@ export interface Locale { "notesSearchNotAvailable": string; "license": string; "draft": string; + "undrafted": string; "unfavoriteConfirm": string; "myClips": string; "drivecleaner": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index dcf413be13..4d35a0263c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -257,6 +257,7 @@ remove: "削除" removed: "削除しました" removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" +undraftAreYouSure: "「{x}」をドラフト解除しますか?" resetAreYouSure: "リセットしますか?" saved: "保存しました" messaging: "チャット" @@ -1022,6 +1023,7 @@ sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キ notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" draft: "ドラフト" +undrafted: "ドラフト解除" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" drivecleaner: "ドライブクリーナー" diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts index ab16d86a3d..8fba829c5e 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list.ts @@ -64,6 +64,7 @@ export const paramDef = { type: 'object', properties: { query: { type: 'string', nullable: true, default: null }, + draft: { type: 'boolean', nullable: true, default: null }, limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, sinceId: { type: 'string', format: 'misskey:id' }, untilId: { type: 'string', format: 'misskey:id' }, @@ -86,6 +87,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- let emojis: MiEmoji[]; + if (ps.draft !== null) { + if (ps.draft) { + q.andWhere('emoji.draft = TRUE'); + } else { + q.andWhere('emoji.draft = FALSE'); + } + } + if (ps.query) { //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); //const emojis = await q.limit(ps.limit).getMany(); diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index ecbdae9ed8..5dcd2009ed 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -51,6 +51,41 @@ SPDX-License-Identifier: AGPL-3.0-only </MkPagination> </div> + <div v-if="tab === 'draft'" class="draft"> + <MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <template v-for="emoji in items" :key="emoji.id"> + <div class="emoji _panel"> + <div class="img"> + <div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> + <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> + </div> + <div class="info"> + <div class="name _monospace">{{ i18n.ts.name }}: {{ emoji.name }}</div> + <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> + <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> + <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> + </div> + <div class="edit-button"> + <button class="edit _button" @click="editDraft(emoji)"> + {{ i18n.ts.edit }} + </button> + <button class="draft _button" @click="undrafted(emoji)"> + {{ i18n.ts.undrafted }} + </button> + <button class="delete _button" @click="deleteDraft(emoji)"> + {{ i18n.ts.delete }} + </button> + </div> + </div> + </template> + </div> + </template> + </MkPagination> + </div> + <div v-else-if="tab === 'remote'" class="remote"> <FormSplit> <MkInput v-model="queryRemote" :debounce="true" type="search"> @@ -89,14 +124,15 @@ import MkInput from '@/components/MkInput.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import FormSplit from '@/components/form/split.vue'; -import { selectFile, selectFiles } from '@/scripts/select-file.js'; +import { selectFile } from '@/scripts/select-file.js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); +const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); -const tab = ref('local'); +const tab = ref('draft'); const query = ref(null); const queryRemote = ref(null); const host = ref(null); @@ -111,6 +147,15 @@ const pagination = { })), }; +const paginationDraft = { + endpoint: 'admin/emoji/list' as const, + limit: 30, + params: computed(() => ({ + query: (query.value && query.value !== '') ? query.value : null, + draft: true, + })), +}; + const remotePagination = { endpoint: 'admin/emoji/list-remote' as const, limit: 30, @@ -166,6 +211,61 @@ const edit = (emoji) => { }, 'closed'); }; +const editDraft = (emoji) => { + os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + emoji: emoji, + isRequest: false, + }, { + done: result => { + if (result.updated) { + emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + ...oldEmoji, + ...result.updated, + })); + emojisDraftPaginationComponent.value.reload(); + } else if (result.deleted) { + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + } + }, + }, 'closed'); +}; + +async function undrafted(emoji) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('undraftAreYouSure', { x: emoji.name }), + }); + if (canceled) return; + + await os.api('admin/emoji/update', { + id: emoji.id, + name: emoji.name, + category: emoji.category, + aliases: emoji.aliases, + license: emoji.license, + draft: false, + isSensitive: emoji.isSensitive, + localOnly: emoji.localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + }); + + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); +} + +async function deleteDraft(emoji) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: emoji.name }), + }); + if (canceled) return; + + os.api('admin/emoji/delete', { + id: emoji.id, + }).then(() => { + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + }); +} + const im = (emoji) => { os.apiWithDialog('admin/emoji/copy', { emojiId: emoji.id, @@ -308,6 +408,9 @@ const headerActions = $computed(() => [{ }]); const headerTabs = $computed(() => [{ + key: 'draft', + title: i18n.ts.draftEmojis, +}, { key: 'local', title: i18n.ts.local, }, { @@ -374,6 +477,122 @@ definePageMetadata(computed(() => ({ } } } + > .draft { + .empty { + margin: var(--margin); + } + + .ldhfsamy { + > .emoji { + display: grid; + grid-template-rows: 40px 1fr; + grid-template-columns: 1fr 150px; + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + width: 100%; + margin: 10px; + + > .img { + display: grid; + grid-row: 1; + grid-column: 1/ span 2; + grid-template-columns: 50% 50%; + place-content: center; + place-items: center; + + > .imgLight { + display: grid; + grid-column: 1; + background-color: #fff; + + > img { + max-height: 30px; + max-width: 100%; + } + } + + > .imgDark { + display: grid; + grid-column: 2; + background-color: #000; + + > img { + max-height: 30px; + max-width: 100%; + } + } + } + + > .info { + display: grid; + grid-row: 2; + grid-template-rows: 30px 30px 30px; + + > .name { + grid-row: 1; + text-overflow: ellipsis; + overflow: hidden; + } + + > .category { + grid-row: 2; + text-overflow: ellipsis; + overflow: hidden; + } + + > .aliases { + grid-row: 3; + text-overflow: ellipsis; + overflow: hidden; + } + + > .license { + grid-row: 4; + text-overflow: ellipsis; + overflow: hidden; + } + } + + > .edit-button { + display: grid; + grid-row: 2; + grid-template-rows: 30px 30px 30px; + + > .edit { + grid-row: 1; + background-color: var(--buttonBg); + margin: 2px; + + &:hover { + color: var(--accent); + } + } + + > .draft { + grid-row: 2; + background-color: var(--buttonBg); + margin: 2px; + + &:hover { + color: var(--accent); + } + } + + > .delete { + background-color: var(--buttonBg); + grid-row: 3; + margin: 2px; + + &:hover { + color: var(--accent); + } + } + } + } + } + } > .remote { .empty { From 4c7cd47fb6e7b3d7e9db02343e3de9e8ab8a0c0f Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Fri, 18 Aug 2023 21:41:33 +0900 Subject: [PATCH 149/501] =?UTF-8?q?fix:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E7=94=BB=E9=9D=A2=E3=81=AE=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E4=B8=8D=E5=85=B7=E5=90=88=E3=81=AE=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 5300ff763bb008699b35dc6e219b50ac1c4fd7af) --- .../src/components/MKCustomEmojiEditDraft.vue | 229 +++++++ .../src/components/MkCustomEmojiEditLocal.vue | 225 +++++++ .../components/MkCustomEmojiEditRemote.vue | 110 ++++ .../MkEmojiEditDialog.vue} | 0 packages/frontend/src/pages/about.emojis.vue | 2 +- .../src/pages/custom-emojis-manager.vue | 562 +----------------- 6 files changed, 582 insertions(+), 546 deletions(-) create mode 100644 packages/frontend/src/components/MKCustomEmojiEditDraft.vue create mode 100644 packages/frontend/src/components/MkCustomEmojiEditLocal.vue create mode 100644 packages/frontend/src/components/MkCustomEmojiEditRemote.vue rename packages/frontend/src/{pages/emoji-edit-dialog.vue => components/MkEmojiEditDialog.vue} (100%) diff --git a/packages/frontend/src/components/MKCustomEmojiEditDraft.vue b/packages/frontend/src/components/MKCustomEmojiEditDraft.vue new file mode 100644 index 0000000000..17576b7b06 --- /dev/null +++ b/packages/frontend/src/components/MKCustomEmojiEditDraft.vue @@ -0,0 +1,229 @@ +<template> +<MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <template v-for="emoji in items" :key="emoji.id"> + <div class="emoji _panel"> + <div class="img"> + <div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> + <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> + </div> + <div class="info"> + <div class="name _monospace">{{ i18n.ts.name }}: {{ emoji.name }}</div> + <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> + <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> + <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> + </div> + <div class="edit-button"> + <button class="edit _button" @click="editDraft(emoji)"> + {{ i18n.ts.edit }} + </button> + <button class="draft _button" @click="undrafted(emoji)"> + {{ i18n.ts.undrafted }} + </button> + <button class="delete _button" @click="deleteDraft(emoji)"> + {{ i18n.ts.delete }} + </button> + </div> + </div> + </template> + </div> + </template> +</MkPagination> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; +import MkPagination from '@/components/MkPagination.vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; + +const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); + +const query = ref(null); + +const paginationDraft = { + endpoint: 'admin/emoji/list' as const, + limit: 30, + params: computed(() => ({ + query: (query.value && query.value !== '') ? query.value : null, + draft: true, + })), +}; + +const editDraft = (emoji) => { + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { + emoji: emoji, + isRequest: false, + }, { + done: result => { + if (result.updated) { + emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + ...oldEmoji, + ...result.updated, + })); + emojisDraftPaginationComponent.value.reload(); + } else if (result.deleted) { + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisDraftPaginationComponent.value.reload(); + } + }, + }, 'closed'); +}; + +async function undrafted(emoji) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('undraftAreYouSure', { x: emoji.name }), + }); + if (canceled) return; + + await os.api('admin/emoji/update', { + id: emoji.id, + name: emoji.name, + category: emoji.category, + aliases: emoji.aliases, + license: emoji.license, + draft: false, + isSensitive: emoji.isSensitive, + localOnly: emoji.localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + }); + + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisDraftPaginationComponent.value.reload(); +} + +async function deleteDraft(emoji) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: emoji.name }), + }); + if (canceled) return; + + os.api('admin/emoji/delete', { + id: emoji.id, + }).then(() => { + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisDraftPaginationComponent.value.reload(); + }); +} +</script> + +<style lang="scss" scoped> +.empty { + margin: var(--margin); +} + +.ldhfsamy { + > .emoji { + display: grid; + grid-template-rows: 40px 1fr; + grid-template-columns: 1fr 150px; + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + width: 100%; + margin: 10px; + + > .img { + display: grid; + grid-row: 1; + grid-column: 1/ span 2; + grid-template-columns: 50% 50%; + place-content: center; + place-items: center; + + > .imgLight { + display: grid; + grid-column: 1; + background-color: #fff; + + > img { + max-height: 30px; + max-width: 100%; + } + } + + > .imgDark { + display: grid; + grid-column: 2; + background-color: #000; + + > img { + max-height: 30px; + max-width: 100%; + } + } + } + + > .info { + display: grid; + grid-row: 2; + grid-template-rows: 30px 30px 30px; + + > .name { + grid-row: 1; + text-overflow: ellipsis; + overflow: hidden; + } + + > .category { + grid-row: 2; + text-overflow: ellipsis; + overflow: hidden; + } + + > .aliases { + grid-row: 3; + text-overflow: ellipsis; + overflow: hidden; + } + + > .license { + grid-row: 4; + text-overflow: ellipsis; + overflow: hidden; + } + } + + > .edit-button { + display: grid; + grid-row: 2; + grid-template-rows: 30px 30px 30px; + + > .edit { + grid-row: 1; + background-color: var(--buttonBg); + margin: 2px; + + &:hover { + color: var(--accent); + } + } + + > .draft { + grid-row: 2; + background-color: var(--buttonBg); + margin: 2px; + + &:hover { + color: var(--accent); + } + } + + > .delete { + background-color: var(--buttonBg); + grid-row: 3; + margin: 2px; + + &:hover { + color: var(--accent); + } + } + } + } +} +</style> diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue new file mode 100644 index 0000000000..7112a38430 --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -0,0 +1,225 @@ +<template> +<MkInput v-model="query" :debounce="true" type="search"> + <template #prefix><i class="ti ti-search"></i></template> + <template #label>{{ i18n.ts.search }}</template> +</MkInput> +<MkSwitch v-model="selectMode" style="margin: 8px 0;"> + <template #label>Select mode</template> +</MkSwitch> +<div v-if="selectMode" class="_buttons"> + <MkButton inline @click="selectAll">Select all</MkButton> + <MkButton inline @click="setCategoryBulk">Set category</MkButton> + <MkButton inline @click="setTagBulk">Set tag</MkButton> + <MkButton inline @click="addTagBulk">Add tag</MkButton> + <MkButton inline @click="removeTagBulk">Remove tag</MkButton> + <MkButton inline @click="setLisenceBulk">Set Lisence</MkButton> + <MkButton inline danger @click="delBulk">Delete</MkButton> +</div> +<MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <div v-for="emoji in items" :key="emoji.id"> + <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + </div> + </div> + </template> +</MkPagination> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; +import MkButton from '@/components/MkButton.vue'; +import MkInput from '@/components/MkInput.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; + +const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); + +const query = ref(null); +const selectMode = ref(false); +const selectedEmojis = ref<string[]>([]); + +const pagination = { + endpoint: 'admin/emoji/list' as const, + limit: 30, + params: computed(() => ({ + query: (query.value && query.value !== '') ? query.value : null, + })), +}; + +const selectAll = () => { + if (selectedEmojis.value.length > 0) { + selectedEmojis.value = []; + } else { + selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id); + } +}; + +const toggleSelect = (emoji) => { + if (selectedEmojis.value.includes(emoji.id)) { + selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id); + } else { + selectedEmojis.value.push(emoji.id); + } +}; + +const edit = (emoji) => { + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { + emoji: emoji, + isRequest: false, + }, { + done: result => { + if (result.updated) { + emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + ...oldEmoji, + ...result.updated, + })); + emojisPaginationComponent.value.reload(); + } else if (result.deleted) { + emojisPaginationComponent.value.removeItem((item) => item.id === emoji.id); + } + }, + }, 'closed'); +}; + +const setCategoryBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Category', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-category-bulk', { + ids: selectedEmojis.value, + category: result, + }); + emojisPaginationComponent.value.reload(); +}; + +const setLisenceBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'License', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-license-bulk', { + ids: selectedEmojis.value, + license: result, + }); + emojisPaginationComponent.value.reload(); +}; + +const addTagBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Tag', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/add-aliases-bulk', { + ids: selectedEmojis.value, + aliases: result.split(' '), + }); + emojisPaginationComponent.value.reload(); +}; + +const removeTagBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Tag', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/remove-aliases-bulk', { + ids: selectedEmojis.value, + aliases: result.split(' '), + }); + emojisPaginationComponent.value.reload(); +}; + +const setTagBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Tag', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-aliases-bulk', { + ids: selectedEmojis.value, + aliases: result.split(' '), + }); + emojisPaginationComponent.value.reload(); +}; + +const delBulk = async () => { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.deleteConfirm, + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/delete-bulk', { + ids: selectedEmojis.value, + }); + emojisPaginationComponent.value.reload(); +}; +</script> + +<style lang="scss" scoped> +.ldhfsamy { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: var(--margin); + + div > .emoji { + display: flex; + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + width: 100%; + + &:hover { + border-color: var(--inputBorderHover); + } + + &.selected { + border-color: var(--accent); + } + + > .img { + width: 42px; + height: 42px; + } + + > .body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; + + > .name { + text-overflow: ellipsis; + overflow: hidden; + } + + > .info { + opacity: 0.5; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} + +.emoji-draft { + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; +} +</style> diff --git a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue new file mode 100644 index 0000000000..26c8dd66ac --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue @@ -0,0 +1,110 @@ +<template> +<FormSplit> + <MkInput v-model="queryRemote" :debounce="true" type="search"> + <template #prefix><i class="ti ti-search"></i></template> + <template #label>{{ i18n.ts.search }}</template> + </MkInput> + <MkInput v-model="host" :debounce="true"> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> +</FormSplit> +<MkPagination :pagination="remotePagination" :displayLimit="100"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.host }}</div> + </div> + </div> + </div> + </template> +</MkPagination> +</template> + +<script lang="ts" setup> +import { computed, ref } from 'vue'; +import MkInput from '@/components/MkInput.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import FormSplit from '@/components/form/split.vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; + +const queryRemote = ref(null); +const host = ref(null); + +const remotePagination = { + endpoint: 'admin/emoji/list-remote' as const, + limit: 30, + params: computed(() => ({ + query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null, + host: (host.value && host.value !== '') ? host.value : null, + })), +}; + +const im = (emoji) => { + os.apiWithDialog('admin/emoji/copy', { + emojiId: emoji.id, + }); +}; + +const remoteMenu = (emoji, ev: MouseEvent) => { + os.popupMenu([{ + type: 'label', + text: ':' + emoji.name + ':', + }, { + text: i18n.ts.import, + icon: 'ti ti-plus', + action: () => { im(emoji); }, + }], ev.currentTarget ?? ev.target); +}; +</script> + +<style lang="scss" scoped> +.empty { + margin: var(--margin); +} + +.ldhfsamy { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: 12px; + margin: var(--margin) 0; + + > .emoji { + display: flex; + align-items: center; + padding: 12px; + text-align: left; + + &:hover { + color: var(--accent); + } + + > .img { + width: 32px; + height: 32px; + } + + > .body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; + + > .name { + text-overflow: ellipsis; + overflow: hidden; + } + + > .info { + opacity: 0.5; + font-size: 90%; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} +</style> diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue similarity index 100% rename from packages/frontend/src/pages/emoji-edit-dialog.vue rename to packages/frontend/src/components/MkEmojiEditDialog.vue diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 805662065c..a49a70c88a 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -120,7 +120,7 @@ function toggleTag(tag) { } const edit = () => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { isRequest: true, }, { done: result => { diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 5dcd2009ed..6e55ffb945 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -10,106 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="900"> <div class="ogwlenmc"> <div v-if="tab === 'local'" class="local"> - <MkInput v-model="query" :debounce="true" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.search }}</template> - </MkInput> - <MkSwitch v-model="selectMode" style="margin: 8px 0;"> - <template #label>Select mode</template> - </MkSwitch> - <div v-if="selectMode" class="_buttons"> - <MkButton inline @click="selectAll">Select all</MkButton> - <MkButton inline @click="setCategoryBulk">Set category</MkButton> - <MkButton inline @click="setTagBulk">Set tag</MkButton> - <MkButton inline @click="addTagBulk">Add tag</MkButton> - <MkButton inline @click="removeTagBulk">Remove tag</MkButton> - <MkButton inline @click="setLicenseBulk">Set License</MkButton> - <MkButton inline danger @click="delBulk">Delete</MkButton> - </div> - <MkPagination ref="emojisPaginationComponent" :pagination="pagination"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <div v-for="emoji in items" :key="emoji.id"> - <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> - <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> - </div> - </div> - </template> - </MkPagination> + <MkCustomEmojiEditLocal/> </div> - <div v-if="tab === 'draft'" class="draft"> - <MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <template v-for="emoji in items" :key="emoji.id"> - <div class="emoji _panel"> - <div class="img"> - <div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> - <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> - </div> - <div class="info"> - <div class="name _monospace">{{ i18n.ts.name }}: {{ emoji.name }}</div> - <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> - <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> - <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> - </div> - <div class="edit-button"> - <button class="edit _button" @click="editDraft(emoji)"> - {{ i18n.ts.edit }} - </button> - <button class="draft _button" @click="undrafted(emoji)"> - {{ i18n.ts.undrafted }} - </button> - <button class="delete _button" @click="deleteDraft(emoji)"> - {{ i18n.ts.delete }} - </button> - </div> - </div> - </template> - </div> - </template> - </MkPagination> + <MkCustomEmojiEditDraft/> </div> - <div v-else-if="tab === 'remote'" class="remote"> - <FormSplit> - <MkInput v-model="queryRemote" :debounce="true" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.search }}</template> - </MkInput> - <MkInput v-model="host" :debounce="true"> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </FormSplit> - <MkPagination :pagination="remotePagination"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.host }}</div> - </div> - </div> - </div> - </template> - </MkPagination> + <MkCustomEmojiEditRemote/> </div> </div> </MkSpacer> @@ -118,171 +25,30 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; -import MkButton from '@/components/MkButton.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkPagination from '@/components/MkPagination.vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import FormSplit from '@/components/form/split.vue'; -import { selectFile } from '@/scripts/select-file.js'; -import * as os from '@/os.js'; -import { i18n } from '@/i18n.js'; -import { definePageMetadata } from '@/scripts/page-metadata.js'; - -const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); -const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); +import { computed, defineAsyncComponent, ref } from 'vue'; +import MkCustomEmojiEditDraft from '@/components/MkCustomEmojiEditDraft.vue'; +import MkCustomEmojiEditLocal from '@/components/MkCustomEmojiEditLocal.vue'; +import MkCustomEmojiEditRemote from '@/components/MkCustomEmojiEditRemote.vue'; +import { selectFile } from '@/scripts/select-file'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; const tab = ref('draft'); -const query = ref(null); -const queryRemote = ref(null); -const host = ref(null); -const selectMode = ref(false); -const selectedEmojis = ref<string[]>([]); - -const pagination = { - endpoint: 'admin/emoji/list' as const, - limit: 30, - params: computed(() => ({ - query: (query.value && query.value !== '') ? query.value : null, - })), -}; - -const paginationDraft = { - endpoint: 'admin/emoji/list' as const, - limit: 30, - params: computed(() => ({ - query: (query.value && query.value !== '') ? query.value : null, - draft: true, - })), -}; - -const remotePagination = { - endpoint: 'admin/emoji/list-remote' as const, - limit: 30, - params: computed(() => ({ - query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null, - host: (host.value && host.value !== '') ? host.value : null, - })), -}; - -const selectAll = () => { - if (selectedEmojis.value.length > 0) { - selectedEmojis.value = []; - } else { - selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); - } -}; - -const toggleSelect = (emoji) => { - if (selectedEmojis.value.includes(emoji.id)) { - selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id); - } else { - selectedEmojis.value.push(emoji.id); - } -}; const add = async (ev: MouseEvent) => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { }, { done: result => { - if (result.created) { - emojisPaginationComponent.value.prepend(result.created); - emojisPaginationComponent.value.reload(); - } + //TODO: emitにして追加を反映 + // if (result.created) { + // emojisPaginationComponent.value.prepend(result.created); + // emojisPaginationComponent.value.reload(); + // } }, }, 'closed'); }; -const edit = (emoji) => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { - emoji: emoji, - }, { - done: result => { - if (result.updated) { - emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ - ...oldEmoji, - ...result.updated, - })); - emojisPaginationComponent.value.reload(); - } else if (result.deleted) { - emojisPaginationComponent.value.removeItem(emoji.id); - } - }, - }, 'closed'); -}; - -const editDraft = (emoji) => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { - emoji: emoji, - isRequest: false, - }, { - done: result => { - if (result.updated) { - emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ - ...oldEmoji, - ...result.updated, - })); - emojisDraftPaginationComponent.value.reload(); - } else if (result.deleted) { - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - } - }, - }, 'closed'); -}; - -async function undrafted(emoji) { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.t('undraftAreYouSure', { x: emoji.name }), - }); - if (canceled) return; - - await os.api('admin/emoji/update', { - id: emoji.id, - name: emoji.name, - category: emoji.category, - aliases: emoji.aliases, - license: emoji.license, - draft: false, - isSensitive: emoji.isSensitive, - localOnly: emoji.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, - }); - - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); -} - -async function deleteDraft(emoji) { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.t('removeAreYouSure', { x: emoji.name }), - }); - if (canceled) return; - - os.api('admin/emoji/delete', { - id: emoji.id, - }).then(() => { - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - }); -} - -const im = (emoji) => { - os.apiWithDialog('admin/emoji/copy', { - emojiId: emoji.id, - }); -}; - -const remoteMenu = (emoji, ev: MouseEvent) => { - os.popupMenu([{ - type: 'label', - text: ':' + emoji.name + ':', - }, { - text: i18n.ts.import, - icon: 'ti ti-plus', - action: () => { im(emoji); }, - }], ev.currentTarget ?? ev.target); -}; - const menu = (ev: MouseEvent) => { os.popupMenu([{ icon: 'ti ti-download', @@ -325,78 +91,6 @@ const menu = (ev: MouseEvent) => { }], ev.currentTarget ?? ev.target); }; -const setCategoryBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Category', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-category-bulk', { - ids: selectedEmojis.value, - category: result, - }); - emojisPaginationComponent.value.reload(); -}; - -const setLicenseBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'License', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-license-bulk', { - ids: selectedEmojis.value, - license: result, - }); - emojisPaginationComponent.value.reload(); -}; - -const addTagBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Tag', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/add-aliases-bulk', { - ids: selectedEmojis.value, - aliases: result.split(' '), - }); - emojisPaginationComponent.value.reload(); -}; - -const removeTagBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Tag', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/remove-aliases-bulk', { - ids: selectedEmojis.value, - aliases: result.split(' '), - }); - emojisPaginationComponent.value.reload(); -}; - -const setTagBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Tag', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-aliases-bulk', { - ids: selectedEmojis.value, - aliases: result.split(' '), - }); - emojisPaginationComponent.value.reload(); -}; - -const delBulk = async () => { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.ts.deleteConfirm, - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/delete-bulk', { - ids: selectedEmojis.value, - }); - emojisPaginationComponent.value.reload(); -}; - const headerActions = $computed(() => [{ asFullButton: true, icon: 'ti ti-plus', @@ -425,226 +119,4 @@ definePageMetadata(computed(() => ({ </script> <style lang="scss" scoped> -.ogwlenmc { - > .local { - .empty { - margin: var(--margin); - } - - .ldhfsamy { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: 12px; - margin: var(--margin) 0; - - div > .emoji { - display: flex; - align-items: center; - padding: 11px; - text-align: left; - border: solid 1px var(--panel); - width: 100%; - - &:hover { - border-color: var(--inputBorderHover); - } - - &.selected { - border-color: var(--accent); - } - - > .img { - width: 42px; - height: 42px; - } - - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; - - > .name { - text-overflow: ellipsis; - overflow: hidden; - } - - > .info { - opacity: 0.5; - text-overflow: ellipsis; - overflow: hidden; - } - } - } - } - } - > .draft { - .empty { - margin: var(--margin); - } - - .ldhfsamy { - > .emoji { - display: grid; - grid-template-rows: 40px 1fr; - grid-template-columns: 1fr 150px; - align-items: center; - padding: 11px; - text-align: left; - border: solid 1px var(--panel); - width: 100%; - margin: 10px; - - > .img { - display: grid; - grid-row: 1; - grid-column: 1/ span 2; - grid-template-columns: 50% 50%; - place-content: center; - place-items: center; - - > .imgLight { - display: grid; - grid-column: 1; - background-color: #fff; - - > img { - max-height: 30px; - max-width: 100%; - } - } - - > .imgDark { - display: grid; - grid-column: 2; - background-color: #000; - - > img { - max-height: 30px; - max-width: 100%; - } - } - } - - > .info { - display: grid; - grid-row: 2; - grid-template-rows: 30px 30px 30px; - - > .name { - grid-row: 1; - text-overflow: ellipsis; - overflow: hidden; - } - - > .category { - grid-row: 2; - text-overflow: ellipsis; - overflow: hidden; - } - - > .aliases { - grid-row: 3; - text-overflow: ellipsis; - overflow: hidden; - } - - > .license { - grid-row: 4; - text-overflow: ellipsis; - overflow: hidden; - } - } - - > .edit-button { - display: grid; - grid-row: 2; - grid-template-rows: 30px 30px 30px; - - > .edit { - grid-row: 1; - background-color: var(--buttonBg); - margin: 2px; - - &:hover { - color: var(--accent); - } - } - - > .draft { - grid-row: 2; - background-color: var(--buttonBg); - margin: 2px; - - &:hover { - color: var(--accent); - } - } - - > .delete { - background-color: var(--buttonBg); - grid-row: 3; - margin: 2px; - - &:hover { - color: var(--accent); - } - } - } - } - } - } - - > .remote { - .empty { - margin: var(--margin); - } - - .ldhfsamy { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: 12px; - margin: var(--margin) 0; - - > .emoji { - display: flex; - align-items: center; - padding: 12px; - text-align: left; - - &:hover { - color: var(--accent); - } - - > .img { - width: 32px; - height: 32px; - } - - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; - - > .name { - text-overflow: ellipsis; - overflow: hidden; - } - - > .info { - opacity: 0.5; - font-size: 90%; - text-overflow: ellipsis; - overflow: hidden; - } - } - } - } - } -} - -.emoji-draft { - --c: rgb(255 196 0 / 15%);; - background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); - background-size: 16px 16px; -} </style> From 624e5e82cd17f93ebe1fd5c2bd191dcf3ae3f852 Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Tue, 23 May 2023 17:46:23 +0900 Subject: [PATCH 150/501] =?UTF-8?q?feature:=20=E3=83=8A=E3=83=93=E3=83=A1?= =?UTF-8?q?=E3=83=8B=E3=83=A5=E3=83=BC=E3=81=8B=E3=82=89=E3=83=97=E3=83=AD?= =?UTF-8?q?=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB=E3=82=92=E5=89=8A=E9=99=A4?= =?UTF-8?q?=E3=81=97=E3=81=A6=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5?= =?UTF-8?q?=E6=96=87=E5=AD=97=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 94b8cea7bb6fea37f3a5c20fb97eadc5828e1272) --- locales/index.d.ts | 2 ++ .../src/components/MkEmojiEditDialog.vue | 6 ++---- packages/frontend/src/navbar.ts | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 60b44e352b..0e4487dbb5 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -657,6 +657,8 @@ export interface Locale { "regenerateLoginToken": string; "regenerateLoginTokenDescription": string; "setMultipleBySeparatingWithSpace": string; + "emojiNameValidation": string; + "isSensitive": string; "fileIdOrUrl": string; "behavior": string; "sample": string; diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 64be684db3..d0075557d1 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -88,7 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch } from 'vue'; +import { computed, watch,ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; @@ -118,7 +118,7 @@ let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); -let chooseFile: DriveFile|null = $ref(null); +let chooseFile: MkDriveFile|null = $ref(null); let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); @@ -127,8 +127,6 @@ watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { }, { immediate: true }); const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); -let draft = $ref(props.emoji.draft); -let isRequest = $ref(props.isRequest); const validation = computed(() => { return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; }) diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 7f182a98f7..e069e225a8 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -149,10 +149,30 @@ export const navbarItemDef = reactive({ location.reload(); }, }, + switchLightOrDarkMode: { + title: i18n.ts.switchLightOrDarkMode, + icon: 'ti ti-palette', + action: (ev) => { + const mode = defaultStore.makeGetterSetter('darkMode'); + mode.set(!mode.get()); + }, + }, + customEmojis: { + title: i18n.ts.customEmojis, + icon: 'ti ti-icons', + to: '/about#emojis', + }, profile: { title: i18n.ts.profile, icon: 'ti ti-user', show: computed(() => $i != null), to: `/@${$i?.username}`, }, + clearCache: { + title: i18n.ts.clearCache, + icon: 'ti ti-trash', + action: (ev) => { + clearCache(); + }, + } }); From e13dc2fec9b39bc3bcfe2c8c79495181a410129f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 02:54:17 +0900 Subject: [PATCH 151/501] . --- locales/index.d.ts | 2 ++ locales/ja-JP.yml | 2 ++ packages/frontend/src/components/MkEmojiEditDialog.vue | 9 +++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 0e4487dbb5..5de0062a9d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -830,6 +830,8 @@ export interface Locale { "list": string; "emailNotConfiguredWarning": string; "ratio": string; + "newEmojis": string; + "draftEmojis": string; "previewNoteText": string; "customCss": string; "customCssWarn": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4d35a0263c..2232e8e721 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -827,6 +827,8 @@ low: "低" list: "一覧" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" +newEmojis: "新しい絵文字" +draftEmojis: "ドラフトされてる絵文字" previewNoteText: "本文をプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index d0075557d1..15a287b4fd 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder v-if="!isRequest" > + <MkFolder> <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> @@ -74,14 +74,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> <div :class="$style.footer"> <div :class="$style.footerButtons"> - <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-check"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> <MkButton v-if="validation" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> <MkButton v-else rounded style="margin: 0 auto;"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> - <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> - {{ i18n.ts.draft }} - </MkSwitch> - <MkButton v-if="!isRequest" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> </div> </MkModalWindow> From 40979b0aa556796927d0c91d59691a1468d4b88d Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Wed, 17 May 2023 00:44:19 +0900 Subject: [PATCH 152/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE?= =?UTF-8?q?=E7=99=BB=E9=8C=B2=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 702a3e86878bc7f210d90f15c4f4417d542ba086) --- .../frontend/src/pages/admin/roles.editor.vue | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 82658daf6a..c4c8c741ae 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -279,6 +279,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix> + <span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> <template #suffix> From ad27562269381e72bc6233e935eda42cdc2513a9 Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Wed, 17 May 2023 00:44:19 +0900 Subject: [PATCH 153/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE?= =?UTF-8?q?=E7=99=BB=E9=8C=B2=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 702a3e86878bc7f210d90f15c4f4417d542ba086) --- .../frontend/src/pages/admin/roles.editor.vue | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index c4c8c741ae..4583387126 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -299,6 +299,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix> + <span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> <template #suffix> From f9efc37eb1b91998cdc70ba417b2ecb6d0183e0e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 04:51:57 +0900 Subject: [PATCH 154/501] =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E7=94=B3?= =?UTF-8?q?=E8=AB=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/ja-JP.yml | 2 +- .../api/endpoints/admin/emoji/add-draft.ts | 7 +-- .../src/components/MKCustomEmojiEditDraft.vue | 57 +++++++------------ .../src/components/MkEmojiEditDialog.vue | 7 +-- packages/frontend/src/pages/about.emojis.vue | 18 +++--- packages/frontend/src/pages/emojis.emoji.vue | 4 +- 6 files changed, 37 insertions(+), 58 deletions(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 2232e8e721..e90bf47670 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -828,7 +828,7 @@ list: "一覧" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" newEmojis: "新しい絵文字" -draftEmojis: "ドラフトされてる絵文字" +draftEmojis: "申請されている絵文字" previewNoteText: "本文をプレビュー" customCss: "カスタムCSS" customCssWarn: "この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。" diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 40b7a12e87..0ed69d335a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -1,6 +1,5 @@ import { Inject, Injectable } from '@nestjs/common'; -import rndstr from 'rndstr'; -import { Endpoint } from '@/server/api/endpoint-base.js'; + import { Endpoint } from '@/server/api/endpoint-base.js'; import type { DriveFilesRepository } from '@/models/index.js'; import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; @@ -73,10 +72,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); - this.moderationLogService.insertModerationLog(me, 'addEmoji', { - emojiId: emoji.id, - }); - return { id: emoji.id, }; diff --git a/packages/frontend/src/components/MKCustomEmojiEditDraft.vue b/packages/frontend/src/components/MKCustomEmojiEditDraft.vue index 17576b7b06..34d033119d 100644 --- a/packages/frontend/src/components/MKCustomEmojiEditDraft.vue +++ b/packages/frontend/src/components/MKCustomEmojiEditDraft.vue @@ -10,21 +10,21 @@ <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> </div> <div class="info"> - <div class="name _monospace">{{ i18n.ts.name }}: {{ emoji.name }}</div> + <div class="name">{{ i18n.ts.name }}: {{ emoji.name }}</div> <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> </div> <div class="edit-button"> - <button class="edit _button" @click="editDraft(emoji)"> + <MkButton primary class="edit" @click="editDraft(emoji)"> {{ i18n.ts.edit }} - </button> - <button class="draft _button" @click="undrafted(emoji)"> + </MkButton> + <MkButton class="draft" @click="undrafted(emoji)"> {{ i18n.ts.undrafted }} - </button> - <button class="delete _button" @click="deleteDraft(emoji)"> + </MkButton> + <MkButton danger class="delete" @click="deleteDraft(emoji)"> {{ i18n.ts.delete }} - </button> + </MkButton> </div> </div> </template> @@ -38,6 +38,7 @@ import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; import MkPagination from '@/components/MkPagination.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; +import MkButton from '@/components/MkButton.vue'; const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); @@ -87,7 +88,7 @@ async function undrafted(emoji) { license: emoji.license, draft: false, isSensitive: emoji.isSensitive, - localOnly: emoji.localOnly, + localOnly: emoji.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, }); @@ -118,14 +119,10 @@ async function deleteDraft(emoji) { .ldhfsamy { > .emoji { - display: grid; - grid-template-rows: 40px 1fr; - grid-template-columns: 1fr 150px; align-items: center; padding: 11px; text-align: left; border: solid 1px var(--panel); - width: 100%; margin: 10px; > .img { @@ -140,9 +137,9 @@ async function deleteDraft(emoji) { display: grid; grid-column: 1; background-color: #fff; - + margin-bottom: 12px; > img { - max-height: 30px; + max-height: 64px; max-width: 100%; } } @@ -151,9 +148,9 @@ async function deleteDraft(emoji) { display: grid; grid-column: 2; background-color: #000; - + margin-bottom: 12px; > img { - max-height: 30px; + max-height: 64px; max-width: 100%; } } @@ -191,37 +188,25 @@ async function deleteDraft(emoji) { > .edit-button { display: grid; - grid-row: 2; - grid-template-rows: 30px 30px 30px; + grid-template-rows: 42px; + margin-top: 6px; > .edit { grid-row: 1; - background-color: var(--buttonBg); - margin: 2px; - - &:hover { - color: var(--accent); - } + width: 100%; + margin: 6px 0; } > .draft { grid-row: 2; - background-color: var(--buttonBg); - margin: 2px; - - &:hover { - color: var(--accent); - } + width: 100%; + margin: 6px 0; } > .delete { - background-color: var(--buttonBg); grid-row: 3; - margin: 2px; - - &:hover { - color: var(--accent); - } + width: 100%; + margin: 6px 0; } } } diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 15a287b4fd..5a337249dd 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" - :with-ok-button="true" + :with-ok-button="false " @close="dialog.close()" @closed="$emit('closed')" > @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> - <div class="_gaps"> + <div class="_gaps" v-if="!isRequest"> <MkButton rounded @click="addRole"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> <div v-for="role in rolesThatCanBeUsedThisEmojiAsReaction" :key="role.id" :class="$style.roleItem"> @@ -69,7 +69,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> {{ i18n.ts.draft }} </MkSwitch> - <MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </MkSpacer> <div :class="$style.footer"> @@ -227,7 +226,7 @@ async function done() { if (file) { params.fileId = file.id; } - + console.log(props.emoji) if (props.emoji) { await os.apiWithDialog('admin/emoji/update', { id: props.emoji.id, diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index a49a70c88a..84469e102c 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> - <MkButton v-if="$i && (!$i.isModerator && !$i.policies.canManageCustomEmojis && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> + <MkButton v-if="$i && (!$i.isModerator || !$i.policies.canManageCustomEmojis || $i.policies.canRequestCustomEmojis)" primary @click="edit" style='margin-top: 8px;' >{{ i18n.ts.requestCustomEmojis }}</MkButton> <div class="query" style="margin-top: 10px;"> <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> @@ -36,11 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFoldableSection> </MkSpacer> - <MkSpacer v-if="tab === 'new'" :contentMax="1000" :marginMin="20"> - <div :class="$style.emojis"> - <XEmoji v-for="emoji in newEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> - </div> - </MkSpacer> + <MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> <XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> @@ -68,9 +64,6 @@ const headerActions = $computed(() => []); const headerTabs = $computed(() => [{ key: 'emojis', title: i18n.ts.list, -}, { - key: 'new', - title: i18n.ts.newEmojis, }, { key: 'draft', title: i18n.ts.draftEmojis, @@ -89,7 +82,7 @@ const pagination = { let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); - +const draftEmojis = customEmojis.value.filter(emoji => emoji.draft); function search() { if ((q === '' || q == null) && selectedTags.size === 0) { searchEmojis = null; @@ -136,6 +129,11 @@ watch($$(q), () => { watch($$(selectedTags), () => { search(); }, { deep: true }); + +definePageMetadata({ + title: i18n.ts.customEmojis, + icon: null, +}); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index fcc1fff104..f1b563a90d 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-else class="_button" :class="$style.root" @click="menu"> <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div :class="$style.body"> - <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> + <div :class="$style.name" >{{ emoji.name }}</div> <div :class="$style.info">{{ emoji.aliases.join(' ') }}</div> </div> </button> @@ -104,5 +104,7 @@ function menu(ev) { --c: rgb(255 196 0 / 15%);; background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); background-size: 16px 16px; + max-width: 64px; + width: 100%; } </style> From 830c952e3d787d5214d4c859817555ff8370ff5e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 12:40:13 +0900 Subject: [PATCH 155/501] Update navbar.ts --- packages/frontend/src/navbar.ts | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index e069e225a8..65e083d1b8 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -12,6 +12,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { ui } from '@/config.js'; import { unisonReload } from '@/scripts/unison-reload.js'; +import {defaultStore} from "@/store.js"; export const navbarItemDef = reactive({ notifications: { @@ -149,30 +150,10 @@ export const navbarItemDef = reactive({ location.reload(); }, }, - switchLightOrDarkMode: { - title: i18n.ts.switchLightOrDarkMode, - icon: 'ti ti-palette', - action: (ev) => { - const mode = defaultStore.makeGetterSetter('darkMode'); - mode.set(!mode.get()); - }, - }, - customEmojis: { - title: i18n.ts.customEmojis, - icon: 'ti ti-icons', - to: '/about#emojis', - }, profile: { title: i18n.ts.profile, icon: 'ti ti-user', show: computed(() => $i != null), to: `/@${$i?.username}`, }, - clearCache: { - title: i18n.ts.clearCache, - icon: 'ti ti-trash', - action: (ev) => { - clearCache(); - }, - } }); From da0c22ece602f66198d652ecec716b10f3100b8b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 13:37:50 +0900 Subject: [PATCH 156/501] fix --- packages/frontend/src/pages/about.emojis.vue | 10 +++++++--- packages/frontend/src/pages/emojis.emoji.vue | 4 ++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 84469e102c..992dc718e6 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -29,8 +29,8 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFoldableSection> - <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> - <template #header>{{ category || i18n.ts.other }}</template> + <MkFoldableSection v-for="category in filteredCategories" v-once :key="category"> + <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> @@ -68,7 +68,11 @@ const headerTabs = $computed(() => [{ key: 'draft', title: i18n.ts.draftEmojis, }]); - +const filteredCategories = computed(() => { + return customEmojiCategories.value.filter((category: any) => { + return customEmojis.value.some((e: any) => e.category === category && !e.draft); + }); +}); definePageMetadata(ref({})); const pagination = { diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index f1b563a90d..5926bc365e 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -5,9 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <button v-if="emoji.draft" class="zuvgdzyu _button emoji-draft" @click="menu"> - <img :src="emoji.url" class="img" loading="lazy"/> + <img style="max-height: 64px;object-fit: contain;" :src="emoji.url" class="img" loading="lazy"/> <div class="body"> - <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> <div class="info">{{ emoji.aliases.join(' ') }}</div> </div> </button> From dfbb2fc4dc7366c5d656f76b07ae85294e04f29c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 13:58:11 +0900 Subject: [PATCH 157/501] update changelog.md --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50334560c9..36d88dca12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,10 @@ ## 2023.10.1 ### General - Enhance: ローカルタイムライン、ソーシャルタイムラインで返信を含むかどうか設定可能に +- Feat: 絵文字申請を追加 + - これによって絵文字リクエストロールが追加されました。 + - カスタム絵文字管理の画面に 申請されている絵文字 タブが追加されました。 + - カスタム絵文字のリクエストボタンが実装されました。 ### Client - Fix: 絵文字ピッカーで横に長いカスタム絵文字が見切れる問題を修正 @@ -68,6 +72,7 @@ - Enhance: 動画再生時のデフォルトボリュームを30%に - Fix: リアクションしたユーザ一覧のUIが稀に左上に残ってしまう不具合を修正 + ### Server - Enhance: drive/files/attached-notes がページネーションに対応しました - Enhance: タイムライン取得時のパフォーマンスを大幅に向上 From 1a84ab32894a2322bd48bf5cf8cf5b824a492591 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 16:32:18 +0900 Subject: [PATCH 158/501] update misskey-js.api.md --- packages/misskey-js/etc/misskey-js.api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 1a0bbeac78..dea79bd314 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -282,6 +282,7 @@ type CustomEmoji = { url: string; category: string; aliases: string[]; + draft: boolean; }; // @public (undocumented) @@ -2984,7 +2985,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:630:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:107:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:600:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:601:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From 4abecb2bc19486bc8729454ce92f347c397e0387 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 16:38:38 +0900 Subject: [PATCH 159/501] rename --- .../{MKCustomEmojiEditDraft.vue => MkCustomEmojiEditDraft.vue} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/frontend/src/components/{MKCustomEmojiEditDraft.vue => MkCustomEmojiEditDraft.vue} (100%) diff --git a/packages/frontend/src/components/MKCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue similarity index 100% rename from packages/frontend/src/components/MKCustomEmojiEditDraft.vue rename to packages/frontend/src/components/MkCustomEmojiEditDraft.vue From 57331536f2c2c17d00dc69986c04ef3db29d96ad Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 17:00:14 +0900 Subject: [PATCH 160/501] lint fix --- .../backend/src/core/CustomEmojiService.ts | 12 +-- .../backend/src/core/UserFollowingService.ts | 73 ++++++++++----- .../api/endpoints/admin/emoji/add-draft.ts | 4 +- .../server/api/endpoints/admin/emoji/add.ts | 22 +++-- .../api/endpoints/admin/emoji/update.ts | 4 +- .../src/components/MkEmojiEditDialog.vue | 93 +++++++++---------- .../src/components/global/MkCustomEmoji.vue | 2 +- packages/frontend/src/custom-emojis.ts | 2 +- packages/frontend/src/navbar.ts | 1 - packages/frontend/src/pages/about.emojis.vue | 46 ++++++--- .../frontend/src/pages/about.federation.vue | 5 +- .../src/pages/admin/instance-block.vue | 6 +- 12 files changed, 157 insertions(+), 113 deletions(-) diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 332a909b8c..99dbc9d54d 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -16,10 +16,8 @@ import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; - const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @Injectable() @@ -31,12 +29,6 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.redis) private redisClient: Redis.Redis, - @Inject(DI.config) - private config: Config, - - @Inject(DI.db) - private db: DataSource, - @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, @@ -119,7 +111,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license?: string | null; isSensitive?: boolean; localOnly?: boolean; - draft: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; }, moderator?: MiUser): Promise<void> { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); @@ -134,7 +126,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license: data.license, isSensitive: data.isSensitive, localOnly: data.localOnly, - draft: data.draft, + draft: data.draft, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, }); diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index beffcc2e9c..61fec08868 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -19,7 +19,13 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { WebhookService } from '@/core/WebhookService.js'; import { NotificationService } from '@/core/NotificationService.js'; import { DI } from '@/di-symbols.js'; -import type { FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { + FollowingsRepository, + FollowRequestsRepository, + InstancesRepository, + UserProfilesRepository, + UsersRepository, +} from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { bindThis } from '@/decorators.js'; @@ -51,25 +57,18 @@ export class UserFollowingService implements OnModuleInit { constructor( private moduleRef: ModuleRef, - @Inject(DI.config) private config: Config, - @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, - @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, - @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, - @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, - private cacheService: CacheService, private userEntityService: UserEntityService, private idService: IdService, @@ -91,7 +90,9 @@ export class UserFollowingService implements OnModuleInit { } @bindThis - public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> { + public async follow(_follower: { id: MiUser['id'] }, _followee: { + id: MiUser['id'] + }, requestId?: string, silent = false): Promise<void> { const [follower, followee] = await Promise.all([ this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }), @@ -184,10 +185,18 @@ export class UserFollowingService implements OnModuleInit { @bindThis private async insertFollowingDoc( followee: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox'] }, follower: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox'] }, silent = false, ): Promise<void> { @@ -233,8 +242,7 @@ export class UserFollowingService implements OnModuleInit { }); // 通知を作成 - this.notificationService.createNotification(follower.id, 'followRequestAccepted', { - }, followee.id); + this.notificationService.createNotification(follower.id, 'followRequestAccepted', {}, followee.id); } if (alreadyFollowed) return; @@ -306,18 +314,25 @@ export class UserFollowingService implements OnModuleInit { }); // 通知を作成 - this.notificationService.createNotification(followee.id, 'follow', { - }, follower.id); + this.notificationService.createNotification(followee.id, 'follow', {}, follower.id); } } @bindThis public async unfollow( follower: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox']; }, followee: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox']; }, silent = false, ): Promise<void> { @@ -446,10 +461,18 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async createFollowRequest( follower: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox']; }, followee: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox']; }, requestId?: string, ): Promise<void> { @@ -541,7 +564,11 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async acceptFollowRequest( followee: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox']; }, follower: MiUser, ): Promise<void> { @@ -569,7 +596,11 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async acceptAllFollowRequests( user: { - id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; + host: MiUser['host']; + uri: MiUser['host']; + inbox: MiUser['inbox']; + sharedInbox: MiUser['sharedInbox']; }, ): Promise<void> { const requests = await this.followRequestsRepository.findBy({ diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 0ed69d335a..7088f801e9 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -1,6 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; - import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 5e1b0e345c..7f4474419c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -41,17 +41,21 @@ export const paramDef = { nullable: true, description: 'Use `null` to reset the category.', }, - aliases: { type: 'array', items: { - type: 'string', - } }, + aliases: { + type: 'array', items: { + type: 'string', + }, + }, license: { type: 'string', nullable: true }, isSensitive: { type: 'boolean' }, localOnly: { type: 'boolean' }, - roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { - type: 'string', - } }, + roleIdsThatCanBeUsedThisEmojiAsReaction: { + type: 'array', items: { + type: 'string', + }, + }, }, - required: ['name','fileId', 'draft'], + required: ['name', 'fileId', 'draft'], } as const; // TODO: ロジックをサービスに切り出す @@ -61,9 +65,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - private customEmojiService: CustomEmojiService, - private emojiEntityService: EmojiEntityService, ) { super(meta, paramDef, async (ps, me) => { @@ -82,7 +84,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, - draft: false, + draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 7519f2d33e..595dd12964 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -57,7 +57,7 @@ export const paramDef = { } }, draft: { type: 'boolean' }, }, - required: ['id', 'name', 'aliases'], + required: ['id', 'name', 'draft', 'aliases'], } as const; @Injectable() @@ -94,7 +94,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, - draft: ps.draft, + draft: ps.draft, }, me); }); } diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 5a337249dd..5490fed3ee 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" - :with-ok-button="false " + :withOkButton="false " @close="dialog.close()" @closed="$emit('closed')" > @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> - <div class="_gaps" v-if="!isRequest"> + <div v-if="!isRequest" class="_gaps"> <MkButton rounded @click="addRole"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> <div v-for="role in rolesThatCanBeUsedThisEmojiAsReaction" :key="role.id" :class="$style.roleItem"> @@ -73,19 +73,19 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> <div :class="$style.footer"> <div :class="$style.footerButtons"> - <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> <MkButton v-if="validation" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> <MkButton v-else rounded style="margin: 0 auto;"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> - - </div> + </div> </div> </MkModalWindow> </template> <script lang="ts" setup> -import { computed, watch,ref } from 'vue'; +import { computed, watch } from 'vue'; import * as Misskey from 'misskey-js'; +import { DriveFile } from 'misskey-js/built/entities.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -114,7 +114,7 @@ let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); -let chooseFile: MkDriveFile|null = $ref(null); +let chooseFile: DriveFile|null = $ref(null); let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); @@ -124,8 +124,8 @@ watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); const validation = computed(() => { - return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; -}) + return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; +}); const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, (ev: 'closed'): void @@ -164,27 +164,27 @@ async function add() { dialog.close(); } async function changeImage(ev) { - file = await selectFile(ev.currentTarget ?? ev.target, null); - const candidate = file.name.replace(/\.(.+)$/, ''); - if (candidate.match(/^[a-z0-9_]+$/)) { - name = candidate; - } + file = await selectFile(ev.currentTarget ?? ev.target, null); + const candidate = file.name.replace(/\.(.+)$/, ''); + if (candidate.match(/^[a-z0-9_]+$/)) { + name = candidate; + } } async function addRole() { - const roles = await os.api('admin/roles/list'); - const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id); + const roles = await os.api('admin/roles/list'); + const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id); - const { canceled, result: role } = await os.select({ - items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), - }); - if (canceled) return; + const { canceled, result: role } = await os.select({ + items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), + }); + if (canceled) return; - rolesThatCanBeUsedThisEmojiAsReaction.push(role); + rolesThatCanBeUsedThisEmojiAsReaction.push(role); } async function removeRole(role, ev) { - rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); + rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); } async function update() { await os.apiWithDialog('admin/emoji/update', { @@ -208,8 +208,7 @@ async function update() { }, }); - dialog.close(); - + dialog.close(); } async function done() { const params = { @@ -223,35 +222,35 @@ async function done() { roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), }; - if (file) { - params.fileId = file.id; - } - console.log(props.emoji) - if (props.emoji) { - await os.apiWithDialog('admin/emoji/update', { - id: props.emoji.id, - ...params, - }); + if (file) { + params.fileId = file.id; + } + console.log(props.emoji); + if (props.emoji) { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, + ...params, + }); - emit('done', { - updated: { - id: props.emoji.id, - ...params, - }, - }); + emit('done', { + updated: { + id: props.emoji.id, + ...params, + }, + }); - dialog.close(); - } else { - const created = isRequest + dialog.close(); + } else { + const created = isRequest ? await os.apiWithDialog('admin/emoji/add-draft', params) : await os.apiWithDialog('admin/emoji/add', params); - emit('done', { - created: created, - }); + emit('done', { + created: created, + }); - dialog.close(); - } + dialog.close(); + } } function chooseFileFrom(ev) { diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index c77fb4c098..248b7da379 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -25,7 +25,7 @@ const props = defineProps<{ const customEmojiName = computed(() => (props.name[0] === ':' ? props.name.substring(1, props.name.length - 1) : props.name).replace('@.', '')); const isLocal = computed(() => !props.host && (customEmojiName.value.endsWith('@.') || !customEmojiName.value.includes('@'))); -const isDraft = computed(() => customEmojisNameMap.value.get(customEmojiName.value)?.draft ?? false); +const isDraft = computed(() => customEmojisMap.get(customEmojiName.value)?.draft ?? false); const rawUrl = computed(() => { if (props.url) { diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts index 9b4b56b7de..2cf568023a 100644 --- a/packages/frontend/src/custom-emojis.ts +++ b/packages/frontend/src/custom-emojis.ts @@ -3,7 +3,7 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { shallowRef, computed, markRaw, triggerRef,watch } from 'vue'; +import { shallowRef, computed, markRaw, triggerRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import { api, apiGet } from '@/os.js'; import { useStream } from '@/stream.js'; diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 65e083d1b8..7f182a98f7 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -12,7 +12,6 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { ui } from '@/config.js'; import { unisonReload } from '@/scripts/unison-reload.js'; -import {defaultStore} from "@/store.js"; export const navbarItemDef = reactive({ notifications: { diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 992dc718e6..68b5a2c9f5 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -5,10 +5,22 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <template #header> + <MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/> + </template> <MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> - <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> - <MkButton v-if="$i && (!$i.isModerator || !$i.policies.canManageCustomEmojis || $i.policies.canRequestCustomEmojis)" primary @click="edit" style='margin-top: 8px;' >{{ i18n.ts.requestCustomEmojis }}</MkButton> + <MkButton + v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link + to="/custom-emojis-manager" + > + {{ i18n.ts.manageCustomEmojis }} + </MkButton> + <MkButton + v-if="$i && (!$i.isModerator || !$i.policies.canManageCustomEmojis || $i.policies.canRequestCustomEmojis)" + primary style="margin-top: 8px;" @click="edit" + > + {{ i18n.ts.requestCustomEmojis }} + </MkButton> <div class="query" style="margin-top: 10px;"> <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> @@ -16,10 +28,10 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> <!-- たくさんあると邪魔 - <div class="tags"> - <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> - </div> - --> + <div class="tags"> + <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> + </div> + --> </div> <MkFoldableSection v-if="searchEmojis"> @@ -30,16 +42,19 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFoldableSection> <MkFoldableSection v-for="category in filteredCategories" v-once :key="category"> - <template #header>{{ category || i18n.ts.other }}</template> + <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji + v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name" + :emoji="emoji" :draft="emoji.draft" + /> </div> </MkFoldableSection> </MkSpacer> <MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> - <XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> </MkSpacer> </MkStickyContainer> @@ -69,9 +84,9 @@ const headerTabs = $computed(() => [{ title: i18n.ts.draftEmojis, }]); const filteredCategories = computed(() => { - return customEmojiCategories.value.filter((category: any) => { - return customEmojis.value.some((e: any) => e.category === category && !e.draft); - }); + return customEmojiCategories.value.filter((category: any) => { + return customEmojis.value.some((e: any) => e.category === category && !e.draft); + }); }); definePageMetadata(ref({})); @@ -87,6 +102,7 @@ let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); const draftEmojis = customEmojis.value.filter(emoji => emoji.draft); + function search() { if ((q === '' || q == null) && selectedTags.size === 0) { searchEmojis = null; @@ -135,8 +151,8 @@ watch($$(selectedTags), () => { }, { deep: true }); definePageMetadata({ - title: i18n.ts.customEmojis, - icon: null, + title: i18n.ts.customEmojis, + icon: null, }); </script> diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue index 2aa5af939b..4d8c51239f 100644 --- a/packages/frontend/src/pages/about.federation.vue +++ b/packages/frontend/src/pages/about.federation.vue @@ -41,7 +41,10 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination"> <div :class="$style.items"> - <MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.item" :to="`/instance-info/${instance.host}`"> + <MkA + v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" + :class="$style.item" :to="`/instance-info/${instance.host}`" + > <MkInstanceCardMini :instance="instance"/> </MkA> </div> diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 9a0f5e026b..b82e425682 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template> + <template #header> + <XHeader :actions="headerActions" :tabs="headerTabs"/> + </template> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> <MkTextarea v-model="blockedHosts"> @@ -20,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; +import {} from 'vue'; import XHeader from './_header_.vue'; import MkButton from '@/components/MkButton.vue'; import MkTextarea from '@/components/MkTextarea.vue'; From c0c6b87c94c987362f2a69aafe08182cb1c01f50 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 14 Oct 2023 17:33:42 +0900 Subject: [PATCH 161/501] lint fix --- packages/frontend/src/components/MkEmojiEditDialog.vue | 6 +++--- packages/frontend/src/pages/about.emojis.vue | 10 +--------- packages/frontend/src/pages/admin/roles.editor.vue | 6 +++--- packages/frontend/src/pages/emojis.emoji.vue | 4 ++-- 4 files changed, 9 insertions(+), 17 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 5490fed3ee..4e5fd70b6d 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -97,7 +97,6 @@ import { customEmojiCategories } from '@/custom-emojis.js'; import MkSwitch from '@/components/MkSwitch.vue'; import { selectFile, selectFiles } from '@/scripts/select-file.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; -import { $i } from '@/account'; const props = defineProps<{ emoji?: any, @@ -117,6 +116,7 @@ let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); +let url; watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); @@ -242,8 +242,8 @@ async function done() { dialog.close(); } else { const created = isRequest - ? await os.apiWithDialog('admin/emoji/add-draft', params) - : await os.apiWithDialog('admin/emoji/add', params); + ? await os.apiWithDialog('admin/emoji/add-draft', params) + : await os.apiWithDialog('admin/emoji/add', params); emit('done', { created: created, diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 68b5a2c9f5..f76e5779f3 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -85,19 +85,11 @@ const headerTabs = $computed(() => [{ }]); const filteredCategories = computed(() => { return customEmojiCategories.value.filter((category: any) => { - return customEmojis.value.some((e: any) => e.category === category && !e.draft); + return customEmojis.value.some((em: any) => em.category === category && !em.draft); }); }); definePageMetadata(ref({})); -const pagination = { - endpoint: 'admin/emoji/list' as const, - limit: 30, - params: computed(() => ({ - query: (query.value && query.value !== '') ? query.value : null, - })), -}; - let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 4583387126..48f2f10695 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -273,7 +273,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -293,7 +293,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> @@ -313,7 +313,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index 5926bc365e..6cd7b1bbb0 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -7,14 +7,14 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="emoji.draft" class="zuvgdzyu _button emoji-draft" @click="menu"> <img style="max-height: 64px;object-fit: contain;" :src="emoji.url" class="img" loading="lazy"/> <div class="body"> - <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> <div class="info">{{ emoji.aliases.join(' ') }}</div> </div> </button> <button v-else class="_button" :class="$style.root" @click="menu"> <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div :class="$style.body"> - <div :class="$style.name" >{{ emoji.name }}</div> + <div :class="$style.name">{{ emoji.name }}</div> <div :class="$style.info">{{ emoji.aliases.join(' ') }}</div> </div> </button> From 81ee7d9938e34ee340a242eb7868e3dfc008c2ed Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 16 Oct 2023 21:46:52 +0900 Subject: [PATCH 162/501] fix --- packages/backend/src/core/CustomEmojiService.ts | 3 +++ packages/frontend/src/components/MkEmojiEditDialog.vue | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 90dcf963fd..e9dfaed228 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -127,6 +127,9 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, draft: data.draft, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, }); diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 4e5fd70b6d..43b03971e7 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -47,11 +47,11 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder> + <MkFolder v-if="!isRequest"> <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> - <div v-if="!isRequest" class="_gaps"> + <div class="_gaps"> <MkButton rounded @click="addRole"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> <div v-for="role in rolesThatCanBeUsedThisEmojiAsReaction" :key="role.id" :class="$style.roleItem"> From a3743a06229b9111b9a38c9d4bbda6a93e9207ea Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 17:42:06 +0900 Subject: [PATCH 163/501] Feat:emoji folder --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../src/components/MkEmojiPicker.section.vue | 136 ++++++++++++++---- .../frontend/src/components/MkEmojiPicker.vue | 40 ++++-- 4 files changed, 142 insertions(+), 36 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index a7182e6c1b..891d84276c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -323,6 +323,7 @@ export interface Locale { "createFolder": string; "renameFolder": string; "deleteFolder": string; + "Folder": string; "addFile": string; "emptyDrive": string; "emptyFolder": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index db45638a8e..2c580b0641 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -320,6 +320,7 @@ folderName: "フォルダー名" createFolder: "フォルダーを作成" renameFolder: "フォルダー名を変更" deleteFolder: "フォルダーを削除" +Folder: "フォルダー" addFile: "ファイルを追加" emptyDrive: "ドライブは空です" emptyFolder: "フォルダーは空です" diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 08297ea5ba..0582cf8b74 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -4,49 +4,129 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> -<section> - <header class="_acrylic" @click="shown = !shown"> - <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) - </header> - <div v-if="shown" class="body"> - <button - v-for="emoji in emojis" - :key="emoji" - :data-emoji="emoji" - class="_button item" - @pointerenter="computeButtonTitle" - @click="emit('chosen', emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> -</section> + <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> + <section> + + + <header v-if="!category" class="_acrylic" @click="shown = !shown"> + <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> + <slot></slot> + ({{ emojis.length }}) + </header> + <header v-else-if="category.length === 1" class="_acrylic" @click="shown = !shown"> + <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> + {{ category[0] }} + ({{ + emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length + }}) + </header> + <header v-else class="_acrylic" style="top:unset;" @click="toggleShown_fol"> + <i class="toggle ti-fw" :class="shown_fol? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> + {{ category[0] || i18n.ts.other }} + <template v-if="category.length !== 1"> + ({{ i18n.ts.Folder }}) + </template> + <template v-else> + ({{ + emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length + }}) 2 + </template> + </header> + <template v-for="(n, index) in category" v-if="shown_fol"> + <header + v-if="emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length !== 0 || index!==0" + style="top:unset;padding-left: 18px;" + class="_acrylic" + @click="toggleShown(index)" + > + <i class="toggle ti-fw" :class="shown_fold[index] ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> + {{ n || i18n.ts.other }} + ({{ + emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === (index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).length + }}) + </header> + <div v-if="shown_fold[index]" class="body"> + <button + v-for="emoji in emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category ===( index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).map(e => `:${e.name}:`)" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </template> + + <div v-if="shown && category" class="body"> + <button + v-for="emoji in emojis.filter(e => e.category === category[0]).map(e => `:${e.name}:`)" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + <div v-else-if="shown && !category" class="body"> + <button + v-for="emoji in emojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + + </section> </template> <script lang="ts" setup> -import { ref, computed, Ref } from 'vue'; -import { getEmojiName } from '@/scripts/emojilist.js'; +import {ref, computed, Ref} from 'vue'; +import {getEmojiName} from '@/scripts/emojilist.js'; +import {i18n} from "../i18n.js"; const props = defineProps<{ - emojis: string[] | Ref<string[]>; - initialShown?: boolean; + emojis: string[] | Ref<string[]>; + initialShown?: boolean; + category?: string[]; }>(); const emit = defineEmits<{ - (ev: 'chosen', v: string, event: MouseEvent): void; + (ev: 'chosen', v: string, event: MouseEvent): void; }>(); -const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value); +const toggleShown = (index) => { + shown_fold.value[index] = !shown_fold.value[index]; +}; + +const toggleShown_fol = () => { + for (let i = 0; i < shown_fold.value.length; i++) { + shown_fold.value[i] = false; + } + shown_fol.value = !shown_fol.value; +}; + +const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value); +const shown_fold = ref(Array(props.category === undefined ? 0 : props.category.length).fill(false)); const shown = ref(!!props.initialShown); +const shown_fol = ref(!!props.initialShown); /** @see MkEmojiPicker.vue */ function computeButtonTitle(ev: MouseEvent): void { - const elm = ev.target as HTMLElement; - const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + const elm = ev.target as HTMLElement; + const emoji = elm.dataset.emoji as string; + elm.title = getEmojiName(emoji) ?? emoji; } </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 7eff637482..33a7d003a6 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -72,19 +72,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-once class="group"> <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> + <XSection - v-for="category in customEmojiCategories" + v-for="category in groupedData" :key="`custom:${category}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" - @chosen="chosen" - > - {{ category || i18n.ts.other }} - </XSection> + :emojis="computed(() => customEmojis.filter(filterAvailable))" + :category="category" + @chosen="chosen" + /> </div> <div v-once class="group"> <header class="_acrylic">{{ i18n.ts.emoji }}</header> - <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" @chosen="chosen">{{ category }}</XSection> + <XSection + v-for="category in categories" + :key="category" + :emojis="emojiCharByCategory.get(category) ?? []" + @chosen="chosen" + >{{ category }} + </XSection> + </div> </div> <div class="tabs"> @@ -107,7 +114,7 @@ import { isTouchUsing } from '@/scripts/touch.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; -import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js'; +import {customEmojiCategories, customEmojis, customEmojisMap} from '@/custom-emojis.js'; import { $i } from '@/account.js'; const props = withDefaults(defineProps<{ @@ -143,6 +150,23 @@ const q = ref<string>(''); const searchResultCustom = ref<Misskey.entities.CustomEmoji[]>([]); const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); +let split_categories = []; +customEmojiCategories.value.forEach(e => { + if (e !== null){ + split_categories.push(e.split('/')) + } +}); + +const groupedData = {}; +split_categories.forEach((item) => { + if (!groupedData[item[0]]) { + groupedData[item[0]] = []; + groupedData[item[0]].push(item[0]); + }else{ + groupedData[item[0]].push(item[1]); + } +}); +console.log(groupedData) watch(q, () => { if (emojisEl.value) emojisEl.value.scrollTop = 0; From 81182b86120487e1ef2637f8139d4d2b504c0d3f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 20:51:43 +0900 Subject: [PATCH 164/501] Feat:emoji folder --- .../src/components/MkEmojiPicker.section.vue | 46 +++++++++++-------- .../frontend/src/components/MkEmojiPicker.vue | 29 +++++++++--- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 0582cf8b74..3be1b2bf37 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -6,13 +6,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> <section> - - + <!-- categoryが定義されてない(Unicodeの絵文字とか) --> <header v-if="!category" class="_acrylic" @click="shown = !shown"> <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) </header> + + <!-- categoryが定義されてるけど中身が1つしかない --> <header v-else-if="category.length === 1" class="_acrylic" @click="shown = !shown"> <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> {{ category[0] }} @@ -20,6 +21,8 @@ SPDX-License-Identifier: AGPL-3.0-only emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length }}) </header> + + <!-- categoryに1つ以上要素がある(フォルダが有る) --> <header v-else class="_acrylic" style="top:unset;" @click="toggleShown_fol"> <i class="toggle ti-fw" :class="shown_fol? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> {{ category[0] || i18n.ts.other }} @@ -29,13 +32,16 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else> ({{ emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length - }}) 2 + }}) </template> </header> + <!-- フォルダが有るときのフォルダと絵文字表示する部分 --> <template v-for="(n, index) in category" v-if="shown_fol"> + + <!-- フォルダの部分 --> <header v-if="emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length !== 0 || index!==0" - style="top:unset;padding-left: 18px;" + style="padding-left: 18px;" class="_acrylic" @click="toggleShown(index)" > @@ -45,21 +51,24 @@ SPDX-License-Identifier: AGPL-3.0-only emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === (index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).length }}) </header> - <div v-if="shown_fold[index]" class="body"> - <button - v-for="emoji in emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category ===( index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).map(e => `:${e.name}:`)" - :key="emoji" - :data-emoji="emoji" - class="_button item" - @pointerenter="computeButtonTitle" - @click="emit('chosen', emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> + + <!-- 絵文字の部分 --> + <div v-if="shown_fold[index]" class="body"> + <button + v-for="emoji in emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category ===( index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).map(e => `:${e.name}:`)" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> </template> + <!-- categoryが1つしかないときのフォルダと絵文字表示する部分 --> <div v-if="shown && category" class="body"> <button v-for="emoji in emojis.filter(e => e.category === category[0]).map(e => `:${e.name}:`)" @@ -73,6 +82,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> </button> </div> + + <!-- Unicodeのやつ --> <div v-else-if="shown && !category" class="body"> <button v-for="emoji in emojis" @@ -107,7 +118,6 @@ const emit = defineEmits<{ const toggleShown = (index) => { shown_fold.value[index] = !shown_fold.value[index]; - }; const toggleShown_fol = () => { diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 33a7d003a6..4f035777f3 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -153,20 +153,35 @@ const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); let split_categories = []; customEmojiCategories.value.forEach(e => { if (e !== null){ - split_categories.push(e.split('/')) + split_categories.push(e.split('/',2)) } }); const groupedData = {}; split_categories.forEach((item) => { - if (!groupedData[item[0]]) { - groupedData[item[0]] = []; - groupedData[item[0]].push(item[0]); - }else{ - groupedData[item[0]].push(item[1]); + if (!groupedData[item[0]]) { + groupedData[item[0]] = {}; + groupedData[item[0]][item[0]] = true; + if (item.length > 1) { + for (let i = 1; i < item.length; i++) { + groupedData[item[0]][item[i]] = true; + } } + } else { + if (item.length > 1) { + for (let i = 1; i < item.length; i++) { + groupedData[item[0]][item[i]] = true; + } + } + } }); -console.log(groupedData) + +// 結果を配列に変換 +for (const key in groupedData) { + groupedData[key] = Object.keys(groupedData[key]); +} + +console.log(split_categories,groupedData) watch(q, () => { if (emojisEl.value) emojisEl.value.scrollTop = 0; From 80d652746d7629a6c68161b0f361b4e280e335cb Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 22:40:40 +0900 Subject: [PATCH 165/501] remove hybrid-all-timeline and update --- .../backend/src/daemons/ServerStatsService.ts | 2 +- packages/backend/src/models/Note.ts | 6 - packages/backend/src/server/ServerModule.ts | 2 - .../backend/src/server/api/EndpointsModule.ts | 4 - packages/backend/src/server/api/endpoints.ts | 2 - .../src/server/api/stream/ChannelsService.ts | 3 - .../frontend/src/components/MkTimeline.vue | 13 +- pnpm-lock.yaml | 130 ++++++++---------- 8 files changed, 60 insertions(+), 102 deletions(-) diff --git a/packages/backend/src/daemons/ServerStatsService.ts b/packages/backend/src/daemons/ServerStatsService.ts index c5ef9b2fa3..fee2fe6f11 100644 --- a/packages/backend/src/daemons/ServerStatsService.ts +++ b/packages/backend/src/daemons/ServerStatsService.ts @@ -66,7 +66,7 @@ export class ServerStatsService implements OnApplicationShutdown { if (log.length > 200) log.pop(); }; - tick(); + await tick(); this.intervalId = setInterval(tick, interval); } diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index c376a75285..b573428e9d 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -17,12 +17,6 @@ import type { MiDriveFile } from './DriveFile.js'; export class MiNote { @PrimaryColumn(id()) public id: string; - - @Column('timestamp with time zone', { - comment: 'The created date of the Note.', - }) - public createdAt: Date; - @Column('timestamp with time zone', { default: null, }) diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index ed2c981136..fa81380f01 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -34,7 +34,6 @@ import { GlobalTimelineChannelService } from './api/stream/channels/global-timel import { HashtagChannelService } from './api/stream/channels/hashtag.js'; import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js'; import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js'; -import { HybridAllTimelineChannelService } from './api/stream/channels/hybrid-all-timeline.js'; import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js'; import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; @@ -80,7 +79,6 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; RoleTimelineChannelService, HomeTimelineChannelService, HybridTimelineChannelService, - HybridAllTimelineChannelService, LocalTimelineChannelService, QueueStatsChannelService, ServerStatsChannelService, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 79164e51df..26d4654785 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -266,7 +266,6 @@ import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; -import * as ep___notes_hybrid_All_Timeline from './endpoints/notes/hybrid-all-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; @@ -623,7 +622,6 @@ const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete' const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }; const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }; -const $notes_hybridAllTimeline: Provider = { provide: 'ep:notes/hybrid-all-timeline', useClass: ep___notes_hybrid_All_Timeline.default }; const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }; const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; @@ -983,7 +981,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_featured, $notes_globalTimeline, $notes_hybridTimeline, - $notes_hybridAllTimeline, $notes_localTimeline, $notes_mentions, $notes_polls_recommendation, @@ -1337,7 +1334,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_featured, $notes_globalTimeline, $notes_hybridTimeline, - $notes_hybridAllTimeline, $notes_localTimeline, $notes_mentions, $notes_polls_recommendation, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index c2ab5d85fa..0e76787ac6 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -266,7 +266,6 @@ import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; -import * as ep___notes_hybrid_All_Timeline from './endpoints/notes/hybrid-all-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; @@ -620,7 +619,6 @@ const eps = [ ['notes/featured', ep___notes_featured], ['notes/global-timeline', ep___notes_globalTimeline], ['notes/hybrid-timeline', ep___notes_hybridTimeline], - ['notes/hybrid-all-timeline', ep___notes_hybrid_All_Timeline], ['notes/local-timeline', ep___notes_localTimeline], ['notes/mentions', ep___notes_mentions], ['notes/polls/recommendation', ep___notes_polls_recommendation], diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index c418d314a6..bc942a9db5 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -19,7 +19,6 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; -import { HybridAllTimelineChannelService } from './channels/hybrid-all-timeline.js'; @Injectable() export class ChannelsService { constructor( @@ -27,7 +26,6 @@ export class ChannelsService { private homeTimelineChannelService: HomeTimelineChannelService, private localTimelineChannelService: LocalTimelineChannelService, private hybridTimelineChannelService: HybridTimelineChannelService, - private hybridAllTimelineChannelService: HybridAllTimelineChannelService, private globalTimelineChannelService: GlobalTimelineChannelService, private userListChannelService: UserListChannelService, private hashtagChannelService: HashtagChannelService, @@ -48,7 +46,6 @@ export class ChannelsService { case 'homeTimeline': return this.homeTimelineChannelService; case 'localTimeline': return this.localTimelineChannelService; case 'hybridTimeline': return this.hybridTimelineChannelService; - case 'hybridAllTimeline': return this.hybridAllTimelineChannelService; case 'globalTimeline': return this.globalTimelineChannelService; case 'userList': return this.userListChannelService; case 'hashtag': return this.hashtagChannelService; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 828a75582d..49215dd83a 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -127,18 +127,7 @@ if (props.src === 'antenna') { withFiles: props.onlyFiles ? true : undefined, }); connection.on('note', prepend); -} else if (props.src === 'all') { - endpoint = 'notes/hybrid-all-timeline'; - query = { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - }; - connection = stream.useChannel('hybridAllTimeline', { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - }); - connection.on('note', prepend); -} else if (props.src === 'global') { +}else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { withRenotes: props.withRenotes, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 08f6e9b121..8c4cf45582 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -673,6 +673,9 @@ importers: '@vue/compiler-sfc': specifier: 3.3.4 version: 3.3.4 + '@vueuse/core': + specifier: ^10.4.1 + version: 10.5.0(vue@3.3.4) astring: specifier: 1.8.6 version: 1.8.6 @@ -814,6 +817,9 @@ importers: vue: specifier: 3.3.4 version: 3.3.4 + vue-multiselect: + specifier: ^2.1.7 + version: 2.1.7 vue-prism-editor: specifier: 2.0.0-alpha.2 version: 2.0.0-alpha.2(vue@3.3.4) @@ -979,7 +985,7 @@ importers: version: 7.5.0 storybook-addon-misskey-theme: specifier: github:misskey-dev/storybook-addon-misskey-theme - version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.4.6)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0) + version: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.5.0)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0) summaly: specifier: github:misskey-dev/summaly version: github.com/misskey-dev/summaly/d2d8db49943ccb201c1b1b283e9d0a630519fac7 @@ -6361,17 +6367,6 @@ packages: - supports-color dev: true - /@storybook/channels@7.4.6: - resolution: {integrity: sha512-yPv/sfo2c18fM3fvG0i1xse63vG8l33Al/OU0k/dtovltPu001/HVa1QgBgsb/QrEfZtvGjGhmtdVeYb39fv3A==} - dependencies: - '@storybook/client-logger': 7.4.6 - '@storybook/core-events': 7.4.6 - '@storybook/global': 5.0.0 - qs: 6.11.1 - telejson: 7.2.0 - tiny-invariant: 1.3.1 - dev: true - /@storybook/channels@7.5.0: resolution: {integrity: sha512-/7QJS1UA7TX3uhZqCpjv4Ib8nfMnDOJrBWvjiXiUONaRcSk/he5X+W1Zz/c7dgt+wkYuAh+evjc7glIaBhVNVQ==} dependencies: @@ -6435,12 +6430,6 @@ packages: - utf-8-validate dev: true - /@storybook/client-logger@7.4.6: - resolution: {integrity: sha512-XDw31ZziU//86PKuMRnmc+L/G0VopaGKENQOGEpvAXCU9IZASwGKlKAtcyosjrpi+ZiUXlMgUXCpXM7x3b1Ehw==} - dependencies: - '@storybook/global': 5.0.0 - dev: true - /@storybook/client-logger@7.5.0: resolution: {integrity: sha512-JV7J9vc69f9Il4uW62NIeweUU7O38VwFWxtCkhd0bcBA/9RG0go4M2avzxYYEAe9kIOX9IBBk8WGzMacwW4gKQ==} dependencies: @@ -6468,29 +6457,6 @@ packages: - supports-color dev: true - /@storybook/components@7.4.6(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-nIRBhewAgrJJVafyCzuaLx1l+YOfvvD5dOZ0JxZsxJsefOdw1jFpUqUZ5fIpQ2moyvrR0mAUFw378rBfMdHz5Q==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@radix-ui/react-select': 1.2.2(react-dom@18.2.0)(react@18.2.0) - '@radix-ui/react-toolbar': 1.0.4(react-dom@18.2.0)(react@18.2.0) - '@storybook/client-logger': 7.4.6 - '@storybook/csf': 0.1.0 - '@storybook/global': 5.0.0 - '@storybook/theming': 7.4.6(react-dom@18.2.0)(react@18.2.0) - '@storybook/types': 7.4.6 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - use-resize-observer: 9.1.0(react-dom@18.2.0)(react@18.2.0) - util-deprecate: 1.0.2 - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - dev: true - /@storybook/components@7.5.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-6lmZ6PbS27xN32vTJ/NvgaiKkFIQRzZuBeBIg2u+FoAEgCiCwRXjZKe/O8NZC2Xr0uf97+7U2P0kD4Hwr9SNhw==} peerDependencies: @@ -6552,12 +6518,6 @@ packages: - supports-color dev: true - /@storybook/core-events@7.4.6: - resolution: {integrity: sha512-r5vrE+32lwrJh1NGFr1a0mWjvxo7q8FXYShylcwRWpacmL5NTtLkrXOoJSeGvJ4yKNYkvxQFtOPId4lzDxa32w==} - dependencies: - ts-dedent: 2.2.0 - dev: true - /@storybook/core-events@7.5.0: resolution: {integrity: sha512-FsD+clTzayqprbVllnL8LLch+uCslJFDgsv7Zh99/zoi7OHtHyauoCZkdLBSiDzgc84qS41dY19HqX1/y7cnOw==} dependencies: @@ -6890,20 +6850,6 @@ packages: ts-dedent: 2.2.0 dev: true - /@storybook/theming@7.4.6(react-dom@18.2.0)(react@18.2.0): - resolution: {integrity: sha512-HW77iJ9ptCMqhoBOYFjRQw7VBap+38fkJGHP5KylEJCyYCgIAm2dEcQmtWpMVYFssSGcb6djfbtAMhYU4TL4Iw==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 - dependencies: - '@emotion/use-insertion-effect-with-fallbacks': 1.0.0(react@18.2.0) - '@storybook/client-logger': 7.4.6 - '@storybook/global': 5.0.0 - memoizerific: 1.11.3 - react: 18.2.0 - react-dom: 18.2.0(react@18.2.0) - dev: true - /@storybook/theming@7.5.0(react-dom@18.2.0)(react@18.2.0): resolution: {integrity: sha512-uTo97oh+pvmlfsZocFq5qae0zGo0VGk7oiBqNSSw6CiTqE1rIuSxoPrMAY+oCTWCUZV7DjONIGvpnGl2QALsAw==} peerDependencies: @@ -6918,15 +6864,6 @@ packages: react-dom: 18.2.0(react@18.2.0) dev: true - /@storybook/types@7.4.6: - resolution: {integrity: sha512-6QLXtMVsFZFpzPkdGWsu/iuc8na9dnS67AMOBKm5qCLPwtUJOYkwhMdFRSSeJthLRpzV7JLAL8Kwvl7MFP3QSw==} - dependencies: - '@storybook/channels': 7.4.6 - '@types/babel__core': 7.20.0 - '@types/express': 4.17.17 - file-system-cache: 2.3.0 - dev: true - /@storybook/types@7.5.0: resolution: {integrity: sha512-fiOUnHKFi/UZSfvc53F0WEQCiquqcSqslL3f5EffwQRiXfeXlGavJb0kU03BO+CvOXcliRn6qKSF2dL0Rgb7Xw==} dependencies: @@ -8054,6 +7991,10 @@ packages: '@types/node': 20.8.6 dev: true + /@types/web-bluetooth@0.0.18: + resolution: {integrity: sha512-v/ZHEj9xh82usl8LMR3GarzFY1IrbXJw5L4QfQhokjRV91q+SelFqxQWSep1ucXEZ22+dSTwLFkXeur25sPIbw==} + dev: false + /@types/web-push@3.6.1: resolution: {integrity: sha512-Zu6Iju7c4IlE8I8eEeFLYRb7XFqvHFmWWAYr1cmug9EX3c6CDarxIXWN/GO0sxjbJLkHPwozUzp6cLdXsrq7Ew==} dependencies: @@ -8485,6 +8426,31 @@ packages: - typescript dev: true + /@vueuse/core@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-z/tI2eSvxwLRjOhDm0h/SXAjNm8N5ld6/SC/JQs6o6kpJ6Ya50LnEL8g5hoYu005i28L0zqB5L5yAl8Jl26K3A==} + dependencies: + '@types/web-bluetooth': 0.0.18 + '@vueuse/metadata': 10.5.0 + '@vueuse/shared': 10.5.0(vue@3.3.4) + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + + /@vueuse/metadata@10.5.0: + resolution: {integrity: sha512-fEbElR+MaIYyCkeM0SzWkdoMtOpIwO72x8WsZHRE7IggiOlILttqttM69AS13nrDxosnDBYdyy3C5mR1LCxHsw==} + dev: false + + /@vueuse/shared@10.5.0(vue@3.3.4): + resolution: {integrity: sha512-18iyxbbHYLst9MqU1X1QNdMHIjks6wC7XTVf0KNOv5es/Ms6gjVFCAAWTVP2JStuGqydg3DT+ExpFORUEi9yhg==} + dependencies: + vue-demi: 0.14.6(vue@3.3.4) + transitivePeerDependencies: + - '@vue/composition-api' + - vue + dev: false + /@webgpu/types@0.1.30: resolution: {integrity: sha512-9AXJSmL3MzY8ZL//JjudA//q+2kBRGhLBFpkdGksWIuxrMy81nFrCzj2Am+mbh8WoU6rXmv7cY5E3rdlyru2Qg==} requiresBuild: true @@ -19229,6 +19195,21 @@ packages: vue: 3.3.4 dev: false + /vue-demi@0.14.6(vue@3.3.4): + resolution: {integrity: sha512-8QA7wrYSHKaYgUxDA5ZC24w+eHm3sYCbp0EzcDwKqN3p6HqtTCGR/GVsPyZW92unff4UlcSh++lmqDWN3ZIq4w==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + peerDependencies: + '@vue/composition-api': ^1.0.0-rc.1 + vue: ^3.0.0-0 || ^2.6.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + dependencies: + vue: 3.3.4 + dev: false + /vue-docgen-api@4.64.1(vue@3.3.4): resolution: {integrity: sha512-jbOf7ByE3Zvtuk+429Jorl+eIeh2aB2Fx1GUo3xJd1aByJWE8KDlSEa6b11PB1ze8f0sRUBraRDinICCk0KY7g==} dependencies: @@ -19273,6 +19254,11 @@ packages: vue: 3.3.4 dev: true + /vue-multiselect@2.1.7: + resolution: {integrity: sha512-KIegcN+Ntwg3cbkY/jhw2s/+XJUM0Lpi/LcKFYCS8PrZHcWBl2iKCVze7ZCnRj3w8H7/lUJ9v7rj9KQiNxApBw==} + engines: {node: '>= 4.0.0', npm: '>= 3.0.0'} + dev: false + /vue-prism-editor@2.0.0-alpha.2(vue@3.3.4): resolution: {integrity: sha512-Gu42ba9nosrE+gJpnAEuEkDMqG9zSUysIR8SdXUw8MQKDjBnnNR9lHC18uOr/ICz7yrA/5c7jHJr9lpElODC7w==} engines: {node: '>=10'} @@ -19753,7 +19739,7 @@ packages: sharp: 0.31.3 dev: false - github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.4.6)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0): + github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640(@storybook/blocks@7.5.0)(@storybook/components@7.5.0)(@storybook/core-events@7.5.0)(@storybook/manager-api@7.5.0)(@storybook/preview-api@7.5.0)(@storybook/theming@7.5.0)(@storybook/types@7.5.0)(react-dom@18.2.0)(react@18.2.0): resolution: {tarball: https://codeload.github.com/misskey-dev/storybook-addon-misskey-theme/tar.gz/cf583db098365b2ccc81a82f63ca9c93bc32b640} id: github.com/misskey-dev/storybook-addon-misskey-theme/cf583db098365b2ccc81a82f63ca9c93bc32b640 name: storybook-addon-misskey-theme @@ -19775,7 +19761,7 @@ packages: optional: true dependencies: '@storybook/blocks': 7.5.0(react-dom@18.2.0)(react@18.2.0) - '@storybook/components': 7.4.6(react-dom@18.2.0)(react@18.2.0) + '@storybook/components': 7.5.0(react-dom@18.2.0)(react@18.2.0) '@storybook/core-events': 7.5.0 '@storybook/manager-api': 7.5.0(react-dom@18.2.0)(react@18.2.0) '@storybook/preview-api': 7.5.0 From 4a941b22a9a7fd165ad5ebe3d832cb2e3b3db72a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 22:41:02 +0900 Subject: [PATCH 166/501] remove hybrid-all-timeline and update --- locales/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index c249eac574..2a400035bf 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -855,9 +855,9 @@ export interface Locale { "list": string; "emailNotConfiguredWarning": string; "ratio": string; + "showVisibilityColor": string; "newEmojis": string; "draftEmojis": string; - "showVisibilityColor": string; "previewNoteText": string; "customCss": string; "customCssWarn": string; From 6cc503dfc9cbe183af3992b12aaedf55417c60a6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 23:14:25 +0900 Subject: [PATCH 167/501] fix --- packages/frontend/src/pages/admin/instance-block.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 7a1aa25423..2058c60e32 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -33,6 +33,8 @@ import * as os from '@/os.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import MkSpacer from "@/components/global/MkSpacer.vue"; +import MkStickyContainer from "@/components/global/MkStickyContainer.vue"; let blockedHosts: string = $ref(''); let silencedHosts: string = $ref(''); From 781cd5dd6de774a673b148f8507ed51388363ce6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 23:21:14 +0900 Subject: [PATCH 168/501] fix --- .../src/components/MkEmojiPicker.section.vue | 20 +- .../frontend/src/components/MkEmojiPicker.vue | 997 +++++++++--------- 2 files changed, 496 insertions(+), 521 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 3be1b2bf37..dc5708f0e8 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -6,14 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> <section> - <!-- categoryが定義されてない(Unicodeの絵文字とか) --> + + <header v-if="!category" class="_acrylic" @click="shown = !shown"> <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) </header> - - <!-- categoryが定義されてるけど中身が1つしかない --> <header v-else-if="category.length === 1" class="_acrylic" @click="shown = !shown"> <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> {{ category[0] }} @@ -21,8 +20,6 @@ SPDX-License-Identifier: AGPL-3.0-only emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length }}) </header> - - <!-- categoryに1つ以上要素がある(フォルダが有る) --> <header v-else class="_acrylic" style="top:unset;" @click="toggleShown_fol"> <i class="toggle ti-fw" :class="shown_fol? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> {{ category[0] || i18n.ts.other }} @@ -32,16 +29,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-else> ({{ emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length - }}) + }}) 2 </template> </header> - <!-- フォルダが有るときのフォルダと絵文字表示する部分 --> <template v-for="(n, index) in category" v-if="shown_fol"> - - <!-- フォルダの部分 --> <header v-if="emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category[0]).length !== 0 || index!==0" - style="padding-left: 18px;" + style="top:unset;padding-left: 18px;" class="_acrylic" @click="toggleShown(index)" > @@ -51,8 +45,6 @@ SPDX-License-Identifier: AGPL-3.0-only emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === (index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).length }}) </header> - - <!-- 絵文字の部分 --> <div v-if="shown_fold[index]" class="body"> <button v-for="emoji in emojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category ===( index === 0 && category !== undefined ? category[0] : `${category[0]}/${n}`)).map(e => `:${e.name}:`)" @@ -68,7 +60,6 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> - <!-- categoryが1つしかないときのフォルダと絵文字表示する部分 --> <div v-if="shown && category" class="body"> <button v-for="emoji in emojis.filter(e => e.category === category[0]).map(e => `:${e.name}:`)" @@ -82,8 +73,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> </button> </div> - - <!-- Unicodeのやつ --> <div v-else-if="shown && !category" class="body"> <button v-for="emoji in emojis" @@ -118,6 +107,7 @@ const emit = defineEmits<{ const toggleShown = (index) => { shown_fold.value[index] = !shown_fold.value[index]; + }; const toggleShown_fol = () => { diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 981ea2f8c9..4d9bd29d37 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -4,103 +4,103 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> - <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> - <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> - <div ref="emojisEl" class="emojis" tabindex="-1"> - <section class="result"> - <div v-if="searchResultCustom.length > 0" class="body"> - <button - v-for="emoji in searchResultCustom" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji class="emoji" :name="emoji.name"/> - </button> - </div> - <div v-if="searchResultUnicode.length > 0" class="body"> - <button - v-for="emoji in searchResultUnicode" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkEmoji class="emoji" :emoji="emoji.char"/> - </button> - </div> - </section> + <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> + <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> + <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> + <div ref="emojisEl" class="emojis" tabindex="-1"> + <section class="result"> + <div v-if="searchResultCustom.length > 0" class="body"> + <button + v-for="emoji in searchResultCustom" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji class="emoji" :name="emoji.name"/> + </button> + </div> + <div v-if="searchResultUnicode.length > 0" class="body"> + <button + v-for="emoji in searchResultUnicode" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkEmoji class="emoji" :emoji="emoji.char"/> + </button> + </div> + </section> - <div v-if="tab === 'index'" class="group index"> - <section v-if="showPinned"> - <div class="body"> - <button - v-for="emoji in pinned" - :key="emoji" - :data-emoji="emoji" - class="_button item" - tabindex="0" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> - </section> + <div v-if="tab === 'index'" class="group index"> + <section v-if="showPinned"> + <div class="body"> + <button + v-for="emoji in pinned" + :key="emoji" + :data-emoji="emoji" + class="_button item" + tabindex="0" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> - <section> - <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> - <div class="body"> - <button - v-for="emoji in recentlyUsedEmojis" - :key="emoji" - class="_button item" - :data-emoji="emoji" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> - </section> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> - <XSection - v-for="category in groupedData" - :key="`custom:${category}`" - :initialShown="false" - :emojis="computed(() => customEmojis.filter(emoji => !emoji.draft).filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" - @chosen="chosen" - > - {{ category || i18n.ts.other }} - </XSection> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.emoji }}</header> - <XSection - v-for="category in categories" - :key="category" - :emojis="emojiCharByCategory.get(category) ?? []" - @chosen="chosen" - >{{ category }} - </XSection> + <section> + <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> + <div class="body"> + <button + v-for="emoji in recentlyUsedEmojis" + :key="emoji" + class="_button item" + :data-emoji="emoji" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> - </div> - </div> - <div class="tabs"> - <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> - </div> -</div> + <XSection + v-for="category in groupedData" + :key="`custom:${category}`" + :initialShown="false" + :emojis="computed(() => customEmojis.filter(filterAvailable))" + :category="category" + @chosen="chosen" + /> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.emoji }}</header> + <XSection + v-for="category in categories" + :key="category" + :emojis="emojiCharByCategory.get(category) ?? []" + @chosen="chosen" + >{{ category }} + </XSection> + + </div> + </div> + <div class="tabs"> + <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> + </div> + </div> </template> <script lang="ts" setup> @@ -118,29 +118,29 @@ import {customEmojiCategories, customEmojis, customEmojisMap} from '@/custom-emo import { $i } from '@/account.js'; const props = withDefaults(defineProps<{ - showPinned?: boolean; - asReactionPicker?: boolean; - maxHeight?: number; - asDrawer?: boolean; - asWindow?: boolean; + showPinned?: boolean; + asReactionPicker?: boolean; + maxHeight?: number; + asDrawer?: boolean; + asWindow?: boolean; }>(), { - showPinned: true, + showPinned: true, }); const emit = defineEmits<{ - (ev: 'chosen', v: string): void; + (ev: 'chosen', v: string): void; }>(); const searchEl = shallowRef<HTMLInputElement>(); const emojisEl = shallowRef<HTMLDivElement>(); const { - reactions: pinned, - reactionPickerSize, - reactionPickerWidth, - reactionPickerHeight, - disableShowingAnimatedImages, - recentlyUsedEmojis, + reactions: pinned, + reactionPickerSize, + reactionPickerWidth, + reactionPickerHeight, + disableShowingAnimatedImages, + recentlyUsedEmojis, } = defaultStore.reactiveState; const size = computed(() => props.asReactionPicker ? reactionPickerSize.value : 1); @@ -153,521 +153,506 @@ const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); let split_categories = []; customEmojiCategories.value.forEach(e => { if (e !== null){ - split_categories.push(e.split('/',2)) + split_categories.push(e.split('/')) } }); const groupedData = {}; split_categories.forEach((item) => { if (!groupedData[item[0]]) { - groupedData[item[0]] = {}; - groupedData[item[0]][item[0]] = true; - if (item.length > 1) { - for (let i = 1; i < item.length; i++) { - groupedData[item[0]][item[i]] = true; - } - } - } else { - if (item.length > 1) { - for (let i = 1; i < item.length; i++) { - groupedData[item[0]][item[i]] = true; - } - } + groupedData[item[0]] = []; + groupedData[item[0]].push(item[0]); + }else{ + groupedData[item[0]].push(item[1]); } }); - -// 結果を配列に変換 -for (const key in groupedData) { - groupedData[key] = Object.keys(groupedData[key]); -} - -console.log(split_categories,groupedData) +console.log(groupedData) watch(q, () => { - if (emojisEl.value) emojisEl.value.scrollTop = 0; + if (emojisEl.value) emojisEl.value.scrollTop = 0; - if (q.value === '') { - searchResultCustom.value = []; - searchResultUnicode.value = []; - return; - } + if (q.value === '') { + searchResultCustom.value = []; + searchResultUnicode.value = []; + return; + } - const newQ = q.value.replace(/:/g, '').toLowerCase(); + const newQ = q.value.replace(/:/g, '').toLowerCase(); - const searchCustom = () => { - const max = 100; - const emojis = customEmojis.value.filter(emoji => !emoji.draft); - const matches = new Set<Misskey.entities.CustomEmoji>(); + const searchCustom = () => { + const max = 100; + const emojis = customEmojis.value; + const matches = new Set<Misskey.entities.CustomEmoji>(); - const exactMatch = emojis.find(emoji => emoji.name === newQ); - if (exactMatch) matches.add(exactMatch); + const exactMatch = emojis.find(emoji => emoji.name === newQ); + if (exactMatch) matches.add(exactMatch); - if (newQ.includes(' ')) { // AND検索 - const keywords = newQ.split(' '); + if (newQ.includes(' ')) { // AND検索 + const keywords = newQ.split(' '); - // 名前にキーワードが含まれている - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + // 名前にキーワードが含まれている + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - // 名前またはエイリアスにキーワードが含まれている - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } else { - for (const emoji of emojis) { - if (emoji.name.startsWith(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + // 名前またはエイリアスにキーワードが含まれている + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } else { + for (const emoji of emojis) { + if (emoji.name.startsWith(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.aliases.some(alias => alias.startsWith(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias.startsWith(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.name.includes(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.name.includes(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.aliases.some(alias => alias.includes(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias.includes(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } - return matches; - }; + return matches; + }; - const searchUnicode = () => { - const max = 100; - const emojis = emojilist; - const matches = new Set<UnicodeEmojiDef>(); + const searchUnicode = () => { + const max = 100; + const emojis = emojilist; + const matches = new Set<UnicodeEmojiDef>(); - const exactMatch = emojis.find(emoji => emoji.name === newQ); - if (exactMatch) matches.add(exactMatch); + const exactMatch = emojis.find(emoji => emoji.name === newQ); + if (exactMatch) matches.add(exactMatch); - if (newQ.includes(' ')) { // AND検索 - const keywords = newQ.split(' '); + if (newQ.includes(' ')) { // AND検索 + const keywords = newQ.split(' '); - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (keywords.every(keyword => index[emoji.char].some(k => k.includes(keyword)))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } - } else { - for (const emoji of emojis) { - if (emoji.name.startsWith(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (keywords.every(keyword => index[emoji.char].some(k => k.includes(keyword)))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } + } else { + for (const emoji of emojis) { + if (emoji.name.startsWith(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (index[emoji.char].some(k => k.startsWith(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (index[emoji.char].some(k => k.startsWith(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } - for (const emoji of emojis) { - if (emoji.name.includes(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.name.includes(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (index[emoji.char].some(k => k.includes(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } - } + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (index[emoji.char].some(k => k.includes(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } + } - return matches; - }; + return matches; + }; - searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); - searchResultUnicode.value = Array.from(searchUnicode()); + searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); + searchResultUnicode.value = Array.from(searchUnicode()); }); function filterAvailable(emoji: Misskey.entities.CustomEmoji): boolean { - return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); + return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); } function focus() { - if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { - searchEl.value?.focus({ - preventScroll: true, - }); - } + if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { + searchEl.value?.focus({ + preventScroll: true, + }); + } } function reset() { - if (emojisEl.value) emojisEl.value.scrollTop = 0; - q.value = ''; + if (emojisEl.value) emojisEl.value.scrollTop = 0; + q.value = ''; } function getKey(emoji: string | Misskey.entities.CustomEmoji | UnicodeEmojiDef): string { - return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; + return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; } /** @see MkEmojiPicker.section.vue */ function computeButtonTitle(ev: MouseEvent): void { - const elm = ev.target as HTMLElement; - const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + const elm = ev.target as HTMLElement; + const emoji = elm.dataset.emoji as string; + elm.title = getEmojiName(emoji) ?? emoji; } function chosen(emoji: any, ev?: MouseEvent) { - const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; - if (el) { - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); - } + const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } - const key = getKey(emoji); - emit('chosen', key); + const key = getKey(emoji); + emit('chosen', key); - // 最近使った絵文字更新 - if (!pinned.value.includes(key)) { - let recents = defaultStore.state.recentlyUsedEmojis; - recents = recents.filter((emoji: any) => emoji !== key); - recents.unshift(key); - defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); - } + // 最近使った絵文字更新 + if (!pinned.value.includes(key)) { + let recents = defaultStore.state.recentlyUsedEmojis; + recents = recents.filter((emoji: any) => emoji !== key); + recents.unshift(key); + defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); + } } function input(): void { - // Using custom input event instead of v-model to respond immediately on - // Android, where composition happens on all languages - // (v-model does not update during composition) - q.value = searchEl.value?.value.trim() ?? ''; + // Using custom input event instead of v-model to respond immediately on + // Android, where composition happens on all languages + // (v-model does not update during composition) + q.value = searchEl.value?.value.trim() ?? ''; } function paste(event: ClipboardEvent): void { - const pasted = event.clipboardData?.getData('text') ?? ''; - if (done(pasted)) { - event.preventDefault(); - } + const pasted = event.clipboardData?.getData('text') ?? ''; + if (done(pasted)) { + event.preventDefault(); + } } function onEnter(ev: KeyboardEvent) { - if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; - done(); + if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; + done(); } function done(query?: string): boolean | void { - if (query == null) query = q.value; - if (query == null || typeof query !== 'string') return; + if (query == null) query = q.value; + if (query == null || typeof query !== 'string') return; - const q2 = query.replace(/:/g, ''); - const exactMatchCustom = customEmojisMap.get(q2); - if (exactMatchCustom) { - chosen(exactMatchCustom); - return true; - } - const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2); - if (exactMatchUnicode) { - chosen(exactMatchUnicode); - return true; - } - if (searchResultCustom.value.length > 0) { - chosen(searchResultCustom.value[0]); - return true; - } - if (searchResultUnicode.value.length > 0) { - chosen(searchResultUnicode.value[0]); - return true; - } + const q2 = query.replace(/:/g, ''); + const exactMatchCustom = customEmojisMap.get(q2); + if (exactMatchCustom) { + chosen(exactMatchCustom); + return true; + } + const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2); + if (exactMatchUnicode) { + chosen(exactMatchUnicode); + return true; + } + if (searchResultCustom.value.length > 0) { + chosen(searchResultCustom.value[0]); + return true; + } + if (searchResultUnicode.value.length > 0) { + chosen(searchResultUnicode.value[0]); + return true; + } } onMounted(() => { - focus(); + focus(); }); defineExpose({ - focus, - reset, + focus, + reset, }); </script> <style lang="scss" scoped> .omfetrab { - $pad: 8px; + $pad: 8px; - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; - &.s1 { - --eachSize: 40px; - } + &.s1 { + --eachSize: 40px; + } - &.s2 { - --eachSize: 45px; - } + &.s2 { + --eachSize: 45px; + } - &.s3 { - --eachSize: 50px; - } + &.s3 { + --eachSize: 50px; + } - &.w1 { - width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr; - } + &.w1 { + width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr; + } - &.w2 { - width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w2 { + width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w3 { - width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w3 { + width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w4 { - width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w4 { + width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w5 { - width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w5 { + width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.h1 { - height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); - } + &.h1 { + height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); + } - &.h2 { - height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); - } + &.h2 { + height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); + } - &.h3 { - height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); - } + &.h3 { + height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); + } - &.h4 { - height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); - } + &.h4 { + height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); + } - &.asDrawer { - width: 100% !important; + &.asDrawer { + width: 100% !important; - > .emojis { - ::v-deep(section) { - > header { - height: 32px; - line-height: 32px; - padding: 0 12px; - font-size: 15px; - } + > .emojis { + ::v-deep(section) { + > header { + height: 32px; + line-height: 32px; + padding: 0 12px; + font-size: 15px; + } - > .body { - display: grid; - grid-template-columns: var(--columns); - font-size: 30px; + > .body { + display: grid; + grid-template-columns: var(--columns); + font-size: 30px; - > .item { - aspect-ratio: 1 / 1; - width: auto; - height: auto; - min-width: 0; - } - } - } - } - } + > .item { + aspect-ratio: 1 / 1; + width: auto; + height: auto; + min-width: 0; + } + } + } + } + } - &.asWindow { - width: 100% !important; - height: 100% !important; + &.asWindow { + width: 100% !important; + height: 100% !important; - > .emojis { - ::v-deep(section) { - > .body { - display: grid; - grid-template-columns: var(--columns); - font-size: 30px; + > .emojis { + ::v-deep(section) { + > .body { + display: grid; + grid-template-columns: var(--columns); + font-size: 30px; - > .item { - aspect-ratio: 1 / 1; - width: auto; - height: auto; - min-width: 0; - } - } - } - } - } + > .item { + aspect-ratio: 1 / 1; + width: auto; + height: auto; + min-width: 0; + } + } + } + } + } - > .search { - width: 100%; - padding: 12px; - box-sizing: border-box; - font-size: 1em; - outline: none; - border: none; - background: transparent; - color: var(--fg); + > .search { + width: 100%; + padding: 12px; + box-sizing: border-box; + font-size: 1em; + outline: none; + border: none; + background: transparent; + color: var(--fg); - &:not(:focus):not(.filled) { - margin-bottom: env(safe-area-inset-bottom, 0px); - } + &:not(:focus):not(.filled) { + margin-bottom: env(safe-area-inset-bottom, 0px); + } - &:not(.filled) { - order: 1; - z-index: 2; - box-shadow: 0px -1px 0 0px var(--divider); - } - } + &:not(.filled) { + order: 1; + z-index: 2; + box-shadow: 0px -1px 0 0px var(--divider); + } + } - > .tabs { - display: flex; - display: none; + > .tabs { + display: flex; + display: none; - > .tab { - flex: 1; - height: 38px; - border-top: solid 0.5px var(--divider); + > .tab { + flex: 1; + height: 38px; + border-top: solid 0.5px var(--divider); - &.active { - border-top: solid 1px var(--accent); - color: var(--accent); - } - } - } + &.active { + border-top: solid 1px var(--accent); + color: var(--accent); + } + } + } - > .emojis { - height: 100%; - overflow-y: auto; - overflow-x: hidden; + > .emojis { + height: 100%; + overflow-y: auto; + overflow-x: hidden; - scrollbar-width: none; + scrollbar-width: none; - &::-webkit-scrollbar { - display: none; - } + &::-webkit-scrollbar { + display: none; + } - > .group { - &:not(.index) { - padding: 4px 0 8px 0; - border-top: solid 0.5px var(--divider); - } + > .group { + &:not(.index) { + padding: 4px 0 8px 0; + border-top: solid 0.5px var(--divider); + } - > header { - /*position: sticky; - top: 0; - left: 0;*/ - height: 32px; - line-height: 32px; - z-index: 2; - padding: 0 8px; - font-size: 12px; - } - } + > header { + /*position: sticky; +top: 0; +left: 0;*/ + height: 32px; + line-height: 32px; + z-index: 2; + padding: 0 8px; + font-size: 12px; + } + } - ::v-deep(section) { - > header { - position: sticky; - top: 0; - left: 0; - height: 32px; - line-height: 32px; - z-index: 1; - padding: 0 8px; - font-size: 12px; - cursor: pointer; + ::v-deep(section) { + > header { + position: sticky; + top: 0; + left: 0; + height: 32px; + line-height: 32px; + z-index: 1; + padding: 0 8px; + font-size: 12px; + cursor: pointer; - &:hover { - color: var(--accent); - } - } + &:hover { + color: var(--accent); + } + } - > .body { - position: relative; - padding: $pad; + > .body { + position: relative; + padding: $pad; - > .item { - position: relative; - padding: 0; - width: var(--eachSize); - height: var(--eachSize); - contain: strict; - border-radius: 4px; - font-size: 24px; + > .item { + position: relative; + padding: 0; + width: var(--eachSize); + height: var(--eachSize); + contain: strict; + border-radius: 4px; + font-size: 24px; - &:focus-visible { - outline: solid 2px var(--focus); - z-index: 1; - } + &:focus-visible { + outline: solid 2px var(--focus); + z-index: 1; + } - &:hover { - background: rgba(0, 0, 0, 0.05); - } + &:hover { + background: rgba(0, 0, 0, 0.05); + } - &:active { - background: var(--accent); - box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); - } + &:active { + background: var(--accent); + box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); + } - > .emoji { - height: 1.25em; - vertical-align: -.25em; - pointer-events: none; - width: 100%; - object-fit: contain; - } - } - } + > .emoji { + height: 1.25em; + vertical-align: -.25em; + pointer-events: none; + width: 100%; + object-fit: contain; + } + } + } - &.result { - border-bottom: solid 0.5px var(--divider); + &.result { + border-bottom: solid 0.5px var(--divider); - &:empty { - display: none; - } - } - } - } + &:empty { + display: none; + } + } + } + } } </style> From 0d4a5338c8d32edd58c21cbe22114e7b407eec3e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 23:29:14 +0900 Subject: [PATCH 169/501] fix --- packages/frontend/src/components/MkEmojiPicker.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 4d9bd29d37..9533463591 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -77,7 +77,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="category in groupedData" :key="`custom:${category}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(filterAvailable))" + :emojis="computed(() => customEmojis.filter(emoji => !emoji.draft).filter(filterAvailable))" :category="category" @chosen="chosen" /> @@ -181,7 +181,7 @@ watch(q, () => { const searchCustom = () => { const max = 100; - const emojis = customEmojis.value; + const emojis = customEmojis.value.filter(emoji => !emoji.draft);; const matches = new Set<Misskey.entities.CustomEmoji>(); const exactMatch = emojis.find(emoji => emoji.name === newQ); From a8c19c624ad5324b12f479124ceb91870007e21e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 18 Oct 2023 23:33:38 +0900 Subject: [PATCH 170/501] auto add --- .../backend/src/server/api/endpoints/admin/emoji/add-draft.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 7088f801e9..53f3f54941 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -66,7 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { aliases: ps.aliases ?? [], license: ps.license ?? null, host: null, - draft: true, + draft: false, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: [], From 4f6b0ccf6b7aeb64ec9ae7968f01e77808d2087d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 19 Oct 2023 01:16:11 +0900 Subject: [PATCH 171/501] emoji --- .../1697641012204-DiscordWebhookUrl.js | 11 +++++ .../migration/1697642704514-EmojiBotToken.js | 11 +++++ .../migration/1697645425687-BaseUrl.js | 15 +++++++ packages/backend/src/models/Meta.ts | 15 +++++++ .../api/endpoints/admin/emoji/add-draft.ts | 41 +++++++++++++++++-- .../src/server/api/endpoints/admin/meta.ts | 7 ++++ .../server/api/endpoints/admin/update-meta.ts | 13 +++++- .../frontend/src/components/MkEmojiPicker.vue | 2 +- .../src/pages/admin/other-settings.vue | 23 ++++++++++- 9 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 packages/backend/migration/1697641012204-DiscordWebhookUrl.js create mode 100644 packages/backend/migration/1697642704514-EmojiBotToken.js create mode 100644 packages/backend/migration/1697645425687-BaseUrl.js diff --git a/packages/backend/migration/1697641012204-DiscordWebhookUrl.js b/packages/backend/migration/1697641012204-DiscordWebhookUrl.js new file mode 100644 index 0000000000..bde713177d --- /dev/null +++ b/packages/backend/migration/1697641012204-DiscordWebhookUrl.js @@ -0,0 +1,11 @@ +export class DiscordWebhookUrl1697641012204 { + name = 'DiscordWebhookUrl1697641012204' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "DiscordWebhookUrl" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "DiscordWebhookUrl"`); + } +} diff --git a/packages/backend/migration/1697642704514-EmojiBotToken.js b/packages/backend/migration/1697642704514-EmojiBotToken.js new file mode 100644 index 0000000000..8e284ede41 --- /dev/null +++ b/packages/backend/migration/1697642704514-EmojiBotToken.js @@ -0,0 +1,11 @@ +export class EmojiBotToken1697642704514 { + name = 'EmojiBotToken1697642704514' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "EmojiBotToken" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "EmojiBotToken"`); + } +} diff --git a/packages/backend/migration/1697645425687-BaseUrl.js b/packages/backend/migration/1697645425687-BaseUrl.js new file mode 100644 index 0000000000..1d1020b1cf --- /dev/null +++ b/packages/backend/migration/1697645425687-BaseUrl.js @@ -0,0 +1,15 @@ +export class BaseUrl1697645425687 { + name = 'BaseUrl1697645425687' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "ApiBase" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "preservedUsernames" SET DEFAULT '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }'`); + await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" SET NOT NULL`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" DROP NOT NULL`); + await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "preservedUsernames" SET DEFAULT '{admin,administrator,root,system,maintainer,host,mod,moderator,owner,superuser,staff,auth,i,me,everyone,all,mention,mentions,example,user,users,account,accounts,official,help,helps,support,supports,info,information,informations,announce,announces,announcement,announcements,notice,notification,notifications,dev,developer,developers,tech,misskey}'`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "ApiBase"`); + } +} diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 23ae513ede..babec97980 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -196,11 +196,26 @@ export class MiMeta { }) public enableRecaptcha: boolean; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public DiscordWebhookUrl: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public EmojiBotToken: string | null; @Column('varchar', { length: 1024, nullable: true, }) public recaptchaSiteKey: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public ApiBase: string | null; @Column('varchar', { length: 1024, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 53f3f54941..aae6f6df39 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -5,7 +5,7 @@ import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; - +import { MetaService } from '@/core/MetaService.js'; export const meta = { tags: ['admin'], @@ -46,12 +46,12 @@ export const paramDef = { // eslint-disable-next-line import/no-default-export @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - + private metaService: MetaService, private customEmojiService: CustomEmojiService, - private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { @@ -71,6 +71,41 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); + const {ApiBase,EmojiBotToken,DiscordWebhookUrl} = (await this.metaService.fetch()) + const data_disc = {"username": "絵文字追加通知ちゃん", + 'content': + '絵文字名 : :'+ ps.name +':\n' + + 'カテゴリ : ' + ps.category + '\n'+ + 'ライセンス : '+ ps.license + '\n'+ + 'タグ : '+ps.aliases+ '\n'+ + '追加したユーザー : ' + '@'+me.username + '\n' + } + + const data_Miss = { + 'i': EmojiBotToken, + 'text': + '絵文字名 : :' + ps.name + ':\n' + + 'カテゴリ : ' + ps.category + '\n' + + 'ライセンス : ' + ps.license + '\n' + + 'タグ : ' + ps.aliases + '\n' + + '追加したユーザー : ' + '@' + me.username + '\n' + }; + + await fetch(ApiBase+'/notes/create', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body:JSON.stringify( data_Miss) + }) + + await fetch(DiscordWebhookUrl, { + 'method':'post', + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data_disc), + }) return { id: emoji.id, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index f294934344..4b0d16b666 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -311,6 +311,10 @@ export const meta = { type: 'number', optional: false, nullable: false, }, + DiscordWebhookUrl: { + type: 'string', + optional: false, nullable: true, + }, }, }, } as const; @@ -424,6 +428,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax, perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, notesPerOneAd: instance.notesPerOneAd, + DiscordWebhookUrl: instance.DiscordWebhookUrl, + EmojiBotToken: instance.EmojiBotToken, + ApiBase: instance.ApiBase }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index f05819b186..adf6680d50 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -81,6 +81,7 @@ export const paramDef = { }, }, summalyProxy: { type: 'string', nullable: true }, + DiscordWebhookUrl:{ type: 'string', nullable: true}, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, enableEmail: { type: 'boolean' }, @@ -132,6 +133,8 @@ export const paramDef = { type: 'string', }, }, + EmojiBotToken:{ type: 'string', nullable: true}, + ApiBase:{ type: 'string',nullable:true} }, required: [], } as const; @@ -175,7 +178,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.themeColor !== undefined) { set.themeColor = ps.themeColor; } - + if (ps.DiscordWebhookUrl !== undefined){ + set.DiscordWebhookUrl = ps.DiscordWebhookUrl + } + if (ps.EmojiBotToken !== undefined){ + set.EmojiBotToken = ps.EmojiBotToken + } + if (ps.ApiBase !== undefined){ + set.ApiBase = ps.ApiBase + } if (ps.mascotImageUrl !== undefined) { set.mascotImageUrl = ps.mascotImageUrl; } diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 9533463591..36a9e5c892 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -181,7 +181,7 @@ watch(q, () => { const searchCustom = () => { const max = 100; - const emojis = customEmojis.value.filter(emoji => !emoji.draft);; + const emojis = customEmojis.value.filter(emoji => !emoji.draft); const matches = new Set<Misskey.entities.CustomEmoji>(); const exactMatch = emojis.find(emoji => emoji.name === newQ); diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index 7574c9d7d9..5cf90f49f9 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -36,6 +36,18 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> </MkSwitch> </div> + <MkInput v-model="DiscordWebhookUrl" type="password"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Discord Webhook URL</template> + </MkInput> + <MkInput v-model="EmojiBotToken" type="password"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>EmojiBotToken</template> + </MkInput> + <MkInput v-model="ApiBase"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>ApiBase</template> + </MkInput> </div> </FormSuspense> </MkSpacer> @@ -51,18 +63,24 @@ import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkSwitch from '@/components/MkSwitch.vue'; +import MkInput from "@/components/MkInput.vue"; let enableServerMachineStats: boolean = $ref(false); let enableIdenticonGeneration: boolean = $ref(false); let enableChartsForRemoteUser: boolean = $ref(false); let enableChartsForFederatedInstances: boolean = $ref(false); - +let DiscordWebhookUrl: string | null = $ref(null); +let EmojiBotToken: string | null = $ref(null); +let ApiBase:string | null = $ref(null) async function init() { const meta = await os.api('admin/meta'); enableServerMachineStats = meta.enableServerMachineStats; enableIdenticonGeneration = meta.enableIdenticonGeneration; enableChartsForRemoteUser = meta.enableChartsForRemoteUser; enableChartsForFederatedInstances = meta.enableChartsForFederatedInstances; + DiscordWebhookUrl = meta.DiscordWebhookUrl; + EmojiBotToken = meta.EmojiBotToken; + ApiBase = meta.ApiBase; } function save() { @@ -71,6 +89,9 @@ function save() { enableIdenticonGeneration, enableChartsForRemoteUser, enableChartsForFederatedInstances, + DiscordWebhookUrl, + EmojiBotToken, + ApiBase }).then(() => { fetchInstance(); }); From 1b558e7243992d97f971fa2caec2b21a65cfe8ff Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 19 Oct 2023 02:17:16 +0900 Subject: [PATCH 172/501] =?UTF-8?q?Feat:=20=E3=82=B0=E3=83=AD=E3=83=BC?= =?UTF-8?q?=E3=83=90=E3=83=AB=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E8=A1=A8=E7=A4=BA=E9=9D=9E=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=82=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/pages/settings/general.vue | 4 +++- packages/frontend/src/pages/timeline.vue | 4 ++-- packages/frontend/src/store.ts | 4 ++++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 2a400035bf..791ae673c4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -21,6 +21,7 @@ export interface Locale { "cancel": string; "noThankYou": string; "enterUsername": string; + "showGlobalTimeline": string; "renotedBy": string; "noNotes": string; "noNotifications": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3506689a66..eebd1a5830 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -18,6 +18,7 @@ gotIt: "わかった" cancel: "キャンセル" noThankYou: "やめておく" enterUsername: "ユーザー名を入力" +showGlobalTimeline: "グローバルタイムラインを表示する" renotedBy: "{user}がリノート" noNotes: "ノートはありません" noNotifications: "通知はありません" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index ddff3fd16e..59a53b5657 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="defaultWithReplies">{{ i18n.ts.withRepliesByDefaultForNewlyFollowed }}</MkSwitch> <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> <MkSwitch v-model="FeaturedOrNote">{{ i18n.ts.FeaturedOrNote}}<template #caption>{{ i18n.ts.FeaturedOrNoteInfo }} </template></MkSwitch> - + <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> @@ -289,6 +289,7 @@ const enableGamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWithSave')); const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); +const showGlobalTimeline = computed(defaultStore.makeGetterSetter('showGlobalTimeline')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) const FeaturedOrNote = computed(defaultStore.makeGetterSetter('FeaturedOrNote')) const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies')); @@ -355,6 +356,7 @@ watch([ showVisibilityColor, enableonlyAndWithSave, FeaturedOrNote, + showGlobalTimeline ], async () => { await reloadAsk(); }); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index f719e6a960..5b6dc80e59 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -50,7 +50,7 @@ provide('shouldOmitHeaderTitle', true); const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); -const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); +const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable && defaultStore.state.showGlobalTimeline) || ($i != null && $i.policies.gtlAvailable && defaultStore.state.showGlobalTimeline); const keymap = { 't': focus, }; @@ -190,7 +190,7 @@ const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLis title: i18n.ts._timelines.social, icon: 'ti ti-rocket', iconOnly: true, -}] : []), ...(isGlobalTimelineAvailable ? [{ +}] : []), ...(isGlobalTimelineAvailable ? [{ key: 'global', title: i18n.ts._timelines.global, icon: 'ti ti-whirl', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 63b64601db..ae29849d55 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -396,6 +396,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: true, }, + showGlobalTimeline:{ + where: 'device', + default: true, + }, showVisibilityColor:{ where: 'device', default: false, From 8ac5dad60b4f0cee66837481bd6b23cc04cca368 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 19 Oct 2023 18:50:37 +0900 Subject: [PATCH 173/501] =?UTF-8?q?Fix:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80=E3=81=8C=E6=AD=A3=E5=B8=B8?= =?UTF-8?q?=E3=81=AB=E5=88=A4=E5=AE=9A=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/MkEmojiPicker.vue | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 36a9e5c892..2ab663dfb0 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -159,15 +159,19 @@ customEmojiCategories.value.forEach(e => { const groupedData = {}; split_categories.forEach((item) => { - if (!groupedData[item[0]]) { - groupedData[item[0]] = []; - groupedData[item[0]].push(item[0]); - }else{ - groupedData[item[0]].push(item[1]); - } -}); -console.log(groupedData) + const key = item[0]; + if (!groupedData[key]) { + groupedData[key] = []; + groupedData[key].push(item[0]); + } + if (item[1] !== undefined){ + groupedData[key].push(item[1]); + } + +}); + +console.log(groupedData) watch(q, () => { if (emojisEl.value) emojisEl.value.scrollTop = 0; From 91edf3887176dc5f1f12c82ded8d905b006b6cfa Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 19 Oct 2023 18:50:07 +0900 Subject: [PATCH 174/501] =?UTF-8?q?Fix:=20=E4=B8=80=E6=8B=AC=E3=81=A7?= =?UTF-8?q?=E9=81=B8=E6=8A=9E=E3=81=A7=E3=81=8D=E3=81=AA=E3=81=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkCustomEmojiEditLocal.vue | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 7112a38430..9f688bc52f 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -64,13 +64,14 @@ const pagination = { }; const selectAll = () => { - if (selectedEmojis.value.length > 0) { - selectedEmojis.value = []; - } else { - selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id); - } + if (selectedEmojis.value.length > 0) { + selectedEmojis.value = []; + } else { + selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); + } }; + const toggleSelect = (emoji) => { if (selectedEmojis.value.includes(emoji.id)) { selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id); From 4d00155a427b938d67e0888e586b699e022617d5 Mon Sep 17 00:00:00 2001 From: Nafu Satsuki <satsuki@nafusoft.dev> Date: Sat, 14 Oct 2023 18:29:57 +0900 Subject: [PATCH 175/501] =?UTF-8?q?feat:=20=E3=83=AF=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=83=9F=E3=83=A5=E3=83=BC=E3=83=88=E3=81=AB=E8=A9=B2=E5=BD=93?= =?UTF-8?q?=E3=81=97=E3=81=9F=E3=83=8E=E3=83=BC=E3=83=88=E3=82=92=E9=9D=9E?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=AB=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 1 + locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/components/MkNote.vue | 186 +++++++++--------- .../frontend/src/pages/settings/general.vue | 2 + packages/frontend/src/store.ts | 4 + 6 files changed, 100 insertions(+), 95 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 7b0f64f50d..0841adb136 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1634,6 +1634,7 @@ _wordMute: muteWords: "Muted words" muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." muteWordsDescription2: "Surround keywords with slashes to use regular expressions." + hideMutedNotes: "Hide notes containing muted words" _instanceMute: instanceMuteDescription: "This will mute any notes/renotes from the listed instances, including those of users replying to a user from a muted instance." instanceMuteDescription2: "Separate with newlines" diff --git a/locales/index.d.ts b/locales/index.d.ts index 791ae673c4..86580f44a9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1772,6 +1772,7 @@ export interface Locale { "muteWords": string; "muteWordsDescription": string; "muteWordsDescription2": string; + "hideMutedNotes": string; }; "_instanceMute": { "instanceMuteDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index eebd1a5830..d77b24804f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1689,6 +1689,7 @@ _wordMute: muteWords: "ミュートするワード" muteWordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。" muteWordsDescription2: "キーワードをスラッシュで囲むと正規表現になります。" + hideMutedNotes: "ミュートされた単語を含むノートを非表示にする" _instanceMute: instanceMuteDescription: "ミュートしたサーバーのユーザーへの返信を含めて、設定したサーバーの全てのノートとRenoteをミュートします。" diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index eff81f3894..0743989e85 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -45,107 +45,102 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> </span> - <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i - class="ti ti-rocket-off"></i></span> - <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span> - </div> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> + <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span> </div> - <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> - <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> - <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" - :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> - </div> - <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> - <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> - <MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/> - <div :class="$style.main"> - <MkNoteHeader :note="appearNote" :mini="true"/> - <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> - <div style="container-type: inline-size;"> - <p v-if="appearNote.cw != null" :class="$style.cw"> - <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" - :i="$i"/> - <MkCwButton v-model="showContent" :note="appearNote" style="margin: 4px 0;"/> - </p> - <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> - <div :class="$style.text"> - <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> - <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i - class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" - :emojiUrls="appearNote.emojis"/> - <div v-if="translating || translation" :class="$style.translation"> - <MkLoading v-if="translating" mini/> - <div v-else> - <b>{{ i18n.t('translatedFrom', {x: translation.sourceLang}) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :i="$i" :emojiUrls="appearNote.emojis"/> - </div> + </div> + <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> + <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> + <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> + </div> + <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> + <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> + <MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/> + <div :class="$style.main"> + <MkNoteHeader :note="appearNote" :mini="true"/> + <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> + <div style="container-type: inline-size;"> + <p v-if="appearNote.cw != null" :class="$style.cw"> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :i="$i"/> + <MkCwButton v-model="showContent" :note="appearNote" style="margin: 4px 0;"/> + </p> + <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> + <div :class="$style.text"> + <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> + <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> + <Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :emojiUrls="appearNote.emojis"/> + <div v-if="translating || translation" :class="$style.translation"> + <MkLoading v-if="translating" mini/> + <div v-else> + <b>{{ i18n.t('translatedFrom', { x: translation.sourceLang }) }}: </b> + <Mfm :text="translation.text" :author="appearNote.user" :i="$i" :emojiUrls="appearNote.emojis"/> </div> </div> - <div v-if="appearNote.files.length > 0"> - <MkMediaList :mediaList="appearNote.files"/> - </div> - <MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll"/> - <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" - :class="$style.urlPreview"/> - <div v-if="appearNote.renote" :class="$style.quote"> - <MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/> - </div> - <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> - <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> - </button> - <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> - <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> - </button> </div> - <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" - :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }} - </MkA> + <div v-if="appearNote.files.length > 0"> + <MkMediaList :mediaList="appearNote.files"/> + </div> + <MkPoll v-if="appearNote.poll" :note="appearNote" :class="$style.poll"/> + <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/> + <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> + <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> + <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> + </button> + <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> + <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> + </button> </div> - <MkReactionsViewer :note="appearNote" :maxNumber="16"> - <template #more> - <div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div> - </template> - </MkReactionsViewer> - <footer :class="$style.footer"> - <button :class="$style.footerButton" class="_button" @click="reply()"> - <i class="ti ti-arrow-back-up"></i> - <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p> - </button> - <button - v-if="canRenote" - ref="renoteButton" - :class="$style.footerButton" - class="_button" - @mousedown="renote()" - > - <i class="ti ti-repeat"></i> - <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p> - </button> - <button v-else :class="$style.footerButton" class="_button" disabled> - <i class="ti ti-ban"></i> - </button> - <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" - @mousedown="react()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> - <i v-else class="ti ti-plus"></i> - </button> - <button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" - @click="undoReact(appearNote)"> - <i class="ti ti-minus"></i> - </button> - <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" - class="_button" @mousedown="clip()"> - <i class="ti ti-paperclip"></i> - </button> - <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> - <i class="ti ti-dots"></i> - </button> - </footer> + <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> </div> - </article> - </div> - <div v-else /> + <MkReactionsViewer :note="appearNote" :maxNumber="16"> + <template #more> + <div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div> + </template> + </MkReactionsViewer> + <footer :class="$style.footer"> + <button :class="$style.footerButton" class="_button" @click="reply()"> + <i class="ti ti-arrow-back-up"></i> + <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ appearNote.repliesCount }}</p> + </button> + <button + v-if="canRenote" + ref="renoteButton" + :class="$style.footerButton" + class="_button" + @mousedown="renote()" + > + <i class="ti ti-repeat"></i> + <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ appearNote.renoteCount }}</p> + </button> + <button v-else :class="$style.footerButton" class="_button" disabled> + <i class="ti ti-ban"></i> + </button> + <button v-if="appearNote.myReaction == null" ref="reactButton" :class="$style.footerButton" class="_button" @mousedown="react()"> + <i v-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> + <i v-else class="ti ti-plus"></i> + </button> + <button v-if="appearNote.myReaction != null" ref="reactButton" :class="$style.footerButton" class="_button" @click="undoReact(appearNote)"> + <i class="ti ti-minus"></i> + </button> + <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()"> + <i class="ti ti-paperclip"></i> + </button> + <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="menu()"> + <i class="ti ti-dots"></i> + </button> + </footer> + </div> + </article> +</div> +<div v-else-if="muted && !hideMutedNotes" :class="$style.muted" @click="muted = false"> + <I18n :src="i18n.ts.userSaysSomething" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> +</div> </template> <script lang="ts" setup> @@ -228,6 +223,7 @@ const translation = ref<any>(null); const translating = ref(false); const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.user.instance); const canRenote = computed(() => ['public', 'home'].includes(appearNote.visibility) || (appearNote.visibility === 'followers' && appearNote.userId === $i.id)); +const hideMutedNotes = defaultStore.state.hideMutedNotes; let renoteCollapsed = $ref(defaultStore.state.collapseRenotes && isRenote && (($i && ($i.id === note.userId || $i.id === appearNote.userId)) || (appearNote.myReaction != null))); const keymap = { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 59a53b5657..03da2884bb 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -50,6 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch> + <MkSwitch v-model="hideMutedNotes">{{ i18n.ts.hideMutedNotes }}</MkSwitch> <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor}}</MkSwitch> <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> <template #label>{{ i18n.ts._visibility.home }}</template> @@ -251,6 +252,7 @@ const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNot const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter')); const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); +const hideMutedNotes = computed(defaultStore.makeGetterSetter('hideMutedNotes')); const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index ae29849d55..d4dbb99555 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -456,6 +456,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: false, }, + hideMutedNotes: { + where: 'device', + default: false, + }, })); From 08311ece4145b3d7e32e90b17412752f71818003 Mon Sep 17 00:00:00 2001 From: tar_bin <tar.bin.master@gmail.com> Date: Wed, 17 May 2023 00:44:19 +0900 Subject: [PATCH 176/501] =?UTF-8?q?Feat:=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E7=94=B3=E8=AB=8B=E6=A9=9F=E8=83=BD=E3=81=AE=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 + locales/ja-JP.yml | 3 + .../1684236161625-addEmojiDraftFlag.js | 11 +++ .../backend/src/core/CustomEmojiService.ts | 50 ++++++++---- packages/backend/src/core/RoleService.ts | 3 + .../src/core/entities/EmojiEntityService.ts | 2 + packages/backend/src/models/Emoji.ts | 6 ++ .../backend/src/models/json-schema/emoji.ts | 8 ++ .../ImportCustomEmojisProcessorService.ts | 1 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/admin/emoji/add-draft.ts | 80 +++++++++++++++++++ .../server/api/endpoints/admin/emoji/add.ts | 4 +- .../api/endpoints/admin/emoji/update.ts | 5 +- .../src/components/MkAutocomplete.vue | 4 + .../frontend/src/components/MkEmojiPicker.vue | 4 +- packages/frontend/src/const.ts | 1 + packages/frontend/src/pages/about.emojis.vue | 26 +++++- .../frontend/src/pages/admin/roles.editor.vue | 20 +++++ packages/frontend/src/pages/admin/roles.vue | 8 ++ .../src/pages/custom-emojis-manager.vue | 35 +++++--- .../frontend/src/pages/emoji-edit-dialog.vue | 73 +++++++++++++++++ packages/frontend/src/pages/emojis.emoji.vue | 16 +++- packages/misskey-js/src/entities.ts | 1 + 24 files changed, 339 insertions(+), 31 deletions(-) create mode 100644 packages/backend/migration/1684236161625-addEmojiDraftFlag.js create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index 2494c1709b..504df64250 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -978,6 +978,7 @@ export interface Locale { "unassign": string; "color": string; "manageCustomEmojis": string; + "requestCustomEmojis": string; "youCannotCreateAnymore": string; "cannotPerformTemporary": string; "cannotPerformTemporaryDescription": string; @@ -1020,6 +1021,7 @@ export interface Locale { "sensitiveWordsDescription2": string; "notesSearchNotAvailable": string; "license": string; + "draft": string; "unfavoriteConfirm": string; "myClips": string; "drivecleaner": string; @@ -1556,6 +1558,7 @@ export interface Locale { "inviteLimitCycle": string; "inviteExpirationTime": string; "canManageCustomEmojis": string; + "canRequestCustomEmojis": string; "driveCapacity": string; "alwaysMarkNsfw": string; "pinMax": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 9adc4381a7..db2420c40d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -975,6 +975,7 @@ assign: "アサイン" unassign: "アサインを解除" color: "色" manageCustomEmojis: "カスタム絵文字の管理" +requestCustomEmojis: "カスタム絵文字のリクエスト" youCannotCreateAnymore: "これ以上作成することはできません。" cannotPerformTemporary: "一時的に利用できません" cannotPerformTemporaryDescription: "操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。" @@ -1017,6 +1018,7 @@ sensitiveWordsDescription: "設定したワードが含まれるノートの公 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" +draft: "ドラフト" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" drivecleaner: "ドライブクリーナー" @@ -1477,6 +1479,7 @@ _role: inviteLimitCycle: "招待コードの発行間隔" inviteExpirationTime: "招待コードの有効期限" canManageCustomEmojis: "カスタム絵文字の管理" + canRequestCustomEmojis: "カスタム絵文字のリクエスト" driveCapacity: "ドライブ容量" alwaysMarkNsfw: "ファイルにNSFWを常に付与" pinMax: "ノートのピン留めの最大数" diff --git a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js new file mode 100644 index 0000000000..b0a13ea498 --- /dev/null +++ b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js @@ -0,0 +1,11 @@ +export class AddEmojiDraftFlag1684236161625 { + name = 'AddEmojiDraftFlag1684236161625' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" ADD "draft" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "draft"`); + } +} diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 505c8e4269..d3152f2685 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -13,10 +13,10 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/index.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; -import { query } from '@/misc/prelude/url.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; @@ -34,6 +34,9 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + private utilityService: UtilityService, private idService: IdService, private emojiEntityService: EmojiEntityService, @@ -66,6 +69,7 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; }, moderator?: MiUser): Promise<MiEmoji> { const emoji = await this.emojisRepository.insert({ @@ -82,6 +86,7 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: data.draft, }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); if (data.host == null) { @@ -109,27 +114,44 @@ export class CustomEmojiService implements OnApplicationShutdown { category?: string | null; aliases?: string[]; license?: string | null; + fileId?: string | null; isSensitive?: boolean; localOnly?: boolean; + draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; }, moderator?: MiUser): Promise<void> { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); + const driveFile = data.fileId !== null ? await this.driveFilesRepository.findOneBy({ id: data.fileId }) : null; const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); - await this.emojisRepository.update(emoji.id, { - updatedAt: new Date(), - name: data.name, - category: data.category, - aliases: data.aliases, - license: data.license, - isSensitive: data.isSensitive, - localOnly: data.localOnly, - originalUrl: data.driveFile != null ? data.driveFile.url : undefined, - publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, - type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, - roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, - }); + if (driveFile !== null) { + await this.emojisRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + draft: data.draft, + }); + } else { + await this.emojisRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + draft: data.draft, + roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, + }); + } this.localEmojisCache.refresh(); diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index d18fb240f7..7f451b6510 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -32,6 +32,7 @@ export type RolePolicies = { inviteLimitCycle: number; inviteExpirationTime: number; canManageCustomEmojis: boolean; + canRequestCustomEmojis: boolean; canSearchNotes: boolean; canUseTranslator: boolean; canHideAds: boolean; @@ -57,6 +58,7 @@ export const DEFAULT_POLICIES: RolePolicies = { inviteLimitCycle: 60 * 24 * 7, inviteExpirationTime: 0, canManageCustomEmojis: false, + canRequestCustomEmojis: false, canSearchNotes: false, canUseTranslator: true, canHideAds: false, @@ -300,6 +302,7 @@ export class RoleService implements OnApplicationShutdown { inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), inviteExpirationTime: calc('inviteExpirationTime', vs => Math.max(...vs)), canManageCustomEmojis: calc('canManageCustomEmojis', vs => vs.some(v => v === true)), + canRequestCustomEmojis: calc('canRequestCustomEmojis', vs => vs.some(v => v === true)), canSearchNotes: calc('canSearchNotes', vs => vs.some(v => v === true)), canUseTranslator: calc('canUseTranslator', vs => vs.some(v => v === true)), canHideAds: calc('canHideAds', vs => vs.some(v => v === true)), diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index 5b97cfad5e..d50d7356ae 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -33,6 +33,7 @@ export class EmojiEntityService { url: emoji.publicUrl || emoji.originalUrl, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, + draft: emoji.draft, }; } @@ -61,6 +62,7 @@ export class EmojiEntityService { isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + draft: emoji.draft, }; } diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index 563ac1d9d3..bd8d54ffc3 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -81,4 +81,10 @@ export class MiEmoji { array: true, length: 128, default: '{}', }) public roleIdsThatCanBeUsedThisEmojiAsReaction: string[]; + + @Column('boolean', { + default: false, + nullable: false, + }) + public draft: boolean; } diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 99a58f8773..90054cbc50 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -40,6 +40,10 @@ export const packedEmojiSimpleSchema = { format: 'id', }, }, + draft: { + type: 'boolean', + optional: false, nullable: true, + }, }, } as const; @@ -81,6 +85,10 @@ export const packedEmojiDetailedSchema = { type: 'string', optional: false, nullable: true, }, + draft: { + type: 'boolean', + optional: false, nullable: true, + }, isSensitive: { type: 'boolean', optional: false, nullable: false, diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index a52af54a39..088596bba6 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -103,6 +103,7 @@ export class ImportCustomEmojisProcessorService { isSensitive: emojiInfo.isSensitive, localOnly: emojiInfo.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: [], + draft: false, }); } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index f834561456..35d44d3579 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -25,6 +25,7 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; @@ -375,6 +376,7 @@ const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; +const $admin_emoji_addDraft: Provider = { provide: 'ep:admin/emoji/add-draft', useClass: ep___admin_emoji_addDraft.default }; const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; @@ -729,6 +731,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, + $admin_emoji_addDraft, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, @@ -1077,6 +1080,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, + $admin_emoji_addDraft, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d12a035afa..202f449efc 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -25,6 +25,7 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; +import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; @@ -373,6 +374,7 @@ const eps = [ ['admin/drive/show-file', ep___admin_drive_showFile], ['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk], ['admin/emoji/add', ep___admin_emoji_add], + ['admin/emoji/add-draft', ep___admin_emoji_addDraft], ['admin/emoji/copy', ep___admin_emoji_copy], ['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk], ['admin/emoji/delete', ep___admin_emoji_delete], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts new file mode 100644 index 0000000000..ac5082091e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -0,0 +1,80 @@ +import { Inject, Injectable } from '@nestjs/common'; +import rndstr from 'rndstr'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { DriveFilesRepository } from '@/models/index.js'; +import { DI } from '@/di-symbols.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; +import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canRequestCustomEmojis', + + errors: { + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, + category: { + type: 'string', + nullable: true, + description: 'Use `null` to reset the category.', + }, + aliases: { type: 'array', items: { + type: 'string', + } }, + license: { type: 'string', nullable: true }, + fileId: { type: 'string', format: 'misskey:id' }, + }, + required: ['name', 'fileId'], +} as const; + +// TODO: ロジックをサービスに切り出す + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + private customEmojiService: CustomEmojiService, + + private moderationLogService: ModerationLogService, + ) { + super(meta, paramDef, async (ps, me) => { + const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + + const emoji = await this.customEmojiService.add({ + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + host: null, + draft: true, + }); + + this.moderationLogService.insertModerationLog(me, 'addEmoji', { + emojiId: emoji.id, + }); + + return { + id: emoji.id, + }; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index faab8ee608..5e1b0e345c 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -51,7 +51,7 @@ export const paramDef = { type: 'string', } }, }, - required: ['name', 'fileId'], + required: ['name','fileId', 'draft'], } as const; // TODO: ロジックをサービスに切り出す @@ -68,6 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.duplicateName); @@ -81,6 +82,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, + draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 04226d8953..bebfdae8f2 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -55,8 +55,9 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, + draft: { type: 'boolean' }, }, - required: ['id', 'name', 'aliases'], + required: ['id', 'name', 'aliases', 'draft'], } as const; @Injectable() @@ -93,6 +94,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, + fileId: ps.fileId ?? null, + draft: ps.draft, }, me); }); } diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 7c4f910559..f8b655e772 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -96,6 +96,10 @@ const emojiDb = computed(() => { const customEmojiDB: EmojiDef[] = []; for (const x of customEmojis.value) { + if (x.draft) { + continue; + } + customEmojiDB.push({ name: x.name, emoji: `:${x.name}:`, diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 7eff637482..25c4d028e6 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="category in customEmojiCategories" :key="`custom:${category}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(emoji => !emoji.draft).filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" @chosen="chosen" > {{ category || i18n.ts.other }} @@ -157,7 +157,7 @@ watch(q, () => { const searchCustom = () => { const max = 100; - const emojis = customEmojis.value; + const emojis = customEmojis.value.filter(emoji => !emoji.draft); const matches = new Set<Misskey.entities.CustomEmoji>(); const exactMatch = emojis.find(emoji => emoji.name === newQ); diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index 3998df9efe..89bd092aa2 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -66,6 +66,7 @@ export const ROLE_POLICIES = [ 'inviteLimitCycle', 'inviteExpirationTime', 'canManageCustomEmojis', + 'canRequestCustomEmojis', 'canSearchNotes', 'canUseTranslator', 'canHideAds', diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index c8f56f1674..6fb691bc5e 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -6,6 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps"> <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> + <MkButton v-if="$i && (!$i.isModerator && !$i.policies.canManageCustomEmojis && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> <div class="query"> <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> @@ -22,21 +23,21 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFoldableSection v-if="searchEmojis"> <template #header>{{ i18n.ts.searchResult }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji"/> + <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> </MkFoldableSection> <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/> + <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> </MkFoldableSection> </div> </template> <script lang="ts" setup> -import { watch } from 'vue'; +import { watch, defineAsyncComponent } from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; @@ -44,6 +45,7 @@ import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js'; import { i18n } from '@/i18n.js'; +import * as os from '@/os'; import { $i } from '@/account.js'; const customEmojiTags = getCustomEmojiTags(); @@ -80,6 +82,24 @@ function toggleTag(tag) { } } +const edit = () => { + os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + emoji: { + name: '', + category: null, + aliases: [], + license: '', + url: '', + draft: true, + }, + isRequest: true, + }, { + done: result => { + window.location.reload(); + }, + }, 'closed'); +}; + watch($$(q), () => { search(); }); diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index ead2250af2..82658daf6a 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -259,6 +259,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix> + <span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> <template #suffix> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 74de9f7396..e119c3b031 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -87,6 +87,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix>{{ policies.canRequestCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canRequestCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index bee73045b7..3435e322a0 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -30,13 +30,22 @@ SPDX-License-Identifier: AGPL-3.0-only <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> <div class="ldhfsamy"> - <button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> + <div v-for="emoji in items" :key="emoji.id"> + <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + </div> </div> </template> </MkPagination> @@ -57,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #default="{items}"> <div class="ldhfsamy"> <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" class="img" :alt="emoji.name"/> + <img :src="emoji.url" class="img" :alt="emoji.name"/> <div class="body"> <div class="name _monospace">{{ emoji.name }}</div> <div class="info">{{ emoji.host }}</div> @@ -141,6 +150,7 @@ const add = async (ev: MouseEvent) => { const edit = (emoji) => { os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { emoji: emoji, + isRequest: false, }, { done: result => { if (result.updated) { @@ -323,12 +333,13 @@ definePageMetadata(computed(() => ({ grid-gap: 12px; margin: var(--margin) 0; - > .emoji { + div > .emoji { display: flex; align-items: center; padding: 11px; text-align: left; border: solid 1px var(--panel); + width: 100%; &:hover { border-color: var(--inputBorderHover); @@ -410,4 +421,10 @@ definePageMetadata(computed(() => ({ } } } + +.emoji-draft { + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; +} </style> diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue index 2e6050490e..f830f09473 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/pages/emoji-edit-dialog.vue @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" + :withOkButton="true" @close="dialog.close()" @closed="$emit('closed')" > @@ -68,6 +69,10 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSpacer> <div :class="$style.footer"> <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> + <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> + {{ i18n.ts.draft }} + </MkSwitch> + <MkButton v-if="!isRequest" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </div> </MkModalWindow> @@ -90,6 +95,7 @@ import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ emoji?: any, + isRequest: boolean, }>(); let dialog = $ref(null); @@ -102,18 +108,55 @@ let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); +let chooseFile: DriveFile|null = $ref(null); +let draft = $ref(props.emoji.draft); +let isRequest = $ref(props.isRequest); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); +let draft = $ref(props.emoji.draft); +let isRequest = $ref(props.isRequest); const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, (ev: 'closed'): void }>(); +function ok() { + if (isRequest) { + if (chooseFile !== null && name.match(/^[a-zA-Z0-9_]+$/)) { + add(); + } + } else { + update(); + } +} + +async function add() { + const ret = await os.api('admin/emoji/add-draft', { + name: name, + category: category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + fileId: chooseFile.id, + }); + + emit('done', { + updated: { + id: ret.id, + name, + category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + draft: true, + }, + }); + + dialog.close(); +} async function changeImage(ev) { file = await selectFile(ev.currentTarget ?? ev.target, null); const candidate = file.name.replace(/\.(.+)$/, ''); @@ -137,7 +180,30 @@ async function addRole() { async function removeRole(role, ev) { rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); } +async function update() { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, + name, + category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + fileId: chooseFile?.id, + draft: draft, + }); + emit('done', { + updated: { + id: props.emoji.id, + name, + category, + aliases: aliases.split(' '), + license: license === '' ? null : license, + draft: draft, + }, + }); + + dialog.close(); +} async function done() { const params = { name, @@ -178,6 +244,13 @@ async function done() { } } +function chooseFileFrom(ev) { + selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { + chooseFile = files_[0]; + url = chooseFile.url; + }); +} + async function del() { const { canceled } = await os.confirm({ type: 'warning', diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index 9aaa7890a9..fcc1fff104 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -4,7 +4,14 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<button class="_button" :class="$style.root" @click="menu"> +<button v-if="emoji.draft" class="zuvgdzyu _button emoji-draft" @click="menu"> + <img :src="emoji.url" class="img" loading="lazy"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="info">{{ emoji.aliases.join(' ') }}</div> + </div> +</button> +<button v-else class="_button" :class="$style.root" @click="menu"> <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div :class="$style.body"> <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> @@ -25,6 +32,7 @@ const props = defineProps<{ aliases: string[]; category: string; url: string; + draft: boolean; }; }>(); @@ -91,4 +99,10 @@ function menu(ev) { text-overflow: ellipsis; overflow: hidden; } + +.emoji-draft { + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; +} </style> diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index bcf5538532..7f4970af85 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -311,6 +311,7 @@ export type CustomEmoji = { url: string; category: string; aliases: string[]; + draft: boolean; }; export type LiteInstanceMetadata = { From 7149961f336130afd605e6a8085c57932c01a700 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 19 Oct 2023 21:22:47 +0900 Subject: [PATCH 177/501] fix: iroiro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> カスタム絵文字一覧を一覧、新着、申請中に分離 (cherry picked from commit 5c464cd1bc177f0276397a686160c5c77e774d10) fix: 絵文字管理画面の表示不具合の修正 (cherry picked from commit 5300ff763bb008699b35dc6e219b50ac1c4fd7af) Signed-off-by: mattyatea <mattyacocacora0@gmail.com> Fix:draftが2重に宣言されてた Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 5 + locales/ja-JP.yml | 5 + package.json | 2 +- .../api/endpoints/admin/emoji/add-draft.ts | 10 +- .../server/api/endpoints/admin/emoji/add.ts | 2 +- .../src/components/MkCustomEmojiEditDraft.vue | 214 ++++++++ .../src/components/MkCustomEmojiEditLocal.vue | 225 ++++++++ .../components/MkCustomEmojiEditRemote.vue | 110 ++++ .../MkEmojiEditDialog.vue} | 100 ++-- packages/frontend/src/pages/about.emojis.vue | 99 ++-- packages/frontend/src/pages/about.vue | 4 +- .../frontend/src/pages/admin/roles.editor.vue | 2 +- .../src/pages/custom-emojis-manager.vue | 490 ++++-------------- 13 files changed, 775 insertions(+), 493 deletions(-) create mode 100644 packages/frontend/src/components/MkCustomEmojiEditDraft.vue create mode 100644 packages/frontend/src/components/MkCustomEmojiEditLocal.vue create mode 100644 packages/frontend/src/components/MkCustomEmojiEditRemote.vue rename packages/frontend/src/{pages/emoji-edit-dialog.vue => components/MkEmojiEditDialog.vue} (79%) diff --git a/locales/index.d.ts b/locales/index.d.ts index 504df64250..1ebf723bed 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -824,6 +824,7 @@ export interface Locale { "high": string; "middle": string; "low": string; + "list": string; "emailNotConfiguredWarning": string; "ratio": string; "previewNoteText": string; @@ -1022,6 +1023,10 @@ export interface Locale { "notesSearchNotAvailable": string; "license": string; "draft": string; + "newEmojis": string; + "undrafted": string; + "draftEmojis": string; + "emojiNameValidation": string; "unfavoriteConfirm": string; "myClips": string; "drivecleaner": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index db2420c40d..5efc26f1f3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -821,6 +821,7 @@ priority: "優先度" high: "高" middle: "中" low: "低" +list: "一覧" emailNotConfiguredWarning: "メールアドレスの設定がされていません。" ratio: "比率" previewNoteText: "本文をプレビュー" @@ -1019,6 +1020,10 @@ sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キ notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" draft: "ドラフト" +newEmojis: "新着の絵文字" +undrafted: "ドラフト解除" +draftEmojis: "ドラフトされてる絵文字" +emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" drivecleaner: "ドライブクリーナー" diff --git a/package.json b/package.json index a9b2b13532..9ebea7dc52 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.1", + "version": "2023.10.123", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index ac5082091e..57de1e0ed8 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -35,6 +35,8 @@ export const paramDef = { type: 'string', } }, license: { type: 'string', nullable: true }, + isSensitive: { type: 'boolean', nullable: true }, + localOnly: { type: 'boolean', nullable: true }, fileId: { type: 'string', format: 'misskey:id' }, }, required: ['name', 'fileId'], @@ -42,8 +44,8 @@ export const paramDef = { // TODO: ロジックをサービスに切り出す -// eslint-disable-next-line import/no-default-export @Injectable() +// eslint-disable-next-line import/no-default-export export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.driveFilesRepository) @@ -64,12 +66,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { category: ps.category ?? null, aliases: ps.aliases ?? [], license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, host: null, draft: true, + roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); - this.moderationLogService.insertModerationLog(me, 'addEmoji', { + await this.moderationLogService.log(me, 'addCustomEmoji', { emojiId: emoji.id, + emoji: emoji, }); return { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 5e1b0e345c..251abbc8d0 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -51,7 +51,7 @@ export const paramDef = { type: 'string', } }, }, - required: ['name','fileId', 'draft'], + required: ['name', 'fileId', 'draft'], } as const; // TODO: ロジックをサービスに切り出す diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue new file mode 100644 index 0000000000..c609898b66 --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue @@ -0,0 +1,214 @@ +<template> +<MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <template v-for="emoji in items" :key="emoji.id"> + <div class="emoji _panel"> + <div class="img"> + <div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> + <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> + </div> + <div class="info"> + <div class="name">{{ i18n.ts.name }}: {{ emoji.name }}</div> + <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> + <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> + <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> + </div> + <div class="edit-button"> + <MkButton primary class="edit" @click="editDraft(emoji)"> + {{ i18n.ts.edit }} + </MkButton> + <MkButton class="draft" @click="undrafted(emoji)"> + {{ i18n.ts.undrafted }} + </MkButton> + <MkButton danger class="delete" @click="deleteDraft(emoji)"> + {{ i18n.ts.delete }} + </MkButton> + </div> + </div> + </template> + </div> + </template> +</MkPagination> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; +import MkPagination from '@/components/MkPagination.vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; +import MkButton from '@/components/MkButton.vue'; + +const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); + +const query = ref(null); + +const paginationDraft = { + endpoint: 'admin/emoji/list' as const, + limit: 30, + params: computed(() => ({ + query: (query.value && query.value !== '') ? query.value : null, + draft: true, + })), +}; + +const editDraft = (emoji) => { + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { + emoji: emoji, + isRequest: false, + }, { + done: result => { + if (result.updated) { + emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + ...oldEmoji, + ...result.updated, + })); + emojisDraftPaginationComponent.value.reload(); + } else if (result.deleted) { + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisDraftPaginationComponent.value.reload(); + } + }, + }, 'closed'); +}; + +async function undrafted(emoji) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('undraftAreYouSure', { x: emoji.name }), + }); + if (canceled) return; + + await os.api('admin/emoji/update', { + id: emoji.id, + name: emoji.name, + category: emoji.category, + aliases: emoji.aliases, + license: emoji.license, + draft: false, + isSensitive: emoji.isSensitive, + localOnly: emoji.localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + }); + + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisDraftPaginationComponent.value.reload(); +} + +async function deleteDraft(emoji) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('removeAreYouSure', { x: emoji.name }), + }); + if (canceled) return; + + os.api('admin/emoji/delete', { + id: emoji.id, + }).then(() => { + emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisDraftPaginationComponent.value.reload(); + }); +} +</script> + +<style lang="scss" scoped> +.empty { + margin: var(--margin); +} + +.ldhfsamy { + > .emoji { + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + margin: 10px; + + > .img { + display: grid; + grid-row: 1; + grid-column: 1/ span 2; + grid-template-columns: 50% 50%; + place-content: center; + place-items: center; + + > .imgLight { + display: grid; + grid-column: 1; + background-color: #fff; + margin-bottom: 12px; + > img { + max-height: 64px; + max-width: 100%; + } + } + + > .imgDark { + display: grid; + grid-column: 2; + background-color: #000; + margin-bottom: 12px; + > img { + max-height: 64px; + max-width: 100%; + } + } + } + + > .info { + display: grid; + grid-row: 2; + grid-template-rows: 30px 30px 30px; + + > .name { + grid-row: 1; + text-overflow: ellipsis; + overflow: hidden; + } + + > .category { + grid-row: 2; + text-overflow: ellipsis; + overflow: hidden; + } + + > .aliases { + grid-row: 3; + text-overflow: ellipsis; + overflow: hidden; + } + + > .license { + grid-row: 4; + text-overflow: ellipsis; + overflow: hidden; + } + } + + > .edit-button { + display: grid; + grid-template-rows: 42px; + margin-top: 6px; + + > .edit { + grid-row: 1; + width: 100%; + margin: 6px 0; + } + + > .draft { + grid-row: 2; + width: 100%; + margin: 6px 0; + } + + > .delete { + grid-row: 3; + width: 100%; + margin: 6px 0; + } + } + } +} +</style> diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue new file mode 100644 index 0000000000..7112a38430 --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -0,0 +1,225 @@ +<template> +<MkInput v-model="query" :debounce="true" type="search"> + <template #prefix><i class="ti ti-search"></i></template> + <template #label>{{ i18n.ts.search }}</template> +</MkInput> +<MkSwitch v-model="selectMode" style="margin: 8px 0;"> + <template #label>Select mode</template> +</MkSwitch> +<div v-if="selectMode" class="_buttons"> + <MkButton inline @click="selectAll">Select all</MkButton> + <MkButton inline @click="setCategoryBulk">Set category</MkButton> + <MkButton inline @click="setTagBulk">Set tag</MkButton> + <MkButton inline @click="addTagBulk">Add tag</MkButton> + <MkButton inline @click="removeTagBulk">Remove tag</MkButton> + <MkButton inline @click="setLisenceBulk">Set Lisence</MkButton> + <MkButton inline danger @click="delBulk">Delete</MkButton> +</div> +<MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <div v-for="emoji in items" :key="emoji.id"> + <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.category }}</div> + </div> + </button> + </div> + </div> + </template> +</MkPagination> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; +import MkButton from '@/components/MkButton.vue'; +import MkInput from '@/components/MkInput.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; + +const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); + +const query = ref(null); +const selectMode = ref(false); +const selectedEmojis = ref<string[]>([]); + +const pagination = { + endpoint: 'admin/emoji/list' as const, + limit: 30, + params: computed(() => ({ + query: (query.value && query.value !== '') ? query.value : null, + })), +}; + +const selectAll = () => { + if (selectedEmojis.value.length > 0) { + selectedEmojis.value = []; + } else { + selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id); + } +}; + +const toggleSelect = (emoji) => { + if (selectedEmojis.value.includes(emoji.id)) { + selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id); + } else { + selectedEmojis.value.push(emoji.id); + } +}; + +const edit = (emoji) => { + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { + emoji: emoji, + isRequest: false, + }, { + done: result => { + if (result.updated) { + emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + ...oldEmoji, + ...result.updated, + })); + emojisPaginationComponent.value.reload(); + } else if (result.deleted) { + emojisPaginationComponent.value.removeItem((item) => item.id === emoji.id); + } + }, + }, 'closed'); +}; + +const setCategoryBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Category', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-category-bulk', { + ids: selectedEmojis.value, + category: result, + }); + emojisPaginationComponent.value.reload(); +}; + +const setLisenceBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'License', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-license-bulk', { + ids: selectedEmojis.value, + license: result, + }); + emojisPaginationComponent.value.reload(); +}; + +const addTagBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Tag', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/add-aliases-bulk', { + ids: selectedEmojis.value, + aliases: result.split(' '), + }); + emojisPaginationComponent.value.reload(); +}; + +const removeTagBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Tag', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/remove-aliases-bulk', { + ids: selectedEmojis.value, + aliases: result.split(' '), + }); + emojisPaginationComponent.value.reload(); +}; + +const setTagBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'Tag', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-aliases-bulk', { + ids: selectedEmojis.value, + aliases: result.split(' '), + }); + emojisPaginationComponent.value.reload(); +}; + +const delBulk = async () => { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.deleteConfirm, + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/delete-bulk', { + ids: selectedEmojis.value, + }); + emojisPaginationComponent.value.reload(); +}; +</script> + +<style lang="scss" scoped> +.ldhfsamy { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: var(--margin); + + div > .emoji { + display: flex; + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + width: 100%; + + &:hover { + border-color: var(--inputBorderHover); + } + + &.selected { + border-color: var(--accent); + } + + > .img { + width: 42px; + height: 42px; + } + + > .body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; + + > .name { + text-overflow: ellipsis; + overflow: hidden; + } + + > .info { + opacity: 0.5; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} + +.emoji-draft { + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; +} +</style> diff --git a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue new file mode 100644 index 0000000000..26c8dd66ac --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue @@ -0,0 +1,110 @@ +<template> +<FormSplit> + <MkInput v-model="queryRemote" :debounce="true" type="search"> + <template #prefix><i class="ti ti-search"></i></template> + <template #label>{{ i18n.ts.search }}</template> + </MkInput> + <MkInput v-model="host" :debounce="true"> + <template #label>{{ i18n.ts.host }}</template> + </MkInput> +</FormSplit> +<MkPagination :pagination="remotePagination" :displayLimit="100"> + <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> + <template #default="{items}"> + <div class="ldhfsamy"> + <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> + <img :src="emoji.url" class="img" :alt="emoji.name"/> + <div class="body"> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.host }}</div> + </div> + </div> + </div> + </template> +</MkPagination> +</template> + +<script lang="ts" setup> +import { computed, ref } from 'vue'; +import MkInput from '@/components/MkInput.vue'; +import MkPagination from '@/components/MkPagination.vue'; +import FormSplit from '@/components/form/split.vue'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; + +const queryRemote = ref(null); +const host = ref(null); + +const remotePagination = { + endpoint: 'admin/emoji/list-remote' as const, + limit: 30, + params: computed(() => ({ + query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null, + host: (host.value && host.value !== '') ? host.value : null, + })), +}; + +const im = (emoji) => { + os.apiWithDialog('admin/emoji/copy', { + emojiId: emoji.id, + }); +}; + +const remoteMenu = (emoji, ev: MouseEvent) => { + os.popupMenu([{ + type: 'label', + text: ':' + emoji.name + ':', + }, { + text: i18n.ts.import, + icon: 'ti ti-plus', + action: () => { im(emoji); }, + }], ev.currentTarget ?? ev.target); +}; +</script> + +<style lang="scss" scoped> +.empty { + margin: var(--margin); +} + +.ldhfsamy { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: 12px; + margin: var(--margin) 0; + + > .emoji { + display: flex; + align-items: center; + padding: 12px; + text-align: left; + + &:hover { + color: var(--accent); + } + + > .img { + width: 32px; + height: 32px; + } + + > .body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; + + > .name { + text-overflow: ellipsis; + overflow: hidden; + } + + > .info { + opacity: 0.5; + font-size: 90%; + text-overflow: ellipsis; + overflow: hidden; + } + } + } +} +</style> diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue similarity index 79% rename from packages/frontend/src/pages/emoji-edit-dialog.vue rename to packages/frontend/src/components/MkEmojiEditDialog.vue index f830f09473..83c7fa4d22 100644 --- a/packages/frontend/src/pages/emoji-edit-dialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -7,11 +7,12 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" - :withOkButton="true" + :withOkButton="false " @close="dialog.close()" @closed="$emit('closed')" > <template v-if="emoji" #header>:{{ emoji.name }}:</template> + <template v-else-if="isRequest" #header>{{ i18n.ts.requestCustomEmojis }}</template> <template v-else #header>New emoji</template> <div> @@ -34,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton rounded style="margin: 0 auto;" @click="changeImage">{{ i18n.ts.selectFile }}</MkButton> <MkInput v-model="name" pattern="[a-z0-9_]"> <template #label>{{ i18n.ts.name }}</template> + <template #caption>{{ i18n.ts.emojiNameValidation }}</template> </MkInput> <MkInput v-model="category" :datalist="customEmojiCategories"> <template #label>{{ i18n.ts.category }}</template> @@ -45,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder> + <MkFolder v-if="!isRequest"> <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> @@ -64,15 +66,17 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkSwitch v-model="isSensitive">isSensitive</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkButton v-if="emoji" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> + {{ i18n.ts.draft }} + </MkSwitch> </div> </MkSpacer> <div :class="$style.footer"> - <MkButton primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> - <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> - {{ i18n.ts.draft }} - </MkSwitch> - <MkButton v-if="!isRequest" danger @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <div :class="$style.footerButtons"> + <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-if="validation" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> + <MkButton v-else rounded style="margin: 0 auto;"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> + </div> </div> </div> </MkModalWindow> @@ -81,6 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, watch } from 'vue'; import * as Misskey from 'misskey-js'; +import { DriveFile } from 'misskey-js/built/entities.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -94,8 +99,8 @@ import { selectFile, selectFiles } from '@/scripts/select-file.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ - emoji?: any, - isRequest: boolean, + emoji?: any, + isRequest: boolean, }>(); let dialog = $ref(null); @@ -109,20 +114,21 @@ let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.rol let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); -let draft = $ref(props.emoji.draft); +let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); +let url; watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); -let draft = $ref(props.emoji.draft); -let isRequest = $ref(props.isRequest); - +const validation = computed(() => { + return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; +}); const emit = defineEmits<{ - (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, - (ev: 'closed'): void + (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, + (ev: 'closed'): void }>(); function ok() { @@ -208,8 +214,9 @@ async function done() { const params = { name, category: category === '' ? null : category, - aliases: aliases.split(' ').filter(x => x !== ''), + aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''), license: license === '' ? null : license, + draft: draft, isSensitive, localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), @@ -218,7 +225,7 @@ async function done() { if (file) { params.fileId = file.id; } - + console.log(props.emoji); if (props.emoji) { await os.apiWithDialog('admin/emoji/update', { id: props.emoji.id, @@ -234,7 +241,9 @@ async function done() { dialog.close(); } else { - const created = await os.apiWithDialog('admin/emoji/add', params); + const created = isRequest + ? await os.apiWithDialog('admin/emoji/add-draft', params) + : await os.apiWithDialog('admin/emoji/add', params); emit('done', { created: created, @@ -271,46 +280,53 @@ async function del() { <style lang="scss" module> .imgs { - display: flex; - gap: 8px; - flex-wrap: wrap; - justify-content: center; + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: center; } .imgContainer { - padding: 8px; - border-radius: 6px; + padding: 8px; + border-radius: 6px; } .img { - display: block; - height: 64px; - width: 64px; - object-fit: contain; + display: block; + height: 64px; + width: 64px; + object-fit: contain; } .roleItem { - display: flex; + display: flex; } .role { - flex: 1; + flex: 1; } .roleUnassign { - width: 32px; - height: 32px; - margin-left: 8px; - align-self: center; + width: 32px; + height: 32px; + margin-left: 8px; + align-self: center; } .footer { - position: sticky; - bottom: 0; - left: 0; - padding: 12px; - border-top: solid 0.5px var(--divider); - -webkit-backdrop-filter: var(--blur, blur(15px)); - backdrop-filter: var(--blur, blur(15px)); + position: sticky; + bottom: 0; + left: 0; + padding: 12px; + border-top: solid 0.5px var(--divider); + -webkit-backdrop-filter: var(--blur, blur(15px)); + backdrop-filter: var(--blur, blur(15px)); +} + +.footerButtons { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: center; } </style> diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 6fb691bc5e..61843c393a 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -4,54 +4,76 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps"> - <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> - <MkButton v-if="$i && (!$i.isModerator && !$i.policies.canManageCustomEmojis && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> +<MkStickyContainer> + <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> + <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> + <MkButton v-if="$i && (!$i.isModerator && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> - <div class="query"> - <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> - <template #prefix><i class="ti ti-search"></i></template> - </MkInput> + <div class="query" style="margin-top: 10px;"> + <MkInput v-model="q" class="" :placeholder="i18n.ts.search"> + <template #prefix><i class="ti ti-search"></i></template> + </MkInput> - <!-- たくさんあると邪魔 - <div class="tags"> - <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> + <!-- たくさんあると邪魔 + <div class="tags"> + <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> + </div> + --> </div> - --> - </div> - <MkFoldableSection v-if="searchEmojis"> - <template #header>{{ i18n.ts.searchResult }}</template> + <MkFoldableSection v-if="searchEmojis"> + <template #header>{{ i18n.ts.searchResult }}</template> + <div :class="$style.emojis"> + <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + </div> + </MkFoldableSection> + + <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> + <template #header>{{ category || i18n.ts.other }}</template> + <div :class="$style.emojis"> + <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + </div> + </MkFoldableSection> + </MkSpacer> + <MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> - <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> </div> - </MkFoldableSection> - - <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> - <template #header>{{ category || i18n.ts.other }}</template> - <div :class="$style.emojis"> - <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> - </div> - </MkFoldableSection> -</div> + </MkSpacer> +</MkStickyContainer> </template> <script lang="ts" setup> -import { watch, defineAsyncComponent } from 'vue'; +import { watch, defineAsyncComponent, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js'; +import { customEmojis, customEmojiCategories } from '@/custom-emojis.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os'; import { $i } from '@/account.js'; +import { definePageMetadata } from '@/scripts/page-metadata'; + +let tab = $ref('emojis'); +const headerActions = $computed(() => []); + +const headerTabs = $computed(() => [{ + key: 'emojis', + title: i18n.ts.list, +}, { + key: 'draft', + title: i18n.ts.draftEmojis, +}]); + +definePageMetadata(ref({})); -const customEmojiTags = getCustomEmojiTags(); let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); +const draftEmojis = customEmojis.value.filter(emoji => emoji.draft); function search() { if ((q === '' || q == null) && selectedTags.size === 0) { @@ -74,24 +96,8 @@ function search() { } } -function toggleTag(tag) { - if (selectedTags.has(tag)) { - selectedTags.delete(tag); - } else { - selectedTags.add(tag); - } -} - const edit = () => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { - emoji: { - name: '', - category: null, - aliases: [], - license: '', - url: '', - draft: true, - }, + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { isRequest: true, }, { done: result => { @@ -107,6 +113,11 @@ watch($$(q), () => { watch($$(selectedTags), () => { search(); }, { deep: true }); + +definePageMetadata({ + title: i18n.ts.customEmojis, + icon: null, +}); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index ee4043f9a5..d889d989e2 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -89,9 +89,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormSection> </div> </MkSpacer> - <MkSpacer v-else-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> - <XEmojis/> - </MkSpacer> + <XEmojis v-else-if="tab === 'emojis'"/> <MkSpacer v-else-if="tab === 'federation'" :contentMax="1000" :marginMin="20"> <XFederation/> </MkSpacer> diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 82658daf6a..4153156328 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -273,7 +273,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> - <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 3435e322a0..b88253a53e 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -4,427 +4,119 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div> - <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="900"> - <div class="ogwlenmc"> - <div v-if="tab === 'local'" class="local"> - <MkInput v-model="query" :debounce="true" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.search }}</template> - </MkInput> - <MkSwitch v-model="selectMode" style="margin: 8px 0;"> - <template #label>Select mode</template> - </MkSwitch> - <div v-if="selectMode" class="_buttons"> - <MkButton inline @click="selectAll">Select all</MkButton> - <MkButton inline @click="setCategoryBulk">Set category</MkButton> - <MkButton inline @click="setTagBulk">Set tag</MkButton> - <MkButton inline @click="addTagBulk">Add tag</MkButton> - <MkButton inline @click="removeTagBulk">Remove tag</MkButton> - <MkButton inline @click="setLicenseBulk">Set License</MkButton> - <MkButton inline danger @click="delBulk">Delete</MkButton> - </div> - <MkPagination ref="emojisPaginationComponent" :pagination="pagination"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <div v-for="emoji in items" :key="emoji.id"> - <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> - <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> - </div> - </div> - </template> - </MkPagination> - </div> - - <div v-else-if="tab === 'remote'" class="remote"> - <FormSplit> - <MkInput v-model="queryRemote" :debounce="true" type="search"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.search }}</template> - </MkInput> - <MkInput v-model="host" :debounce="true"> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </FormSplit> - <MkPagination :pagination="remotePagination"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.host }}</div> - </div> - </div> - </div> - </template> - </MkPagination> - </div> - </div> - </MkSpacer> - </MkStickyContainer> -</div> + <div> + <MkStickyContainer> + <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer :contentMax="900"> + <div class="ogwlenmc"> + <div v-if="tab === 'local'" class="local"> + <MkCustomEmojiEditLocal/> + </div> + <div v-if="tab === 'draft'" class="draft"> + <MkCustomEmojiEditDraft/> + </div> + <div v-else-if="tab === 'remote'" class="remote"> + <MkCustomEmojiEditRemote/> + </div> + </div> + </MkSpacer> + </MkStickyContainer> + </div> </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; -import MkButton from '@/components/MkButton.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkPagination from '@/components/MkPagination.vue'; -import MkSwitch from '@/components/MkSwitch.vue'; -import FormSplit from '@/components/form/split.vue'; -import { selectFile, selectFiles } from '@/scripts/select-file.js'; -import * as os from '@/os.js'; -import { i18n } from '@/i18n.js'; -import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { computed, defineAsyncComponent, ref } from 'vue'; +import MkCustomEmojiEditDraft from '@/components/MkCustomEmojiEditDraft.vue'; +import MkCustomEmojiEditLocal from '@/components/MkCustomEmojiEditLocal.vue'; +import MkCustomEmojiEditRemote from '@/components/MkCustomEmojiEditRemote.vue'; +import { selectFile } from '@/scripts/select-file'; +import * as os from '@/os'; +import { i18n } from '@/i18n'; +import { definePageMetadata } from '@/scripts/page-metadata'; -const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); - -const tab = ref('local'); -const query = ref(null); -const queryRemote = ref(null); -const host = ref(null); -const selectMode = ref(false); -const selectedEmojis = ref<string[]>([]); - -const pagination = { - endpoint: 'admin/emoji/list' as const, - limit: 30, - params: computed(() => ({ - query: (query.value && query.value !== '') ? query.value : null, - })), -}; - -const remotePagination = { - endpoint: 'admin/emoji/list-remote' as const, - limit: 30, - params: computed(() => ({ - query: (queryRemote.value && queryRemote.value !== '') ? queryRemote.value : null, - host: (host.value && host.value !== '') ? host.value : null, - })), -}; - -const selectAll = () => { - if (selectedEmojis.value.length > 0) { - selectedEmojis.value = []; - } else { - selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); - } -}; - -const toggleSelect = (emoji) => { - if (selectedEmojis.value.includes(emoji.id)) { - selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id); - } else { - selectedEmojis.value.push(emoji.id); - } -}; +const tab = ref('draft'); const add = async (ev: MouseEvent) => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { - }, { - done: result => { - if (result.created) { - emojisPaginationComponent.value.prepend(result.created); - } - }, - }, 'closed'); -}; - -const edit = (emoji) => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { - emoji: emoji, - isRequest: false, - }, { - done: result => { - if (result.updated) { - emojisPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ - ...oldEmoji, - ...result.updated, - })); - } else if (result.deleted) { - emojisPaginationComponent.value.removeItem(emoji.id); - } - }, - }, 'closed'); -}; - -const im = (emoji) => { - os.apiWithDialog('admin/emoji/copy', { - emojiId: emoji.id, - }); -}; - -const remoteMenu = (emoji, ev: MouseEvent) => { - os.popupMenu([{ - type: 'label', - text: ':' + emoji.name + ':', - }, { - text: i18n.ts.import, - icon: 'ti ti-plus', - action: () => { im(emoji); }, - }], ev.currentTarget ?? ev.target); + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { + }, { + done: result => { + //TODO: emitにして追加を反映 + // if (result.created) { + // emojisPaginationComponent.value.prepend(result.created); + // emojisPaginationComponent.value.reload(); + // } + }, + }, 'closed'); }; const menu = (ev: MouseEvent) => { - os.popupMenu([{ - icon: 'ti ti-download', - text: i18n.ts.export, - action: async () => { - os.api('export-custom-emojis', { - }) - .then(() => { - os.alert({ - type: 'info', - text: i18n.ts.exportRequested, - }); - }).catch((err) => { - os.alert({ - type: 'error', - text: err.message, - }); - }); - }, - }, { - icon: 'ti ti-upload', - text: i18n.ts.import, - action: async () => { - const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('admin/emoji/import-zip', { - fileId: file.id, - }) - .then(() => { - os.alert({ - type: 'info', - text: i18n.ts.importRequested, - }); - }).catch((err) => { - os.alert({ - type: 'error', - text: err.message, - }); - }); - }, - }], ev.currentTarget ?? ev.target); -}; - -const setCategoryBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Category', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-category-bulk', { - ids: selectedEmojis.value, - category: result, - }); - emojisPaginationComponent.value.reload(); -}; - -const setLicenseBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'License', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-license-bulk', { - ids: selectedEmojis.value, - license: result, - }); - emojisPaginationComponent.value.reload(); -}; - -const addTagBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Tag', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/add-aliases-bulk', { - ids: selectedEmojis.value, - aliases: result.split(' '), - }); - emojisPaginationComponent.value.reload(); -}; - -const removeTagBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Tag', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/remove-aliases-bulk', { - ids: selectedEmojis.value, - aliases: result.split(' '), - }); - emojisPaginationComponent.value.reload(); -}; - -const setTagBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'Tag', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-aliases-bulk', { - ids: selectedEmojis.value, - aliases: result.split(' '), - }); - emojisPaginationComponent.value.reload(); -}; - -const delBulk = async () => { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.ts.deleteConfirm, - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/delete-bulk', { - ids: selectedEmojis.value, - }); - emojisPaginationComponent.value.reload(); + os.popupMenu([{ + icon: 'ti ti-download', + text: i18n.ts.export, + action: async () => { + os.api('export-custom-emojis', { + }) + .then(() => { + os.alert({ + type: 'info', + text: i18n.ts.exportRequested, + }); + }).catch((err) => { + os.alert({ + type: 'error', + text: err.message, + }); + }); + }, + }, { + icon: 'ti ti-upload', + text: i18n.ts.import, + action: async () => { + const file = await selectFile(ev.currentTarget ?? ev.target); + os.api('admin/emoji/import-zip', { + fileId: file.id, + }) + .then(() => { + os.alert({ + type: 'info', + text: i18n.ts.importRequested, + }); + }).catch((err) => { + os.alert({ + type: 'error', + text: err.message, + }); + }); + }, + }], ev.currentTarget ?? ev.target); }; const headerActions = $computed(() => [{ - asFullButton: true, - icon: 'ti ti-plus', - text: i18n.ts.addEmoji, - handler: add, + asFullButton: true, + icon: 'ti ti-plus', + text: i18n.ts.addEmoji, + handler: add, }, { - icon: 'ti ti-dots', - handler: menu, + icon: 'ti ti-dots', + handler: menu, }]); const headerTabs = $computed(() => [{ - key: 'local', - title: i18n.ts.local, + key: 'draft', + title: i18n.ts.draftEmojis, }, { - key: 'remote', - title: i18n.ts.remote, + key: 'local', + title: i18n.ts.local, +}, { + key: 'remote', + title: i18n.ts.remote, }]); definePageMetadata(computed(() => ({ - title: i18n.ts.customEmojis, - icon: 'ti ti-icons', + title: i18n.ts.customEmojis, + icon: 'ti ti-icons', }))); </script> <style lang="scss" scoped> -.ogwlenmc { - > .local { - .empty { - margin: var(--margin); - } - - .ldhfsamy { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: 12px; - margin: var(--margin) 0; - - div > .emoji { - display: flex; - align-items: center; - padding: 11px; - text-align: left; - border: solid 1px var(--panel); - width: 100%; - - &:hover { - border-color: var(--inputBorderHover); - } - - &.selected { - border-color: var(--accent); - } - - > .img { - width: 42px; - height: 42px; - } - - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; - - > .name { - text-overflow: ellipsis; - overflow: hidden; - } - - > .info { - opacity: 0.5; - text-overflow: ellipsis; - overflow: hidden; - } - } - } - } - } - - > .remote { - .empty { - margin: var(--margin); - } - - .ldhfsamy { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: 12px; - margin: var(--margin) 0; - - > .emoji { - display: flex; - align-items: center; - padding: 12px; - text-align: left; - - &:hover { - color: var(--accent); - } - - > .img { - width: 32px; - height: 32px; - } - - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; - - > .name { - text-overflow: ellipsis; - overflow: hidden; - } - - > .info { - opacity: 0.5; - font-size: 90%; - text-overflow: ellipsis; - overflow: hidden; - } - } - } - } - } -} - -.emoji-draft { - --c: rgb(255 196 0 / 15%);; - background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); - background-size: 16px 16px; -} </style> From 6f964f56cc700c44775c065165332d18c3666ff6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 19 Oct 2023 22:51:25 +0900 Subject: [PATCH 178/501] =?UTF-8?q?fix:=20unused=E3=81=AA=E3=82=84?= =?UTF-8?q?=E3=81=A4=E6=B6=88=E3=81=97=E3=81=9F=E3=82=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../api/endpoints/admin/emoji/add-draft.ts | 3 +-- .../src/components/MkEmojiEditDialog.vue | 19 ------------------- packages/frontend/src/pages/about.emojis.vue | 2 +- packages/frontend/src/pages/emojis.emoji.vue | 1 - 4 files changed, 2 insertions(+), 23 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 57de1e0ed8..5212d7285b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -1,7 +1,6 @@ import { Inject, Injectable } from '@nestjs/common'; -import rndstr from 'rndstr'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 83c7fa4d22..65690042a5 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialog" :width="400" - :withOkButton="false " @close="dialog.close()" @closed="$emit('closed')" > @@ -116,7 +115,6 @@ let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); -let url; watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); @@ -131,16 +129,6 @@ const emit = defineEmits<{ (ev: 'closed'): void }>(); -function ok() { - if (isRequest) { - if (chooseFile !== null && name.match(/^[a-zA-Z0-9_]+$/)) { - add(); - } - } else { - update(); - } -} - async function add() { const ret = await os.api('admin/emoji/add-draft', { name: name, @@ -253,13 +241,6 @@ async function done() { } } -function chooseFileFrom(ev) { - selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { - chooseFile = files_[0]; - url = chooseFile.url; - }); -} - async function del() { const { canceled } = await os.confirm({ type: 'warning', diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 61843c393a..f51cc1b640 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch, defineAsyncComponent, ref, computed } from 'vue'; +import { watch, defineAsyncComponent, ref } from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index fcc1fff104..bd2cb33f55 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -21,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { } from 'vue'; import * as os from '@/os.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { i18n } from '@/i18n.js'; From 7bd7fe996c720eb16a2f2b2c2944e9baff536085 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 11:21:32 +0900 Subject: [PATCH 179/501] =?UTF-8?q?Fix:=20=E3=81=AA=E3=82=93=E3=81=8BReply?= =?UTF-8?q?=E3=81=8C=E3=81=8A=E3=81=8B=E3=81=97=E3=81=84=E3=81=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/endpoints/notes/global-timeline.ts | 10 - .../api/stream/channels/global-timeline.ts | 22 +- .../api/stream/channels/home-timeline.ts | 9 +- .../api/stream/channels/hybrid-timeline.ts | 8 +- .../api/stream/channels/local-timeline.ts | 20 +- packages/frontend/src/pages/timeline.vue | 323 +++++++++--------- 6 files changed, 170 insertions(+), 222 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts index 834dcb23aa..be7557c213 100644 --- a/packages/backend/src/server/api/endpoints/notes/global-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/global-timeline.ts @@ -87,16 +87,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.withFiles) { query.andWhere('note.fileIds != \'{}\''); } - - if (ps.withRenotes === false) { - query.andWhere(new Brackets(qb => { - qb.orWhere('note.renoteId IS NULL'); - qb.orWhere(new Brackets(qb => { - qb.orWhere('note.text IS NOT NULL'); - qb.orWhere('note.fileIds != \'{}\''); - })); - })); - } //#endregion const timeline = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/stream/channels/global-timeline.ts b/packages/backend/src/server/api/stream/channels/global-timeline.ts index 38fc49d679..553c44071f 100644 --- a/packages/backend/src/server/api/stream/channels/global-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/global-timeline.ts @@ -18,9 +18,8 @@ class GlobalTimelineChannel extends Channel { public readonly chName = 'globalTimeline'; public static shouldShare = false; public static requireCredential = false; - private withReplies: boolean; - private withFiles: boolean; private withRenotes: boolean; + private withFiles: boolean; constructor( private metaService: MetaService, @@ -39,7 +38,6 @@ class GlobalTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.gtlAvailable) return; - this.withReplies = params.withReplies ?? false; this.withRenotes = params.withRenotes ?? true; this.withFiles = params.withFiles ?? false; @@ -49,25 +47,11 @@ class GlobalTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.visibility !== 'public') return; if (note.channelId != null) return; - // ファイルを含まない投稿は除外 - if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; - - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } - // 関係ない返信は除外 if (note.reply && !this.following[note.userId]?.withReplies) { const reply = note.reply; diff --git a/packages/backend/src/server/api/stream/channels/home-timeline.ts b/packages/backend/src/server/api/stream/channels/home-timeline.ts index b855f1835c..46071e82fa 100644 --- a/packages/backend/src/server/api/stream/channels/home-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/home-timeline.ts @@ -16,9 +16,8 @@ class HomeTimelineChannel extends Channel { public readonly chName = 'homeTimeline'; public static shouldShare = false; public static requireCredential = true; - private withReplies: boolean; - private withFiles: boolean; private withRenotes: boolean; + private withFiles: boolean; constructor( private noteEntityService: NoteEntityService, @@ -32,9 +31,8 @@ class HomeTimelineChannel extends Channel { @bindThis public async init(params: any) { - this.withReplies = params.withReplies ?? false; this.withRenotes = params.withRenotes ?? true; - this.withFiles = params.withFiles as boolean; + this.withFiles = params.withFiles ?? false; this.subscriber.on('notesStream', this.onNote); } @@ -55,9 +53,6 @@ class HomeTimelineChannel extends Channel { // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return; - // ファイルを含まない投稿は除外 - if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; - if (note.visibility === 'followers') { if (!isMe && !Object.hasOwn(this.following, note.userId)) return; } else if (note.visibility === 'specified') { diff --git a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts index 788ca9678d..8d7973d907 100644 --- a/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/hybrid-timeline.ts @@ -39,9 +39,9 @@ class HybridTimelineChannel extends Channel { const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null); if (!policies.ltlAvailable) return; - this.withReplies = params.withReplies ?? false; this.withRenotes = params.withRenotes ?? true; - this.withFiles = params.withFiles as boolean; + this.withReplies = params.withReplies ?? false; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -64,9 +64,6 @@ class HybridTimelineChannel extends Channel { (note.channelId != null && this.followingChannels.has(note.channelId)) )) return; - // ファイルを含まない投稿は除外 - if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; - if (note.visibility === 'followers') { if (!isMe && !Object.hasOwn(this.following, note.userId)) return; } else if (note.visibility === 'specified') { @@ -75,7 +72,6 @@ class HybridTimelineChannel extends Channel { // Ignore notes from instances the user has muted if (isInstanceMuted(note, new Set<string>(this.userProfile!.mutedInstances))) return; - // 関係ない返信は除外 if (note.reply && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; diff --git a/packages/backend/src/server/api/stream/channels/local-timeline.ts b/packages/backend/src/server/api/stream/channels/local-timeline.ts index f93f57d1ed..9dd05b9b08 100644 --- a/packages/backend/src/server/api/stream/channels/local-timeline.ts +++ b/packages/backend/src/server/api/stream/channels/local-timeline.ts @@ -40,7 +40,7 @@ class LocalTimelineChannel extends Channel { this.withRenotes = params.withRenotes ?? true; this.withReplies = params.withReplies ?? false; - this.withFiles = params.withFiles as boolean; + this.withFiles = params.withFiles ?? false; // Subscribe events this.subscriber.on('notesStream', this.onNote); @@ -48,26 +48,12 @@ class LocalTimelineChannel extends Channel { @bindThis private async onNote(note: Packed<'Note'>) { + if (this.withFiles && (note.fileIds == null || note.fileIds.length === 0)) return; + if (note.user.host !== null) return; if (note.visibility !== 'public') return; if (note.channelId != null && !this.followingChannels.has(note.channelId)) return; - // ファイルを含まない投稿は除外 - if (this.withFiles && (note.files === undefined || note.files.length === 0)) return; - - // リプライなら再pack - if (note.replyId != null) { - note.reply = await this.noteEntityService.pack(note.replyId, this.user, { - detail: true, - }); - } - // Renoteなら再pack - if (note.renoteId != null) { - note.renote = await this.noteEntityService.pack(note.renoteId, this.user, { - detail: true, - }); - } - // 関係ない返信は除外 if (note.reply && this.user && !this.following[note.userId]?.withReplies && !this.withReplies) { const reply = note.reply; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 5b6dc80e59..3d18ab2d3b 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -4,30 +4,30 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> - <MkSpacer :contentMax="800"> - <div ref="rootEl" v-hotkey.global="keymap"> - <XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/> - <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <MkStickyContainer> + <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> + <MkSpacer :contentMax="800"> + <div ref="rootEl" v-hotkey.global="keymap"> + <XTutorial v-if="$i && defaultStore.reactiveState.timelineTutorial.value != -1" class="_panel" style="margin-bottom: var(--margin);"/> + <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> - <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <div :class="$style.tl"> - <MkTimeline - ref="tlComponent" - :key="src + withRenotes + withReplies + onlyFiles" - :src="src.split(':')[0]" - :list="src.split(':')[1]" - :withRenotes="withRenotes" - :withReplies="withReplies" - :onlyFiles="onlyFiles" - :sound="true" - @queue="queueUpdated" - /> - </div> - </div> - </MkSpacer> -</MkStickyContainer> + <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> + <div :class="$style.tl"> + <MkTimeline + ref="tlComponent" + :key="src + withRenotes + withReplies + onlyFiles" + :src="src.split(':')[0]" + :list="src.split(':')[1]" + :withRenotes="withRenotes" + :withReplies="withReplies" + :onlyFiles="onlyFiles" + :sound="true" + @queue="queueUpdated" + /> + </div> + </div> + </MkSpacer> + </MkStickyContainer> </template> <script lang="ts" setup> @@ -52,7 +52,7 @@ const XTutorial = defineAsyncComponent(() => import('./timeline.tutorial.vue')); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable && defaultStore.state.showGlobalTimeline) || ($i != null && $i.policies.gtlAvailable && defaultStore.state.showGlobalTimeline); const keymap = { - 't': focus, + 't': focus, }; const tlComponent = $shallowRef<InstanceType<typeof MkTimeline>>(); @@ -61,205 +61,202 @@ const rootEl = $shallowRef<HTMLElement>(); let queue = $ref(0); let srcWhenNotSignin = $ref(isLocalTimelineAvailable ? 'local' : 'global'); const src = $computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin), set: (x) => saveSrc(x) }); -const withReplies_store = computed(defaultStore.makeGetterSetter('withRenotes')) -const withRenotes_store = computed(defaultStore.makeGetterSetter('withReplies')) -const onlyFiles_store = computed(defaultStore.makeGetterSetter('onlyFiles')) -const withRenotes = $ref(defaultStore.state.onlyAndWithSave ? withRenotes_store : true); -const withReplies = $ref(defaultStore.state.onlyAndWithSave ? withReplies_store : true); -const onlyFiles = $ref(defaultStore.state.onlyAndWithSave ? onlyFiles_store : false); +const withRenotes = $ref(true); +const withReplies = $ref($i ? defaultStore.state.tlWithReplies : false); +const onlyFiles = $ref(false); const isShowMediaTimeline = $ref(defaultStore.state.showMediaTimeline) + watch($$(src), () => queue = 0); watch($$(withReplies), (x) => { - if ($i) defaultStore.set('tlWithReplies', x); + if ($i) defaultStore.set('tlWithReplies', x); }); function queueUpdated(q: number): void { - queue = q; + queue = q; } function top(): void { - if (rootEl) scroll(rootEl, { top: 0 }); + if (rootEl) scroll(rootEl, { top: 0 }); } async function chooseList(ev: MouseEvent): Promise<void> { - const lists = await userListsCache.fetch(); - const items = lists.map(list => ({ - type: 'link' as const, - text: list.name, - to: `/timeline/list/${list.id}`, - })); - os.popupMenu(items, ev.currentTarget ?? ev.target); + const lists = await userListsCache.fetch(); + const items = lists.map(list => ({ + type: 'link' as const, + text: list.name, + to: `/timeline/list/${list.id}`, + })); + os.popupMenu(items, ev.currentTarget ?? ev.target); } async function chooseAntenna(ev: MouseEvent): Promise<void> { - const antennas = await antennasCache.fetch(); - const items = antennas.map(antenna => ({ - type: 'link' as const, - text: antenna.name, - indicate: antenna.hasUnreadNote, - to: `/timeline/antenna/${antenna.id}`, - })); - os.popupMenu(items, ev.currentTarget ?? ev.target); + const antennas = await antennasCache.fetch(); + const items = antennas.map(antenna => ({ + type: 'link' as const, + text: antenna.name, + indicate: antenna.hasUnreadNote, + to: `/timeline/antenna/${antenna.id}`, + })); + os.popupMenu(items, ev.currentTarget ?? ev.target); } async function chooseChannel(ev: MouseEvent): Promise<void> { - const channels = await os.api('channels/my-favorites', { - limit: 100, - }); - const items = channels.map(channel => ({ - type: 'link' as const, - text: channel.name, - indicate: channel.hasUnreadNote, - to: `/channels/${channel.id}`, - })); - os.popupMenu(items, ev.currentTarget ?? ev.target); + const channels = await os.api('channels/my-favorites', { + limit: 100, + }); + const items = channels.map(channel => ({ + type: 'link' as const, + text: channel.name, + indicate: channel.hasUnreadNote, + to: `/channels/${channel.id}`, + })); + os.popupMenu(items, ev.currentTarget ?? ev.target); } -function saveSrc(newSrc: 'home' | 'local' | 'social' | 'global' | `list:${string}`): void { - let userList = null; - if (newSrc.startsWith('userList:')) { - const id = newSrc.substring('userList:'.length); - userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id); - } - defaultStore.set('tl', { - src: newSrc, - userList, - }); - srcWhenNotSignin = newSrc; +function saveSrc(newSrc: 'home' | 'local' | 'media' | 'social' | 'global' | `list:${string}`): void { + let userList = null; + if (newSrc.startsWith('userList:')) { + const id = newSrc.substring('userList:'.length); + userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id); + } + defaultStore.set('tl', { + src: newSrc, + userList, + }); + srcWhenNotSignin = newSrc; } async function timetravel(): Promise<void> { - const { canceled, result: date } = await os.inputDate({ - title: i18n.ts.date, - }); - if (canceled) return; + const { canceled, result: date } = await os.inputDate({ + title: i18n.ts.date, + }); + if (canceled) return; - tlComponent.timetravel(date); + tlComponent.timetravel(date); } function focus(): void { - tlComponent.focus(); + tlComponent.focus(); } const headerActions = $computed(() => [{ - icon: 'ti ti-dots', - text: i18n.ts.options, - handler: (ev) => { - os.popupMenu([{ - type: 'switch', - text: i18n.ts.showRenotes, - icon: 'ti ti-repeat', - ref: $$(withRenotes), - }, { - type: 'switch', - text: i18n.ts.withReplies, - icon: 'ti ti-arrow-back-up', - ref: $$(withReplies), - }, { - type: 'switch', - text: i18n.ts.fileAttachedOnly, - icon: 'ti ti-photo', - ref: $$(onlyFiles), - }], ev.currentTarget ?? ev.target); - }, + icon: 'ti ti-dots', + text: i18n.ts.options, + handler: (ev) => { + os.popupMenu([{ + type: 'switch', + text: i18n.ts.showRenotes, + icon: 'ti ti-repeat', + ref: $$(withRenotes), + }, src === 'local' || src === 'social' ? { + type: 'switch', + text: i18n.ts.showRepliesToOthersInTimeline, + ref: $$(withReplies), + } : undefined, { + type: 'switch', + text: i18n.ts.fileAttachedOnly, + icon: 'ti ti-photo', + ref: $$(onlyFiles), + }], ev.currentTarget ?? ev.target); + }, }]); const headerTabs = $computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({ - key: 'list:' + l.id, - title: l.name, - icon: 'ti ti-star', - iconOnly: true, + key: 'list:' + l.id, + title: l.name, + icon: 'ti ti-star', + iconOnly: true, }))), { - key: 'home', - title: i18n.ts._timelines.home, - icon: 'ti ti-home', - iconOnly: true, + key: 'home', + title: i18n.ts._timelines.home, + icon: 'ti ti-home', + iconOnly: true, }, ...(isLocalTimelineAvailable ? [{ - key: 'local', - title: i18n.ts._timelines.local, - icon: 'ti ti-planet', - iconOnly: true, + key: 'local', + title: i18n.ts._timelines.local, + icon: 'ti ti-planet', + iconOnly: true, }, ...(isShowMediaTimeline ? [{ - key: 'media', - title: i18n.ts._timelines.media, - icon: 'ti ti-photo', - iconOnly: true, -}] : []),{ - key: 'social', - title: i18n.ts._timelines.social, - icon: 'ti ti-rocket', - iconOnly: true, -}] : []), ...(isGlobalTimelineAvailable ? [{ - key: 'global', - title: i18n.ts._timelines.global, - icon: 'ti ti-whirl', - iconOnly: true, + key: 'media', + title: i18n.ts._timelines.media, + icon: 'ti ti-photo', + iconOnly: true, }] : []), { - icon: 'ti ti-list', - title: i18n.ts.lists, - iconOnly: true, - onClick: chooseList, + key: 'social', + title: i18n.ts._timelines.social, + icon: 'ti ti-universe', + iconOnly: true, +}] : []), ...(isGlobalTimelineAvailable ? [{ + key: 'global', + title: i18n.ts._timelines.global, + icon: 'ti ti-whirl', + iconOnly: true, +}] : []), { + icon: 'ti ti-list', + title: i18n.ts.lists, + iconOnly: true, + onClick: chooseList, }, { - icon: 'ti ti-antenna', - title: i18n.ts.antennas, - iconOnly: true, - onClick: chooseAntenna, + icon: 'ti ti-antenna', + title: i18n.ts.antennas, + iconOnly: true, + onClick: chooseAntenna, }, { - icon: 'ti ti-device-tv', - title: i18n.ts.channel, - iconOnly: true, - onClick: chooseChannel, + icon: 'ti ti-device-tv', + title: i18n.ts.channel, + iconOnly: true, + onClick: chooseChannel, }] as Tab[]); const headerTabsWhenNotLogin = $computed(() => [ - ...(isLocalTimelineAvailable ? [{ - key: 'local', - title: i18n.ts._timelines.local, - icon: 'ti ti-planet', - iconOnly: true, - }] : []), - ...(isGlobalTimelineAvailable ? [{ - key: 'global', - title: i18n.ts._timelines.global, - icon: 'ti ti-whirl', - iconOnly: true, - }] : []), + ...(isLocalTimelineAvailable ? [{ + key: 'local', + title: i18n.ts._timelines.local, + icon: 'ti ti-planet', + iconOnly: true, + }] : []), + ...(isGlobalTimelineAvailable ? [{ + key: 'global', + title: i18n.ts._timelines.global, + icon: 'ti ti-whirl', + iconOnly: true, + }] : []), ] as Tab[]); definePageMetadata(computed(() => ({ - title: i18n.ts.timeline, - icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-universe' : src === 'global' ? 'ti ti-whirl' : 'ti ti-home', + title: i18n.ts.timeline, + icon: src === 'local' ? 'ti ti-planet' : src === 'social' ? 'ti ti-universe' : src === 'global' ? 'ti ti-whirl' : 'ti ti-home', }))); </script> <style lang="scss" module> .new { - position: sticky; - top: calc(var(--stickyTop, 0px) + 16px); - z-index: 1000; - width: 100%; - margin: calc(-0.675em - 8px) 0; + position: sticky; + top: calc(var(--stickyTop, 0px) + 16px); + z-index: 1000; + width: 100%; + margin: calc(-0.675em - 8px) 0; - &:first-child { - margin-top: calc(-0.675em - 8px - var(--margin)); - } + &:first-child { + margin-top: calc(-0.675em - 8px - var(--margin)); + } } .newButton { - display: block; - margin: var(--margin) auto 0 auto; - padding: 8px 16px; - border-radius: 32px; + display: block; + margin: var(--margin) auto 0 auto; + padding: 8px 16px; + border-radius: 32px; } .postForm { - border-radius: var(--radius); + border-radius: var(--radius); } .tl { - background: var(--bg); - border-radius: var(--radius); - overflow: clip; + background: var(--bg); + border-radius: var(--radius); + overflow: clip; } </style> From bbf1ba6a540d9c9859ba77e526a3eeee4fd74db6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 14:39:25 +0900 Subject: [PATCH 180/501] Fix: mute --- packages/frontend/src/components/MkNote.vue | 20 ++++++++++++++++--- .../frontend/src/pages/settings/general.vue | 2 -- .../pages/settings/mute-block.word-mute.vue | 7 ++++--- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 7b326ecbdd..b2374def60 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -141,6 +141,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </I18n> </div> + <div v-else-if="hideMutedNotes" /> + </template> <script lang="ts" setup> @@ -274,6 +276,7 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi } function renote(viaKeyboard = false) { + if (muted && !hideMutedNotes) return; pleaseLogin(); showMovedDialog(); @@ -286,7 +289,7 @@ function renote(viaKeyboard = false) { action: () => { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { - const rect = el.getBoundingClientRect(); + const rect = el?.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, {x, y}, {}, 'end'); @@ -317,7 +320,7 @@ function renote(viaKeyboard = false) { action: () => { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { - const rect = el.getBoundingClientRect(); + const rect = el?.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, {x, y}, {}, 'end'); @@ -356,6 +359,7 @@ function renote(viaKeyboard = false) { } function reply(viaKeyboard = false): void { + if (muted && !hideMutedNotes) return; pleaseLogin(); os.post({ reply: appearNote, @@ -367,6 +371,7 @@ function reply(viaKeyboard = false): void { } function react(viaKeyboard = false): void { + if (muted && !hideMutedNotes) return; pleaseLogin(); showMovedDialog(); if (appearNote.reactionAcceptance === 'likeOnly') { @@ -376,7 +381,7 @@ function react(viaKeyboard = false): void { }); const el = reactButton.value as HTMLElement | null | undefined; if (el) { - const rect = el.getBoundingClientRect(); + const rect = el?.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, {x, y}, {}, 'end'); @@ -398,6 +403,7 @@ function react(viaKeyboard = false): void { } function undoReact(note): void { + if (muted && !hideMutedNotes) return; const oldReaction = note.myReaction; if (!oldReaction) return; os.api('notes/reactions/delete', { @@ -406,6 +412,7 @@ function undoReact(note): void { } function onContextmenu(ev: MouseEvent): void { + if (muted && !hideMutedNotes) return; const isLink = (el: HTMLElement) => { if (el.tagName === 'A') return true; // 再生速度の選択などのために、Audio要素のコンテキストメニューはブラウザデフォルトとする。 @@ -434,6 +441,7 @@ function onContextmenu(ev: MouseEvent): void { } function menu(viaKeyboard = false): void { + if (muted && !hideMutedNotes) return; const {menu, cleanup} = getNoteMenu({ note: note, translating, @@ -448,6 +456,7 @@ function menu(viaKeyboard = false): void { } async function clip() { + if (muted && !hideMutedNotes) return; os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, @@ -456,6 +465,7 @@ async function clip() { } function showRenoteMenu(viaKeyboard = false): void { + if (muted && !hideMutedNotes) return; function getUnrenote(): MenuItem { return { text: i18n.ts.unrenote, @@ -492,18 +502,22 @@ function showRenoteMenu(viaKeyboard = false): void { } function focus() { + if (muted && !hideMutedNotes) return; el.value.focus(); } function blur() { + if (muted && !hideMutedNotes) return; el.value.blur(); } function focusBefore() { + if (muted && !hideMutedNotes) return; focusPrev(el.value); } function focusAfter() { + if (muted && !hideMutedNotes) return; focusNext(el.value); } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 03da2884bb..59a53b5657 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -50,7 +50,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch> - <MkSwitch v-model="hideMutedNotes">{{ i18n.ts.hideMutedNotes }}</MkSwitch> <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor}}</MkSwitch> <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> <template #label>{{ i18n.ts._visibility.home }}</template> @@ -252,7 +251,6 @@ const showNoteActionsOnlyHover = computed(defaultStore.makeGetterSetter('showNot const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showClipButtonInNoteFooter')); const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); -const hideMutedNotes = computed(defaultStore.makeGetterSetter('hideMutedNotes')); const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue index 25a836ea55..5c8b0c30df 100644 --- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue +++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue @@ -11,12 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> </MkTextarea> </div> + <MkSwitch v-model="hideMutedNotes">{{ i18n.ts._wordMute.hideMutedNotes }}</MkSwitch> <MkButton primary inline :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </template> <script lang="ts" setup> -import { ref, watch } from 'vue'; +import {computed, ref, watch} from 'vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkButton from '@/components/MkButton.vue'; @@ -28,6 +29,7 @@ import { defaultStore } from '@/store.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import MkSwitch from "@/components/MkSwitch.vue"; const render = (mutedWords) => mutedWords.map(x => { if (Array.isArray(x)) { @@ -37,10 +39,9 @@ const render = (mutedWords) => mutedWords.map(x => { } }).join('\n'); -const tab = ref('soft'); const mutedWords = ref(render($i!.mutedWords)); const changed = ref(false); - +const hideMutedNotes = computed(defaultStore.makeGetterSetter('hideMutedNotes')); watch(mutedWords, () => { changed.value = true; }); From 9719dba14aa6c22a1e5d416ee4654d1ab25e9a74 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 15:44:43 +0900 Subject: [PATCH 181/501] Update CHANGELOG.md Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5feec00c4..a4ad0244f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ ### General - Feat: アンテナでローカルの投稿のみ収集できるようになりました - Feat: サーバーサイレンス機能が追加されました +- Feat: 絵文字のリクエスト機能が追加されました - Enhance: 新規にフォローした人の返信をデフォルトでTLに追加できるオプションを追加 - Enhance: HTL/LTL/STLを2023.10.0アップデート以前まで遡れるように - Enhance: フォロー/フォロー解除したときに過去分のHTLにも含まれる投稿が反映されるように From ff1ba99ea1f4a33828ee5f600491e405fc46a9ab Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 16:15:19 +0900 Subject: [PATCH 182/501] Update misskey-js.api.md Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/misskey-js/etc/misskey-js.api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0136df2030..f88590d90c 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -284,6 +284,7 @@ type CustomEmoji = { url: string; category: string; aliases: string[]; + draft: boolean; }; // @public (undocumented) @@ -2990,7 +2991,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:109:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:605:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:606:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From 82d9720cf7f68a281ccdab41c5d47025efa07d46 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 16:20:57 +0900 Subject: [PATCH 183/501] =?UTF-8?q?fix:=20index.js=20=E3=81=AB=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/src/core/CustomEmojiService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index ddba55dc45..cdc7cae852 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -12,8 +12,7 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; -import type { EmojisRepository, MiRole, MiUser } from '@/models/_.js'; -import type { DriveFilesRepository } from '@/models/index.js'; +import type { EmojisRepository, MiRole, MiUser, DriveFilesRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; From c911b51bbe674bef40c3eb950e249ecc28ef235a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 17:02:13 +0900 Subject: [PATCH 184/501] Update locales Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 3 +-- locales/ja-JP.yml | 7 +++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 30200d2d51..d6d7f5752a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1027,9 +1027,8 @@ export interface Locale { "notesSearchNotAvailable": string; "license": string; "draft": string; - "newEmojis": string; "undrafted": string; - "draftEmojis": string; + "requestEmojisEmojis": string; "emojiNameValidation": string; "unfavoriteConfirm": string; "myClips": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8c9c9dfe4c..448aa7429c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1023,10 +1023,9 @@ sensitiveWordsDescription: "設定したワードが含まれるノートの公 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" -draft: "ドラフト" -newEmojis: "新着の絵文字" -undrafted: "ドラフト解除" -draftEmojis: "ドラフトされてる絵文字" +draft: "下書き" +undrafted: "下書き解除" +requestEmojisEmojis: "申請されてる絵文字" emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" From b10b2d747e2259ba02ffc7ef29dea277340576f1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 17:03:09 +0900 Subject: [PATCH 185/501] Update Locale keys Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/pages/about.emojis.vue | 2 +- .../src/pages/custom-emojis-manager.vue | 162 +++++++++--------- 2 files changed, 82 insertions(+), 82 deletions(-) diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index f51cc1b640..ced2881786 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -65,7 +65,7 @@ const headerTabs = $computed(() => [{ title: i18n.ts.list, }, { key: 'draft', - title: i18n.ts.draftEmojis, + title: i18n.ts.requestEmojis, }]); definePageMetadata(ref({})); diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index b88253a53e..68486b9cca 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -4,24 +4,24 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div> - <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> - <MkSpacer :contentMax="900"> - <div class="ogwlenmc"> - <div v-if="tab === 'local'" class="local"> - <MkCustomEmojiEditLocal/> - </div> - <div v-if="tab === 'draft'" class="draft"> - <MkCustomEmojiEditDraft/> - </div> - <div v-else-if="tab === 'remote'" class="remote"> - <MkCustomEmojiEditRemote/> - </div> - </div> - </MkSpacer> - </MkStickyContainer> - </div> +<div> + <MkStickyContainer> + <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <MkSpacer :contentMax="900"> + <div class="ogwlenmc"> + <div v-if="tab === 'local'" class="local"> + <MkCustomEmojiEditLocal/> + </div> + <div v-if="tab === 'draft'" class="draft"> + <MkCustomEmojiEditDraft/> + </div> + <div v-else-if="tab === 'remote'" class="remote"> + <MkCustomEmojiEditRemote/> + </div> + </div> + </MkSpacer> + </MkStickyContainer> +</div> </template> <script lang="ts" setup> @@ -37,84 +37,84 @@ import { definePageMetadata } from '@/scripts/page-metadata'; const tab = ref('draft'); const add = async (ev: MouseEvent) => { - os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { - }, { - done: result => { - //TODO: emitにして追加を反映 - // if (result.created) { - // emojisPaginationComponent.value.prepend(result.created); - // emojisPaginationComponent.value.reload(); - // } - }, - }, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { + }, { + done: result => { + //TODO: emitにして追加を反映 + // if (result.created) { + // emojisPaginationComponent.value.prepend(result.created); + // emojisPaginationComponent.value.reload(); + // } + }, + }, 'closed'); }; const menu = (ev: MouseEvent) => { - os.popupMenu([{ - icon: 'ti ti-download', - text: i18n.ts.export, - action: async () => { - os.api('export-custom-emojis', { - }) - .then(() => { - os.alert({ - type: 'info', - text: i18n.ts.exportRequested, - }); - }).catch((err) => { - os.alert({ - type: 'error', - text: err.message, - }); - }); - }, - }, { - icon: 'ti ti-upload', - text: i18n.ts.import, - action: async () => { - const file = await selectFile(ev.currentTarget ?? ev.target); - os.api('admin/emoji/import-zip', { - fileId: file.id, - }) - .then(() => { - os.alert({ - type: 'info', - text: i18n.ts.importRequested, - }); - }).catch((err) => { - os.alert({ - type: 'error', - text: err.message, - }); - }); - }, - }], ev.currentTarget ?? ev.target); + os.popupMenu([{ + icon: 'ti ti-download', + text: i18n.ts.export, + action: async () => { + os.api('export-custom-emojis', { + }) + .then(() => { + os.alert({ + type: 'info', + text: i18n.ts.exportRequested, + }); + }).catch((err) => { + os.alert({ + type: 'error', + text: err.message, + }); + }); + }, + }, { + icon: 'ti ti-upload', + text: i18n.ts.import, + action: async () => { + const file = await selectFile(ev.currentTarget ?? ev.target); + os.api('admin/emoji/import-zip', { + fileId: file.id, + }) + .then(() => { + os.alert({ + type: 'info', + text: i18n.ts.importRequested, + }); + }).catch((err) => { + os.alert({ + type: 'error', + text: err.message, + }); + }); + }, + }], ev.currentTarget ?? ev.target); }; const headerActions = $computed(() => [{ - asFullButton: true, - icon: 'ti ti-plus', - text: i18n.ts.addEmoji, - handler: add, + asFullButton: true, + icon: 'ti ti-plus', + text: i18n.ts.addEmoji, + handler: add, }, { - icon: 'ti ti-dots', - handler: menu, + icon: 'ti ti-dots', + handler: menu, }]); const headerTabs = $computed(() => [{ - key: 'draft', - title: i18n.ts.draftEmojis, + key: 'draft', + title: i18n.ts.requestEmojis, }, { - key: 'local', - title: i18n.ts.local, + key: 'local', + title: i18n.ts.local, }, { - key: 'remote', - title: i18n.ts.remote, + key: 'remote', + title: i18n.ts.remote, }]); definePageMetadata(computed(() => ({ - title: i18n.ts.customEmojis, - icon: 'ti ti-icons', + title: i18n.ts.customEmojis, + icon: 'ti ti-icons', }))); </script> From fb2482eb9806d4ddd964ba41a98165de48a4a626 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 17:04:13 +0900 Subject: [PATCH 186/501] Fix Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index d6d7f5752a..e662093c9b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1028,7 +1028,7 @@ export interface Locale { "license": string; "draft": string; "undrafted": string; - "requestEmojisEmojis": string; + "requestEmojis": string; "emojiNameValidation": string; "unfavoriteConfirm": string; "myClips": string; From a4fef11e3c37cc1f293b7faaa6da1dddd4cf4d91 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 20 Oct 2023 17:07:26 +0900 Subject: [PATCH 187/501] Fix Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/ja-JP.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 448aa7429c..723e8b2b3e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1025,7 +1025,7 @@ notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" draft: "下書き" undrafted: "下書き解除" -requestEmojisEmojis: "申請されてる絵文字" +requestEmojis: "リクエストされている絵文字" emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" From 2edbd4eca4ab72288fc03563b93f254531098d2e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 11:32:09 +0900 Subject: [PATCH 188/501] =?UTF-8?q?Fix:=20=E3=81=AA=E3=82=93=E3=81=8B?= =?UTF-8?q?=E3=81=8A=E3=81=8B=E3=81=97=E3=81=84=E3=81=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkNote.vue | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index b2374def60..139749670e 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -276,7 +276,6 @@ function smallerVisibility(a: Visibility | string, b: Visibility | string): Visi } function renote(viaKeyboard = false) { - if (muted && !hideMutedNotes) return; pleaseLogin(); showMovedDialog(); @@ -289,7 +288,7 @@ function renote(viaKeyboard = false) { action: () => { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { - const rect = el?.getBoundingClientRect(); + const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, {x, y}, {}, 'end'); @@ -320,7 +319,7 @@ function renote(viaKeyboard = false) { action: () => { const el = renoteButton.value as HTMLElement | null | undefined; if (el) { - const rect = el?.getBoundingClientRect(); + const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, {x, y}, {}, 'end'); @@ -359,7 +358,6 @@ function renote(viaKeyboard = false) { } function reply(viaKeyboard = false): void { - if (muted && !hideMutedNotes) return; pleaseLogin(); os.post({ reply: appearNote, @@ -371,7 +369,6 @@ function reply(viaKeyboard = false): void { } function react(viaKeyboard = false): void { - if (muted && !hideMutedNotes) return; pleaseLogin(); showMovedDialog(); if (appearNote.reactionAcceptance === 'likeOnly') { @@ -381,7 +378,7 @@ function react(viaKeyboard = false): void { }); const el = reactButton.value as HTMLElement | null | undefined; if (el) { - const rect = el?.getBoundingClientRect(); + const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); os.popup(MkRippleEffect, {x, y}, {}, 'end'); @@ -403,7 +400,6 @@ function react(viaKeyboard = false): void { } function undoReact(note): void { - if (muted && !hideMutedNotes) return; const oldReaction = note.myReaction; if (!oldReaction) return; os.api('notes/reactions/delete', { @@ -412,7 +408,6 @@ function undoReact(note): void { } function onContextmenu(ev: MouseEvent): void { - if (muted && !hideMutedNotes) return; const isLink = (el: HTMLElement) => { if (el.tagName === 'A') return true; // 再生速度の選択などのために、Audio要素のコンテキストメニューはブラウザデフォルトとする。 @@ -441,7 +436,6 @@ function onContextmenu(ev: MouseEvent): void { } function menu(viaKeyboard = false): void { - if (muted && !hideMutedNotes) return; const {menu, cleanup} = getNoteMenu({ note: note, translating, @@ -456,7 +450,6 @@ function menu(viaKeyboard = false): void { } async function clip() { - if (muted && !hideMutedNotes) return; os.popupMenu(await getNoteClipMenu({ note: note, isDeleted, @@ -465,7 +458,6 @@ async function clip() { } function showRenoteMenu(viaKeyboard = false): void { - if (muted && !hideMutedNotes) return; function getUnrenote(): MenuItem { return { text: i18n.ts.unrenote, @@ -502,22 +494,18 @@ function showRenoteMenu(viaKeyboard = false): void { } function focus() { - if (muted && !hideMutedNotes) return; el.value.focus(); } function blur() { - if (muted && !hideMutedNotes) return; el.value.blur(); } function focusBefore() { - if (muted && !hideMutedNotes) return; focusPrev(el.value); } function focusAfter() { - if (muted && !hideMutedNotes) return; focusNext(el.value); } From 5291931e995a30381acdbda9aace723a9d0d45e1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 11:32:32 +0900 Subject: [PATCH 189/501] 2023.10.2-beta.2-prismisskey.2 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 5c1a532a93..a9df9b2650 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.10.2-beta.2-prismisskey.1", + "version": "2023.10.2-beta.2-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", @@ -18,7 +18,7 @@ "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", "build-storybook": "pnpm --filter frontend build-storybook", - "build-and-start": "pnpm build && pnpm start", + "build-and-start": "cross-env NODE_ENV=development pnpm build && cross-env NODE_ENV=development pnpm start", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", "init": "pnpm migrate", From 97590f2567e85b740c2dface424cd898e3a09a5d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 15:55:18 +0900 Subject: [PATCH 190/501] Update locales Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/ja-JP.yml | 1 + .../backend/migration/1697813507149-EmojiDraft.js | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 packages/backend/migration/1697813507149-EmojiDraft.js diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 723e8b2b3e..77485e0de1 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -258,6 +258,7 @@ announcements: "お知らせ" imageUrl: "画像URL" remove: "削除" removed: "削除しました" +undraftAreYouSure: "「{x}」をドラフト解除しますか?" removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" resetAreYouSure: "リセットしますか?" diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js new file mode 100644 index 0000000000..8aadc76f80 --- /dev/null +++ b/packages/backend/migration/1697813507149-EmojiDraft.js @@ -0,0 +1,15 @@ +export class EmojiDraft1697813507149 { + name = 'EmojiDraft1697813507149' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_j7skve3cwn4pfb2qg9mx1la8iy" ON "emoji_draft" ("name") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e99cd746a9632624dae39cfb35" ON "emoji_draft" ("name") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_e99cd746a9632624dae39cfb35"`); + await queryRunner.query(`DROP INDEX "public"."IDX_j7skve3cwn4pfb2qg9mx1la8iy"`); + await queryRunner.query(`DROP TABLE "emoji_draft"`); + } +} From fe938bf8e68a20035861ed549650314e7c30a420 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 16:29:09 +0900 Subject: [PATCH 191/501] =?UTF-8?q?Feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E7=94=B3=E8=AB=8B=E4=B8=AD=E3=81=AE=E3=82=84=E3=81=A4=E3=81=AE?= =?UTF-8?q?=E3=83=86=E3=83=BC=E3=83=96=E3=83=AB=E3=82=92=E5=88=86=E3=81=91?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 1 + .../1684236161625-addEmojiDraftFlag.js | 11 -- packages/backend/src/core/CoreModule.ts | 6 + .../backend/src/core/CustomEmojiService.ts | 127 +++++++++++++----- .../core/entities/DriveFileEntityService.ts | 6 + .../core/entities/EmojiDraftsEntityService.ts | 70 ++++++++++ .../src/core/entities/EmojiEntityService.ts | 2 - packages/backend/src/di-symbols.ts | 1 + packages/backend/src/misc/json-schema.ts | 4 +- packages/backend/src/models/Emoji.ts | 6 - packages/backend/src/models/EmojiDraft.ts | 72 ++++++++++ .../backend/src/models/RepositoryModule.ts | 10 +- packages/backend/src/models/_.ts | 3 + .../backend/src/models/json-schema/emoji.ts | 82 ++++++++++- packages/backend/src/postgres.ts | 2 + .../backend/src/server/api/EndpointsModule.ts | 8 ++ packages/backend/src/server/api/endpoints.ts | 4 + .../api/endpoints/admin/emoji/add-draft.ts | 14 +- .../server/api/endpoints/admin/emoji/add.ts | 3 +- .../api/endpoints/admin/emoji/delete.ts | 9 +- .../api/endpoints/admin/emoji/draft-update.ts | 120 +++++++++++++++++ .../api/endpoints/admin/emoji/update.ts | 45 ++++--- .../src/server/api/endpoints/emoji-drafts.ts | 65 +++++++++ packages/backend/src/types.ts | 5 + .../src/components/MkCustomEmojiEditDraft.vue | 11 +- .../src/components/MkEmojiEditDialog.vue | 78 +++-------- .../frontend/src/components/MkPagination.vue | 11 +- packages/frontend/src/pages/about.emojis.vue | 7 +- packages/frontend/src/pages/emojis.emoji.vue | 72 +++++----- 29 files changed, 666 insertions(+), 189 deletions(-) delete mode 100644 packages/backend/migration/1684236161625-addEmojiDraftFlag.js create mode 100644 packages/backend/src/core/entities/EmojiDraftsEntityService.ts create mode 100644 packages/backend/src/models/EmojiDraft.ts create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts create mode 100644 packages/backend/src/server/api/endpoints/emoji-drafts.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index e662093c9b..12f61f1aac 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -261,6 +261,7 @@ export interface Locale { "imageUrl": string; "remove": string; "removed": string; + "undraftAreYouSure": string; "removeAreYouSure": string; "deleteAreYouSure": string; "resetAreYouSure": string; diff --git a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js b/packages/backend/migration/1684236161625-addEmojiDraftFlag.js deleted file mode 100644 index b0a13ea498..0000000000 --- a/packages/backend/migration/1684236161625-addEmojiDraftFlag.js +++ /dev/null @@ -1,11 +0,0 @@ -export class AddEmojiDraftFlag1684236161625 { - name = 'AddEmojiDraftFlag1684236161625' - - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" ADD "draft" boolean NOT NULL DEFAULT false`); - } - - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "emoji" DROP COLUMN "draft"`); - } -} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index e7e66646fc..44894ffa25 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -86,6 +86,7 @@ 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 { EmojiDraftsEntityService } from './entities/EmojiDraftsEntityService.js'; import { FollowingEntityService } from './entities/FollowingEntityService.js'; import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js'; import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js'; @@ -217,6 +218,7 @@ const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService }; const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService }; const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService }; +const $EmojiDraftsEntityService: Provider = { provide: 'EmojiDraftsEntityService', useExisting: EmojiDraftsEntityService }; const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService }; const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService }; const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService }; @@ -348,6 +350,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, + EmojiDraftsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -474,6 +477,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, + $EmojiDraftsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, @@ -600,6 +604,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, + EmojiDraftsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -725,6 +730,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, + $EmojiDraftsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index cdc7cae852..1aa6377be3 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -12,12 +12,13 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; -import type { EmojisRepository, MiRole, MiUser, DriveFilesRepository } from '@/models/_.js'; +import type { EmojisRepository, EmojiDraftsRepository, MiRole, MiUser } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @@ -33,8 +34,8 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, private utilityService: UtilityService, private idService: IdService, @@ -58,6 +59,40 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } + @bindThis + public async draft(data: { + driveFile: MiDriveFile; + name: string; + category: string | null; + aliases: string[]; + license: string | null; + isSensitive: boolean; + localOnly: boolean; + }, me?: MiUser): Promise<MiEmojiDraft> { + const emoji = await this.emojiDraftsRepository.insert({ + id: this.idService.gen(), + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + originalUrl: data.driveFile.url, + publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url, + type: data.driveFile.webpublicType ?? data.driveFile.type, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + fileId: data.driveFile.id, + }).then(x => this.emojiDraftsRepository.findOneByOrFail(x.identifiers[0])); + + if (me) { + this.moderationLogService.log(me, 'addCustomEmoji', { + emojiId: emoji.id, + emoji: emoji, + }); + } + + return emoji; + } @bindThis public async add(data: { driveFile: MiDriveFile; @@ -68,7 +103,6 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; - draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][]; }, moderator?: MiUser): Promise<MiEmoji> { const emoji = await this.emojisRepository.insert({ @@ -85,7 +119,6 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction, - draft: data.draft, }).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0])); if (data.host == null) { @@ -113,44 +146,27 @@ export class CustomEmojiService implements OnApplicationShutdown { category?: string | null; aliases?: string[]; license?: string | null; - fileId?: string | null; isSensitive?: boolean; localOnly?: boolean; - draft: boolean; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][]; }, moderator?: MiUser): Promise<void> { const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); - const driveFile = data.fileId !== null ? await this.driveFilesRepository.findOneBy({ id: data.fileId }) : null; const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); - if (driveFile !== null) { - await this.emojisRepository.update(emoji.id, { - updatedAt: new Date(), - name: data.name, - category: data.category, - aliases: data.aliases, - license: data.license, - isSensitive: data.isSensitive, - localOnly: data.localOnly, - originalUrl: data.driveFile != null ? data.driveFile.url : undefined, - publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, - type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, - draft: data.draft, - }); - } else { - await this.emojisRepository.update(emoji.id, { - updatedAt: new Date(), - name: data.name, - category: data.category, - aliases: data.aliases, - license: data.license, - isSensitive: data.isSensitive, - localOnly: data.localOnly, - draft: data.draft, - roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, - }); - } + await this.emojisRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + roleIdsThatCanBeUsedThisEmojiAsReaction: data.roleIdsThatCanBeUsedThisEmojiAsReaction ?? undefined, + }); this.localEmojisCache.refresh(); @@ -179,7 +195,35 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } } + @bindThis + public async draftUpdate(id: MiEmoji['id'], data: { + driveFile?: MiDriveFile; + name?: string; + category?: string | null; + aliases?: string[]; + license?: string | null; + isSensitive?: boolean; + localOnly?: boolean; + }, moderator?: MiUser): Promise<void> { + const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); + const sameNameEmoji = await this.emojiDraftsRepository.findOneBy({ name: data.name }); + if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); + await this.emojiDraftsRepository.update(emoji.id, { + updatedAt: new Date(), + name: data.name, + category: data.category, + aliases: data.aliases, + license: data.license, + isSensitive: data.isSensitive, + localOnly: data.localOnly, + originalUrl: data.driveFile != null ? data.driveFile.url : undefined, + publicUrl: data.driveFile != null ? (data.driveFile.webpublicUrl ?? data.driveFile.url) : undefined, + type: data.driveFile != null ? (data.driveFile.webpublicType ?? data.driveFile.type) : undefined, + }); + + this.localEmojisCache.refresh(); + } @bindThis public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) { const emojis = await this.emojisRepository.findBy({ @@ -287,7 +331,12 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } } + @bindThis + public async draftDelete(id: MiEmojiDraft['id']) { + const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); + await this.emojiDraftsRepository.delete(emoji.id); + } @bindThis public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) { const emojis = await this.emojisRepository.findBy({ @@ -408,12 +457,20 @@ export class CustomEmojiService implements OnApplicationShutdown { public checkDuplicate(name: string): Promise<boolean> { return this.emojisRepository.exist({ where: { name, host: IsNull() } }); } + @bindThis + public checkDraftDuplicate(name: string): Promise<boolean> { + return this.emojiDraftsRepository.exist({ where: { name } }); + } @bindThis public getEmojiById(id: string): Promise<MiEmoji | null> { return this.emojisRepository.findOneBy({ id }); } + @bindThis + public getEmojiDraftById(id: string): Promise<MiEmojiDraft | null> { + return this.emojiDraftsRepository.findOneBy({ id }); + } @bindThis public dispose(): void { this.cache.dispose(); diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index 14be000367..d5406373b7 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -133,7 +133,13 @@ export class DriveFileEntityService { } return url; } + @bindThis + public async getFromUrl(url: string): Promise<MiDriveFile | null> { + const file = await this.driveFilesRepository.findOneBy({ url: url }); + if (file === null ) return null; + return file; + } @bindThis public async calcDriveUsageOf(user: MiUser['id'] | { id: MiUser['id'] }): Promise<number> { const id = typeof user === 'object' ? user.id : user; diff --git a/packages/backend/src/core/entities/EmojiDraftsEntityService.ts b/packages/backend/src/core/entities/EmojiDraftsEntityService.ts new file mode 100644 index 0000000000..de1a621553 --- /dev/null +++ b/packages/backend/src/core/entities/EmojiDraftsEntityService.ts @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { Packed } from '@/misc/json-schema.js'; +import { bindThis } from '@/decorators.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; + +@Injectable() +export class EmojiDraftsEntityService { + constructor( + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + ) { + } + + @bindThis + public async packSimple( + src: MiEmojiDraft['id'] | MiEmojiDraft, + ): Promise<Packed<'EmojiDraftSimple'>> { + const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + + return { + aliases: emoji.aliases, + name: emoji.name, + category: emoji.category, + // || emoji.originalUrl してるのは後方互換性のため(publicUrlはstringなので??はだめ) + url: emoji.publicUrl, + isSensitive: emoji.isSensitive ? true : undefined, + }; + } + + @bindThis + public packSimpleMany( + emojis: any[], + ) { + return Promise.all(emojis.map(x => this.packSimple(x))); + } + + @bindThis + public async packDetailed( + src: MiEmojiDraft['id'] | MiEmojiDraft, + ): Promise<Packed<'EmojiDraftDetailed'>> { + const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + + return { + id: emoji.id, + aliases: emoji.aliases, + name: emoji.name, + category: emoji.category, + url: emoji.publicUrl, + license: emoji.license, + isSensitive: emoji.isSensitive, + localOnly: emoji.localOnly, + fileId: emoji.fileId, + }; + } + + @bindThis + public packDetailedMany( + emojis: any[], + ) { + return Promise.all(emojis.map(x => this.packDetailed(x))); + } +} + diff --git a/packages/backend/src/core/entities/EmojiEntityService.ts b/packages/backend/src/core/entities/EmojiEntityService.ts index d50d7356ae..5b97cfad5e 100644 --- a/packages/backend/src/core/entities/EmojiEntityService.ts +++ b/packages/backend/src/core/entities/EmojiEntityService.ts @@ -33,7 +33,6 @@ export class EmojiEntityService { url: emoji.publicUrl || emoji.originalUrl, isSensitive: emoji.isSensitive ? true : undefined, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length > 0 ? emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : undefined, - draft: emoji.draft, }; } @@ -62,7 +61,6 @@ export class EmojiEntityService { isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, - draft: emoji.draft, }; } diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index edcdd21d60..a374370edc 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -39,6 +39,7 @@ export const DI = { followRequestsRepository: Symbol('followRequestsRepository'), instancesRepository: Symbol('instancesRepository'), emojisRepository: Symbol('emojisRepository'), + emojiDraftsRepository: Symbol('emojiDraftsRepository'), driveFilesRepository: Symbol('driveFilesRepository'), driveFoldersRepository: Symbol('driveFoldersRepository'), metasRepository: Symbol('metasRepository'), diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index 80c1041c62..d203d5fa82 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -33,7 +33,7 @@ import { packedClipSchema } from '@/models/json-schema/clip.js'; import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js'; import { packedQueueCountSchema } from '@/models/json-schema/queue.js'; import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js'; -import { packedEmojiDetailedSchema, packedEmojiSimpleSchema } from '@/models/json-schema/emoji.js'; +import { packedEmojiDetailedSchema, packedEmojiDraftSimpleSchema, packedEmojiSimpleSchema, packedEmojiDraftDetailedSchema } from '@/models/json-schema/emoji.js'; import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; @@ -69,7 +69,9 @@ export const refs = { FederationInstance: packedFederationInstanceSchema, GalleryPost: packedGalleryPostSchema, EmojiSimple: packedEmojiSimpleSchema, + EmojiDraftSimple: packedEmojiDraftSimpleSchema, EmojiDetailed: packedEmojiDetailedSchema, + EmojiDraftDetailed: packedEmojiDraftDetailedSchema, Flash: packedFlashSchema, }; diff --git a/packages/backend/src/models/Emoji.ts b/packages/backend/src/models/Emoji.ts index bd8d54ffc3..563ac1d9d3 100644 --- a/packages/backend/src/models/Emoji.ts +++ b/packages/backend/src/models/Emoji.ts @@ -81,10 +81,4 @@ export class MiEmoji { array: true, length: 128, default: '{}', }) public roleIdsThatCanBeUsedThisEmojiAsReaction: string[]; - - @Column('boolean', { - default: false, - nullable: false, - }) - public draft: boolean; } diff --git a/packages/backend/src/models/EmojiDraft.ts b/packages/backend/src/models/EmojiDraft.ts new file mode 100644 index 0000000000..6d469fd200 --- /dev/null +++ b/packages/backend/src/models/EmojiDraft.ts @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; +import { id } from './util/id.js'; + +@Entity('emoji_draft') +@Index(['name'], { unique: true }) +export class MiEmojiDraft { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + nullable: true, + }) + public updatedAt: Date | null; + + @Index() + @Column('varchar', { + length: 128, + }) + public name: string; + + @Column('varchar', { + length: 128, nullable: true, + }) + public category: string | null; + + @Column('varchar', { + length: 512, + }) + public originalUrl: string; + + @Column('varchar', { + length: 512, + default: '', + }) + public publicUrl: string; + + // publicUrlの方のtypeが入る + @Column('varchar', { + length: 64, nullable: true, + }) + public type: string | null; + + @Column('varchar', { + array: true, length: 128, default: '{}', + }) + public aliases: string[]; + + @Column('varchar', { + length: 1024, nullable: true, + }) + public license: string | null; + + @Column('varchar', { + length: 1024, nullable: false, + }) + public fileId: string; + + @Column('boolean', { + default: false, + }) + public localOnly: boolean; + + @Column('boolean', { + default: false, + }) + public isSensitive: boolean; +} diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 9efd6841b1..30f11cbc4e 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,7 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiEmojiDraft, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -165,6 +165,12 @@ const $emojisRepository: Provider = { inject: [DI.db], }; +const $emojiDraftsRepository: Provider = { + provide: DI.emojiDraftsRepository, + useFactory: (db: DataSource) => db.getRepository(MiEmojiDraft), + inject: [DI.db], +}; + const $driveFilesRepository: Provider = { provide: DI.driveFilesRepository, useFactory: (db: DataSource) => db.getRepository(MiDriveFile), @@ -423,6 +429,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, + $emojiDraftsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, @@ -489,6 +496,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, + $emojiDraftsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index f974f95ed8..98392c0af4 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -67,6 +67,7 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; import type { Repository } from 'typeorm'; export { @@ -87,6 +88,7 @@ export { MiDriveFile, MiDriveFolder, MiEmoji, + MiEmojiDraft, MiFollowing, MiFollowRequest, MiGalleryLike, @@ -153,6 +155,7 @@ export type ClipFavoritesRepository = Repository<MiClipFavorite>; export type DriveFilesRepository = Repository<MiDriveFile>; export type DriveFoldersRepository = Repository<MiDriveFolder>; export type EmojisRepository = Repository<MiEmoji>; +export type EmojiDraftsRepository = Repository<MiEmojiDraft>; export type FollowingsRepository = Repository<MiFollowing>; export type FollowRequestsRepository = Repository<MiFollowRequest>; export type GalleryLikesRepository = Repository<MiGalleryLike>; diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 90054cbc50..70da8dc67f 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -40,10 +40,36 @@ export const packedEmojiSimpleSchema = { format: 'id', }, }, - draft: { - type: 'boolean', + }, +} as const; +export const packedEmojiDraftSimpleSchema = { + type: 'object', + properties: { + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', optional: false, nullable: true, }, + url: { + type: 'string', + optional: false, nullable: false, + }, + isSensitive: { + type: 'boolean', + optional: true, nullable: false, + }, }, } as const; @@ -85,10 +111,6 @@ export const packedEmojiDetailedSchema = { type: 'string', optional: false, nullable: true, }, - draft: { - type: 'boolean', - optional: false, nullable: true, - }, isSensitive: { type: 'boolean', optional: false, nullable: false, @@ -108,3 +130,51 @@ export const packedEmojiDetailedSchema = { }, }, } as const; + +export const packedEmojiDraftDetailedSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + url: { + type: 'string', + optional: false, nullable: false, + }, + license: { + type: 'string', + optional: false, nullable: true, + }, + isSensitive: { + type: 'boolean', + optional: false, nullable: false, + }, + localOnly: { + type: 'boolean', + optional: false, nullable: false, + }, + fileId: { + type: 'string', + optional: false, nullable: false, + }, + }, +} as const; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index d4c6ad82ce..16c3387ad2 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -28,6 +28,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; +import { MiEmojiDraft } from '@/models/EmojiDraft.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -160,6 +161,7 @@ export const entities = [ MiPoll, MiPollVote, MiEmoji, + MiEmojiDraft, MiHashtag, MiSwSubscription, MiAbuseUserReport, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 35d44d3579..f52791fe62 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -37,6 +37,7 @@ import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-al import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -243,6 +244,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; +import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -388,6 +390,7 @@ const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-ali const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default }; const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; +const $admin_emoji_draftUpdate: Provider = { provide: 'ep:admin/emoji/draft-update', useClass: ep___admin_emoji_draftUpdate.default }; const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; @@ -594,6 +597,7 @@ const $invite_list: Provider = { provide: 'ep:invite/list', useClass: ep___invit const $invite_limit: Provider = { provide: 'ep:invite/limit', useClass: ep___invite_limit.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; +const $emoji_drafts: Provider = { provide: 'ep:emoji-drafts', useClass: ep___emojiDrafts.default }; const $emoji: Provider = { provide: 'ep:emoji', useClass: ep___emoji.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; @@ -743,6 +747,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, + $admin_emoji_draftUpdate, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -949,6 +954,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, + $emoji_drafts, $emoji, $miauth_genToken, $mute_create, @@ -1092,6 +1098,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, + $admin_emoji_draftUpdate, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -1298,6 +1305,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, + $emoji_drafts, $emoji, $miauth_genToken, $mute_create, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 202f449efc..748a73b17e 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -37,6 +37,7 @@ import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-al import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; +import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -243,6 +244,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; +import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -386,6 +388,7 @@ const eps = [ ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], + ['admin/emoji/draft-update', ep___admin_emoji_draftUpdate], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], @@ -592,6 +595,7 @@ const eps = [ ['invite/limit', ep___invite_limit], ['meta', ep___meta], ['emojis', ep___emojis], + ['emoji-drafts', ep___emojiDrafts], ['emoji', ep___emoji], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index 5212d7285b..af85b2c10a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -18,6 +18,11 @@ export const meta = { code: 'NO_SUCH_FILE', id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', }, + duplicateName: { + message: 'Duplicate name.', + code: 'DUPLICATE_NAME', + id: 'f7a3462c-4e6e-4069-8421-b9bd4f4c3975', + }, }, } as const; @@ -55,11 +60,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { + const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); + const isDraftDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + + if (isDuplicate || isDraftDuplicate) throw new ApiError(meta.errors.duplicateName); const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const emoji = await this.customEmojiService.add({ + const emoji = await this.customEmojiService.draft({ driveFile, name: ps.name, category: ps.category ?? null, @@ -67,9 +76,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, - host: null, - draft: true, - roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); await this.moderationLogService.log(me, 'addCustomEmoji', { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 251abbc8d0..292cc89f54 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -51,7 +51,7 @@ export const paramDef = { type: 'string', } }, }, - required: ['name', 'fileId', 'draft'], + required: ['name', 'fileId'], } as const; // TODO: ロジックをサービスに切り出す @@ -82,7 +82,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, - draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 58aa0b9950..b3ec350272 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -36,7 +36,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private customEmojiService: CustomEmojiService, ) { super(meta, paramDef, async (ps, me) => { - await this.customEmojiService.delete(ps.id, me); + const emoji = await this.customEmojiService.getEmojiById(ps.id); + const draftEmoji = await this.customEmojiService.getEmojiDraftById(ps.id); + if (emoji != null) { + await this.customEmojiService.delete(ps.id, me); + } + if (draftEmoji != null) { + await this.customEmojiService.draftDelete(ps.id); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts new file mode 100644 index 0000000000..3e1aa4131a --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts @@ -0,0 +1,120 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; +import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { ApiError } from '../../../error.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', + + errors: { + noSuchEmoji: { + message: 'No such emoji.', + code: 'NO_SUCH_EMOJI', + id: '684dec9d-a8c2-4364-9aa8-456c49cb1dc8', + }, + noSuchFile: { + message: 'No such file.', + code: 'NO_SUCH_FILE', + id: '14fb9fd9-0731-4e2f-aeb9-f09e4740333d', + }, + sameNameEmojiExists: { + message: 'Emoji that have same name already exists.', + code: 'SAME_NAME_EMOJI_EXISTS', + id: '7180fe9d-1ee3-bff9-647d-fe9896d2ffb8', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + id: { type: 'string', format: 'misskey:id' }, + name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, + fileId: { type: 'string', format: 'misskey:id' }, + category: { + type: 'string', + nullable: true, + description: 'Use `null` to reset the category.', + }, + aliases: { type: 'array', items: { + type: 'string', + } }, + license: { type: 'string', nullable: true }, + isSensitive: { type: 'boolean' }, + localOnly: { type: 'boolean' }, + roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { + type: 'string', + } }, + draft: { type: 'boolean' }, + }, + required: ['id', 'name', 'aliases'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + + private customEmojiService: CustomEmojiService, + private driveFileEntityService: DriveFileEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + let driveFile; + const isDraft = !!ps.draft; + if (ps.fileId) { + driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + } + + const emoji = await this.customEmojiService.getEmojiDraftById(ps.id); + if (emoji != null) { + if (ps.name !== emoji.name) { + const isDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); + } + } else { + throw new ApiError(meta.errors.noSuchEmoji); + } + if (!isDraft) { + const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); + if (file === null) throw new ApiError(meta.errors.noSuchFile); + await this.customEmojiService.add({ + driveFile: file, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + host: null, + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + roleIdsThatCanBeUsedThisEmojiAsReaction: [], + }, me); + await this.customEmojiService.draftDelete(ps.id); + } else { + await this.customEmojiService.draftUpdate(ps.id, { + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + }, me); + } + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index bebfdae8f2..f996560ce1 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -5,8 +5,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository } from '@/models/_.js'; +import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -57,7 +58,7 @@ export const paramDef = { } }, draft: { type: 'boolean' }, }, - required: ['id', 'name', 'aliases', 'draft'], + required: ['id', 'name', 'aliases'], } as const; @Injectable() @@ -67,10 +68,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private driveFilesRepository: DriveFilesRepository, private customEmojiService: CustomEmojiService, + private driveFileEntityService: DriveFileEntityService, ) { super(meta, paramDef, async (ps, me) => { let driveFile; - + const isDraft = !!ps.draft; if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); @@ -85,18 +87,31 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.noSuchEmoji); } - await this.customEmojiService.update(ps.id, { - driveFile, - name: ps.name, - category: ps.category ?? null, - aliases: ps.aliases, - license: ps.license ?? null, - isSensitive: ps.isSensitive, - localOnly: ps.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, - fileId: ps.fileId ?? null, - draft: ps.draft, - }, me); + if (!isDraft) { + await this.customEmojiService.update(ps.id, { + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases, + license: ps.license ?? null, + isSensitive: ps.isSensitive, + localOnly: ps.localOnly, + roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, + }, me); + } else { + const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); + if (file === null) throw new ApiError(meta.errors.noSuchFile); + await this.customEmojiService.draft({ + driveFile: file, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + }, me); + await this.customEmojiService.delete(ps.id); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/emoji-drafts.ts b/packages/backend/src/server/api/endpoints/emoji-drafts.ts new file mode 100644 index 0000000000..5b23dac093 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emoji-drafts.ts @@ -0,0 +1,65 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { EmojiDraftsRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['meta'], + + requireCredential: false, + allowGet: true, + cacheSec: 3600, + + res: { + type: 'object', + optional: false, nullable: false, + properties: { + emojis: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'EmojiDraftDetailed', + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + + private emojiDraftsEntityService: EmojiDraftsEntityService, + ) { + super(meta, paramDef, async () => { + const emojis = await this.emojiDraftsRepository.find({ + order: { + category: 'ASC', + name: 'ASC', + }, + }); + + return { + emojis: await this.emojiDraftsEntityService.packDetailedMany(emojis), + }; + }); + } +} diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 316073c992..f7ebabca2d 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -33,6 +33,7 @@ export const moderationLogTypes = [ 'unsuspend', 'updateUserNote', 'addCustomEmoji', + 'requestCustomEmoji', 'updateCustomEmoji', 'deleteCustomEmoji', 'assignRole', @@ -88,6 +89,10 @@ export type ModerationLogPayloads = { emojiId: string; emoji: any; }; + requestCustomEmoji: { + emojiId: string; + emoji: any; + }; updateCustomEmoji: { emojiId: string; before: any; diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue index c609898b66..d5446eb18e 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue @@ -45,18 +45,19 @@ const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPaginati const query = ref(null); const paginationDraft = { - endpoint: 'admin/emoji/list' as const, + endpoint: 'emoji-drafts' as const, limit: 30, params: computed(() => ({ query: (query.value && query.value !== '') ? query.value : null, - draft: true, })), }; const editDraft = (emoji) => { + emoji.isDraft = true; os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { emoji: emoji, isRequest: false, + isDraftEdit: true, }, { done: result => { if (result.updated) { @@ -80,16 +81,16 @@ async function undrafted(emoji) { }); if (canceled) return; - await os.api('admin/emoji/update', { + await os.api('admin/emoji/draft-update', { id: emoji.id, + fileId: emoji.fileId, name: emoji.name, category: emoji.category, aliases: emoji.aliases, license: emoji.license, - draft: false, isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, + isDraft: false, }); emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 65690042a5..cc0c17fd10 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder v-if="!isRequest"> + <MkFolder v-if="!isRequest && !isDraftEdit"> <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> @@ -65,7 +65,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkSwitch v-model="isSensitive">isSensitive</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> + <MkSwitch v-if="!isRequest" v-model="isDraft" :disabled="isRequest"> {{ i18n.ts.draft }} </MkSwitch> </div> @@ -100,6 +100,7 @@ import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ emoji?: any, isRequest: boolean, + isDraftEdit?: boolean, }>(); let dialog = $ref(null); @@ -109,18 +110,18 @@ let aliases: string = $ref(props.emoji ? props.emoji.aliases.join(' ') : ''); let license: string = $ref(props.emoji ? (props.emoji.license ?? '') : ''); let isSensitive = $ref(props.emoji ? props.emoji.isSensitive : false); let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); -let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref(props.emoji ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); +let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); -let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); - +let isDraftEdit = $ref(props.isDraftEdit ?? false); +let isDraft = $ref(!!(isDraftEdit || props.isRequest)); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); -const imgUrl = computed(() => file ? file.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null); +const imgUrl = computed(() => file ? file.url : props.emoji && !isDraftEdit ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); const validation = computed(() => { return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; }); @@ -129,28 +130,6 @@ const emit = defineEmits<{ (ev: 'closed'): void }>(); -async function add() { - const ret = await os.api('admin/emoji/add-draft', { - name: name, - category: category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - fileId: chooseFile.id, - }); - - emit('done', { - updated: { - id: ret.id, - name, - category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - draft: true, - }, - }); - - dialog.close(); -} async function changeImage(ev) { file = await selectFile(ev.currentTarget ?? ev.target, null); const candidate = file.name.replace(/\.(.+)$/, ''); @@ -174,37 +153,13 @@ async function addRole() { async function removeRole(role, ev) { rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); } -async function update() { - await os.apiWithDialog('admin/emoji/update', { - id: props.emoji.id, - name, - category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - fileId: chooseFile?.id, - draft: draft, - }); - - emit('done', { - updated: { - id: props.emoji.id, - name, - category, - aliases: aliases.split(' '), - license: license === '' ? null : license, - draft: draft, - }, - }); - - dialog.close(); -} async function done() { const params = { name, category: category === '' ? null : category, aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''), license: license === '' ? null : license, - draft: draft, + draft: isDraft, isSensitive, localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), @@ -213,12 +168,19 @@ async function done() { if (file) { params.fileId = file.id; } - console.log(props.emoji); + if (props.emoji) { - await os.apiWithDialog('admin/emoji/update', { - id: props.emoji.id, - ...params, - }); + if (isDraftEdit) { + await os.apiWithDialog('admin/emoji/draft-update', { + id: props.emoji.id, + ...params, + }); + } else { + await os.apiWithDialog('admin/emoji/update', { + id: props.emoji.id, + ...params, + }); + } emit('done', { updated: { diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 5a87273386..2a0b0fa025 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -201,11 +201,13 @@ async function init(): Promise<void> { ...params, limit: props.pagination.limit ?? 10, }).then(res => { + if (props.pagination.endpoint === 'emoji-drafts') { + res = res.emojis; + } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 3) item._shouldInsertAd_ = true; } - if (res.length === 0 || props.pagination.noPaging) { concatItems(res); more.value = false; @@ -214,7 +216,6 @@ async function init(): Promise<void> { concatItems(res); more.value = true; } - offset.value = res.length; error.value = false; fetching.value = false; @@ -241,6 +242,9 @@ const fetchMore = async (): Promise<void> => { untilId: Array.from(items.value.keys()).at(-1), }), }).then(res => { + if (props.pagination.endpoint === 'emoji-drafts') { + res = res.emojis; + } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 10) item._shouldInsertAd_ = true; @@ -305,6 +309,9 @@ const fetchMoreAhead = async (): Promise<void> => { sinceId: Array.from(items.value.keys()).at(-1), }), }).then(res => { + if (props.pagination.endpoint === 'emoji-drafts') { + res = res.emojis; + } if (res.length === 0) { items.value = concatMapWithArray(items.value, res); more.value = false; diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index ced2881786..ecbefbd086 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -32,13 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category && !e.draft)" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/> </div> </MkFoldableSection> </MkSpacer> <MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> - <XEmoji v-for="emoji in draftEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in draftEmojis.emojis" :key="emoji.name" :emoji="emoji" :draft="true"/> </div> </MkSpacer> </MkStickyContainer> @@ -73,8 +73,7 @@ definePageMetadata(ref({})); let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); -const draftEmojis = customEmojis.value.filter(emoji => emoji.draft); - +const draftEmojis = await os.apiGet('emoji-drafts'); function search() { if ((q === '' || q == null) && selectedTags.size === 0) { searchEmojis = null; diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index bd2cb33f55..d112cfcb7c 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -4,8 +4,8 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<button v-if="emoji.draft" class="zuvgdzyu _button emoji-draft" @click="menu"> - <img :src="emoji.url" class="img" loading="lazy"/> +<button v-if="draft" class="_button emoji-draft" :class="$style.root" @click="menu"> + <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div class="body"> <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> <div class="info">{{ emoji.aliases.join(' ') }}</div> @@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-else class="_button" :class="$style.root" @click="menu"> <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div :class="$style.body"> - <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> - <div :class="$style.info">{{ emoji.aliases.join(' ') }}</div> + <div class="name _monospace">{{ emoji.name }}</div> + <div class="info">{{ emoji.aliases.join(' ') }}</div> </div> </button> </template> @@ -26,13 +26,13 @@ import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - emoji: { - name: string; - aliases: string[]; - category: string; - url: string; - draft: boolean; - }; + emoji: { + name: string; + aliases: string[]; + category: string; + url: string; + }; + draft?: boolean; }>(); function menu(ev) { @@ -50,7 +50,7 @@ function menu(ev) { text: i18n.ts.info, icon: 'ti ti-info-circle', action: () => { - os.apiGet('emoji', { name: props.emoji.name }).then(res => { + os.apiGet('emoji-drafts', { name: props.emoji.name }).then(res => { os.alert({ type: 'info', text: `License: ${res.license}`, @@ -63,45 +63,45 @@ function menu(ev) { <style lang="scss" module> .root { - display: flex; - align-items: center; - padding: 12px; - text-align: left; - background: var(--panel); - border-radius: 8px; + display: flex; + align-items: center; + padding: 12px; + text-align: left; + background: var(--panel); + border-radius: 8px; - &:hover { - border-color: var(--accent); - } + &:hover { + border-color: var(--accent); + } } .img { - width: 42px; - height: 42px; - object-fit: contain; + width: 42px; + height: 42px; + object-fit: contain; } .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; } .name { - text-overflow: ellipsis; - overflow: hidden; + text-overflow: ellipsis; + overflow: hidden; } .info { - opacity: 0.5; - font-size: 0.9em; - text-overflow: ellipsis; - overflow: hidden; + opacity: 0.5; + font-size: 0.9em; + text-overflow: ellipsis; + overflow: hidden; } .emoji-draft { - --c: rgb(255 196 0 / 15%);; - background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); - background-size: 16px 16px; + --c: rgb(255 196 0 / 15%);; + background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); + background-size: 16px 16px; } </style> From 761eaeafbb770029d921096f79bd5596b6b2de3e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 16:40:20 +0900 Subject: [PATCH 192/501] type fix Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../src/queue/processors/ImportCustomEmojisProcessorService.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts index 088596bba6..a52af54a39 100644 --- a/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts +++ b/packages/backend/src/queue/processors/ImportCustomEmojisProcessorService.ts @@ -103,7 +103,6 @@ export class ImportCustomEmojisProcessorService { isSensitive: emojiInfo.isSensitive, localOnly: emojiInfo.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: [], - draft: false, }); } From 52e3fefa2023a6677eed6d62bee821de3532b24e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 18:43:14 +0900 Subject: [PATCH 193/501] ? Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/migration/1697813507149-EmojiDraft.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js index 8aadc76f80..423a76c568 100644 --- a/packages/backend/migration/1697813507149-EmojiDraft.js +++ b/packages/backend/migration/1697813507149-EmojiDraft.js @@ -4,11 +4,11 @@ export class EmojiDraft1697813507149 { async up(queryRunner) { await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); await queryRunner.query(`CREATE INDEX "IDX_j7skve3cwn4pfb2qg9mx1la8iy" ON "emoji_draft" ("name") `); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_e99cd746a9632624dae39cfb35" ON "emoji_draft" ("name") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_aw4zqjtyvxcsh797kbpzxmca6x" ON "emoji_draft" ("name") `); } async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_e99cd746a9632624dae39cfb35"`); + await queryRunner.query(`DROP INDEX "public"."IDX_aw4zqjtyvxcsh797kbpzxmca6x"`); await queryRunner.query(`DROP INDEX "public"."IDX_j7skve3cwn4pfb2qg9mx1la8iy"`); await queryRunner.query(`DROP TABLE "emoji_draft"`); } From b8dd1c5806228a13770c49d6b6e8ff132b44d21e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 19:01:12 +0900 Subject: [PATCH 194/501] Update migration file Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/migration/1697813507149-EmojiDraft.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js index 423a76c568..10e03ce949 100644 --- a/packages/backend/migration/1697813507149-EmojiDraft.js +++ b/packages/backend/migration/1697813507149-EmojiDraft.js @@ -3,13 +3,11 @@ export class EmojiDraft1697813507149 { async up(queryRunner) { await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE INDEX "IDX_j7skve3cwn4pfb2qg9mx1la8iy" ON "emoji_draft" ("name") `); await queryRunner.query(`CREATE UNIQUE INDEX "IDX_aw4zqjtyvxcsh797kbpzxmca6x" ON "emoji_draft" ("name") `); } async down(queryRunner) { await queryRunner.query(`DROP INDEX "public"."IDX_aw4zqjtyvxcsh797kbpzxmca6x"`); - await queryRunner.query(`DROP INDEX "public"."IDX_j7skve3cwn4pfb2qg9mx1la8iy"`); await queryRunner.query(`DROP TABLE "emoji_draft"`); } } From 00c4624dc8810a124213b87546a021ffb5e17d6f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 19:54:03 +0900 Subject: [PATCH 195/501] Update EmojiDraft.ts Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/src/models/EmojiDraft.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/models/EmojiDraft.ts b/packages/backend/src/models/EmojiDraft.ts index 6d469fd200..1d4ee95319 100644 --- a/packages/backend/src/models/EmojiDraft.ts +++ b/packages/backend/src/models/EmojiDraft.ts @@ -17,7 +17,6 @@ export class MiEmojiDraft { }) public updatedAt: Date | null; - @Index() @Column('varchar', { length: 128, }) From 66e8548403175c8370e4bf30a845f757db93be7f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 19:57:05 +0900 Subject: [PATCH 196/501] Update misskey-js.api.md Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/misskey-js/etc/misskey-js.api.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 7e513f0780..24d4e2eae2 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -3022,6 +3022,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/entities.ts:113:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts +// src/entities.ts:610:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From d14a7c58a2a49bfa56281735983d36a2444fe937 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 20:48:50 +0900 Subject: [PATCH 197/501] 2023.11.0-beta.1-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 75e992545f..fd41d57282 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.1", + "version": "2023.11.0-beta.1-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 8b425704994f7425628e06be55b4e0213b9836e0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 20:54:02 +0900 Subject: [PATCH 198/501] fix --- packages/frontend/src/components/global/MkAvatar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 133e82ab48..023cef0e75 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <component :is="link ? MkA : 'span'" v-user-preview="preview ? user.id : undefined" v-bind="bound" class="_noSelect" :class="[$style.root, { [$style.animation]: animation, [$style.cat]: user.isCat, [$style.square]: squareAvatars }]" :style="{ color }" :title="acct(user)" @click="onClick"> - <MkImgWithBlurhash :class="$style.inner" :src="url" :hash="defaultStore.state.enableUltimateDataSaverMode ?user.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> + <MkImgWithBlurhash :class="$style.inner" :src="url" :hash="defaultStore.state.enableUltimateDataSaverMode ? null:user.avatarBlurhash" :cover="true" :onlyAvgColor="true"/> <MkUserOnlineIndicator v-if="indicator" :class="$style.indicator" :user="user"/> <div v-if="user.isCat" :class="[$style.ears]"> <div :class="$style.earLeft"> From 29cae2ca2a66dd78e9f0b199f9c9188507f4e1e3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 20:58:02 +0900 Subject: [PATCH 199/501] fix --- packages/frontend/src/components/MkMention.vue | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index 7767f77dc6..e77102a63a 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -16,7 +16,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { toUnicode } from 'punycode'; import {computed, ref, watch} from 'vue'; -import { computed } from 'vue'; import tinycolor from 'tinycolor2'; import { host as localHost } from '@/config.js'; import { $i } from '@/account.js'; @@ -68,7 +67,6 @@ const isMe = $i && ( const bg = tinycolor(getComputedStyle(document.documentElement).getPropertyValue(isMe ? '--mentionMe' : '--mention')); bg.setAlpha(0.1); -const bgCss = bg.toRgbString(); const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(`/avatar/@${props.username}@${props.host}`) From c15ac0580069e2682f62fb5ade81cdc919b1873c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 21 Oct 2023 23:31:37 +0900 Subject: [PATCH 200/501] Update CHANGELOG.md Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae566fd1d7..854c5f70ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ ## 2023.x.x (unreleased) ### General -- +- Feat: 絵文字のリクエスト機能が追加されました ## Client - Feat: プラグイン・テーマを外部サイトから直接インストールできるようになりました @@ -30,7 +30,6 @@ ### General - Feat: アンテナでローカルの投稿のみ収集できるようになりました - Feat: サーバーサイレンス機能が追加されました -- Feat: 絵文字のリクエスト機能が追加されました - Enhance: 新規にフォローした人の返信をデフォルトでTLに追加できるオプションを追加 - Enhance: HTL/LTL/STLを2023.10.0アップデート以前まで遡れるように - Enhance: フォロー/フォロー解除したときに過去分のHTLにも含まれる投稿が反映されるように From fc0b1abfdcb064c5f52db84fe70883b399fe347b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 22 Oct 2023 13:21:20 +0900 Subject: [PATCH 201/501] 2023.11.0-beta.1-prismisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd41d57282..52f0003a97 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.1-prismisskey.1", + "version": "2023.11.0-beta.1-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From a5c4da66d6303e75b76655b12ad513bb84f5c97a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 22 Oct 2023 19:48:14 +0900 Subject: [PATCH 202/501] 2023.11.0-beta.2-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fd2847e2ee..673b4031f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.2", + "version": "2023.11.0-beta.2-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From f382520593989b8ff857d465b1c68864a9e32a6e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 22 Oct 2023 21:17:20 +0900 Subject: [PATCH 203/501] Update misskey-js.api.md Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/misskey-js/etc/misskey-js.api.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 0a6806ae60..29cdd5eead 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -284,6 +284,7 @@ type CustomEmoji = { url: string; category: string; aliases: string[]; + draft: boolean; }; // @public (undocumented) @@ -3024,7 +3025,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:115:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:611:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:612:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From 76aad3f869b13759178a946613858c7c3de5583a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 23 Oct 2023 05:16:08 +0900 Subject: [PATCH 204/501] 2023.11.0-beta.2-prismisskey.1 --- packages/frontend/src/pages/settings/general.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 664e166c81..efb0379434 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -35,6 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> + <MkSwitch v-model="FeaturedOrNote">{{ i18n.ts.FeaturedOrNote}}<template #caption>{{ i18n.ts.FeaturedOrNoteInfo }} </template></MkSwitch> + <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> </MkFolder> </div> </FormSection> From ccf1ab55647807033bc31692305c28f1a95e63da Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 23 Oct 2023 19:30:28 +0900 Subject: [PATCH 205/501] 2023.11.0-beta.3-prismisskey.1 --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 7700c6df67..fa0b49deb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.3", + "version": "2023.11.0-beta.3-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", @@ -17,6 +17,7 @@ "build-pre": "node ./scripts/build-pre.js", "build-assets": "node ./scripts/build-assets.mjs", "build": "pnpm build-pre && pnpm -r build && pnpm build-assets", + "build-and-start": "cross-env NODE_ENV=development pnpm build && cross-env NODE_ENV=development pnpm start", "build-storybook": "pnpm --filter frontend build-storybook", "start": "pnpm check:connect && cd packages/backend && node ./built/boot/entry.js", "start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/entry.js", From 53d250dddf02a450191f076fc71ef35e866232a1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 24 Oct 2023 15:37:43 +0900 Subject: [PATCH 206/501] =?UTF-8?q?draft-update=20=E3=81=8B=E3=82=89=20upd?= =?UTF-8?q?ate-draft=20=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit admin/emoji/list-draft を追加 Update MkPagination.vue Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../backend/src/server/api/EndpointsModule.ts | 12 +- packages/backend/src/server/api/endpoints.ts | 6 +- .../api/endpoints/admin/emoji/list-draft.ts | 108 ++++++++++++++++++ .../{draft-update.ts => update-draft.ts} | 5 +- .../src/server/api/endpoints/emoji-drafts.ts | 4 +- .../src/components/MkCustomEmojiEditDraft.vue | 4 +- .../src/components/MkEmojiEditDialog.vue | 2 +- .../frontend/src/components/MkPagination.vue | 9 -- 8 files changed, 126 insertions(+), 24 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts rename packages/backend/src/server/api/endpoints/admin/emoji/{draft-update.ts => update-draft.ts} (95%) diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 92e6da4ba5..6db60b456f 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -36,12 +36,13 @@ import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; +import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -396,12 +397,13 @@ const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useCla const $admin_emoji_importZip: Provider = { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }; const $admin_emoji_listRemote: Provider = { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }; const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }; +const $admin_emoji_listDraft: Provider = { provide: 'ep:admin/emoji/list-draft', useClass: ep___admin_emoji_listDraft.default }; const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }; const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }; const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default }; const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; -const $admin_emoji_draftUpdate: Provider = { provide: 'ep:admin/emoji/draft-update', useClass: ep___admin_emoji_draftUpdate.default }; +const $admin_emoji_updateDraft: Provider = { provide: 'ep:admin/emoji/update-draft', useClass: ep___admin_emoji_updateDraft.default }; const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; @@ -760,12 +762,13 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, + $admin_emoji_listDraft, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_draftUpdate, + $admin_emoji_updateDraft, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -1118,12 +1121,13 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, + $admin_emoji_listDraft, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_draftUpdate, + $admin_emoji_updateDraft, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 26b91b1f41..37f60cf210 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -36,12 +36,13 @@ import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; +import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_draftUpdate from './endpoints/admin/emoji/draft-update.js'; +import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -393,13 +394,14 @@ const eps = [ ['admin/emoji/delete', ep___admin_emoji_delete], ['admin/emoji/import-zip', ep___admin_emoji_importZip], ['admin/emoji/list-remote', ep___admin_emoji_listRemote], + ['admin/emoji/list-draft', ep___admin_emoji_listDraft], ['admin/emoji/list', ep___admin_emoji_list], ['admin/emoji/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk], ['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk], ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], - ['admin/emoji/draft-update', ep___admin_emoji_draftUpdate], + ['admin/emoji/update-draft', ep___admin_emoji_updateDraft], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts new file mode 100644 index 0000000000..95dea00426 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts @@ -0,0 +1,108 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { QueryService } from '@/core/QueryService.js'; +import { DI } from '@/di-symbols.js'; +import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +//import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + aliases: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + name: { + type: 'string', + optional: false, nullable: false, + }, + category: { + type: 'string', + optional: false, nullable: true, + }, + url: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + query: { type: 'string', nullable: true, default: null }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.emojiDraftsRepository) + private emojiDraftsRepository: EmojiDraftsRepository, + + private emojiDraftsEntityService: EmojiDraftsEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const q = this.queryService.makePaginationQuery(this.emojiDraftsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + + let emojis: MiEmojiDraft[]; + + if (ps.query) { + //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); + //const emojis = await q.limit(ps.limit).getMany(); + + emojis = await q.getMany(); + const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g); + + if (queryarry) { + emojis = emojis.filter(emoji => + queryarry.includes(`:${emoji.name}:`), + ); + } else { + emojis = emojis.filter(emoji => + emoji.name.includes(ps.query!) || + emoji.aliases.some(a => a.includes(ps.query!)) || + emoji.category?.includes(ps.query!)); + } + emojis.splice(ps.limit + 1); + } else { + emojis = await q.limit(ps.limit).getMany(); + } + + return this.emojiDraftsEntityService.packDetailedMany(emojis); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts similarity index 95% rename from packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts index 3e1aa4131a..79eee1b019 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/draft-update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -67,9 +67,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, - private customEmojiService: CustomEmojiService, private driveFileEntityService: DriveFileEntityService, ) { diff --git a/packages/backend/src/server/api/endpoints/emoji-drafts.ts b/packages/backend/src/server/api/endpoints/emoji-drafts.ts index 5b23dac093..13afee2a08 100644 --- a/packages/backend/src/server/api/endpoints/emoji-drafts.ts +++ b/packages/backend/src/server/api/endpoints/emoji-drafts.ts @@ -27,7 +27,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'EmojiDraftDetailed', + ref: 'EmojiDraftSimple', }, }, }, @@ -58,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); return { - emojis: await this.emojiDraftsEntityService.packDetailedMany(emojis), + emojis: await this.emojiDraftsEntityService.packSimpleMany(emojis), }; }); } diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue index d5446eb18e..6a97ae2019 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue @@ -45,7 +45,7 @@ const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPaginati const query = ref(null); const paginationDraft = { - endpoint: 'emoji-drafts' as const, + endpoint: 'admin/emoji/list-draft' as const, limit: 30, params: computed(() => ({ query: (query.value && query.value !== '') ? query.value : null, @@ -81,7 +81,7 @@ async function undrafted(emoji) { }); if (canceled) return; - await os.api('admin/emoji/draft-update', { + await os.api('admin/emoji/update-draft', { id: emoji.id, fileId: emoji.fileId, name: emoji.name, diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index cc0c17fd10..150d0b63c1 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -171,7 +171,7 @@ async function done() { if (props.emoji) { if (isDraftEdit) { - await os.apiWithDialog('admin/emoji/draft-update', { + await os.apiWithDialog('admin/emoji/update-draft', { id: props.emoji.id, ...params, }); diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 2a0b0fa025..ae309da82e 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -201,9 +201,6 @@ async function init(): Promise<void> { ...params, limit: props.pagination.limit ?? 10, }).then(res => { - if (props.pagination.endpoint === 'emoji-drafts') { - res = res.emojis; - } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 3) item._shouldInsertAd_ = true; @@ -242,9 +239,6 @@ const fetchMore = async (): Promise<void> => { untilId: Array.from(items.value.keys()).at(-1), }), }).then(res => { - if (props.pagination.endpoint === 'emoji-drafts') { - res = res.emojis; - } for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 10) item._shouldInsertAd_ = true; @@ -309,9 +303,6 @@ const fetchMoreAhead = async (): Promise<void> => { sinceId: Array.from(items.value.keys()).at(-1), }), }).then(res => { - if (props.pagination.endpoint === 'emoji-drafts') { - res = res.emojis; - } if (res.length === 0) { items.value = concatMapWithArray(items.value, res); more.value = false; From 8fd5eb7010ec91f3a189b967ccbe35a8ce373463 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 24 Oct 2023 16:55:22 +0900 Subject: [PATCH 207/501] =?UTF-8?q?draft=E3=81=8B=E3=82=89request=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 6 +-- locales/ja-JP.yml | 6 +-- .../migration/1697813507149-EmojiDraft.js | 13 ----- packages/backend/src/core/CoreModule.ts | 12 ++--- .../backend/src/core/CustomEmojiService.ts | 38 +++++++------- ...rvice.ts => EmojiRequestsEntityService.ts} | 22 ++++---- packages/backend/src/di-symbols.ts | 2 +- packages/backend/src/misc/json-schema.ts | 6 +-- .../models/{EmojiDraft.ts => EmojiRequest.ts} | 4 +- .../backend/src/models/RepositoryModule.ts | 12 ++--- packages/backend/src/models/_.ts | 6 +-- .../backend/src/models/json-schema/emoji.ts | 4 +- packages/backend/src/postgres.ts | 4 +- .../backend/src/server/api/EndpointsModule.ts | 32 ++++++------ packages/backend/src/server/api/endpoints.ts | 16 +++--- .../emoji/{add-draft.ts => add-request.ts} | 6 +-- .../api/endpoints/admin/emoji/delete.ts | 6 +-- .../emoji/{list-draft.ts => list-request.ts} | 18 +++---- .../{update-draft.ts => update-request.ts} | 14 ++--- .../api/endpoints/admin/emoji/update.ts | 10 ++-- .../{emoji-drafts.ts => emoji-requests.ts} | 17 +++--- .../src/components/MkCustomEmojiEditLocal.vue | 8 +-- ...Draft.vue => MkCustomEmojiEditRequest.vue} | 52 +++++++++---------- .../src/components/MkEmojiEditDialog.vue | 30 +++++------ .../frontend/src/components/MkEmojiPicker.vue | 2 +- packages/frontend/src/pages/about.emojis.vue | 12 ++--- .../src/pages/custom-emojis-manager.vue | 10 ++-- packages/frontend/src/pages/emojis.emoji.vue | 10 ++-- packages/misskey-js/etc/misskey-js.api.md | 2 +- packages/misskey-js/src/entities.ts | 2 +- 30 files changed, 184 insertions(+), 198 deletions(-) delete mode 100644 packages/backend/migration/1697813507149-EmojiDraft.js rename packages/backend/src/core/entities/{EmojiDraftsEntityService.ts => EmojiRequestsEntityService.ts} (71%) rename packages/backend/src/models/{EmojiDraft.ts => EmojiRequest.ts} (95%) rename packages/backend/src/server/api/endpoints/admin/emoji/{add-draft.ts => add-request.ts} (90%) rename packages/backend/src/server/api/endpoints/admin/emoji/{list-draft.ts => list-request.ts} (79%) rename packages/backend/src/server/api/endpoints/admin/emoji/{update-draft.ts => update-request.ts} (89%) rename packages/backend/src/server/api/endpoints/{emoji-drafts.ts => emoji-requests.ts} (66%) rename packages/frontend/src/components/{MkCustomEmojiEditDraft.vue => MkCustomEmojiEditRequest.vue} (73%) diff --git a/locales/index.d.ts b/locales/index.d.ts index 81352c786c..3090f0cd04 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -261,7 +261,7 @@ export interface Locale { "imageUrl": string; "remove": string; "removed": string; - "undraftAreYouSure": string; + "requestApprovalAreYouSure": string; "removeAreYouSure": string; "deleteAreYouSure": string; "resetAreYouSure": string; @@ -1027,8 +1027,8 @@ export interface Locale { "sensitiveWordsDescription2": string; "notesSearchNotAvailable": string; "license": string; - "draft": string; - "undrafted": string; + "requestPending": string; + "requestApproval": string; "requestEmojis": string; "emojiNameValidation": string; "unfavoriteConfirm": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index f6a9050331..92c209c681 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -258,7 +258,7 @@ announcements: "お知らせ" imageUrl: "画像URL" remove: "削除" removed: "削除しました" -undraftAreYouSure: "「{x}」をドラフト解除しますか?" +requestApprovalAreYouSure: "「{x}」のリクエストを承認しますか?" removeAreYouSure: "「{x}」を削除しますか?" deleteAreYouSure: "「{x}」を削除しますか?" resetAreYouSure: "リセットしますか?" @@ -1024,8 +1024,8 @@ sensitiveWordsDescription: "設定したワードが含まれるノートの公 sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。" notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" -draft: "下書き" -undrafted: "下書き解除" +requestPending: "申請中" +requestApproval: "リクエストを承認" requestEmojis: "リクエストされている絵文字" emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" diff --git a/packages/backend/migration/1697813507149-EmojiDraft.js b/packages/backend/migration/1697813507149-EmojiDraft.js deleted file mode 100644 index 10e03ce949..0000000000 --- a/packages/backend/migration/1697813507149-EmojiDraft.js +++ /dev/null @@ -1,13 +0,0 @@ -export class EmojiDraft1697813507149 { - name = 'EmojiDraft1697813507149' - - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "emoji_draft" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_6c7c36f693e1cb8ba1343e3336f" PRIMARY KEY ("id"))`); - await queryRunner.query(`CREATE UNIQUE INDEX "IDX_aw4zqjtyvxcsh797kbpzxmca6x" ON "emoji_draft" ("name") `); - } - - async down(queryRunner) { - await queryRunner.query(`DROP INDEX "public"."IDX_aw4zqjtyvxcsh797kbpzxmca6x"`); - await queryRunner.query(`DROP TABLE "emoji_draft"`); - } -} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index 295197acd2..10fceb87e7 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -87,7 +87,7 @@ 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 { EmojiDraftsEntityService } from './entities/EmojiDraftsEntityService.js'; +import { EmojiRequestsEntityService } from './entities/EmojiRequestsEntityService.js'; import { FollowingEntityService } from './entities/FollowingEntityService.js'; import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js'; import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js'; @@ -220,7 +220,7 @@ const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService }; const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService }; const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService }; -const $EmojiDraftsEntityService: Provider = { provide: 'EmojiDraftsEntityService', useExisting: EmojiDraftsEntityService }; +const $EmojiRequestsEntityService: Provider = { provide: 'EmojiRequestsEntityService', useExisting: EmojiRequestsEntityService }; const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService }; const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService }; const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService }; @@ -353,7 +353,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, - EmojiDraftsEntityService, + EmojiRequestsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -481,7 +481,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, - $EmojiDraftsEntityService, + $EmojiRequestsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, @@ -609,7 +609,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting DriveFileEntityService, DriveFolderEntityService, EmojiEntityService, - EmojiDraftsEntityService, + EmojiRequestsEntityService, FollowingEntityService, FollowRequestEntityService, GalleryLikeEntityService, @@ -736,7 +736,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $DriveFileEntityService, $DriveFolderEntityService, $EmojiEntityService, - $EmojiDraftsEntityService, + $EmojiRequestsEntityService, $FollowingEntityService, $FollowRequestEntityService, $GalleryLikeEntityService, diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 1aa6377be3..1346d3d839 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -12,13 +12,13 @@ import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiEmoji } from '@/models/Emoji.js'; -import type { EmojisRepository, EmojiDraftsRepository, MiRole, MiUser } from '@/models/_.js'; +import type { EmojisRepository, EmojiRequestsRepository, MiRole, MiUser } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { UtilityService } from '@/core/UtilityService.js'; import type { Serialized } from '@/types.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/; @@ -34,8 +34,8 @@ export class CustomEmojiService implements OnApplicationShutdown { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, private utilityService: UtilityService, private idService: IdService, @@ -60,7 +60,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async draft(data: { + public async Request(data: { driveFile: MiDriveFile; name: string; category: string | null; @@ -68,8 +68,8 @@ export class CustomEmojiService implements OnApplicationShutdown { license: string | null; isSensitive: boolean; localOnly: boolean; - }, me?: MiUser): Promise<MiEmojiDraft> { - const emoji = await this.emojiDraftsRepository.insert({ + }, me?: MiUser): Promise<MiEmojiRequest> { + const emoji = await this.emojiRequestsRepository.insert({ id: this.idService.gen(), updatedAt: new Date(), name: data.name, @@ -82,7 +82,7 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive: data.isSensitive, localOnly: data.localOnly, fileId: data.driveFile.id, - }).then(x => this.emojiDraftsRepository.findOneByOrFail(x.identifiers[0])); + }).then(x => this.emojiRequestsRepository.findOneByOrFail(x.identifiers[0])); if (me) { this.moderationLogService.log(me, 'addCustomEmoji', { @@ -196,7 +196,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async draftUpdate(id: MiEmoji['id'], data: { + public async RequestUpdate(id: MiEmoji['id'], data: { driveFile?: MiDriveFile; name?: string; category?: string | null; @@ -205,11 +205,11 @@ export class CustomEmojiService implements OnApplicationShutdown { isSensitive?: boolean; localOnly?: boolean; }, moderator?: MiUser): Promise<void> { - const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); - const sameNameEmoji = await this.emojiDraftsRepository.findOneBy({ name: data.name }); + const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id }); + const sameNameEmoji = await this.emojiRequestsRepository.findOneBy({ name: data.name }); if (sameNameEmoji != null && sameNameEmoji.id !== id) throw new Error('name already exists'); - await this.emojiDraftsRepository.update(emoji.id, { + await this.emojiRequestsRepository.update(emoji.id, { updatedAt: new Date(), name: data.name, category: data.category, @@ -332,10 +332,10 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async draftDelete(id: MiEmojiDraft['id']) { - const emoji = await this.emojiDraftsRepository.findOneByOrFail({ id: id }); + public async RequestDelete(id: MiEmojiRequest['id']) { + const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id }); - await this.emojiDraftsRepository.delete(emoji.id); + await this.emojiRequestsRepository.delete(emoji.id); } @bindThis public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) { @@ -458,8 +458,8 @@ export class CustomEmojiService implements OnApplicationShutdown { return this.emojisRepository.exist({ where: { name, host: IsNull() } }); } @bindThis - public checkDraftDuplicate(name: string): Promise<boolean> { - return this.emojiDraftsRepository.exist({ where: { name } }); + public checkRequestDuplicate(name: string): Promise<boolean> { + return this.emojiRequestsRepository.exist({ where: { name } }); } @bindThis @@ -468,8 +468,8 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public getEmojiDraftById(id: string): Promise<MiEmojiDraft | null> { - return this.emojiDraftsRepository.findOneBy({ id }); + public getEmojiRequestById(id: string): Promise<MiEmojiRequest | null> { + return this.emojiRequestsRepository.findOneBy({ id }); } @bindThis public dispose(): void { diff --git a/packages/backend/src/core/entities/EmojiDraftsEntityService.ts b/packages/backend/src/core/entities/EmojiRequestsEntityService.ts similarity index 71% rename from packages/backend/src/core/entities/EmojiDraftsEntityService.ts rename to packages/backend/src/core/entities/EmojiRequestsEntityService.ts index de1a621553..46308e654c 100644 --- a/packages/backend/src/core/entities/EmojiDraftsEntityService.ts +++ b/packages/backend/src/core/entities/EmojiRequestsEntityService.ts @@ -5,24 +5,24 @@ import { Inject, Injectable } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { EmojiRequestsRepository } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { bindThis } from '@/decorators.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; @Injectable() -export class EmojiDraftsEntityService { +export class EmojiRequestsEntityService { constructor( - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, ) { } @bindThis public async packSimple( - src: MiEmojiDraft['id'] | MiEmojiDraft, - ): Promise<Packed<'EmojiDraftSimple'>> { - const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + src: MiEmojiRequest['id'] | MiEmojiRequest, + ): Promise<Packed<'EmojiRequestSimple'>> { + const emoji = typeof src === 'object' ? src : await this.emojiRequestsRepository.findOneByOrFail({ id: src }); return { aliases: emoji.aliases, @@ -43,9 +43,9 @@ export class EmojiDraftsEntityService { @bindThis public async packDetailed( - src: MiEmojiDraft['id'] | MiEmojiDraft, - ): Promise<Packed<'EmojiDraftDetailed'>> { - const emoji = typeof src === 'object' ? src : await this.emojiDraftsRepository.findOneByOrFail({ id: src }); + src: MiEmojiRequest['id'] | MiEmojiRequest, + ): Promise<Packed<'EmojiRequestDetailed'>> { + const emoji = typeof src === 'object' ? src : await this.emojiRequestsRepository.findOneByOrFail({ id: src }); return { id: emoji.id, diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 7da4e75ae7..8c56300750 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -40,7 +40,7 @@ export const DI = { followRequestsRepository: Symbol('followRequestsRepository'), instancesRepository: Symbol('instancesRepository'), emojisRepository: Symbol('emojisRepository'), - emojiDraftsRepository: Symbol('emojiDraftsRepository'), + emojiRequestsRepository: Symbol('emojiRequestsRepository'), driveFilesRepository: Symbol('driveFilesRepository'), driveFoldersRepository: Symbol('driveFoldersRepository'), metasRepository: Symbol('metasRepository'), diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index d203d5fa82..d99a3edac4 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -33,7 +33,7 @@ import { packedClipSchema } from '@/models/json-schema/clip.js'; import { packedFederationInstanceSchema } from '@/models/json-schema/federation-instance.js'; import { packedQueueCountSchema } from '@/models/json-schema/queue.js'; import { packedGalleryPostSchema } from '@/models/json-schema/gallery-post.js'; -import { packedEmojiDetailedSchema, packedEmojiDraftSimpleSchema, packedEmojiSimpleSchema, packedEmojiDraftDetailedSchema } from '@/models/json-schema/emoji.js'; +import { packedEmojiDetailedSchema, packedEmojiRequestSimpleSchema, packedEmojiSimpleSchema, packedEmojiRequestDetailedSchema } from '@/models/json-schema/emoji.js'; import { packedFlashSchema } from '@/models/json-schema/flash.js'; import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; @@ -69,9 +69,9 @@ export const refs = { FederationInstance: packedFederationInstanceSchema, GalleryPost: packedGalleryPostSchema, EmojiSimple: packedEmojiSimpleSchema, - EmojiDraftSimple: packedEmojiDraftSimpleSchema, + EmojiRequestSimple: packedEmojiRequestSimpleSchema, EmojiDetailed: packedEmojiDetailedSchema, - EmojiDraftDetailed: packedEmojiDraftDetailedSchema, + EmojiRequestDetailed: packedEmojiRequestDetailedSchema, Flash: packedFlashSchema, }; diff --git a/packages/backend/src/models/EmojiDraft.ts b/packages/backend/src/models/EmojiRequest.ts similarity index 95% rename from packages/backend/src/models/EmojiDraft.ts rename to packages/backend/src/models/EmojiRequest.ts index 1d4ee95319..53a5563f7e 100644 --- a/packages/backend/src/models/EmojiDraft.ts +++ b/packages/backend/src/models/EmojiRequest.ts @@ -6,9 +6,9 @@ import { PrimaryColumn, Entity, Index, Column } from 'typeorm'; import { id } from './util/id.js'; -@Entity('emoji_draft') +@Entity('emoji_request') @Index(['name'], { unique: true }) -export class MiEmojiDraft { +export class MiEmojiRequest { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index b8202c0f81..bc5c61c547 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,7 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiEmojiDraft, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiEmojiRequest, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -171,9 +171,9 @@ const $emojisRepository: Provider = { inject: [DI.db], }; -const $emojiDraftsRepository: Provider = { - provide: DI.emojiDraftsRepository, - useFactory: (db: DataSource) => db.getRepository(MiEmojiDraft), +const $emojiRequestsRepository: Provider = { + provide: DI.emojiRequestsRepository, + useFactory: (db: DataSource) => db.getRepository(MiEmojiRequest), inject: [DI.db], }; @@ -436,7 +436,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, - $emojiDraftsRepository, + $emojiRequestsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, @@ -504,7 +504,7 @@ const $userMemosRepository: Provider = { $followRequestsRepository, $instancesRepository, $emojisRepository, - $emojiDraftsRepository, + $emojiRequestsRepository, $driveFilesRepository, $driveFoldersRepository, $metasRepository, diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index f72c7e1065..14b94e30fc 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -21,6 +21,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -68,7 +69,6 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; import type { Repository } from 'typeorm'; export { @@ -90,7 +90,7 @@ export { MiDriveFile, MiDriveFolder, MiEmoji, - MiEmojiDraft, + MiEmojiRequest, MiFollowing, MiFollowRequest, MiGalleryLike, @@ -158,7 +158,7 @@ export type ClipFavoritesRepository = Repository<MiClipFavorite>; export type DriveFilesRepository = Repository<MiDriveFile>; export type DriveFoldersRepository = Repository<MiDriveFolder>; export type EmojisRepository = Repository<MiEmoji>; -export type EmojiDraftsRepository = Repository<MiEmojiDraft>; +export type EmojiRequestsRepository = Repository<MiEmojiRequest>; export type FollowingsRepository = Repository<MiFollowing>; export type FollowRequestsRepository = Repository<MiFollowRequest>; export type GalleryLikesRepository = Repository<MiGalleryLike>; diff --git a/packages/backend/src/models/json-schema/emoji.ts b/packages/backend/src/models/json-schema/emoji.ts index 70da8dc67f..56b888c62a 100644 --- a/packages/backend/src/models/json-schema/emoji.ts +++ b/packages/backend/src/models/json-schema/emoji.ts @@ -42,7 +42,7 @@ export const packedEmojiSimpleSchema = { }, }, } as const; -export const packedEmojiDraftSimpleSchema = { +export const packedEmojiRequestSimpleSchema = { type: 'object', properties: { aliases: { @@ -131,7 +131,7 @@ export const packedEmojiDetailedSchema = { }, } as const; -export const packedEmojiDraftDetailedSchema = { +export const packedEmojiRequestDetailedSchema = { type: 'object', properties: { id: { diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index d62ad62ba6..a22b39f766 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -29,7 +29,7 @@ import { MiClipFavorite } from '@/models/ClipFavorite.js'; import { MiDriveFile } from '@/models/DriveFile.js'; import { MiDriveFolder } from '@/models/DriveFolder.js'; import { MiEmoji } from '@/models/Emoji.js'; -import { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import { MiEmojiRequest } from '@/models/EmojiRequest.js'; import { MiFollowing } from '@/models/Following.js'; import { MiFollowRequest } from '@/models/FollowRequest.js'; import { MiGalleryLike } from '@/models/GalleryLike.js'; @@ -163,7 +163,7 @@ export const entities = [ MiPoll, MiPollVote, MiEmoji, - MiEmojiDraft, + MiEmojiRequest, MiHashtag, MiSwSubscription, MiAbuseUserReport, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 6db60b456f..6a86447c42 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -29,20 +29,20 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; -import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; +import * as ep___admin_emoji_addRequest from './endpoints/admin/emoji/add-request.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; -import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; +import * as ep___admin_emoji_listRequest from './endpoints/admin/emoji/list-request.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; +import * as ep___admin_emoji_updateRequest from './endpoints/admin/emoji/update-request.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -251,7 +251,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; -import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; +import * as ep___emojiRequests from './endpoints/emoji-requests.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -390,20 +390,20 @@ const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass const $admin_drive_showFile: Provider = { provide: 'ep:admin/drive/show-file', useClass: ep___admin_drive_showFile.default }; const $admin_emoji_addAliasesBulk: Provider = { provide: 'ep:admin/emoji/add-aliases-bulk', useClass: ep___admin_emoji_addAliasesBulk.default }; const $admin_emoji_add: Provider = { provide: 'ep:admin/emoji/add', useClass: ep___admin_emoji_add.default }; -const $admin_emoji_addDraft: Provider = { provide: 'ep:admin/emoji/add-draft', useClass: ep___admin_emoji_addDraft.default }; +const $admin_emoji_addRequest: Provider = { provide: 'ep:admin/emoji/add-request', useClass: ep___admin_emoji_addRequest.default }; const $admin_emoji_copy: Provider = { provide: 'ep:admin/emoji/copy', useClass: ep___admin_emoji_copy.default }; const $admin_emoji_deleteBulk: Provider = { provide: 'ep:admin/emoji/delete-bulk', useClass: ep___admin_emoji_deleteBulk.default }; const $admin_emoji_delete: Provider = { provide: 'ep:admin/emoji/delete', useClass: ep___admin_emoji_delete.default }; const $admin_emoji_importZip: Provider = { provide: 'ep:admin/emoji/import-zip', useClass: ep___admin_emoji_importZip.default }; const $admin_emoji_listRemote: Provider = { provide: 'ep:admin/emoji/list-remote', useClass: ep___admin_emoji_listRemote.default }; const $admin_emoji_list: Provider = { provide: 'ep:admin/emoji/list', useClass: ep___admin_emoji_list.default }; -const $admin_emoji_listDraft: Provider = { provide: 'ep:admin/emoji/list-draft', useClass: ep___admin_emoji_listDraft.default }; +const $admin_emoji_listRequest: Provider = { provide: 'ep:admin/emoji/list-request', useClass: ep___admin_emoji_listRequest.default }; const $admin_emoji_removeAliasesBulk: Provider = { provide: 'ep:admin/emoji/remove-aliases-bulk', useClass: ep___admin_emoji_removeAliasesBulk.default }; const $admin_emoji_setAliasesBulk: Provider = { provide: 'ep:admin/emoji/set-aliases-bulk', useClass: ep___admin_emoji_setAliasesBulk.default }; const $admin_emoji_setCategoryBulk: Provider = { provide: 'ep:admin/emoji/set-category-bulk', useClass: ep___admin_emoji_setCategoryBulk.default }; const $admin_emoji_setLicenseBulk: Provider = { provide: 'ep:admin/emoji/set-license-bulk', useClass: ep___admin_emoji_setLicenseBulk.default }; const $admin_emoji_update: Provider = { provide: 'ep:admin/emoji/update', useClass: ep___admin_emoji_update.default }; -const $admin_emoji_updateDraft: Provider = { provide: 'ep:admin/emoji/update-draft', useClass: ep___admin_emoji_updateDraft.default }; +const $admin_emoji_updateRequest: Provider = { provide: 'ep:admin/emoji/update-request', useClass: ep___admin_emoji_updateRequest.default }; const $admin_federation_deleteAllFiles: Provider = { provide: 'ep:admin/federation/delete-all-files', useClass: ep___admin_federation_deleteAllFiles.default }; const $admin_federation_refreshRemoteInstanceMetadata: Provider = { provide: 'ep:admin/federation/refresh-remote-instance-metadata', useClass: ep___admin_federation_refreshRemoteInstanceMetadata.default }; const $admin_federation_removeAllFollowing: Provider = { provide: 'ep:admin/federation/remove-all-following', useClass: ep___admin_federation_removeAllFollowing.default }; @@ -612,7 +612,7 @@ const $invite_list: Provider = { provide: 'ep:invite/list', useClass: ep___invit const $invite_limit: Provider = { provide: 'ep:invite/limit', useClass: ep___invite_limit.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; -const $emoji_drafts: Provider = { provide: 'ep:emoji-drafts', useClass: ep___emojiDrafts.default }; +const $emoji_requests: Provider = { provide: 'ep:emoji-requests', useClass: ep___emojiRequests.default }; const $emoji: Provider = { provide: 'ep:emoji', useClass: ep___emoji.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; @@ -755,20 +755,20 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, - $admin_emoji_addDraft, + $admin_emoji_addRequest, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, - $admin_emoji_listDraft, + $admin_emoji_listRequest, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_updateDraft, + $admin_emoji_updateRequest, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -977,7 +977,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, - $emoji_drafts, + $emoji_requests, $emoji, $miauth_genToken, $mute_create, @@ -1114,20 +1114,20 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $admin_drive_showFile, $admin_emoji_addAliasesBulk, $admin_emoji_add, - $admin_emoji_addDraft, + $admin_emoji_addRequest, $admin_emoji_copy, $admin_emoji_deleteBulk, $admin_emoji_delete, $admin_emoji_importZip, $admin_emoji_listRemote, $admin_emoji_list, - $admin_emoji_listDraft, + $admin_emoji_listRequest, $admin_emoji_removeAliasesBulk, $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, $admin_emoji_update, - $admin_emoji_updateDraft, + $admin_emoji_updateRequest, $admin_federation_deleteAllFiles, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, @@ -1336,7 +1336,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $invite_limit, $meta, $emojis, - $emoji_drafts, + $emoji_requests, $emoji, $miauth_genToken, $mute_create, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 37f60cf210..74df9a959a 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -29,20 +29,20 @@ import * as ep___admin_drive_files from './endpoints/admin/drive/files.js'; import * as ep___admin_drive_showFile from './endpoints/admin/drive/show-file.js'; import * as ep___admin_emoji_addAliasesBulk from './endpoints/admin/emoji/add-aliases-bulk.js'; import * as ep___admin_emoji_add from './endpoints/admin/emoji/add.js'; -import * as ep___admin_emoji_addDraft from './endpoints/admin/emoji/add-draft.js'; +import * as ep___admin_emoji_addRequest from './endpoints/admin/emoji/add-request.js'; import * as ep___admin_emoji_copy from './endpoints/admin/emoji/copy.js'; import * as ep___admin_emoji_deleteBulk from './endpoints/admin/emoji/delete-bulk.js'; import * as ep___admin_emoji_delete from './endpoints/admin/emoji/delete.js'; import * as ep___admin_emoji_importZip from './endpoints/admin/emoji/import-zip.js'; import * as ep___admin_emoji_listRemote from './endpoints/admin/emoji/list-remote.js'; import * as ep___admin_emoji_list from './endpoints/admin/emoji/list.js'; -import * as ep___admin_emoji_listDraft from './endpoints/admin/emoji/list-draft.js'; +import * as ep___admin_emoji_listRequest from './endpoints/admin/emoji/list-request.js'; import * as ep___admin_emoji_removeAliasesBulk from './endpoints/admin/emoji/remove-aliases-bulk.js'; import * as ep___admin_emoji_setAliasesBulk from './endpoints/admin/emoji/set-aliases-bulk.js'; import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-category-bulk.js'; import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; -import * as ep___admin_emoji_updateDraft from './endpoints/admin/emoji/update-draft.js'; +import * as ep___admin_emoji_updateRequest from './endpoints/admin/emoji/update-request.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -251,7 +251,7 @@ import * as ep___invite_list from './endpoints/invite/list.js'; import * as ep___invite_limit from './endpoints/invite/limit.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; -import * as ep___emojiDrafts from './endpoints/emoji-drafts.js'; +import * as ep___emojiRequests from './endpoints/emoji-requests.js'; import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; @@ -388,20 +388,20 @@ const eps = [ ['admin/drive/show-file', ep___admin_drive_showFile], ['admin/emoji/add-aliases-bulk', ep___admin_emoji_addAliasesBulk], ['admin/emoji/add', ep___admin_emoji_add], - ['admin/emoji/add-draft', ep___admin_emoji_addDraft], + ['admin/emoji/add-request', ep___admin_emoji_addRequest], ['admin/emoji/copy', ep___admin_emoji_copy], ['admin/emoji/delete-bulk', ep___admin_emoji_deleteBulk], ['admin/emoji/delete', ep___admin_emoji_delete], ['admin/emoji/import-zip', ep___admin_emoji_importZip], ['admin/emoji/list-remote', ep___admin_emoji_listRemote], - ['admin/emoji/list-draft', ep___admin_emoji_listDraft], ['admin/emoji/list', ep___admin_emoji_list], + ['admin/emoji/list-request', ep___admin_emoji_listRequest], ['admin/emoji/remove-aliases-bulk', ep___admin_emoji_removeAliasesBulk], ['admin/emoji/set-aliases-bulk', ep___admin_emoji_setAliasesBulk], ['admin/emoji/set-category-bulk', ep___admin_emoji_setCategoryBulk], ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], - ['admin/emoji/update-draft', ep___admin_emoji_updateDraft], + ['admin/emoji/update-request', ep___admin_emoji_updateRequest], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], @@ -610,7 +610,7 @@ const eps = [ ['invite/limit', ep___invite_limit], ['meta', ep___meta], ['emojis', ep___emojis], - ['emoji-drafts', ep___emojiDrafts], + ['emoji-requests', ep___emojiRequests], ['emoji', ep___emoji], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts similarity index 90% rename from packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts index af85b2c10a..50ffb89b06 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts @@ -61,14 +61,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { ) { super(meta, paramDef, async (ps, me) => { const isDuplicate = await this.customEmojiService.checkDuplicate(ps.name); - const isDraftDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + const isRequestDuplicate = await this.customEmojiService.checkRequestDuplicate(ps.name); - if (isDuplicate || isDraftDuplicate) throw new ApiError(meta.errors.duplicateName); + if (isDuplicate || isRequestDuplicate) throw new ApiError(meta.errors.duplicateName); const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const emoji = await this.customEmojiService.draft({ + const emoji = await this.customEmojiService.Request({ driveFile, name: ps.name, category: ps.category ?? null, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index b3ec350272..9711db5a11 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -37,12 +37,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { const emoji = await this.customEmojiService.getEmojiById(ps.id); - const draftEmoji = await this.customEmojiService.getEmojiDraftById(ps.id); + const RequestEmoji = await this.customEmojiService.getEmojiRequestById(ps.id); if (emoji != null) { await this.customEmojiService.delete(ps.id, me); } - if (draftEmoji != null) { - await this.customEmojiService.draftDelete(ps.id); + if (RequestEmoji != null) { + await this.customEmojiService.RequestDelete(ps.id); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/list-request.ts similarity index 79% rename from packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/list-request.ts index 95dea00426..f5faca972f 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/list-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/list-request.ts @@ -5,11 +5,11 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { EmojiDraftsRepository } from '@/models/_.js'; -import type { MiEmojiDraft } from '@/models/EmojiDraft.js'; +import type { EmojiRequestsRepository } from '@/models/_.js'; +import type { MiEmojiRequest } from '@/models/EmojiRequest.js'; import { QueryService } from '@/core/QueryService.js'; import { DI } from '@/di-symbols.js'; -import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +import { EmojiRequestsEntityService } from '@/core/entities/EmojiRequestsEntityService.js'; //import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; export const meta = { @@ -69,16 +69,16 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, - private emojiDraftsEntityService: EmojiDraftsEntityService, + private emojiRequestsEntityService: EmojiRequestsEntityService, private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const q = this.queryService.makePaginationQuery(this.emojiDraftsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); + const q = this.queryService.makePaginationQuery(this.emojiRequestsRepository.createQueryBuilder('emoji'), ps.sinceId, ps.untilId); - let emojis: MiEmojiDraft[]; + let emojis: MiEmojiRequest[]; if (ps.query) { //q.andWhere('emoji.name ILIKE :q', { q: `%${ sqlLikeEscape(ps.query) }%` }); @@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- emojis = await q.limit(ps.limit).getMany(); } - return this.emojiDraftsEntityService.packDetailedMany(emojis); + return this.emojiRequestsEntityService.packDetailedMany(emojis); }); } } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts similarity index 89% rename from packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts rename to packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts index 79eee1b019..98e50389cc 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts @@ -56,7 +56,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, - draft: { type: 'boolean' }, + Request: { type: 'boolean' }, }, required: ['id', 'name', 'aliases'], } as const; @@ -72,22 +72,22 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { let driveFile; - const isDraft = !!ps.draft; + const isRequest = !!ps.Request; if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); } - const emoji = await this.customEmojiService.getEmojiDraftById(ps.id); + const emoji = await this.customEmojiService.getEmojiRequestById(ps.id); if (emoji != null) { if (ps.name !== emoji.name) { - const isDuplicate = await this.customEmojiService.checkDraftDuplicate(ps.name); + const isDuplicate = await this.customEmojiService.checkRequestDuplicate(ps.name); if (isDuplicate) throw new ApiError(meta.errors.sameNameEmojiExists); } } else { throw new ApiError(meta.errors.noSuchEmoji); } - if (!isDraft) { + if (!isRequest) { const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); if (file === null) throw new ApiError(meta.errors.noSuchFile); await this.customEmojiService.add({ @@ -101,9 +101,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: [], }, me); - await this.customEmojiService.draftDelete(ps.id); + await this.customEmojiService.RequestDelete(ps.id); } else { - await this.customEmojiService.draftUpdate(ps.id, { + await this.customEmojiService.RequestUpdate(ps.id, { name: ps.name, category: ps.category ?? null, aliases: ps.aliases ?? [], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index f996560ce1..7f7dc774d5 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import type { DriveFilesRepository, EmojiDraftsRepository } from '@/models/_.js'; +import type { DriveFilesRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { ApiError } from '../../../error.js'; @@ -56,7 +56,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisEmojiAsReaction: { type: 'array', items: { type: 'string', } }, - draft: { type: 'boolean' }, + Request: { type: 'boolean' }, }, required: ['id', 'name', 'aliases'], } as const; @@ -72,7 +72,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { let driveFile; - const isDraft = !!ps.draft; + const isRequest = !!ps.Request; if (ps.fileId) { driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); @@ -87,7 +87,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.noSuchEmoji); } - if (!isDraft) { + if (!isRequest) { await this.customEmojiService.update(ps.id, { driveFile, name: ps.name, @@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } else { const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); if (file === null) throw new ApiError(meta.errors.noSuchFile); - await this.customEmojiService.draft({ + await this.customEmojiService.Request({ driveFile: file, name: ps.name, category: ps.category ?? null, diff --git a/packages/backend/src/server/api/endpoints/emoji-drafts.ts b/packages/backend/src/server/api/endpoints/emoji-requests.ts similarity index 66% rename from packages/backend/src/server/api/endpoints/emoji-drafts.ts rename to packages/backend/src/server/api/endpoints/emoji-requests.ts index 13afee2a08..63ca88e93e 100644 --- a/packages/backend/src/server/api/endpoints/emoji-drafts.ts +++ b/packages/backend/src/server/api/endpoints/emoji-requests.ts @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { IsNull } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; -import type { EmojiDraftsRepository } from '@/models/_.js'; +import type { EmojiRequestsRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { EmojiDraftsEntityService } from '@/core/entities/EmojiDraftsEntityService.js'; +import { EmojiRequestsEntityService } from '@/core/entities/EmojiRequestsEntityService.js'; import { DI } from '@/di-symbols.js'; export const meta = { @@ -27,7 +26,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'EmojiDraftSimple', + ref: 'EmojiRequestSimple', }, }, }, @@ -44,13 +43,13 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.emojiDraftsRepository) - private emojiDraftsRepository: EmojiDraftsRepository, + @Inject(DI.emojiRequestsRepository) + private emojiRequestsRepository: EmojiRequestsRepository, - private emojiDraftsEntityService: EmojiDraftsEntityService, + private emojiRequestsEntityService: EmojiRequestsEntityService, ) { super(meta, paramDef, async () => { - const emojis = await this.emojiDraftsRepository.find({ + const emojis = await this.emojiRequestsRepository.find({ order: { category: 'ASC', name: 'ASC', @@ -58,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); return { - emojis: await this.emojiDraftsEntityService.packSimpleMany(emojis), + emojis: await this.emojiRequestsEntityService.packSimpleMany(emojis), }; }); } diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 7112a38430..7060f2080b 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -20,10 +20,10 @@ <template #default="{items}"> <div class="ldhfsamy"> <div v-for="emoji in items" :key="emoji.id"> - <button v-if="emoji.draft" class="emoji _panel _button emoji-draft" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <button v-if="emoji.request" class="emoji _panel _button emoji-request" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> <img :src="emoji.url" class="img" :alt="emoji.name"/> <div class="body"> - <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="name _monospace">{{ emoji.name }}</div> <div class="info">{{ emoji.category }}</div> </div> </button> @@ -82,7 +82,7 @@ const toggleSelect = (emoji) => { const edit = (emoji) => { os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { emoji: emoji, - isRequest: false, + requestNow: false, }, { done: result => { if (result.updated) { @@ -217,7 +217,7 @@ const delBulk = async () => { } } -.emoji-draft { +.emoji-request { --c: rgb(255 196 0 / 15%);; background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); background-size: 16px 16px; diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue similarity index 73% rename from packages/frontend/src/components/MkCustomEmojiEditDraft.vue rename to packages/frontend/src/components/MkCustomEmojiEditRequest.vue index 6a97ae2019..cf88a5bc63 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue @@ -1,5 +1,5 @@ <template> -<MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft"> +<MkPagination ref="emojisRequestPaginationComponent" :pagination="paginationRequest"> <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> <div class="ldhfsamy"> @@ -16,13 +16,13 @@ <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> </div> <div class="edit-button"> - <MkButton primary class="edit" @click="editDraft(emoji)"> + <MkButton primary class="edit" @click="editRequest(emoji)"> {{ i18n.ts.edit }} </MkButton> - <MkButton class="draft" @click="undrafted(emoji)"> - {{ i18n.ts.undrafted }} + <MkButton class="request" @click="unrequested(emoji)"> + {{ i18n.ts.requestApproval }} </MkButton> - <MkButton danger class="delete" @click="deleteDraft(emoji)"> + <MkButton danger class="delete" @click="deleteRequest(emoji)"> {{ i18n.ts.delete }} </MkButton> </div> @@ -40,48 +40,48 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import MkButton from '@/components/MkButton.vue'; -const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); +const emojisRequestPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); const query = ref(null); -const paginationDraft = { - endpoint: 'admin/emoji/list-draft' as const, +const paginationRequest = { + endpoint: 'admin/emoji/list-request' as const, limit: 30, params: computed(() => ({ query: (query.value && query.value !== '') ? query.value : null, })), }; -const editDraft = (emoji) => { - emoji.isDraft = true; +const editRequest = (emoji) => { + emoji.requestNow = true; os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { emoji: emoji, - isRequest: false, - isDraftEdit: true, + requestNow: false, + isRequestEdit: true, }, { done: result => { if (result.updated) { - emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ + emojisRequestPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ ...oldEmoji, ...result.updated, })); - emojisDraftPaginationComponent.value.reload(); + emojisRequestPaginationComponent.value.reload(); } else if (result.deleted) { - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - emojisDraftPaginationComponent.value.reload(); + emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisRequestPaginationComponent.value.reload(); } }, }, 'closed'); }; -async function undrafted(emoji) { +async function unrequested(emoji) { const { canceled } = await os.confirm({ type: 'warning', - text: i18n.t('undraftAreYouSure', { x: emoji.name }), + text: i18n.t('requestApprovalAreYouSure', { x: emoji.name }), }); if (canceled) return; - await os.api('admin/emoji/update-draft', { + await os.api('admin/emoji/update-request', { id: emoji.id, fileId: emoji.fileId, name: emoji.name, @@ -90,14 +90,14 @@ async function undrafted(emoji) { license: emoji.license, isSensitive: emoji.isSensitive, localOnly: emoji.localOnly, - isDraft: false, + isRequest: false, }); - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - emojisDraftPaginationComponent.value.reload(); + emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisRequestPaginationComponent.value.reload(); } -async function deleteDraft(emoji) { +async function deleteRequest(emoji) { const { canceled } = await os.confirm({ type: 'warning', text: i18n.t('removeAreYouSure', { x: emoji.name }), @@ -107,8 +107,8 @@ async function deleteDraft(emoji) { os.api('admin/emoji/delete', { id: emoji.id, }).then(() => { - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - emojisDraftPaginationComponent.value.reload(); + emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id); + emojisRequestPaginationComponent.value.reload(); }); } </script> @@ -198,7 +198,7 @@ async function deleteDraft(emoji) { margin: 6px 0; } - > .draft { + > .request { grid-row: 2; width: 100%; margin: 6px 0; diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 150d0b63c1..145bf4f8ae 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="$emit('closed')" > <template v-if="emoji" #header>:{{ emoji.name }}:</template> - <template v-else-if="isRequest" #header>{{ i18n.ts.requestCustomEmojis }}</template> + <template v-else-if="requestNow" #header>{{ i18n.ts.requestCustomEmojis }}</template> <template v-else #header>New emoji</template> <div> @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder v-if="!isRequest && !isDraftEdit"> + <MkFolder v-if="!requestNow && !isRequestEdit"> <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> @@ -65,14 +65,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkSwitch v-model="isSensitive">isSensitive</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkSwitch v-if="!isRequest" v-model="isDraft" :disabled="isRequest"> - {{ i18n.ts.draft }} + <MkSwitch v-if="!requestNow" v-model="isRequest" :disabled="requestNow"> + {{ i18n.ts.requestPending }} </MkSwitch> </div> </MkSpacer> <div :class="$style.footer"> <div :class="$style.footerButtons"> - <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-if="!requestNow" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> <MkButton v-if="validation" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> <MkButton v-else rounded style="margin: 0 auto;"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> @@ -99,8 +99,8 @@ import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ emoji?: any, - isRequest: boolean, - isDraftEdit?: boolean, + requestNow: boolean, + isRequestEdit?: boolean, }>(); let dialog = $ref(null); @@ -114,14 +114,14 @@ let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref((props.emoji && props.emoji.r let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); -let isRequest = $ref(props.isRequest); -let isDraftEdit = $ref(props.isDraftEdit ?? false); -let isDraft = $ref(!!(isDraftEdit || props.isRequest)); +let requestNow = $ref(props.requestNow); +let isRequestEdit = $ref(props.isRequestEdit ?? false); +let isRequest = $ref(!!(isRequestEdit || props.requestNow)); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); -const imgUrl = computed(() => file ? file.url : props.emoji && !isDraftEdit ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); +const imgUrl = computed(() => file ? file.url : props.emoji && !isRequestEdit ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); const validation = computed(() => { return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; }); @@ -159,7 +159,7 @@ async function done() { category: category === '' ? null : category, aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''), license: license === '' ? null : license, - draft: isDraft, + Request: isRequest, isSensitive, localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), @@ -170,8 +170,8 @@ async function done() { } if (props.emoji) { - if (isDraftEdit) { - await os.apiWithDialog('admin/emoji/update-draft', { + if (isRequestEdit) { + await os.apiWithDialog('admin/emoji/update-request', { id: props.emoji.id, ...params, }); @@ -192,7 +192,7 @@ async function done() { dialog.close(); } else { const created = isRequest - ? await os.apiWithDialog('admin/emoji/add-draft', params) + ? await os.apiWithDialog('admin/emoji/add-request', params) : await os.apiWithDialog('admin/emoji/add', params); emit('done', { diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 25c4d028e6..6237004606 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="category in customEmojiCategories" :key="`custom:${category}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(emoji => !emoji.draft).filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(e => category === null ? (e.category === 'null' || !e.category) : e.category === category).filter(filterAvailable).map(e => `:${e.name}:`))" @chosen="chosen" > {{ category || i18n.ts.other }} diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index ecbefbd086..0f33bc6b88 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFoldableSection v-if="searchEmojis"> <template #header>{{ i18n.ts.searchResult }}</template> <div :class="$style.emojis"> - <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :draft="emoji.draft"/> + <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :request="emoji.request"/> </div> </MkFoldableSection> @@ -36,9 +36,9 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFoldableSection> </MkSpacer> - <MkSpacer v-if="tab === 'draft'" :contentMax="1000" :marginMin="20"> + <MkSpacer v-if="tab === 'request'" :contentMax="1000" :marginMin="20"> <div :class="$style.emojis"> - <XEmoji v-for="emoji in draftEmojis.emojis" :key="emoji.name" :emoji="emoji" :draft="true"/> + <XEmoji v-for="emoji in requestEmojis.emojis" :key="emoji.name" :emoji="emoji" :request="true"/> </div> </MkSpacer> </MkStickyContainer> @@ -64,7 +64,7 @@ const headerTabs = $computed(() => [{ key: 'emojis', title: i18n.ts.list, }, { - key: 'draft', + key: 'request', title: i18n.ts.requestEmojis, }]); @@ -73,7 +73,7 @@ definePageMetadata(ref({})); let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); -const draftEmojis = await os.apiGet('emoji-drafts'); +const requestEmojis = await os.apiGet('emoji-requests'); function search() { if ((q === '' || q == null) && selectedTags.size === 0) { searchEmojis = null; @@ -97,7 +97,7 @@ function search() { const edit = () => { os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { - isRequest: true, + requestNow: true, }, { done: result => { window.location.reload(); diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 68486b9cca..9fd95efbfb 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -12,8 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="tab === 'local'" class="local"> <MkCustomEmojiEditLocal/> </div> - <div v-if="tab === 'draft'" class="draft"> - <MkCustomEmojiEditDraft/> + <div v-if="tab === 'request'" class="request"> + <MkCustomEmojiEditRequest/> </div> <div v-else-if="tab === 'remote'" class="remote"> <MkCustomEmojiEditRemote/> @@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, defineAsyncComponent, ref } from 'vue'; -import MkCustomEmojiEditDraft from '@/components/MkCustomEmojiEditDraft.vue'; +import MkCustomEmojiEditRequest from '@/components/MkCustomEmojiEditRequest.vue'; import MkCustomEmojiEditLocal from '@/components/MkCustomEmojiEditLocal.vue'; import MkCustomEmojiEditRemote from '@/components/MkCustomEmojiEditRemote.vue'; import { selectFile } from '@/scripts/select-file'; @@ -34,7 +34,7 @@ import * as os from '@/os'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; -const tab = ref('draft'); +const tab = ref('request'); const add = async (ev: MouseEvent) => { os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { @@ -102,7 +102,7 @@ const headerActions = $computed(() => [{ }]); const headerTabs = $computed(() => [{ - key: 'draft', + key: 'request', title: i18n.ts.requestEmojis, }, { key: 'local', diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index d112cfcb7c..8a53f54e69 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -4,10 +4,10 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<button v-if="draft" class="_button emoji-draft" :class="$style.root" @click="menu"> +<button v-if="request" class="_button emoji-request" :class="$style.root" @click="menu"> <img :src="emoji.url" :class="$style.img" loading="lazy"/> <div class="body"> - <div class="name _monospace">{{ emoji.name + ' (draft)' }}</div> + <div class="name _monospace">{{ emoji.name + ' (request)' }}</div> <div class="info">{{ emoji.aliases.join(' ') }}</div> </div> </button> @@ -32,7 +32,7 @@ const props = defineProps<{ category: string; url: string; }; - draft?: boolean; + request?: boolean; }>(); function menu(ev) { @@ -50,7 +50,7 @@ function menu(ev) { text: i18n.ts.info, icon: 'ti ti-info-circle', action: () => { - os.apiGet('emoji-drafts', { name: props.emoji.name }).then(res => { + os.apiGet('emoji-requests', { name: props.emoji.name }).then(res => { os.alert({ type: 'info', text: `License: ${res.license}`, @@ -99,7 +99,7 @@ function menu(ev) { overflow: hidden; } -.emoji-draft { +.emoji-request { --c: rgb(255 196 0 / 15%);; background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); background-size: 16px 16px; diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 29cdd5eead..c165bb4d2c 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -284,7 +284,7 @@ type CustomEmoji = { url: string; category: string; aliases: string[]; - draft: boolean; + Request: boolean; }; // @public (undocumented) diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 67c6b09d06..4f0bd6cf68 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -319,7 +319,7 @@ export type CustomEmoji = { url: string; category: string; aliases: string[]; - draft: boolean; + Request: boolean; }; export type LiteInstanceMetadata = { From cd00655ae41c90b274cb288a42956c12b7893870 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 24 Oct 2023 17:04:18 +0900 Subject: [PATCH 208/501] =?UTF-8?q?draft=E3=82=92=E6=8A=B9=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/components/MkAutocomplete.vue | 4 ---- packages/frontend/src/components/MkEmojiPicker.vue | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index f8b655e772..7c4f910559 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -96,10 +96,6 @@ const emojiDb = computed(() => { const customEmojiDB: EmojiDef[] = []; for (const x of customEmojis.value) { - if (x.draft) { - continue; - } - customEmojiDB.push({ name: x.name, emoji: `:${x.name}:`, diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 6237004606..7eff637482 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -157,7 +157,7 @@ watch(q, () => { const searchCustom = () => { const max = 100; - const emojis = customEmojis.value.filter(emoji => !emoji.draft); + const emojis = customEmojis.value; const matches = new Set<Misskey.entities.CustomEmoji>(); const exactMatch = emojis.find(emoji => emoji.name === newQ); From fa2447e71baca01165d7a4cc7b9b740563ca27fd Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 24 Oct 2023 20:46:08 +0900 Subject: [PATCH 209/501] migration file Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../backend/migration/1698131657286-EmojiRequest.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/backend/migration/1698131657286-EmojiRequest.js diff --git a/packages/backend/migration/1698131657286-EmojiRequest.js b/packages/backend/migration/1698131657286-EmojiRequest.js new file mode 100644 index 0000000000..7db57c9cd0 --- /dev/null +++ b/packages/backend/migration/1698131657286-EmojiRequest.js @@ -0,0 +1,13 @@ +export class EmojiRequest1698131657286 { + name = 'EmojiRequest1698131657286' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "emoji_request" ("id" character varying(32) NOT NULL, "updatedAt" TIMESTAMP WITH TIME ZONE, "name" character varying(128) NOT NULL, "category" character varying(128), "originalUrl" character varying(512) NOT NULL, "publicUrl" character varying(512) NOT NULL DEFAULT '', "type" character varying(64), "aliases" character varying(128) array NOT NULL DEFAULT '{}', "license" character varying(1024), "fileId" character varying(1024) NOT NULL, "localOnly" boolean NOT NULL DEFAULT false, "isSensitive" boolean NOT NULL DEFAULT false, CONSTRAINT "PK_3c74521e048dc744f0c7eb65f4a" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ea1d771e867e9843300f09d02c" ON "emoji_request" ("name") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_ea1d771e867e9843300f09d02c"`); + await queryRunner.query(`DROP TABLE "emoji_request"`); + } +} From ff2df67c57185b79d65334181b810a6270e723d4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 24 Oct 2023 21:13:30 +0900 Subject: [PATCH 210/501] ref: gaming mode --- packages/frontend/src/components/MkButton.vue | 40 ++----------- .../src/components/MkChannelFollowButton.vue | 41 ++------------ .../src/components/MkFollowButton.vue | 46 +++------------ .../frontend/src/components/MkLaunchPad.vue | 35 +----------- .../frontend/src/components/MkMention.vue | 32 +---------- packages/frontend/src/components/MkMenu.vue | 56 ++++--------------- .../frontend/src/components/MkPostForm.vue | 4 +- packages/frontend/src/components/MkRadio.vue | 35 +----------- packages/frontend/src/components/MkRange.vue | 8 +-- .../components/MkReactionsViewer.reaction.vue | 35 +----------- .../src/components/MkSignupDialog.form.vue | 32 +---------- .../src/components/MkSignupDialog.rules.vue | 34 +---------- .../frontend/src/components/MkSuperMenu.vue | 37 ++---------- .../src/components/MkSwitch.button.vue | 40 ++----------- packages/frontend/src/components/MkSwitch.vue | 38 +------------ packages/frontend/src/components/MkTab.vue | 33 +---------- .../src/components/MkUserSetupDialog.vue | 35 +----------- .../components/global/MkPageHeader.tabs.vue | 32 +---------- packages/frontend/src/ui/_common_/navbar.vue | 49 ++++++---------- 19 files changed, 85 insertions(+), 577 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 379b928477..2770fdcd7c 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -20,8 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike, - [$style.gamingDark]: gaming === 'dark', - [$style.gamingLight]: gaming === 'light', + [$style.gamingDark]: gamingType === 'dark', + [$style.gamingLight]: gamingType === 'light', } ]" :type="type" @@ -50,8 +50,8 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike, - [$style.gamingDark]: gaming === 'dark', - [$style.gamingLight]: gaming === 'light', + [$style.gamingDark]: gamingType === 'dark', + [$style.gamingLight]: gamingType === 'light', } ]" :to="to" @@ -90,39 +90,9 @@ const props = defineProps<{ name?: string; value?: string; }>(); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -// gamingをrefで初期化する -let gaming = ref(''); // 0-off , 1-dark , 2-light -// gaming.valueに新しい値を代入する -if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'light'; -}else{ - gaming.value = ''; -} +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -watch(darkMode, () => { - if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate) { - gaming.value = 'light'; - }else{ - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'light'; - }else{ - gaming.value = ''; - } -}) const emit = defineEmits<{ (ev: 'click', payload: MouseEvent): void; diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue index 888866be56..e0b0872e80 100644 --- a/packages/frontend/src/components/MkChannelFollowButton.vue +++ b/packages/frontend/src/components/MkChannelFollowButton.vue @@ -6,27 +6,27 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <button class="_button" - :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing, [$style.full]: full },[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]]" + :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing, [$style.full]: full },[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]]" :disabled="wait" @click="onClick" > <template v-if="!wait"> <template v-if="isFollowing"> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> </template> <template v-else> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> </template> </template> <template v-else> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]">{{ i18n.ts.processing }}</span> <MkLoading :em="true"/> @@ -47,39 +47,8 @@ const props = withDefaults(defineProps<{ full: false, }); +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -let gaming = ref(''); - -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - console.log(gaming) - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const isFollowing = ref<boolean>(props.channel.isFollowing); const wait = ref(false); diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue index a5883a5309..f50e4d2c72 100644 --- a/packages/frontend/src/components/MkFollowButton.vue +++ b/packages/frontend/src/components/MkFollowButton.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <button class="_button" - :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' + :class="[$style.root, { [$style.wait]: wait, [$style.active]: isFollowing || hasPendingFollowRequestFromYou, [$style.full]: full, [$style.large]: large },{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' ,}]" :disabled="wait" @click="onClick" @@ -13,40 +13,40 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="!wait"> <template v-if="hasPendingFollowRequestFromYou && user.isLocked"> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light',}]">{{ i18n.ts.followRequestPending }}</span><i class="ti ti-hourglass-empty"></i> </template> <template v-else-if="hasPendingFollowRequestFromYou && !user.isLocked"> <!-- つまりリモートフォローの場合。 --> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] ">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }] ">{{ i18n.ts.processing }}</span> <MkLoading :em="true" :colored="false"/> </template> <template v-else-if="isFollowing"> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] ">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }] ">{{ i18n.ts.unfollow }}</span><i class="ti ti-minus"></i> </template> <template v-else-if="!isFollowing && user.isLocked"> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]">{{ i18n.ts.followRequest }}</span><i class="ti ti-plus"></i> </template> <template v-else-if="!isFollowing && !user.isLocked"> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]">{{ i18n.ts.follow }}</span><i class="ti ti-plus"></i> </template> </template> <template v-else> <span v-if="full" - :class="[$style.text,{[$style.gamingDark]: gaming === 'dark' ,[$style.gamingLight]: gaming === 'light'} ]">{{ + :class="[$style.text,{[$style.gamingDark]: gamingType === 'dark' ,[$style.gamingLight]: gamingType === 'light'} ]">{{ i18n.ts.processing }}</span> <MkLoading :em="true" :colored="false"/> @@ -64,37 +64,7 @@ import {claimAchievement} from '@/scripts/achievements.js'; import {$i} from '@/account.js'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); - -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const props = withDefaults(defineProps<{ user: Misskey.entities.UserDetailed, diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index 75ccd06074..29872122bf 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -8,12 +8,12 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="szkkfdyq _popup _shadow" :class="{ asDrawer: type === 'drawer' }" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : '' }"> <div class="main"> <template v-for="item in items"> - <button v-if="item.action" v-click-anime class="_button item" :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }" @click="$event => { item.action($event); close(); }"> + <button v-if="item.action" v-click-anime class="_button item" :class="{gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }" @click="$event => { item.action($event); close(); }"> <i class="icon" :class="item.icon"></i> <div class="text">{{ item.text }}</div> <span v-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span> </button> - <MkA v-else v-click-anime :to="item.to" class="item" :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }" @click.passive="close()"> + <MkA v-else v-click-anime :to="item.to" class="item" :class="{gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }" @click.passive="close()"> <i class="icon" :class="item.icon"></i> <div class="text">{{ item.text }}</div> <span v-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span> @@ -31,37 +31,8 @@ import { navbarItemDef } from '@/navbar'; import { defaultStore } from '@/store.js'; import { deviceKind } from '@/scripts/device-kind.js'; -let gaming = ref(''); +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = withDefaults(defineProps<{ src?: HTMLElement; anchor?: { x: string; y: string; }; diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index e77102a63a..b2fad5e0bd 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe && gaming === '' , [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :to="url" :style="{ background: bgCss }"> +<MkA v-user-preview="canonical" :class="[$style.root, { [$style.isMe]: isMe && gamingType === '' , [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :to="url" :style="{ background: bgCss }"> <img :class="$style.icon" :src="avatarUrl" alt=""> <span> <span>@{{ username }}</span> @@ -21,37 +21,9 @@ import { host as localHost } from '@/config.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; import { getStaticImageUrl } from '@/scripts/media-proxy.js'; -let gaming = ref(''); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = defineProps<{ username: string; host: string; diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 73a9b55fef..b94f6b395d 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -8,44 +8,44 @@ SPDX-License-Identifier: AGPL-3.0-only <div ref="itemsEl" v-hotkey="keymap" class="_popup _shadow" - :class="[$style.root, { [$style.center]: align === 'center', [$style.asDrawer]: asDrawer },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.root, { [$style.center]: align === 'center', [$style.asDrawer]: asDrawer },{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :style="{ width: (width && !asDrawer) ? width + 'px' : '', maxHeight: maxHeight ? maxHeight + 'px' : '' }" @contextmenu.self="e => e.preventDefault()" > <template v-for="(item, i) in items2"> <div v-if="item === null" role="separator" :class="$style.divider"></div> - <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> + <span v-else-if="item.type === 'label'" role="menuitem" :class="[$style.label, $style.item,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"> <span>{{ item.text }}</span> </span> - <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> + <span v-else-if="item.type === 'pending'" role="menuitem" :tabindex="i" :class="[$style.pending, $style.item,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"> <span><MkEllipsis/></span> </span> - <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="[$style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <MkA v-else-if="item.type === 'link'" role="menuitem" :to="item.to" :tabindex="i" class="_button" :class="[$style.item,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @click.passive="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </MkA> - <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="[$style.item,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <a v-else-if="item.type === 'a'" role="menuitem" :href="item.href" :target="item.target" :download="item.download" :tabindex="i" class="_button" :class="[$style.item,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @click="close(true)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]"></i> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </a> - <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <button v-else-if="item.type === 'user'" role="menuitem" :tabindex="i" class="_button" :class="[$style.item, { [$style.active]: item.active },{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkAvatar :user="item.user" :class="$style.avatar"/><MkUserName :user="item.user"/> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </button> - <button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } , { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } , { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> <MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/> <span :class="$style.switchText">{{ item.text }}</span> </button> - <button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item } , { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)"> + <button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item } , { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)"> <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon]" style="pointer-events: none;"></i> <span style="pointer-events: none;">{{ item.text }}</span> <span :class="$style.caret" style="pointer-events: none;"><i class="ti ti-chevron-right ti-fw"></i></span> </button> - <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> - <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"></i> + <button v-else :tabindex="i" class="_button" role="menuitem" :class="[$style.item, { [$style.danger]: item.danger, [$style.active]: item.active }, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :disabled="item.active" @click="clicked(item.action, $event)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> + <i v-if="item.icon" class="ti-fw" :class="[$style.icon, item.icon, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"></i> <MkAvatar v-if="item.avatar" :user="item.avatar" :class="$style.avatar"/> <span>{{ item.text }}</span> <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> @@ -62,48 +62,16 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts"> -import { Ref, computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; +import { computed, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import { focusPrev, focusNext } from '@/scripts/focus.js'; -import MkSwitchButton from '@/components/MkSwitch.button.vue'; import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { isTouchUsing } from '@/scripts/touch.js'; import {defaultStore} from '@/store.js' -let gaming = ref(''); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const childrenCache = new WeakMap<MenuParent, MenuItem[]>(); </script> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 31791092ea..1c8bbfafd1 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -42,7 +42,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else><i class="ti ti-icons"></i></span> </button> <button v-click-anime class="_button" :class="$style.submit" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> - <div :class="[$style.submitInner ,{ [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"> + <div :class="[$style.submitInner ,{ [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"> <template v-if="posted"></template> <template v-else-if="posting"><MkEllipsis/></template> <template v-else>{{ submitText }}</template> @@ -135,7 +135,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement } from '@/scripts/achievements.js'; const modal = inject('modal'); -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); diff --git a/packages/frontend/src/components/MkRadio.vue b/packages/frontend/src/components/MkRadio.vue index b7344cffe4..63c8e6f053 100644 --- a/packages/frontend/src/components/MkRadio.vue +++ b/packages/frontend/src/components/MkRadio.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div v-adaptive-border - :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked ,[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' } ]" + :class="[$style.root, { [$style.disabled]: disabled, [$style.checked]: checked ,[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' } ]" :aria-checked="checked" :aria-disabled="disabled" @click="toggle" @@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only :disabled="disabled" :class="$style.input" > - <span :class="[$style.button , {[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> + <span :class="[$style.button , {[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"> <span></span> </span> <span :class="$style.label"><slot></slot></span> @@ -27,37 +27,8 @@ SPDX-License-Identifier: AGPL-3.0-only import { ref,computed,watch } from 'vue'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = defineProps<{ modelValue: any; value: any; diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index e8e4d64273..2e240c844e 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -9,12 +9,12 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-adaptive-border class="body"> <div ref="containerEl" class="container"> <div class="track"> - <div :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light'}" class="highlight" :style="{ width: (steppedRawValue * 100) + '%' }"></div> + <div :class="{gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light'}" class="highlight" :style="{ width: (steppedRawValue * 100) + '%' }"></div> </div> <div v-if="steps && showTicks" class="ticks"> <div v-for="i in (steps + 1)" class="tick" :style="{ left: (((i - 1) / steps) * 100) + '%' }"></div> </div> - <div ref="thumbEl" v-tooltip="textConverter(finalValue)" :class="{gamingDark: gaming === 'dark',gamingLight: gaming === 'light'}" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div> + <div ref="thumbEl" v-tooltip="textConverter(finalValue)" :class="{gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light'}" class="thumb" :style="{ left: thumbPosition + 'px' }" @mousedown="onMousedown" @touchstart="onMousedown"></div> </div> </div> <div class="caption"><slot name="caption"></slot></div> @@ -27,10 +27,8 @@ import * as os from '@/os.js'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 558e34504b..92ef02cf84 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -8,11 +8,11 @@ SPDX-License-Identifier: AGPL-3.0-only ref="buttonEl" v-ripple="canToggle" class="_button" - :class="[$style.root, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' ,[$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" + :class="[$style.root, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' ,[$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" @click="toggleReaction()" > <MkReactionIcon :class="$style.icon" :reaction="reaction" :emojiUrl="note.reactionEmojis[reaction.substring(1, reaction.length - 1)]"/> - <span :class="[$style.count,{ [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]">{{ count }}</span> + <span :class="[$style.count,{ [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]">{{ count }}</span> </button> </template> @@ -29,37 +29,8 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = defineProps<{ reaction: string; count: number; diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 495b1a81ea..cd8765102d 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> - <div :class="[$style.banner ,{[$style.gamingDark]: gaming==='dark' , [$style.gamingLight]: gaming==='light'}]"> + <div :class="[$style.banner ,{[$style.gamingDark]: gamingType ==='dark' , [$style.gamingLight]: gamingType ==='light'}]"> <i class="ti ti-user-edit"></i> </div> <MkSpacer :marginMin="20" :marginMax="32"> @@ -124,37 +124,9 @@ import {instance} from '@/instance.js'; import {i18n} from '@/i18n.js'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = withDefaults(defineProps<{ autoSet?: boolean; }>(), { diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index 236e9f7e5e..6e8ef6389b 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> - <div :class="[$style.banner ,{[$style.gamingDark]: gaming==='dark' , [$style.gamingLight]: gaming==='light'}]"> + <div :class="[$style.banner ,{[$style.gamingDark]: gamingType ==='dark' , [$style.gamingLight]: gamingType ==='light'}]"> <i class="ti ti-checklist"></i> </div> <MkSpacer :marginMin="20" :marginMax="28"> @@ -71,38 +71,8 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; import {defaultStore} from "@/store.js"; +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -let gaming = ref(''); - -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const availableServerRules = instance.serverRules.length > 0; const availableTos = instance.tosUrl != null && instance.tosUrl !== ''; const availablePrivacyPolicy = instance.privacyPolicyUrl != null && instance.privacyPolicyUrl !== ''; diff --git a/packages/frontend/src/components/MkSuperMenu.vue b/packages/frontend/src/components/MkSuperMenu.vue index ea7488bd88..fa1c59e9cd 100644 --- a/packages/frontend/src/components/MkSuperMenu.vue +++ b/packages/frontend/src/components/MkSuperMenu.vue @@ -10,15 +10,15 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="items"> <template v-for="(item, i) in group.items"> - <a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active, gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }"> + <a v-if="item.type === 'a'" :href="item.href" :target="item.target" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active, gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }"> <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> <span class="text">{{ item.text }}</span> </a> - <button v-else-if="item.type === 'button'" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active , gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }" :disabled="item.active" @click="ev => item.action(ev)"> + <button v-else-if="item.type === 'button'" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active , gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }" :disabled="item.active" @click="ev => item.action(ev)"> <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> <span class="text">{{ item.text }}</span> </button> - <MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active , gamingDark: gaming === 'dark',gamingLight: gaming === 'light' }"> + <MkA v-else :to="item.to" :tabindex="i" class="_button item" :class="{ danger: item.danger, active: item.active , gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }"> <span v-if="item.icon" class="icon"><i :class="item.icon" class="ti-fw"></i></span> <span class="text">{{ item.text }}</span> </MkA> @@ -32,37 +32,8 @@ SPDX-License-Identifier: AGPL-3.0-only import {ref , computed , watch } from 'vue'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) defineProps<{ def: any[]; grid?: boolean; diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue index 3cda474da3..e84fcee027 100644 --- a/packages/frontend/src/components/MkSwitch.button.vue +++ b/packages/frontend/src/components/MkSwitch.button.vue @@ -8,8 +8,8 @@ SPDX-License-Identifier: AGPL-3.0-only v-tooltip="checked ? i18n.ts.itsOn : i18n.ts.itsOff" :class="{ [$style.button]: true, - [$style.gamingDark]: gaming === 'dark' && checked, - [$style.gamingLight]: gaming === 'light' && checked, + [$style.gamingDark]: gamingType === 'dark' && checked, + [$style.gamingLight]: gamingType === 'light' && checked, [$style.buttonChecked]: checked, [$style.buttonDisabled]: props.disabled, @@ -18,47 +18,17 @@ SPDX-License-Identifier: AGPL-3.0-only @click.prevent.stop="toggle" > <div - :class="{ [$style.knob]: true, [$style.knobChecked]: checked, [$style.gamingDark]: gaming === 'dark' && checked,[$style.gamingLight]: gaming === 'light' && checked}"></div> + :class="{ [$style.knob]: true, [$style.knobChecked]: checked, [$style.gamingDark]: gamingType === 'dark' && checked,[$style.gamingLight]: gamingType === 'light' && checked}"></div> </span> </template> <script lang="ts" setup> -import {toRefs, Ref, ref, computed, watch} from 'vue'; +import {toRefs, Ref, computed} from 'vue'; import {i18n} from '@/i18n.js'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - console.log(gaming) - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = withDefaults(defineProps<{ checked: boolean | Ref<boolean>; disabled?: boolean; diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 25c20e67a5..db71f010e7 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.disabled]: disabled && gaming === '', [$style.checked]: checked && gaming === '' }]"> +<div :class="[$style.root, { [$style.disabled]: disabled && gamingType === '', [$style.checked]: checked && gamingType === '' }]"> <input ref="input" type="checkbox" @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only @keydown.enter="toggle" > <XButton :checked="checked" :disabled="disabled" @toggle="toggle"/> - <span :class="$style.body,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}"> + <span :class="$style.body,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}"> <!-- TODO: 無名slotの方は廃止 --> <span :class="$style.label"> <span @click="toggle"> @@ -30,39 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only import {toRefs, Ref, ref, computed, watch} from 'vue'; import XButton from '@/components/MkSwitch.button.vue'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); - -// gaming.valueに新しい値を代入する -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - console.log(gaming) - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const props = defineProps<{ modelValue: boolean | Ref<boolean>; diff --git a/packages/frontend/src/components/MkTab.vue b/packages/frontend/src/components/MkTab.vue index 2972df6a16..ca37a97445 100644 --- a/packages/frontend/src/components/MkTab.vue +++ b/packages/frontend/src/components/MkTab.vue @@ -7,37 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only import {computed, defineComponent, h, resolveDirective, withDirectives , ref , watch} from 'vue'; import {defaultStore} from "@/store.js"; -let gaming = ref(''); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) export default defineComponent({ props: { modelValue: { @@ -50,7 +21,7 @@ export default defineComponent({ return () => h('div', { class: 'pxhvhrfw', }, options.map(option => withDirectives(h('button', { - class: ['_button', { active: props.modelValue === option.props.value , gamingDark: gaming.value == 'dark' && props.modelValue === option.props.value,gamingLight: gaming.value == 'light' && props.modelValue === option.props.value } ], + class: ['_button', { active: props.modelValue === option.props.value , gamingDark: gamingType.value == 'dark' && props.modelValue === option.props.value,gamingLight: gamingType.value == 'light' && props.modelValue === option.props.value } ], key: option.key, disabled: props.modelValue === option.props.value, onClick: () => { diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index 9dfaf9471d..51a8248641 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div style="overflow-x: clip;"> <div :class="$style.progressBar"> - <div :class="[$style.progressBarValue , {[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" :style="{ width: `${(page / 5) * 100}%` }"></div> + <div :class="[$style.progressBarValue , {[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :style="{ width: `${(page / 5) * 100}%` }"></div> </div> <Transition mode="out-in" @@ -136,39 +136,8 @@ import { host } from '@/config.js'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -let gaming = ref(''); - -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const emit = defineEmits<{ (ev: 'closed'): void; }>(); diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 9c6999bd2d..507c7eec06 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div ref="tabHighlightEl" - :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value , [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.tabHighlight, { [$style.animate]: defaultStore.reactiveState.animation.value , [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" ></div> </div> </template> @@ -55,37 +55,9 @@ export type Tab = { <script lang="ts" setup> import {onMounted, onUnmounted, watch, nextTick, shallowRef, ref, computed} from 'vue'; import { defaultStore } from '@/store.js'; -let gaming = ref(''); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = withDefaults(defineProps<{ tabs?: Tab[]; tab?: string; diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 54d641aa9f..d46a19cf66 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.middle"> <MkA v-tooltip.noDelay.right="i18n.ts.timeline" - :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.item, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :activeClass="$style.active" to="/" exact> <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.timeline @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-tooltip.noDelay.right="navbarItemDef[item].title" class="_button" - :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.item, { [$style.active]: gamingType === '' && navbarItemDef[item].active, [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :activeClass="$style.active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" @@ -36,29 +36,29 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" - :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i + :class="[$style.itemIndicator,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"><i class="_indicatorCircle"></i></span> </component> </template> <div :class="$style.divider"></div> <MkA v-if="$i.isAdmin || $i.isModerator" v-tooltip.noDelay.right="i18n.ts.controlPanel" - :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.item, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :activeClass="$style.active" to="/admin"> <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> </MkA> <button class="_button" - :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.item, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @click="more"> <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ i18n.ts.more }}</span> <span v-if="otherMenuItemIndicated" - :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i + :class="[$style.itemIndicator,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"><i class="_indicatorCircle"></i></span> </button> <MkA v-tooltip.noDelay.right="i18n.ts.settings" - :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :class="[$style.item, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" :activeClass="$style.active" to="/settings"> <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span @@ -67,11 +67,11 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div :class="$style.bottom"> <button v-tooltip.noDelay.right="i18n.ts.note" class="_button" - :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" + :class="[$style.post ,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light',}]" data-cy-open-post-form @click="os.post"> <i class="ti ti-pencil ti-fw" :class="$style.postIcon"></i><span - :class="$style.postText,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}">{{ + :class="$style.postText,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light',}">{{ i18n.ts.note }}</span> </button> @@ -96,10 +96,7 @@ import {i18n} from '@/i18n'; import {instance} from '@/instance'; function hexToRgb(hex) { - // 16進数のカラーコードから "#" を除去 hex = hex.replace(/^#/, ''); - - // 16進数をRGBに変換 const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); @@ -116,7 +113,7 @@ document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.n const iconOnly = ref(false); let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(); -let gaming = ref(''); +let gamingType = ref(defaultStore.state.gamingType); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); @@ -141,35 +138,23 @@ watch(darkMode, () => { }) if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gamingType.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gamingType.value = 'light'; } else { - gaming.value = ''; + gamingType.value = ''; } -watch(darkMode, () => { - +watch([darkMode,gamingMode], () => { if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gamingType.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gamingType.value = 'light'; } else { - gaming.value = ''; + gamingType.value = ''; } }) -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - - const menu = computed(() => defaultStore.state.menu); const otherMenuItemIndicated = computed(() => { for (const def in navbarItemDef) { From 85c9b35d0315a82f03930aa3c665034d4acecba5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 26 Sep 2023 10:54:01 +0900 Subject: [PATCH 211/501] fix --- packages/frontend/src/components/MkButton.vue | 40 +++++++++++++-- .../src/components/MkCustomEmojiEditLocal.vue | 51 ++++++++++++++++++- packages/frontend/src/ui/_common_/navbar.vue | 4 +- 3 files changed, 87 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 2770fdcd7c..379b928477 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -20,8 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike, - [$style.gamingDark]: gamingType === 'dark', - [$style.gamingLight]: gamingType === 'light', + [$style.gamingDark]: gaming === 'dark', + [$style.gamingLight]: gaming === 'light', } ]" :type="type" @@ -50,8 +50,8 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.large]: large, [$style.transparent]: transparent, [$style.asLike]: asLike, - [$style.gamingDark]: gamingType === 'dark', - [$style.gamingLight]: gamingType === 'light', + [$style.gamingDark]: gaming === 'dark', + [$style.gamingLight]: gaming === 'light', } ]" :to="to" @@ -90,9 +90,39 @@ const props = defineProps<{ name?: string; value?: string; }>(); +const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); +const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); +// gamingをrefで初期化する +let gaming = ref(''); // 0-off , 1-dark , 2-light -const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); +// gaming.valueに新しい値を代入する +if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'dark'; +} else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'light'; +}else{ + gaming.value = ''; +} +watch(darkMode, () => { + if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate) { + gaming.value = 'light'; + }else{ + gaming.value = ''; + } +}) + +watch(gamingMode, () => { + if (darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'light'; + }else{ + gaming.value = ''; + } +}) const emit = defineEmits<{ (ev: 'click', payload: MouseEvent): void; diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 9f688bc52f..a02702e6bc 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -13,6 +13,8 @@ <MkButton inline @click="addTagBulk">Add tag</MkButton> <MkButton inline @click="removeTagBulk">Remove tag</MkButton> <MkButton inline @click="setLisenceBulk">Set Lisence</MkButton> + <MkButton inline @click="setisSensitiveBulk">Set isSensitive</MkButton> + <MkButton inline @click="setlocalOnlyBulk">Set localOnly</MkButton> <MkButton inline danger @click="delBulk">Delete</MkButton> </div> <MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100"> @@ -62,7 +64,30 @@ const pagination = { query: (query.value && query.value !== '') ? query.value : null, })), }; - +const setisSensitiveBulk = async () => { + const { canceled, result } = await os.switch1({ + title: 'isSensitive', + type: "mksw" + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-issensitive-bulk', { + ids: selectedEmojis.value, + isSensitive: result + }); + emojisPaginationComponent.value.reload(); +}; +const setlocalOnlyBulk = async () => { + const { canceled, result } = await os.switch1({ + title: 'localOnly', + type: "mksw" + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-localonly-bulk', { + ids: selectedEmojis.value, + localOnly: result + }); + emojisPaginationComponent.value.reload(); +}; const selectAll = () => { if (selectedEmojis.value.length > 0) { selectedEmojis.value = []; @@ -123,6 +148,30 @@ const setLisenceBulk = async () => { emojisPaginationComponent.value.reload(); }; +const isLocalBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'License', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-islocal-bulk', { + ids: selectedEmojis.value, + isLocal: result, + }); + emojisPaginationComponent.value.reload(); +}; + +const isSensitiveBulk = async () => { + const { canceled, result } = await os.inputText({ + title: 'License', + }); + if (canceled) return; + await os.apiWithDialog('admin/emoji/set-issensitive-bulk', { + ids: selectedEmojis.value, + license: result, + }); + emojisPaginationComponent.value.reload(); +}; + const addTagBulk = async () => { const { canceled, result } = await os.inputText({ title: 'Tag', diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index d46a19cf66..9c539d8f54 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -111,9 +111,9 @@ document.documentElement.style.setProperty("--localOnlyColor",hexToRgb(defaultSt document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed+'s'); const iconOnly = ref(false); -let bannerUrl = ref(defaultStore.state.bannerUrl); +let bannerUrl = computed(defaultStore.makeGetterSetter('bannerUrl')); let iconUrl = ref(); -let gamingType = ref(defaultStore.state.gamingType); +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); From d03d75602fb7259f0036a0713c30327fdd5d8655 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 26 Oct 2023 17:34:34 +0900 Subject: [PATCH 212/501] fix --- packages/frontend/src/ui/_common_/navbar.vue | 23 ++++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 9c539d8f54..33c973f80a 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -127,16 +127,6 @@ if (darkMode.value) { iconUrl.value = iconLight; } -watch(darkMode, () => { - if (darkMode.value) { - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; - } else { - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; - } -}) - if (darkMode.value && gamingMode.value == true) { gamingType.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { @@ -146,9 +136,18 @@ if (darkMode.value && gamingMode.value == true) { } watch([darkMode,gamingMode], () => { - if (darkMode.value && gamingMode.value == true) { + + if (darkMode.value) { + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; + } else { + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; + } + + if (darkMode.value && gamingMode.value) { gamingType.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { + } else if (!darkMode.value && gamingMode.value) { gamingType.value = 'light'; } else { gamingType.value = ''; From 7242b195ab7425c389ba5981394612259a282ba5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 06:06:41 +0900 Subject: [PATCH 213/501] fix --- packages/frontend/src/pages/settings/general.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index efb0379434..3383a9f861 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -35,10 +35,10 @@ SPDX-License-Identifier: AGPL-3.0-only <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> - <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> - <MkSwitch v-model="FeaturedOrNote">{{ i18n.ts.FeaturedOrNote}}<template #caption>{{ i18n.ts.FeaturedOrNoteInfo }} </template></MkSwitch> - <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> </MkFolder> + <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> + <MkSwitch v-model="FeaturedOrNote">{{ i18n.ts.FeaturedOrNote}}<template #caption>{{ i18n.ts.FeaturedOrNoteInfo }} </template></MkSwitch> + <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> </div> </FormSection> From f4bc2e82911243c69e3c04289e67b789be3a94c6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 06:12:35 +0900 Subject: [PATCH 214/501] =?UTF-8?q?=E5=A4=9A=E5=88=86=E5=8B=95=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/ui/_common_/navbar.vue | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 33c973f80a..04f710e818 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -117,7 +117,7 @@ let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); - +const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); if (darkMode.value) { bannerUrl.value = bannerDark; @@ -127,9 +127,9 @@ if (darkMode.value) { iconUrl.value = iconLight; } -if (darkMode.value && gamingMode.value == true) { +if (darkMode.value && gamingMode.value) { gamingType.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { +} else if (!darkMode.value && gamingMode.value) { gamingType.value = 'light'; } else { gamingType.value = ''; @@ -138,11 +138,11 @@ if (darkMode.value && gamingMode.value == true) { watch([darkMode,gamingMode], () => { if (darkMode.value) { - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; + bannerUrl.value = enablehanntenn ? bannerLight : bannerDark; + iconUrl.value = enablehanntenn ? bannerLight : bannerDark; } else { - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; + bannerUrl.value = enablehanntenn ? bannerDark : bannerLight; + iconUrl.value = enablehanntenn ? bannerDark : bannerLight; } if (darkMode.value && gamingMode.value) { @@ -152,6 +152,7 @@ watch([darkMode,gamingMode], () => { } else { gamingType.value = ''; } + }) const menu = computed(() => defaultStore.state.menu); From edc475d2328c54da3f0961357acac416b3765685 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 06:17:09 +0900 Subject: [PATCH 215/501] fix --- .../frontend/src/components/MkPostForm.vue | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 1c8bbfafd1..909c5c8f9d 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -137,27 +137,6 @@ import { claimAchievement } from '@/scripts/achievements.js'; const modal = inject('modal'); let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); -const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); - -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = withDefaults(defineProps<{ reply?: Misskey.entities.Note; From c58105a16094b4c403ce974c89a77110fcd65210 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 06:22:01 +0900 Subject: [PATCH 216/501] fix --- packages/frontend/src/ui/_common_/navbar.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 04f710e818..4e83c1502b 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -120,11 +120,11 @@ const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); if (darkMode.value) { - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; + bannerUrl.value = enablehanntenn.value ? bannerLight : bannerDark; + iconUrl.value = enablehanntenn.value ? iconLight : iconDark; } else { - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; + bannerUrl.value = enablehanntenn.value ? bannerDark : bannerLight; + iconUrl.value = enablehanntenn.value ? iconDark : iconLight; } if (darkMode.value && gamingMode.value) { @@ -138,11 +138,11 @@ if (darkMode.value && gamingMode.value) { watch([darkMode,gamingMode], () => { if (darkMode.value) { - bannerUrl.value = enablehanntenn ? bannerLight : bannerDark; - iconUrl.value = enablehanntenn ? bannerLight : bannerDark; + bannerUrl.value = enablehanntenn.value ? bannerLight : bannerDark; + iconUrl.value = enablehanntenn.value ? iconLight : iconDark; } else { - bannerUrl.value = enablehanntenn ? bannerDark : bannerLight; - iconUrl.value = enablehanntenn ? bannerDark : bannerLight; + bannerUrl.value = enablehanntenn.value ? bannerDark : bannerLight; + iconUrl.value = enablehanntenn.value ? iconDark : iconLight; } if (darkMode.value && gamingMode.value) { From 154dd54887996306056bd0f311da84546620e233 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 15:59:10 +0900 Subject: [PATCH 217/501] fix --- .../frontend/src/components/MkMention.vue | 2 +- packages/frontend/src/components/MkRange.vue | 28 ------------------- 2 files changed, 1 insertion(+), 29 deletions(-) diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index b2fad5e0bd..6ba2cadc8c 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -44,7 +44,7 @@ const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(`/avatar/@${props.username}@${props.host}`) : `/avatar/@${props.username}@${props.host}`, ); -const bgCss = (gaming.value === '') ? bg.toRgbString() : ""; +const bgCss = (gamingType.value === '') ? bg.toRgbString() : ""; //const bgCss = `background:${bg.toRgbString()}; ${result}` ; </script> diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue index 2e240c844e..77d2e60e0d 100644 --- a/packages/frontend/src/components/MkRange.vue +++ b/packages/frontend/src/components/MkRange.vue @@ -29,34 +29,6 @@ import {defaultStore} from "@/store.js"; let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; -} else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; -} else { - gaming.value = ''; -} - -watch(darkMode, () => { - console.log(gaming) - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) - -watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) const props = withDefaults(defineProps<{ modelValue: number; disabled?: boolean; From 5bc38889b800259b9742df2254a35caadf34202a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 16:06:31 +0900 Subject: [PATCH 218/501] 2023.11.0-beta.4-prismisskey.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 742b2d4578..16ff3c39ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.4", + "version": "2023.11.0-beta.4-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From d94747b1c039713982f8e943e009187361c3f0e4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 16:13:16 +0900 Subject: [PATCH 219/501] updare locales Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 4 ++-- locales/ja-JP.yml | 4 ++-- packages/frontend/src/components/MkCustomEmojiEditRequest.vue | 2 +- packages/frontend/src/pages/about.emojis.vue | 2 +- packages/frontend/src/pages/custom-emojis-manager.vue | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index b0e7f452e5..55a53aa2d7 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1028,8 +1028,8 @@ export interface Locale { "notesSearchNotAvailable": string; "license": string; "requestPending": string; - "requestApproval": string; - "requestEmojis": string; + "approve": string; + "requestingEmojis": string; "emojiNameValidation": string; "unfavoriteConfirm": string; "myClips": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index ff0fe9bf95..7bc5e3ab9a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1025,8 +1025,8 @@ sensitiveWordsDescription2: "スペースで区切るとAND指定になり、キ notesSearchNotAvailable: "ノート検索は利用できません。" license: "ライセンス" requestPending: "申請中" -requestApproval: "リクエストを承認" -requestEmojis: "リクエストされている絵文字" +approval: "承認" +requestingEmojis: "リクエストされている絵文字" emojiNameValidation: "名前には英数字と_が利用できます。" unfavoriteConfirm: "お気に入り解除しますか?" myClips: "自分のクリップ" diff --git a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue index cf88a5bc63..0d25614c82 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue @@ -20,7 +20,7 @@ {{ i18n.ts.edit }} </MkButton> <MkButton class="request" @click="unrequested(emoji)"> - {{ i18n.ts.requestApproval }} + {{ i18n.ts.approve }} </MkButton> <MkButton danger class="delete" @click="deleteRequest(emoji)"> {{ i18n.ts.delete }} diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 0f33bc6b88..2452f36a52 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -65,7 +65,7 @@ const headerTabs = $computed(() => [{ title: i18n.ts.list, }, { key: 'request', - title: i18n.ts.requestEmojis, + title: i18n.ts.requestingEmojis, }]); definePageMetadata(ref({})); diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 9fd95efbfb..891a9b1e11 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -103,7 +103,7 @@ const headerActions = $computed(() => [{ const headerTabs = $computed(() => [{ key: 'request', - title: i18n.ts.requestEmojis, + title: i18n.ts.requestingEmojis, }, { key: 'local', title: i18n.ts.local, From 1ed0e7e8a349fec4d026770badc36125f1bcf07c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 16:58:36 +0900 Subject: [PATCH 220/501] update CustomEmojiService.ts update add-request.ts update delete.ts update DriveFileEntityService.ts update update-request.ts update update.ts Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/src/core/CustomEmojiService.ts | 6 +++--- .../backend/src/core/entities/DriveFileEntityService.ts | 5 +---- .../src/server/api/endpoints/admin/emoji/add-request.ts | 2 +- .../backend/src/server/api/endpoints/admin/emoji/delete.ts | 2 +- .../src/server/api/endpoints/admin/emoji/update-request.ts | 4 ++-- .../backend/src/server/api/endpoints/admin/emoji/update.ts | 2 +- 6 files changed, 9 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 1346d3d839..044ab854b7 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -60,7 +60,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } @bindThis - public async Request(data: { + public async request(data: { driveFile: MiDriveFile; name: string; category: string | null; @@ -196,7 +196,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async RequestUpdate(id: MiEmoji['id'], data: { + public async updateRequest(id: MiEmoji['id'], data: { driveFile?: MiDriveFile; name?: string; category?: string | null; @@ -332,7 +332,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async RequestDelete(id: MiEmojiRequest['id']) { + public async deleteRequest(id: MiEmojiRequest['id']) { const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id }); await this.emojiRequestsRepository.delete(emoji.id); diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts index d5406373b7..9ff986592e 100644 --- a/packages/backend/src/core/entities/DriveFileEntityService.ts +++ b/packages/backend/src/core/entities/DriveFileEntityService.ts @@ -135,10 +135,7 @@ export class DriveFileEntityService { } @bindThis public async getFromUrl(url: string): Promise<MiDriveFile | null> { - const file = await this.driveFilesRepository.findOneBy({ url: url }); - if (file === null ) return null; - - return file; + return this.driveFilesRepository.findOneBy({ url: url }); } @bindThis public async calcDriveUsageOf(user: MiUser['id'] | { id: MiUser['id'] }): Promise<number> { diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts index 50ffb89b06..609297f667 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts @@ -68,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const emoji = await this.customEmojiService.Request({ + const emoji = await this.customEmojiService.request({ driveFile, name: ps.name, category: ps.category ?? null, diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts index 9711db5a11..99747d3808 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/delete.ts @@ -42,7 +42,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.customEmojiService.delete(ps.id, me); } if (RequestEmoji != null) { - await this.customEmojiService.RequestDelete(ps.id); + await this.customEmojiService.deleteRequest(ps.id); } }); } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts index 98e50389cc..4be16bb780 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update-request.ts @@ -101,9 +101,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: [], }, me); - await this.customEmojiService.RequestDelete(ps.id); + await this.customEmojiService.deleteRequest(ps.id); } else { - await this.customEmojiService.RequestUpdate(ps.id, { + await this.customEmojiService.updateRequest(ps.id, { name: ps.name, category: ps.category ?? null, aliases: ps.aliases ?? [], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index 7f7dc774d5..89bf0880f6 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -101,7 +101,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } else { const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); if (file === null) throw new ApiError(meta.errors.noSuchFile); - await this.customEmojiService.Request({ + await this.customEmojiService.request({ driveFile: file, name: ps.name, category: ps.category ?? null, From 6ad6ab03c6491886b0ec325fc3766bebf9573280 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 16:59:43 +0900 Subject: [PATCH 221/501] update MkCustomEmojiEditLocal.vue update MkCustomEmojiEditRemote.vue Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/components/MkCustomEmojiEditLocal.vue | 2 +- .../frontend/src/components/MkCustomEmojiEditRemote.vue | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 7060f2080b..5e2d4438cf 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -1,5 +1,5 @@ <template> -<MkInput v-model="query" :debounce="true" type="search"> +<MkInput v-model="query" :debounce="true" type="search" autocapitalize="off"> <template #prefix><i class="ti ti-search"></i></template> <template #label>{{ i18n.ts.search }}</template> </MkInput> diff --git a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue index 26c8dd66ac..3575d9d9a4 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue @@ -1,6 +1,6 @@ <template> <FormSplit> - <MkInput v-model="queryRemote" :debounce="true" type="search"> + <MkInput v-model="queryRemote" :debounce="true" type="search" autocapitalize="off"> <template #prefix><i class="ti ti-search"></i></template> <template #label>{{ i18n.ts.search }}</template> </MkInput> @@ -8,12 +8,12 @@ <template #label>{{ i18n.ts.host }}</template> </MkInput> </FormSplit> -<MkPagination :pagination="remotePagination" :displayLimit="100"> +<MkPagination :pagination="remotePagination"> <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> <div class="ldhfsamy"> <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> + <img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" class="img" :alt="emoji.name"/> <div class="body"> <div class="name _monospace">{{ emoji.name }}</div> <div class="info">{{ emoji.host }}</div> From 04c34204206a265a8c49ce823380253d77364ac3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 28 Oct 2023 17:17:16 +0900 Subject: [PATCH 222/501] update CustomEmojiService.ts Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/src/core/CustomEmojiService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 044ab854b7..396e3df701 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -196,7 +196,7 @@ export class CustomEmojiService implements OnApplicationShutdown { } } @bindThis - public async updateRequest(id: MiEmoji['id'], data: { + public async updateRequest(id: MiEmojiRequest['id'], data: { driveFile?: MiDriveFile; name?: string; category?: string | null; From cb0c2cbb14b7caf8f6727cf8bfda69c69bc995a2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 29 Oct 2023 06:34:19 +0900 Subject: [PATCH 223/501] test --- packages/frontend/src/scripts/nyaize.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts index 0ac77e1006..f262f28240 100644 --- a/packages/frontend/src/scripts/nyaize.ts +++ b/packages/frontend/src/scripts/nyaize.ts @@ -8,9 +8,9 @@ export function nyaize(text: string): string { // ja-JP .replaceAll('な', 'にゃ').replaceAll('ナ', 'ニャ').replaceAll('ナ', 'ニャ') // en-US - .replace(/(?<=n)a/gi, x => x === 'A' ? 'YA' : 'ya') - .replace(/(?<=morn)ing/gi, x => x === 'ING' ? 'YAN' : 'yan') - .replace(/(?<=every)one/gi, x => x === 'ONE' ? 'NYAN' : 'nyan') + .replace(/(n)a/gi, (match, p1) => p1 === 'A' ? 'YA' : 'ya') + .replace(/(morn)ing/gi, (match, p1) => p1 === 'ING' ? 'YAN' : 'yan') + .replace(/(every)one/gi, (match, p1) => p1 === 'ONE' ? 'NYAN' : 'nyan') // ko-KR .replace(/[나-낳]/g, match => String.fromCharCode( match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0), From 2c72b21977779ad4978e09a40203a13c2fc13cf2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 29 Oct 2023 16:52:39 +0900 Subject: [PATCH 224/501] fix: lint Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/components/MkEmojiEditDialog.vue | 1 + packages/frontend/src/pages/about.emojis.vue | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index fd54b9d86e..056230f096 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -153,6 +153,7 @@ async function addRole() { async function removeRole(role, ev) { rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); } + async function done() { const params = { name, diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 1c8417cff5..e4cbc04709 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -74,6 +74,7 @@ let q = $ref(''); let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = $ref(new Set()); const requestEmojis = await os.apiGet('emoji-requests'); + function search() { if ((q === '' || q == null) && selectedTags.size === 0) { searchEmojis = null; From 41760797bbceffcd565316762ec07a031229ab77 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 29 Oct 2023 18:10:42 +0900 Subject: [PATCH 225/501] fix --- .../src/pages/custom-emojis-manager.vue | 73 +++---------------- 1 file changed, 11 insertions(+), 62 deletions(-) diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 11fd24fda7..9fdcb0d902 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -8,68 +8,17 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="900"> - <div class="ogwlenmc"> - <div v-if="tab === 'local'" class="local"> - <MkInput v-model="query" :debounce="true" type="search" autocapitalize="off"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.search }}</template> - </MkInput> - <MkSwitch v-model="selectMode" style="margin: 8px 0;"> - <template #label>Select mode</template> - </MkSwitch> - <div v-if="selectMode" class="_buttons"> - <MkButton inline @click="selectAll">Select all</MkButton> - <MkButton inline @click="setCategoryBulk">Set category</MkButton> - <MkButton inline @click="setTagBulk">Set tag</MkButton> - <MkButton inline @click="addTagBulk">Add tag</MkButton> - <MkButton inline @click="removeTagBulk">Remove tag</MkButton> - <MkButton inline @click="setLicenseBulk">Set License</MkButton> - <MkButton inline danger @click="delBulk">Delete</MkButton> - </div> - <MkPagination ref="emojisPaginationComponent" :pagination="pagination"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <button v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="`/emoji/${emoji.name}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> - </div> - </button> - </div> - </template> - </MkPagination> - </div> - <div v-if="tab === 'draft'" class="draft"> - <MkCustomEmojiEditDraft/> - </div> - <div v-else-if="tab === 'remote'" class="remote"> - <FormSplit> - <MkInput v-model="queryRemote" :debounce="true" type="search" autocapitalize="off"> - <template #prefix><i class="ti ti-search"></i></template> - <template #label>{{ i18n.ts.search }}</template> - </MkInput> - <MkInput v-model="host" :debounce="true"> - <template #label>{{ i18n.ts.host }}</template> - </MkInput> - </FormSplit> - <MkPagination :pagination="remotePagination"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.host }}</div> - </div> - </div> - </div> - </template> - </MkPagination> - </div> - </div> + <div class="ogwlenmc"> + <div v-if="tab === 'local'" class="local"> + <MkCustomEmojiEditLocal/> + </div> + <div v-if="tab === 'request'" class="request"> + <MkCustomEmojiEditDraft/> + </div> + <div v-else-if="tab === 'remote'" class="remote"> + <MkCustomEmojiEditRemote/> + </div> + </div> </MkSpacer> </MkStickyContainer> </div> From 699d4634b29e82c1092c58f4fcfb70404b36653f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 29 Oct 2023 19:26:49 +0900 Subject: [PATCH 226/501] fix --- packages/frontend/src/scripts/nyaize.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts index f262f28240..38724bb8e1 100644 --- a/packages/frontend/src/scripts/nyaize.ts +++ b/packages/frontend/src/scripts/nyaize.ts @@ -8,9 +8,9 @@ export function nyaize(text: string): string { // ja-JP .replaceAll('な', 'にゃ').replaceAll('ナ', 'ニャ').replaceAll('ナ', 'ニャ') // en-US - .replace(/(n)a/gi, (match, p1) => p1 === 'A' ? 'YA' : 'ya') - .replace(/(morn)ing/gi, (match, p1) => p1 === 'ING' ? 'YAN' : 'yan') - .replace(/(every)one/gi, (match, p1) => p1 === 'ONE' ? 'NYAN' : 'nyan') + .replace(/(na|NA)(?=[^a-z])/g, (match) => match === 'NA' ? 'NYA' : 'nya') + .replace(/(morn)(ing)(?=[^a-z])/gi, (match, p1, p2) => p2 === 'ING' ? `${p1}YAN` : `${p1.toLowerCase()}yan`) + .replace(/(every)(one)(?=[^a-z])/gi, (match, p1, p2) => p2 === 'ONE' ? `${p1}NYAN` : `${p1.toLowerCase()}nyan`) // ko-KR .replace(/[나-낳]/g, match => String.fromCharCode( match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0), From 2f1a4a5e67a59682d68a30e1841171e4b6f84777 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 29 Oct 2023 19:48:26 +0900 Subject: [PATCH 227/501] fix --- packages/frontend/src/scripts/nyaize.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/scripts/nyaize.ts b/packages/frontend/src/scripts/nyaize.ts index 38724bb8e1..f3fc745e43 100644 --- a/packages/frontend/src/scripts/nyaize.ts +++ b/packages/frontend/src/scripts/nyaize.ts @@ -8,9 +8,9 @@ export function nyaize(text: string): string { // ja-JP .replaceAll('な', 'にゃ').replaceAll('ナ', 'ニャ').replaceAll('ナ', 'ニャ') // en-US - .replace(/(na|NA)(?=[^a-z])/g, (match) => match === 'NA' ? 'NYA' : 'nya') - .replace(/(morn)(ing)(?=[^a-z])/gi, (match, p1, p2) => p2 === 'ING' ? `${p1}YAN` : `${p1.toLowerCase()}yan`) - .replace(/(every)(one)(?=[^a-z])/gi, (match, p1, p2) => p2 === 'ONE' ? `${p1}NYAN` : `${p1.toLowerCase()}nyan`) + .replace(/na/gi, (match) => match === 'NA' ? 'NYA' : 'nya') + .replace(/morning/gi, (match) => match === 'MORNING' ? 'MORNYAN' : 'mornyan') + .replace(/everyone/gi, (match) => match === 'EVERYONE' ? 'EVERYNYAN' : 'everynyan') // ko-KR .replace(/[나-낳]/g, match => String.fromCharCode( match.charCodeAt(0)! + '냐'.charCodeAt(0) - '나'.charCodeAt(0), From 28dd05216f8d4807175420f37d71a01f4485e59f Mon Sep 17 00:00:00 2001 From: syuilo <Syuilotan@yahoo.co.jp> Date: Mon, 30 Oct 2023 17:34:54 +0900 Subject: [PATCH 228/501] lint fixes --- packages/backend/src/core/CustomEmojiService.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts index 396e3df701..b9a1181a69 100644 --- a/packages/backend/src/core/CustomEmojiService.ts +++ b/packages/backend/src/core/CustomEmojiService.ts @@ -93,6 +93,7 @@ export class CustomEmojiService implements OnApplicationShutdown { return emoji; } + @bindThis public async add(data: { driveFile: MiDriveFile; @@ -195,6 +196,7 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } } + @bindThis public async updateRequest(id: MiEmojiRequest['id'], data: { driveFile?: MiDriveFile; @@ -224,6 +226,7 @@ export class CustomEmojiService implements OnApplicationShutdown { this.localEmojisCache.refresh(); } + @bindThis public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) { const emojis = await this.emojisRepository.findBy({ @@ -331,12 +334,14 @@ export class CustomEmojiService implements OnApplicationShutdown { }); } } + @bindThis public async deleteRequest(id: MiEmojiRequest['id']) { const emoji = await this.emojiRequestsRepository.findOneByOrFail({ id: id }); await this.emojiRequestsRepository.delete(emoji.id); } + @bindThis public async deleteBulk(ids: MiEmoji['id'][], moderator?: MiUser) { const emojis = await this.emojisRepository.findBy({ @@ -457,6 +462,7 @@ export class CustomEmojiService implements OnApplicationShutdown { public checkDuplicate(name: string): Promise<boolean> { return this.emojisRepository.exist({ where: { name, host: IsNull() } }); } + @bindThis public checkRequestDuplicate(name: string): Promise<boolean> { return this.emojiRequestsRepository.exist({ where: { name } }); @@ -471,6 +477,7 @@ export class CustomEmojiService implements OnApplicationShutdown { public getEmojiRequestById(id: string): Promise<MiEmojiRequest | null> { return this.emojiRequestsRepository.findOneBy({ id }); } + @bindThis public dispose(): void { this.cache.dispose(); From a76a9773588dadbfba235fa9f759e528c5cb2fd3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 30 Oct 2023 18:44:15 +0900 Subject: [PATCH 229/501] fix --- packages/frontend/src/pages/settings/general.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index fd9b86ec87..cec8d6ac38 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -360,7 +360,7 @@ watch([ showVisibilityColor, enableonlyAndWithSave, FeaturedOrNote, - showGlobalTimeline + showGlobalTimeline, disableStreamingTimeline, ], async () => { await reloadAsk(); From 5a3c9f98a2effaa17ff2c5603fb4c4601043b4c0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 31 Oct 2023 01:49:15 +0900 Subject: [PATCH 230/501] version fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77494abac7..789f712f7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.6-prismisskey.1", + "version": "2023.11.0.beta.6.prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 5fff78eed09ebb810ec70fe5b1a18a2623b921ff Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 31 Oct 2023 07:46:31 +0900 Subject: [PATCH 231/501] version fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 789f712f7a..77494abac7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0.beta.6.prismisskey.1", + "version": "2023.11.0-beta.6-prismisskey.1", "codename": "nasubi", "repository": { "type": "git", From 3e170ad6281c81051d469c979cc228ab2949173b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 31 Oct 2023 22:14:00 +0900 Subject: [PATCH 232/501] version fix --- packages/frontend/src/store.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 37279776a6..9d459ebd16 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -41,10 +41,10 @@ export const noteActions: NoteAction[] = []; export const noteViewInterruptors: NoteViewInterruptor[] = []; export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; -export const bannerDark='https://files.prismisskey.space/misskey/5182a391-5b9f-4ba9-90a5-a692aa59b938.png' -export const bannerLight ='https://files.prismisskey.space/misskey/96e65f59-eab3-47d4-bb1e-d141100bd2fc.png' -export const iconDark='https://files.prismisskey.space/misskey/f3b3c9f8-ff2a-474d-a858-64ffe9023e22.png' -export const iconLight='https://files.prismisskey.space/misskey/c7e56b1d-4c4f-408f-bf73-3175f4eb26ca.png' +export const bannerDark='https://files.prismisskey.space/misskey/e088c6d1-b07f-4312-8d41-fee2f64071e9.png' +export const bannerLight ='https://files.prismisskey.space/misskey/85500d2f-41a9-48ff-a737-65d6fdf74604.png' +export const iconDark='https://files.prismisskey.space/misskey/484efc68-de41-4786-b2b6-e5085c31c2c4.webp' +export const iconLight='https://files.prismisskey.space/misskey/c3d722fe-379f-4c85-9414-90c232d53237.webp' // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) From dd66f5395161dc261ada6b0bcb4ee5535c7b6773 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 31 Oct 2023 22:16:10 +0900 Subject: [PATCH 233/501] version fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 77494abac7..9ffcde67ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.6-prismisskey.1", + "version": "2023.11.0-beta.6-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", From 7703b2e5f8ca79c76654a40b6ea3b7ac9bc53491 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 1 Nov 2023 05:24:45 +0900 Subject: [PATCH 234/501] fix --- packages/backend/src/core/activitypub/ApRendererService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 49f9ebe3fb..41db0b91ef 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -27,7 +27,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil import { bindThis } from '@/decorators.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { isNotNull } from '@/misc/is-not-null.js'; -import { IdService } from '@/core/IdService.js'; +import { IdService } from '@/core/IdService.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; From 52939f5661646caf17cf11276510fd5d7e18f89e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 07:40:28 +0900 Subject: [PATCH 235/501] fix --- packages/frontend/src/ui/universal.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index af0e2165eb..7a504a75c2 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -37,8 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> - <span class="_indicateCounter" : - class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> + <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> </span></button> <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i> From b1b6966b6b2e4f5deeb8dbc61b86c8d519e06829 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 10:16:01 +0900 Subject: [PATCH 236/501] ? --- packages/backend/src/core/QueryService.ts | 47 ------------- .../backend/src/core/UserFollowingService.ts | 69 ++++++------------- .../src/core/activitypub/ApRendererService.ts | 2 +- 3 files changed, 21 insertions(+), 97 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 68d9d14512..f006ed4944 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -251,51 +251,4 @@ export class QueryService { q.setParameters(mutingQuery.getParameters()); } - @bindThis - public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void { - if (me == null) { - q.andWhere('note.channelId IS NULL'); - } else { - q.leftJoinAndSelect('note.channel', 'channel'); - - const channelFollowingQuery = this.channelFollowingsRepository.createQueryBuilder('channelFollowing') - .select('channelFollowing.followeeId') - .where('channelFollowing.followerId = :followerId', { followerId: me.id }); - - q.andWhere(new Brackets(qb => { qb - // チャンネルのノートではない - .where('note.channelId IS NULL') - // または自分がフォローしているチャンネルのノート - .orWhere(`note.channelId IN (${ channelFollowingQuery.getQuery() })`); - })); - - q.setParameters(channelFollowingQuery.getParameters()); - } - } - @bindThis - public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<MiUser, 'id'> | null): void { - if (me == null) { - q.andWhere(new Brackets(qb => { qb - .where('note.replyId IS NULL') // 返信ではない - .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 - .where('note.replyId IS NOT NULL') - .andWhere('note.replyUserId = note.userId'); - })); - })); - } else if (!withReplies) { - q.andWhere(new Brackets(qb => { qb - .where('note.replyId IS NULL') // 返信ではない - .orWhere('note.replyUserId = :meId', { meId: me.id }) // 返信だけど自分のノートへの返信 - .orWhere(new Brackets(qb => { qb // 返信だけど自分の行った返信 - .where('note.replyId IS NOT NULL') - .andWhere('note.userId = :meId', { meId: me.id }); - })) - .orWhere(new Brackets(qb => { qb // 返信だけど投稿者自身への返信 - .where('note.replyId IS NOT NULL') - .andWhere('note.replyUserId = note.userId'); - })); - })); - } - } - } diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index e1230012be..4d7e14f683 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -19,13 +19,7 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { WebhookService } from '@/core/WebhookService.js'; import { NotificationService } from '@/core/NotificationService.js'; import { DI } from '@/di-symbols.js'; -import type { - FollowingsRepository, - FollowRequestsRepository, - InstancesRepository, - UserProfilesRepository, - UsersRepository, -} from '@/models/_.js'; +import type { FollowingsRepository, FollowRequestsRepository, InstancesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { bindThis } from '@/decorators.js'; @@ -59,18 +53,25 @@ export class UserFollowingService implements OnModuleInit { constructor( private moduleRef: ModuleRef, + @Inject(DI.config) private config: Config, + @Inject(DI.usersRepository) private usersRepository: UsersRepository, + @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, + @Inject(DI.followRequestsRepository) private followRequestsRepository: FollowRequestsRepository, + @Inject(DI.instancesRepository) private instancesRepository: InstancesRepository, + private cacheService: CacheService, private utilityService: UtilityService, private userEntityService: UserEntityService, @@ -196,18 +197,10 @@ export class UserFollowingService implements OnModuleInit { @bindThis private async insertFollowingDoc( followee: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox'] + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] }, follower: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox'] + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'] }, silent = false, withReplies?: boolean, @@ -254,7 +247,8 @@ export class UserFollowingService implements OnModuleInit { }); // 通知を作成 - this.notificationService.createNotification(follower.id, 'followRequestAccepted', {}, followee.id); + this.notificationService.createNotification(follower.id, 'followRequestAccepted', { + }, followee.id); } if (alreadyFollowed) return; @@ -328,25 +322,18 @@ export class UserFollowingService implements OnModuleInit { }); // 通知を作成 - this.notificationService.createNotification(followee.id, 'follow', {}, follower.id); + this.notificationService.createNotification(followee.id, 'follow', { + }, follower.id); } } @bindThis public async unfollow( follower: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, followee: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, silent = false, ): Promise<void> { @@ -477,18 +464,10 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async createFollowRequest( follower: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, followee: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, requestId?: string, withReplies?: boolean, @@ -581,11 +560,7 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async acceptFollowRequest( followee: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, follower: MiUser, ): Promise<void> { @@ -613,11 +588,7 @@ export class UserFollowingService implements OnModuleInit { @bindThis public async acceptAllFollowRequests( user: { - id: MiUser['id']; - host: MiUser['host']; - uri: MiUser['host']; - inbox: MiUser['inbox']; - sharedInbox: MiUser['sharedInbox']; + id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']; }, ): Promise<void> { const requests = await this.followRequestsRepository.findBy({ diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 41db0b91ef..49f9ebe3fb 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -27,7 +27,7 @@ import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFil import { bindThis } from '@/decorators.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { isNotNull } from '@/misc/is-not-null.js'; -import { IdService } from '@/core/IdService.js'; +import { IdService } from '@/core/IdService.js'; import { LdSignatureService } from './LdSignatureService.js'; import { ApMfmService } from './ApMfmService.js'; import type { IAccept, IActivity, IAdd, IAnnounce, IApDocument, IApEmoji, IApHashtag, IApImage, IApMention, IBlock, ICreate, IDelete, IFlag, IFollow, IKey, ILike, IMove, IObject, IPost, IQuestion, IReject, IRemove, ITombstone, IUndo, IUpdate } from './type.js'; From c2208fb1c51828757a15cace5fffb4cbc8085c01 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 14:32:07 +0900 Subject: [PATCH 237/501] =?UTF-8?q?=E3=82=A4=E3=83=B3=E3=82=B8=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=82=BF=E3=83=BC=E3=81=A8=E3=81=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + package.json | 2 +- packages/backend/src/server/api/endpoints/users/stats.ts | 4 ++-- packages/frontend/src/components/MkLaunchPad.vue | 5 +++-- packages/frontend/src/components/MkNotifications.vue | 3 --- packages/frontend/src/pages/settings/general.vue | 2 ++ packages/frontend/src/pages/settings/notifications.vue | 4 ++-- packages/frontend/src/store.ts | 4 ++++ packages/frontend/src/ui/_common_/navbar-for-mobile.vue | 4 ++-- packages/frontend/src/ui/_common_/navbar.vue | 6 +++--- packages/frontend/src/ui/classic.sidebar.vue | 4 ++-- packages/frontend/src/ui/deck.vue | 3 ++- packages/frontend/src/ui/universal.vue | 4 ++-- packages/misskey-js/etc/misskey-js.api.md | 3 ++- 15 files changed, 29 insertions(+), 21 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index dbc4a16504..b3ace43563 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -14,6 +14,7 @@ export interface Locale { "forgotPassword": string; "fetchingAsApObject": string; "ok": string; + "notificationIndicator": string; "hanntenn": string; "hanntennInfo": string; "ruby": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 78723c06b0..aed6b801f1 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -11,6 +11,7 @@ password: "パスワード" forgotPassword: "パスワードを忘れた" fetchingAsApObject: "連合に照会中" ok: "OK" +notificationIndicator: "通知のインジケーターの数字を表示する" hanntenn: "アイコンとバナーを反転させる" hanntennInfo: "ダークだったらライトのアイコンに、ライトだったらダークのアイコンに。" ruby: "ルビ" diff --git a/package.json b/package.json index 42f7e5a4ec..1b0ca617af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0-beta.7-prismisskey.1", + "version": "2023.11.0-beta.7-prismisskey.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/src/server/api/endpoints/users/stats.ts b/packages/backend/src/server/api/endpoints/users/stats.ts index f51da1baad..6ee9eba839 100644 --- a/packages/backend/src/server/api/endpoints/users/stats.ts +++ b/packages/backend/src/server/api/endpoints/users/stats.ts @@ -3,7 +3,7 @@ import { awaitAll } from '@/misc/prelude/await-all.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository, NotesRepository, FollowingsRepository, DriveFilesRepository, NoteReactionsRepository, PageLikesRepository, NoteFavoritesRepository, PollVotesRepository } from '@/models/index.js'; +import type { UsersRepository, NotesRepository, FollowingsRepository, DriveFilesRepository, NoteReactionsRepository, PageLikesRepository, NoteFavoritesRepository, PollVotesRepository } from '@/models/_.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -148,7 +148,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private driveFileEntityService: DriveFileEntityService, ) { - super(meta, paramDef, async (ps, me) => { + super(meta, paramDef, async (ps) => { const user = await this.usersRepository.findOneBy({ id: ps.userId }); if (user == null) { throw new ApiError(meta.errors.noSuchUser); diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue index 10ad4b2fe3..05f14589fc 100644 --- a/packages/frontend/src/components/MkLaunchPad.vue +++ b/packages/frontend/src/components/MkLaunchPad.vue @@ -11,13 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only <button v-if="item.action" v-click-anime class="_button item" :class="{gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }" @click="$event => { item.action($event); close(); }"> <i class="icon" :class="item.icon"></i> <div class="text">{{ item.text }}</div> - <span v-if="item.indicate && item.indicateValue" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span> + <span v-if="item.indicate && item.indicateValue && indicatorCounterToggle" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span> <span v-else-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span> </button> <MkA v-else v-click-anime :to="item.to" class="item" :class="{gamingDark: gamingType === 'dark',gamingLight: gamingType === 'light' }" @click.passive="close()"> <i class="icon" :class="item.icon"></i> <div class="text">{{ item.text }}</div> - <span v-if="item.indicate && item.indicateValue" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span> + <span v-if="item.indicate && item.indicateValue && indicatorCounterToggle" class="_indicateCounter indicatorWithValue">{{ item.indicateValue }}</span> <span v-else-if="item.indicate" class="indicator"><i class="_indicatorCircle"></i></span> </MkA> </template> @@ -34,6 +34,7 @@ import { defaultStore } from '@/store.js'; import { deviceKind } from '@/scripts/device-kind.js'; const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); const props = withDefaults(defineProps<{ src?: HTMLElement; diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 896f97a48d..a77fa23661 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -69,9 +69,6 @@ onUnmounted(() => { if (connection) connection.dispose(); }); -onDeactivated(() => { - if (connection) connection.dispose(); -}); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index cec8d6ac38..acb7922ae8 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -144,6 +144,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave}}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> <MkSwitch v-model="enablehanntenn">{{ i18n.ts.hanntenn }}<template #caption>{{ i18n.ts.hanntennInfo }} </template></MkSwitch> + <MkSwitch v-model="indicatorCounterToggle">{{ i18n.ts.notificationIndicator }}</MkSwitch> </div> <div> <MkRadios v-model="emojiStyle"> @@ -297,6 +298,7 @@ const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibili const FeaturedOrNote = computed(defaultStore.makeGetterSetter('FeaturedOrNote')) const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index 2222381df6..88e02af1f4 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -74,11 +74,11 @@ let sendReadMessage = $computed(() => pushRegistrationInServer?.sendReadMessage const userLists = await os.api('users/lists/list'); async function readAllUnreadNotes() { - await os.apiWithDialog('i/read-all-unread-notes'); + await os.api('i/read-all-unread-notes'); } async function readAllNotifications() { - await os.apiWithDialog('notifications/mark-all-as-read'); + await os.api('notifications/mark-all-as-read'); } async function updateReceiveConfig(type, value) { diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 9d459ebd16..0f2ec0a22e 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -276,6 +276,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 'dark', }, + indicatorCounterToggle: { + where: 'device', + default: 'true', + }, bannerUrl:{ where: 'device', default: bannerDark diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 61b1565a0a..c4842fc31d 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.itemText">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> - <span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span><i + <span v-if="navbarItemDef[item].indicateValue && indicatorCounterToggle" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span><i v-else class="_indicatorCircle"></i></span> </component> </template> @@ -82,7 +82,7 @@ import {$i, openAccountMenu as openAccountMenu_} from '@/account'; import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; import {i18n} from '@/i18n'; import {instance} from '@/instance'; - +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); function hexToRgb(hex) { diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 551e54c867..70fc2ce40c 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -36,8 +36,8 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span :class="$style.itemText">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" - :class="[$style.itemIndicator,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"> - <span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span><i + :class="[$style.itemIndicator ,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"> + <span v-if="navbarItemDef[item].indicateValue && indicatorCounterToggle" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span><i v-else class="_indicatorCircle"></i></span> </component> </template> @@ -95,7 +95,7 @@ import {$i, openAccountMenu as openAccountMenu_} from '@/account'; import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; import {i18n} from '@/i18n'; import {instance} from '@/instance'; - +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); function hexToRgb(hex) { hex = hex.replace(/^#/, ''); const r = parseInt(hex.substring(0, 2), 16); diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue index 01dbf1c9c0..4cf5c3e593 100644 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> <i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span> <span v-if="navbarItemDef[item].indicated" class="indicator"> - <span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span> + <span v-if="navbarItemDef[item].indicateValue && indicatorCounterToggle" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span> <i v-else class="_indicatorCircle"></i> </span> </component> @@ -64,7 +64,7 @@ import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; const WINDOW_THRESHOLD = 1400; - +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); const menu = $ref(defaultStore.state.menu); const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay')); const otherNavItemIndicated = computed<boolean>(() => { diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue index 1d51e08f78..40c375c688 100644 --- a/packages/frontend/src/ui/deck.vue +++ b/packages/frontend/src/ui/deck.vue @@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"> <i :class="$style.navButtonIcon" class="ti ti-bell"></i> <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator"> - <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> + <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 && indicatorCounterToggle ? '99+' : $i.unreadNotificationsCount }}</span> </span> </button> <button :class="$style.postButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button> @@ -120,6 +120,7 @@ import XDirectColumn from '@/ui/deck/direct-column.vue'; import XRoleTimelineColumn from '@/ui/deck/role-timeline-column.vue'; const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); const XAnnouncements = defineAsyncComponent(() => import('@/ui/_common_/announcements.vue')); +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); const columnComponents = { main: XMainColumn, diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 7a504a75c2..e9be376429 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -37,7 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> - <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> + <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 && indicatorCounterToggle ? '99+' : $i.unreadNotificationsCount }}</span> </span></button> <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i> @@ -126,7 +126,7 @@ import {useScrollPositionManager} from '@/nirax'; const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); - +const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); let gaming = ref() // gaming.valueに新しい値を代入する if (darkMode.value && gamingMode.value == true) { diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 9b5d98f625..88704a8236 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2676,6 +2676,7 @@ export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; type Note = { id: ID; createdAt: DateString; + updatedAt?: DateString | null; text: string | null; cw: string | null; user: User; @@ -3026,7 +3027,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:612:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:614:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) From ba9e11870cef652a2cbd9a09258574bf43f44e67 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 14:45:46 +0900 Subject: [PATCH 238/501] Revert ":art:" This reverts commit 821633f878a8bf9c1155546a3d44547ecc59be76. --- packages/frontend/src/components/global/MkFooterSpacer.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/global/MkFooterSpacer.vue b/packages/frontend/src/components/global/MkFooterSpacer.vue index e78df6b8d9..07df76b256 100644 --- a/packages/frontend/src/components/global/MkFooterSpacer.vue +++ b/packages/frontend/src/components/global/MkFooterSpacer.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.spacer, defaultStore.reactiveState.darkMode.value ? $style.dark : $style.light]"></div> +<div :class="[$style.spacer, defaultStore.reactiveState.darkMode ? $style.dark : $style.light]"></div> </template> <script lang="ts" setup> @@ -22,7 +22,7 @@ import { defaultStore } from '@/store.js'; background-color: rgba(255, 255, 255, 0); &.light { - background-image: repeating-linear-gradient(135deg, transparent, transparent 16px, #00000010 16px, #00000010 20px ); + background-image: repeating-linear-gradient(135deg, transparent, transparent 16px, #00000026 16px, #00000026 20px ); } &.dark { From 801637ef1d45f386e1435a9658bd62ede1a99cd8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 14:45:50 +0900 Subject: [PATCH 239/501] Revert ":art:" This reverts commit e6e5bf1da4198e3d2c19b02ede3a6777c37e1c7e. --- .../src/components/global/MkFooterSpacer.vue | 32 ------------------- packages/frontend/src/components/index.ts | 7 ++-- .../frontend/src/pages/settings/index.vue | 1 - 3 files changed, 2 insertions(+), 38 deletions(-) delete mode 100644 packages/frontend/src/components/global/MkFooterSpacer.vue diff --git a/packages/frontend/src/components/global/MkFooterSpacer.vue b/packages/frontend/src/components/global/MkFooterSpacer.vue deleted file mode 100644 index 07df76b256..0000000000 --- a/packages/frontend/src/components/global/MkFooterSpacer.vue +++ /dev/null @@ -1,32 +0,0 @@ -<!-- -SPDX-FileCopyrightText: syuilo and other misskey contributors -SPDX-License-Identifier: AGPL-3.0-only ---> - -<template> -<div :class="[$style.spacer, defaultStore.reactiveState.darkMode ? $style.dark : $style.light]"></div> -</template> - -<script lang="ts" setup> -import { defaultStore } from '@/store.js'; -</script> - -<style lang="scss" module> -.spacer { - box-sizing: border-box; - padding: 32px; - margin: 0 auto; - height: 300px; - background-clip: content-box; - background-size: auto auto; - background-color: rgba(255, 255, 255, 0); - - &.light { - background-image: repeating-linear-gradient(135deg, transparent, transparent 16px, #00000026 16px, #00000026 20px ); - } - - &.dark { - background-image: repeating-linear-gradient(135deg, transparent, transparent 16px, #FFFFFF16 16px, #FFFFFF16 20px ); - } -} -</style> diff --git a/packages/frontend/src/components/index.ts b/packages/frontend/src/components/index.ts index c740d181f9..48af4754d7 100644 --- a/packages/frontend/src/components/index.ts +++ b/packages/frontend/src/components/index.ts @@ -5,7 +5,7 @@ import { App } from 'vue'; -import Mfm from './global/MkMisskeyFlavoredMarkdown.js'; +import Mfm from './global/MkMisskeyFlavoredMarkdown.ts'; import MkA from './global/MkA.vue'; import MkAcct from './global/MkAcct.vue'; import MkAvatar from './global/MkAvatar.vue'; @@ -16,14 +16,13 @@ import MkUserName from './global/MkUserName.vue'; import MkEllipsis from './global/MkEllipsis.vue'; import MkTime from './global/MkTime.vue'; import MkUrl from './global/MkUrl.vue'; -import I18n from './global/i18n.js'; +import I18n from './global/i18n'; import RouterView from './global/RouterView.vue'; import MkLoading from './global/MkLoading.vue'; import MkError from './global/MkError.vue'; import MkAd from './global/MkAd.vue'; import MkPageHeader from './global/MkPageHeader.vue'; import MkSpacer from './global/MkSpacer.vue'; -import MkFooterSpacer from './global/MkFooterSpacer.vue'; import MkStickyContainer from './global/MkStickyContainer.vue'; export default function(app: App) { @@ -51,7 +50,6 @@ export const components = { MkAd: MkAd, MkPageHeader: MkPageHeader, MkSpacer: MkSpacer, - MkFooterSpacer: MkFooterSpacer, MkStickyContainer: MkStickyContainer, }; @@ -75,7 +73,6 @@ declare module '@vue/runtime-core' { MkAd: typeof MkAd; MkPageHeader: typeof MkPageHeader; MkSpacer: typeof MkSpacer; - MkFooterSpacer: typeof MkFooterSpacer; MkStickyContainer: typeof MkStickyContainer; } } diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 361a6c8c78..cfabbbbf65 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -23,7 +23,6 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkSpacer> - <MkFooterSpacer/> </mkstickycontainer> </template> From 14ee2f7e6a33874e2f1660b68e59ac711190421e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 14:47:19 +0900 Subject: [PATCH 240/501] =?UTF-8?q?Revert=20"enhance(frontend):=20?= =?UTF-8?q?=E3=80=8C=E5=86=85=E5=AE=B9=E3=82=92=E9=9A=A0=E3=81=99=E3=80=8D?= =?UTF-8?q?=E3=81=A7=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=82=E9=9A=A0=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit e85b8217c0eda4b0cb2ebf5642cabd2af7212140. --- packages/frontend/src/components/MkNote.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 0c04eba517..6c456a6705 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -92,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> </div> - <MkReactionsViewer v-show="appearNote.cw == null || showContent" :note="appearNote" :maxNumber="16"> + <MkReactionsViewer :note="appearNote" :maxNumber="16"> <template #more> <div :class="$style.reactionOmitted">{{ i18n.ts.more }}</div> </template> From 5335e68bde94e960632db3042d5f47fa86b79b5a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 2 Nov 2023 16:51:03 +0900 Subject: [PATCH 241/501] =?UTF-8?q?Feat:=E3=82=B4=E3=83=AA=E3=83=A9?= =?UTF-8?q?=E3=83=A2=E3=83=BC=E3=83=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 2 + locales/ja-JP.yml | 2 + .../backend/src/core/NoteCreateService.ts | 1 + .../src/core/entities/UserEntityService.ts | 1 + packages/backend/src/models/User.ts | 6 +++ .../backend/src/models/json-schema/user.ts | 4 ++ .../src/server/api/endpoints/i/update.ts | 8 +++- .../global/MkMisskeyFlavoredMarkdown.ts | 43 +++++++++++++++++-- .../frontend/src/pages/settings/profile.vue | 8 +++- packages/frontend/src/scripts/achievements.ts | 5 +++ packages/frontend/src/scripts/uhoize.ts | 28 ++++++++++++ packages/misskey-js/etc/misskey-js.api.md | 6 ++- packages/misskey-js/src/entities.ts | 2 + 13 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 packages/frontend/src/scripts/uhoize.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index 77cce21f84..f13b8c7712 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -175,7 +175,9 @@ export interface Locale { "flagAsBot": string; "flagAsBotDescription": string; "flagAsCat": string; + "flagAsGorilla": string; "flagAsCatDescription": string; + "flagAsGorillaDescription": string; "flagShowTimelineReplies": string; "showMediaTimeline": string; "showMediaTimelineInfo": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index e884b44382..3500c6b1b1 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -172,7 +172,9 @@ cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リ flagAsBot: "Botとして設定" flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。" flagAsCat: "にゃああああああああああああああ!!!!!!!!!!!!" +flagAsGorilla: "ウホウホウホホウホウホウホウホホホ!!!!!!!!!!!" flagAsCatDescription: "にゃにゃにゃ??" +flagAsGorillaDescription: "ウホウホウホ??" flagShowTimelineReplies: "タイムラインにノートへの返信を表示する" showMediaTimeline: "メディアタイムラインを表示する" showMediaTimelineInfo: "オンにするとメディアタイムラインを上のバーに表示します。 オフにすると表示しなくなります。" diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index acd11a9fa7..37dad286a7 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -224,6 +224,7 @@ export class NoteCreateService implements OnApplicationShutdown { host: MiUser['host']; isBot: MiUser['isBot']; isCat: MiUser['isCat']; + isGorilla: MiUser['isGorilla']; }, data: Option, silent = false): Promise<MiNote> { // チャンネル外にリプライしたら対象のスコープに合わせる // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 17e7988176..328786e837 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -366,6 +366,7 @@ export class UserEntityService implements OnModuleInit { }))) : [], isBot: user.isBot, isCat: user.isCat, + isGorilla: user.isGorilla, instance: user.host ? this.federatedInstanceService.federatedInstanceCache.fetch(user.host).then(instance => instance ? { name: instance.name, softwareName: instance.softwareName, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index c3762fcd3e..691866da6a 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -177,6 +177,12 @@ export class MiUser { }) public isCat: boolean; + @Column('boolean', { + default: false, + comment: 'Whether the User is a gorilla.', + }) + public isGorilla: boolean; + @Column('boolean', { default: false, comment: 'Whether the User is the root.', diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 37bdcbe281..f0f225d4dc 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -83,6 +83,10 @@ export const packedUserLiteSchema = { type: 'boolean', nullable: false, optional: true, }, + isGorilla: { + type: 'boolean', + nullable: false, optional: true, + }, onlineStatus: { type: 'string', format: 'url', diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 0e6a4d2e36..67fcd81860 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -165,6 +165,7 @@ export const paramDef = { preventAiLearning: { type: 'boolean' }, isBot: { type: 'boolean' }, isCat: { type: 'boolean' }, + isGorilla: { type: 'boolean' }, injectFeaturedNote: { type: 'boolean' }, receiveAnnouncementEmail: { type: 'boolean' }, alwaysMarkNsfw: { type: 'boolean' }, @@ -267,7 +268,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (typeof ps.autoAcceptFollowed === 'boolean') profileUpdates.autoAcceptFollowed = ps.autoAcceptFollowed; if (typeof ps.noCrawle === 'boolean') profileUpdates.noCrawle = ps.noCrawle; if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning; - if (typeof ps.isCat === 'boolean') updates.isCat = ps.isCat; + if (typeof ps.isCat === 'boolean' && !ps.isGorilla) { + updates.isCat = ps.isCat; + }; + if (typeof ps.isGorilla === 'boolean' && !ps.isCat) { + updates.isGorilla = ps.isGorilla + }; if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.alwaysMarkNsfw === 'boolean') { diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index cb1abcd8c8..6bc513e301 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -16,6 +16,8 @@ import { defaultStore } from '@/store'; import { mixEmoji } from '@/scripts/emojiKitchen/emojiMixer'; import MkRuby from "@/components/global/MkRuby.vue"; import { nyaize as doNyaize } from '@/scripts/nyaize.js'; +import { uhoize as doUhoize } from '@/scripts/uhoize.js'; +import {ID, Instance} from "misskey-js/built/entities.js"; const QUOTE_STYLE = ` display: block; @@ -62,12 +64,41 @@ type MfmProps = { text: string; plain?: boolean; nowrap?: boolean; - author?: Misskey.entities.UserLite; + author?: { + id: ID; + username: string; + host: string | null; + name: string | null; + onlineStatus: 'online' | 'active' | 'offline' | 'unknown'; + avatarUrl: string; + avatarBlurhash: string; + avatarDecorations: { + id: ID; + url: string; + angle?: number; + flipH?: boolean; + }[]; + emojis: { + name: string; + url: string; + }[]; + instance?: { + name: Instance['name']; + softwareName: Instance['softwareName']; + softwareVersion: Instance['softwareVersion']; + iconUrl: Instance['iconUrl']; + faviconUrl: Instance['faviconUrl']; + themeColor: Instance['themeColor']; + }; + isGorilla?: boolean; + isCat?: boolean; + isBot?: boolean;}; i?: Misskey.entities.UserLite | null; isNote?: boolean; emojiUrls?: string[]; rootScale?: number; nyaize: boolean | 'account'; + uhoize: boolean | 'account'; parsedNodes?: mfm.MfmNode[] | null; }; @@ -75,7 +106,8 @@ type MfmProps = { export default function(props: MfmProps) { const isNote = props.isNote ?? true; const shouldNyaize = props.nyaize ? props.nyaize === 'account' ? props.author?.isCat : false : false; - + const shouldUhoize = props.nyaize ? props.nyaize === 'account' ? props.author?.isGorilla : false : false; + console.log(shouldUhoize, props.nyaize,props.author?.isGorilla) // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (props.text == null || props.text === '') return; @@ -93,15 +125,18 @@ export default function(props: MfmProps) { * @param ast MFM AST * @param scale How times large the text is * @param disableNyaize Whether nyaize is disabled or not + * @param disableUhoize */ - const genEl = (ast: mfm.MfmNode[], scale: number, disableNyaize = false) => ast.map((token): VNode | string | (VNode | string)[] => { + const genEl = (ast: mfm.MfmNode[], scale: number, disableNyaize = false, disableUhoize = false) => ast.map((token): VNode | string | (VNode | string)[] => { switch (token.type) { case 'text': { let text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n'); if (!disableNyaize && shouldNyaize) { text = doNyaize(text); } - + if (!disableUhoize && shouldUhoize) { + text = doUhoize(text); + } if (!props.plain) { const res: (VNode | string)[] = []; for (const t of text.split('\n')) { diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 2ac8d15545..1ecde64d05 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -105,7 +105,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.advancedSettings }}</template> <div class="_gaps_m"> - <MkSwitch v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></MkSwitch> + <MkSwitch :disabled="profile.isGorilla" v-model="profile.isCat">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></MkSwitch> + <MkSwitch :disabled="profile.isCat" v-model="profile.isGorilla">{{ i18n.ts.flagAsGorilla }}<template #caption>{{ i18n.ts.flagAsGorillaDescription }}</template></MkSwitch> <MkSwitch v-model="profile.isBot">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></MkSwitch> </div> </MkFolder> @@ -154,6 +155,7 @@ const profile = reactive({ lang: $i.lang, isBot: $i.isBot, isCat: $i.isCat, + isGorilla: $i.isGorilla, }); watch(() => profile, () => { @@ -206,6 +208,7 @@ function save() { lang: profile.lang || null, isBot: !!profile.isBot, isCat: !!profile.isCat, + isGorilla: !!profile.isGorilla, }); claimAchievement('profileFilled'); if (profile.name === 'syuilo' || profile.name === 'しゅいろ') { @@ -214,6 +217,9 @@ function save() { if (profile.isCat) { claimAchievement('markedAsCat'); } + if (profile.isGorilla) { + claimAchievement('markedAsCat'); + } } function changeAvatar(ev) { diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index af7c6b060c..e9d12eb6f2 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -265,6 +265,11 @@ export const ACHIEVEMENT_BADGES = { bg: 'linear-gradient(0deg, rgb(187 183 59), rgb(255 143 77))', frame: 'bronze', }, + 'markedAsGorilla': { + img: '/fluent-emoji/1f98D.png', + bg: 'linear-gradient(0deg, rgba(55,0,0,1) 0%, rgba(107,5,5,1) 59%, rgba(158,6,6,1) 100%)', + frame: 'bronze', + }, 'following1': { img: '/fluent-emoji/2618.png', bg: 'linear-gradient(0deg, rgb(59 187 116), rgb(199 211 102))', diff --git a/packages/frontend/src/scripts/uhoize.ts b/packages/frontend/src/scripts/uhoize.ts new file mode 100644 index 0000000000..25d3760722 --- /dev/null +++ b/packages/frontend/src/scripts/uhoize.ts @@ -0,0 +1,28 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export function uhoize(text) { + const gorillaNoises = ['ウホ', 'ウホホ', 'ウホッ']; + let result = ''; + let noiseIndex = 0; + for (let i = 0; i < text.length; i++) { + if (!(/[、。.,\/#!$%\^&\*;:{}=\-_`~()]/.test(text[i]))) { + if (/[^\x00-\x7F]/.test(text[i])) { + noiseIndex = Math.floor(Math.random() * 3); + const japaneseNoises = ['ウホ', 'ウホホ', 'ウホッ']; + result += japaneseNoises[noiseIndex]; + } else { + noiseIndex = Math.floor(Math.random() * 2); + const englishNoises = ['uho', 'uhoho']; + result += englishNoises[noiseIndex]; + } + }else{ + result += text[i]; + } + if (text.length*1.3 < result.length) return result + + } + return result; +} diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 88704a8236..e39a89fdbd 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -2951,6 +2951,7 @@ type UserDetailed = UserLite & { isBlocking: boolean; isBot: boolean; isCat: boolean; + isGorilla: boolean; isFollowed: boolean; isFollowing: boolean; isLocked: boolean; @@ -3014,6 +3015,7 @@ type UserLite = { faviconUrl: Instance['faviconUrl']; themeColor: Instance['themeColor']; }; + isGorilla?: boolean; isCat?: boolean; isBot?: boolean; }; @@ -3026,8 +3028,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts // src/api.types.ts:633:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts -// src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts -// src/entities.ts:614:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts +// src/entities.ts:118:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts +// src/entities.ts:616:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts // (No @packageDocumentation comment for this package) diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index ffe1109227..e2cea8c7e4 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -34,6 +34,7 @@ export type UserLite = { faviconUrl: Instance['faviconUrl']; themeColor: Instance['themeColor']; }; + isGorilla?: boolean; isCat?: boolean; isBot?: boolean; }; @@ -58,6 +59,7 @@ export type UserDetailed = UserLite & { isBlocking: boolean; isBot: boolean; isCat: boolean; + isGorilla: boolean; isFollowed: boolean; isFollowing: boolean; isLocked: boolean; From 1d30c51d77cfa177a670ea94d0d0c9085c105722 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 3 Nov 2023 03:07:05 +0900 Subject: [PATCH 242/501] =?UTF-8?q?Fix:=20css=20modules=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=20=E3=81=9D=E3=81=AE?= =?UTF-8?q?=E4=BB=96=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- misskey-assets | 2 +- .../src/components/MkCustomEmojiEditLocal.vue | 91 ++++--- .../components/MkCustomEmojiEditRemote.vue | 84 +++---- .../components/MkCustomEmojiEditRequest.vue | 229 ++++++++---------- .../src/components/MkEmojiEditDialog.vue | 20 +- packages/frontend/src/pages/about.emojis.vue | 2 +- packages/misskey-js/src/entities.ts | 1 - 7 files changed, 198 insertions(+), 231 deletions(-) diff --git a/misskey-assets b/misskey-assets index 0179793ec8..cf3ce27b2e 160000 --- a/misskey-assets +++ b/misskey-assets @@ -1 +1 @@ -Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5 +Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364 diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 5e2d4438cf..93faae5e37 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -18,20 +18,20 @@ <MkPagination ref="emojisPaginationComponent" :pagination="pagination" :displayLimit="100"> <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> - <div class="ldhfsamy"> + <div :class="$style.root"> <div v-for="emoji in items" :key="emoji.id"> - <button v-if="emoji.request" class="emoji _panel _button emoji-request" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <button v-if="emoji.request" class="_panel _button" :class="[{ selected: selectedEmojis.includes(emoji.id) },$style.emoji,$style.emojirequest]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> <img :src="emoji.url" class="img" :alt="emoji.name"/> <div class="body"> <div class="name _monospace">{{ emoji.name }}</div> <div class="info">{{ emoji.category }}</div> </div> </button> - <button v-else class="emoji _panel _button" :class="{ selected: selectedEmojis.includes(emoji.id) }" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> - <img :src="emoji.url" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.category }}</div> + <button v-else class="_panel _button" :class="[{ selected: selectedEmojis.includes(emoji.id) },$style.emoji]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <img :src="emoji.url" :class="$style.img" :alt="emoji.name"/> + <div :class="$style.body"> + <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> + <div :class="$style.info">{{ emoji.category }}</div> </div> </button> </div> @@ -82,7 +82,7 @@ const toggleSelect = (emoji) => { const edit = (emoji) => { os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { emoji: emoji, - requestNow: false, + isRequest: false, }, { done: result => { if (result.updated) { @@ -171,53 +171,48 @@ const delBulk = async () => { }; </script> -<style lang="scss" scoped> -.ldhfsamy { +<style lang="scss" module> +.root { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); grid-gap: var(--margin); +} +.emoji { + display: flex; + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + width: 100%; - div > .emoji { - display: flex; - align-items: center; - padding: 11px; - text-align: left; - border: solid 1px var(--panel); - width: 100%; + &:hover { + border-color: var(--inputBorderHover); + } - &:hover { - border-color: var(--inputBorderHover); - } - - &.selected { - border-color: var(--accent); - } - - > .img { - width: 42px; - height: 42px; - } - - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; - - > .name { - text-overflow: ellipsis; - overflow: hidden; - } - - > .info { - opacity: 0.5; - text-overflow: ellipsis; - overflow: hidden; - } - } - } + &.selected { + border-color: var(--accent); + } +} +.img { + width: 42px; + height: 42px; +} +.body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; +} +.name { + text-overflow: ellipsis; + overflow: hidden; } -.emoji-request { +.info { + opacity: 0.5; + text-overflow: ellipsis; + overflow: hidden; +} +.emojirequest { --c: rgb(255 196 0 / 15%);; background-image: linear-gradient(45deg,var(--c) 16.67%,transparent 16.67%,transparent 50%,var(--c) 50%,var(--c) 66.67%,transparent 66.67%,transparent 100%); background-size: 16px 16px; diff --git a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue index 3575d9d9a4..b5d5b31e40 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditRemote.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRemote.vue @@ -11,12 +11,12 @@ <MkPagination :pagination="remotePagination"> <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> - <div class="ldhfsamy"> - <div v-for="emoji in items" :key="emoji.id" class="emoji _panel _button" @click="remoteMenu(emoji, $event)"> - <img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" class="img" :alt="emoji.name"/> - <div class="body"> - <div class="name _monospace">{{ emoji.name }}</div> - <div class="info">{{ emoji.host }}</div> + <div :class="$style.root"> + <div v-for="emoji in items" :key="emoji.id" :class="$style.emoji" class="_panel _button" @click="remoteMenu(emoji, $event)"> + <img :src="`/emoji/${emoji.name}@${emoji.host}.webp`" :class="$style.img" :alt="emoji.name"/> + <div :class="$style.body"> + <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> + <div :class="$style.info">{{ emoji.host }}</div> </div> </div> </div> @@ -62,49 +62,45 @@ const remoteMenu = (emoji, ev: MouseEvent) => { }; </script> -<style lang="scss" scoped> -.empty { - margin: var(--margin); +<style lang="scss" module> + +.root { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); + grid-gap: 12px; + margin: var(--margin) 0; } -.ldhfsamy { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); - grid-gap: 12px; - margin: var(--margin) 0; +.emoji { + display: flex; + align-items: center; + padding: 12px; + text-align: left; - > .emoji { - display: flex; - align-items: center; - padding: 12px; - text-align: left; + &:hover { + color: var(--accent); + } +} - &:hover { - color: var(--accent); - } +.img { + width: 32px; + height: 32px; +} - > .img { - width: 32px; - height: 32px; - } +.body { + padding: 0 0 0 8px; + white-space: nowrap; + overflow: hidden; +} +.name { + text-overflow: ellipsis; + overflow: hidden; +} - > .body { - padding: 0 0 0 8px; - white-space: nowrap; - overflow: hidden; - - > .name { - text-overflow: ellipsis; - overflow: hidden; - } - - > .info { - opacity: 0.5; - font-size: 90%; - text-overflow: ellipsis; - overflow: hidden; - } - } - } +.info { + opacity: 0.5; + font-size: 90%; + text-overflow: ellipsis; + overflow: hidden; } </style> diff --git a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue index 0d25614c82..dba4c66e54 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue @@ -2,33 +2,31 @@ <MkPagination ref="emojisRequestPaginationComponent" :pagination="paginationRequest"> <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> <template #default="{items}"> - <div class="ldhfsamy"> - <template v-for="emoji in items" :key="emoji.id"> - <div class="emoji _panel"> - <div class="img"> - <div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> - <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> - </div> - <div class="info"> - <div class="name">{{ i18n.ts.name }}: {{ emoji.name }}</div> - <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> - <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> - <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> - </div> - <div class="edit-button"> - <MkButton primary class="edit" @click="editRequest(emoji)"> - {{ i18n.ts.edit }} - </MkButton> - <MkButton class="request" @click="unrequested(emoji)"> - {{ i18n.ts.approve }} - </MkButton> - <MkButton danger class="delete" @click="deleteRequest(emoji)"> - {{ i18n.ts.delete }} - </MkButton> - </div> + <template v-for="emoji in items" :key="emoji.id"> + <div :class="$style.emoji" class="_panel"> + <div :class="$style.img"> + <div :class="$style.imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> + <div :class="$style.imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> </div> - </template> - </div> + <div :class="$style.info"> + <div :class="$style.name">{{ i18n.ts.name }}: {{ emoji.name }}</div> + <div :class="$style.category">{{ i18n.ts.category }}:{{ emoji.category }}</div> + <div :class="$style.aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> + <div :class="$style.license">{{ i18n.ts.license }}:{{ emoji.license }}</div> + </div> + <div :class="$style.editbutton"> + <MkButton primary :class="$style.edit" @click="editRequest(emoji)"> + {{ i18n.ts.edit }} + </MkButton> + <MkButton :class="$style.request" @click="unrequested(emoji)"> + {{ i18n.ts.approve }} + </MkButton> + <MkButton danger :class="$style.delete" @click="deleteRequest(emoji)"> + {{ i18n.ts.delete }} + </MkButton> + </div> + </div> + </template> </template> </MkPagination> </template> @@ -52,12 +50,10 @@ const paginationRequest = { })), }; -const editRequest = (emoji) => { - emoji.requestNow = true; +function editRequest(emoji) { os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { emoji: emoji, - requestNow: false, - isRequestEdit: true, + isRequest: true, }, { done: result => { if (result.updated) { @@ -72,7 +68,7 @@ const editRequest = (emoji) => { } }, }, 'closed'); -}; +} async function unrequested(emoji) { const { canceled } = await os.confirm({ @@ -113,103 +109,90 @@ async function deleteRequest(emoji) { } </script> -<style lang="scss" scoped> -.empty { - margin: var(--margin); +<style lang="scss" module> +.emoji { + align-items: center; + padding: 11px; + text-align: left; + border: solid 1px var(--panel); + margin: 10px; +} +.img { + display: grid; + grid-row: 1; + grid-column: 1/ span 2; + grid-template-columns: 50% 50%; + place-content: center; + place-items: center; +} +.imgLight { + display: grid; + grid-column: 1; + background-color: #fff; + margin-bottom: 12px; + img { + max-height: 64px; + max-width: 100%; + } +} +.imgDark { + display: grid; + grid-column: 2; + background-color: #000; + margin-bottom: 12px; + img { + max-height: 64px; + max-width: 100%; + } +} +.info { + display: grid; + grid-row: 2; + grid-template-rows: 30px 30px 30px; +} +.name { + grid-row: 1; + text-overflow: ellipsis; + overflow: hidden; } -.ldhfsamy { - > .emoji { - align-items: center; - padding: 11px; - text-align: left; - border: solid 1px var(--panel); - margin: 10px; +.category { + grid-row: 2; + text-overflow: ellipsis; + overflow: hidden; +} - > .img { - display: grid; - grid-row: 1; - grid-column: 1/ span 2; - grid-template-columns: 50% 50%; - place-content: center; - place-items: center; +.aliases { + grid-row: 3; + text-overflow: ellipsis; + overflow: hidden; +} - > .imgLight { - display: grid; - grid-column: 1; - background-color: #fff; - margin-bottom: 12px; - > img { - max-height: 64px; - max-width: 100%; - } - } +.license { + grid-row: 4; + text-overflow: ellipsis; + overflow: hidden; +} +.editbutton { + display: grid; + grid-template-rows: 42px; + margin-top: 6px; +} +.edit { + grid-row: 1; + width: 100%; + margin: 6px 0; +} - > .imgDark { - display: grid; - grid-column: 2; - background-color: #000; - margin-bottom: 12px; - > img { - max-height: 64px; - max-width: 100%; - } - } - } +.request { + grid-row: 2; + width: 100%; + margin: 6px 0; +} - > .info { - display: grid; - grid-row: 2; - grid-template-rows: 30px 30px 30px; - - > .name { - grid-row: 1; - text-overflow: ellipsis; - overflow: hidden; - } - - > .category { - grid-row: 2; - text-overflow: ellipsis; - overflow: hidden; - } - - > .aliases { - grid-row: 3; - text-overflow: ellipsis; - overflow: hidden; - } - - > .license { - grid-row: 4; - text-overflow: ellipsis; - overflow: hidden; - } - } - - > .edit-button { - display: grid; - grid-template-rows: 42px; - margin-top: 6px; - - > .edit { - grid-row: 1; - width: 100%; - margin: 6px 0; - } - - > .request { - grid-row: 2; - width: 100%; - margin: 6px 0; - } - - > .delete { - grid-row: 3; - width: 100%; - margin: 6px 0; - } - } - } +.delete { + grid-row: 3; + width: 100%; + margin: 6px 0; } </style> diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 056230f096..3c3f127d11 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="$emit('closed')" > <template v-if="emoji" #header>:{{ emoji.name }}:</template> - <template v-else-if="requestNow" #header>{{ i18n.ts.requestCustomEmojis }}</template> + <template v-else-if="isRequest && !emoji" #header>{{ i18n.ts.requestCustomEmojis }}</template> <template v-else #header>New emoji</template> <div> @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="license"> <template #label>{{ i18n.ts.license }}</template> </MkInput> - <MkFolder v-if="!requestNow && !isRequestEdit"> + <MkFolder v-if="!isRequest"> <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> @@ -65,14 +65,11 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkSwitch v-model="isSensitive">isSensitive</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkSwitch v-if="!requestNow" v-model="isRequest" :disabled="requestNow"> - {{ i18n.ts.requestPending }} - </MkSwitch> </div> </MkSpacer> <div :class="$style.footer"> <div :class="$style.footerButtons"> - <MkButton v-if="!requestNow" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> <MkButton v-if="validation" primary rounded style="margin: 0 auto;" @click="done"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> <MkButton v-else rounded style="margin: 0 auto;"><i class="ti ti-check"></i> {{ props.emoji ? i18n.ts.update : i18n.ts.create }}</MkButton> </div> @@ -99,8 +96,7 @@ import MkRolePreview from '@/components/MkRolePreview.vue'; const props = defineProps<{ emoji?: any, - requestNow: boolean, - isRequestEdit?: boolean, + isRequest: boolean, }>(); let dialog = $ref(null); @@ -114,14 +110,12 @@ let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref((props.emoji && props.emoji.r let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); -let requestNow = $ref(props.requestNow); -let isRequestEdit = $ref(props.isRequestEdit ?? false); -let isRequest = $ref(!!(isRequestEdit || props.requestNow)); +let isRequest = $ref(props.isRequest ?? false); watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); -const imgUrl = computed(() => file ? file.url : props.emoji && !isRequestEdit ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); +const imgUrl = computed(() => file ? file.url : props.emoji && !isRequest ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); const validation = computed(() => { return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; }); @@ -171,7 +165,7 @@ async function done() { } if (props.emoji) { - if (isRequestEdit) { + if (isRequest) { await os.apiWithDialog('admin/emoji/update-request', { id: props.emoji.id, ...params, diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index e4cbc04709..e465cdc84c 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -98,7 +98,7 @@ function search() { const edit = () => { os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { - requestNow: true, + isRequest: true, }, { done: result => { window.location.reload(); diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts index 4f0bd6cf68..38bac3b7c3 100644 --- a/packages/misskey-js/src/entities.ts +++ b/packages/misskey-js/src/entities.ts @@ -319,7 +319,6 @@ export type CustomEmoji = { url: string; category: string; aliases: string[]; - Request: boolean; }; export type LiteInstanceMetadata = { From c2f4847428f1d37792870fc026fde07d92c48219 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 3 Nov 2023 03:17:48 +0900 Subject: [PATCH 243/501] Update MkCustomEmojiEditRequest.vue Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/components/MkCustomEmojiEditRequest.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue index dba4c66e54..e875155dec 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue @@ -19,7 +19,7 @@ {{ i18n.ts.edit }} </MkButton> <MkButton :class="$style.request" @click="unrequested(emoji)"> - {{ i18n.ts.approve }} + {{ i18n.ts.approval }} </MkButton> <MkButton danger :class="$style.delete" @click="deleteRequest(emoji)"> {{ i18n.ts.delete }} From bffb7c028335b1f5c97b236a187ef2cab3780d11 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 4 Nov 2023 01:29:56 +0900 Subject: [PATCH 244/501] build fix --- .../src/server/api/endpoints/notes/create.ts | 2 -- .../frontend/src/components/MkNoteHeader.vue | 25 +++++++------------ 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 8300565c8e..3b1bdc0b91 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -16,8 +16,6 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; -import { ApiError } from '../../error.js'; -import {noteVisibilities} from "@/types.js"; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { ApiError } from '../../error.js'; diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index da3c9535ad..23d46b86ec 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -5,10 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <header :class="$style.root"> - <div v-if="mock" :class="$style.name"> - <MkUserName :user="note.user"/> - </div> - <MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)"> + <MkA v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)"> <MkUserName :user="note.user"/> </MkA> <div v-if="note.user.isBot" :class="$style.isBot">bot</div> @@ -17,14 +14,12 @@ SPDX-License-Identifier: AGPL-3.0-only <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> </div> <div :class="$style.info"> - <span v-if="note.updatedAt" style="margin-right: 0.5em;" :title="i18n.ts.edited"><i class="ti ti-pencil"></i></span> - <MkA :to="notePage(note)"> - <div v-if="mock"> - <MkTime :time="note.createdAt" colored/> - </div> - <MkA v-else :to="notePage(note)"> - <MkTime :time="note.createdAt" colored/> - </MkA> + <div v-if="mock" :class="$style.name"> + <MkUserName :user="note.user"/> + </div> + <MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)"> + <MkUserName :user="note.user"/> + </MkA> <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> <i v-if="note.visibility === 'home'" class="ti ti-home"></i> <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> @@ -37,17 +32,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject } from 'vue'; +import {inject} from 'vue'; import * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import { notePage } from '@/filters/note.js'; import { userPage } from '@/filters/user.js'; - +const mock = inject<boolean>('mock', false); defineProps<{ note: Misskey.entities.Note; }>(); - -const mock = inject<boolean>('mock', false); </script> <style lang="scss" module> From 4d9db395ebba5febd3d4efe8809db3f27afa99ca Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 4 Nov 2023 01:33:23 +0900 Subject: [PATCH 245/501] migrate push --- .../backend/migration/1698907074200-gorillamode.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/backend/migration/1698907074200-gorillamode.js diff --git a/packages/backend/migration/1698907074200-gorillamode.js b/packages/backend/migration/1698907074200-gorillamode.js new file mode 100644 index 0000000000..6d7d747407 --- /dev/null +++ b/packages/backend/migration/1698907074200-gorillamode.js @@ -0,0 +1,13 @@ +export class Gorillamode1698907074200 { + name = 'Gorillamode1698907074200' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" ADD "isGorilla" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`COMMENT ON COLUMN "user"."isGorilla" IS 'Whether the User is a gorilla.'`); + } + + async down(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "user"."isGorilla" IS 'Whether the User is a gorilla.'`); + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "isGorilla"`); + } +} From ec64c9da11f14a8a0284aaab5f94e6578cf023de Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 4 Nov 2023 01:36:32 +0900 Subject: [PATCH 246/501] test --- packages/frontend/src/components/MkNoteHeader.vue | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index 23d46b86ec..7c5ac1f9ee 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -14,12 +14,13 @@ SPDX-License-Identifier: AGPL-3.0-only <img v-for="role in note.user.badgeRoles" :key="role.id" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl"/> </div> <div :class="$style.info"> - <div v-if="mock" :class="$style.name"> - <MkUserName :user="note.user"/> - </div> - <MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)"> - <MkUserName :user="note.user"/> - </MkA> + <span v-if="note.updatedAt" style="margin-right: 0.5em;" :title="i18n.ts.edited"><i class="ti ti-pencil"></i></span> + <div v-if="mock"> + <MkTime :time="note.createdAt" colored/> + </div> + <MkA v-else :to="notePage(note)"> + <MkTime :time="note.createdAt" colored/> + </MkA> <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> <i v-if="note.visibility === 'home'" class="ti ti-home"></i> <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> From 9d020798903ef349ef27cb48685f57e20c78189c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 4 Nov 2023 10:12:13 +0900 Subject: [PATCH 247/501] uhoize fix --- .../frontend/src/pages/settings/profile.vue | 2 +- packages/frontend/src/scripts/uhoize.ts | 24 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue index 1ecde64d05..a5f1a15577 100644 --- a/packages/frontend/src/pages/settings/profile.vue +++ b/packages/frontend/src/pages/settings/profile.vue @@ -218,7 +218,7 @@ function save() { claimAchievement('markedAsCat'); } if (profile.isGorilla) { - claimAchievement('markedAsCat'); + claimAchievement('markedAsGorilla'); } } diff --git a/packages/frontend/src/scripts/uhoize.ts b/packages/frontend/src/scripts/uhoize.ts index 25d3760722..e0c5572802 100644 --- a/packages/frontend/src/scripts/uhoize.ts +++ b/packages/frontend/src/scripts/uhoize.ts @@ -1,8 +1,23 @@ +function uhoize(str) { + const punctuation = ['。', '!', '?', '.', '!', '?']; + let lines = str.split('\n'); + let voice = 'ウホ'; + return lines.map(line => { + if (Math.floor(Math.random() * 2) === 0) { + voice = 'ウホッ' + } else { + voice = 'ウホ' + } + let lastChar = line.trim().slice(-1); + if (punctuation.includes(lastChar)) { + let lineWithoutPunctuation = line.trim().slice(0, -1); + return lineWithoutPunctuation + voice + lastChar; + } else { + return line + voice; + } + }).join('\n'); +} /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - export function uhoize(text) { const gorillaNoises = ['ウホ', 'ウホホ', 'ウホッ']; let result = ''; @@ -26,3 +41,4 @@ export function uhoize(text) { } return result; } +*/ From 90a28a07796ee97925ccb872021fa718373eed57 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 4 Nov 2023 10:15:23 +0900 Subject: [PATCH 248/501] =?UTF-8?q?export=20=E3=82=8F=E3=81=99=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/uhoize.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/scripts/uhoize.ts b/packages/frontend/src/scripts/uhoize.ts index e0c5572802..ffe799d1a8 100644 --- a/packages/frontend/src/scripts/uhoize.ts +++ b/packages/frontend/src/scripts/uhoize.ts @@ -1,4 +1,4 @@ -function uhoize(str) { +export function uhoize(str) { const punctuation = ['。', '!', '?', '.', '!', '?']; let lines = str.split('\n'); let voice = 'ウホ'; From 435ad91ed6f22e40c028b1b12824555df7cc7c91 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 5 Nov 2023 11:15:26 +0900 Subject: [PATCH 249/501] =?UTF-8?q?README.md=20=E3=82=92=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 93 +++++++++---------------------------------------------- 1 file changed, 15 insertions(+), 78 deletions(-) diff --git a/README.md b/README.md index ab4388c2eb..80a3c6ce19 100644 --- a/README.md +++ b/README.md @@ -1,78 +1,15 @@ -<div align="center"> -<a href="https://misskey-hub.net"> - <img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/> -</a> - -**🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀** - ---- - -<a href="https://misskey-hub.net/instances.html"> - <img src="https://custom-icon-badges.herokuapp.com/badge/find_an-instance-acea31?logoColor=acea31&style=for-the-badge&logo=misskey&labelColor=363B40" alt="find an instance"/></a> - -<a href="https://misskey-hub.net/docs/install.html"> - <img src="https://custom-icon-badges.herokuapp.com/badge/create_an-instance-FBD53C?logoColor=FBD53C&style=for-the-badge&logo=server&labelColor=363B40" alt="create an instance"/></a> - -<a href="./CONTRIBUTING.md"> - <img src="https://custom-icon-badges.herokuapp.com/badge/become_a-contributor-A371F7?logoColor=A371F7&style=for-the-badge&logo=git-merge&labelColor=363B40" alt="become a contributor"/></a> - -<a href="https://discord.gg/Wp8gVStHW3"> - <img src="https://custom-icon-badges.herokuapp.com/badge/join_the-community-5865F2?logoColor=5865F2&style=for-the-badge&logo=discord&labelColor=363B40" alt="join the community"/></a> - -<a href="https://www.patreon.com/syuilo"> - <img src="https://custom-icon-badges.herokuapp.com/badge/become_a-patron-F96854?logoColor=F96854&style=for-the-badge&logo=patreon&labelColor=363B40" alt="become a patron"/></a> - ---- - -[![codecov](https://codecov.io/gh/misskey-dev/misskey/branch/develop/graph/badge.svg?token=R6IQZ3QJOL)](https://codecov.io/gh/misskey-dev/misskey) - -</div> - -<div> - -<a href="https://xn--931a.moe/"><img src="https://github.com/misskey-dev/misskey/blob/develop/assets/ai.png?raw=true" align="right" height="320px"/></a> - -## ✨ Features -- **ActivityPub support**\ -Not on Misskey? No problem! Not only can Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed! -- **Reactions**\ -You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button. -- **Drive**\ -With Misskey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made! -- **Rich Web UI**\ - Misskey has a rich and easy to use Web UI! - It is highly customizable, from changing the layout and adding widgets to making custom themes. - Furthermore, plugins can be created using AiScript, an original programming language. -- And much more... - -</div> - -<div style="clear: both;"></div> - -## Documentation - -Misskey Documentation can be found at [Misskey Hub](https://misskey-hub.net/), some of the links and graphics above also lead to specific portions of it. - -## Sponsors - -<div align="center"> - <a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank"><img src="https://rss3.mypinata.cloud/ipfs/QmUG6H3Z7D5P511shn7sB4CPmpjH5uZWu4m5mWX7U3Gqbu" alt="RSS3" height="60"></a> -</div> - -## Thanks - -<a href="https://www.chromatic.com/"><img src="https://user-images.githubusercontent.com/321738/84662277-e3db4f80-af1b-11ea-88f5-91d67a5e59f6.png" height="30" alt="Chromatic" /></a> - -Thanks to [Chromatic](https://www.chromatic.com/) for providing the visual testing platform that helps us review UI changes and catch visual regressions. - -<a href="https://about.codecov.io/for/open-source/"><img src="https://about.codecov.io/wp-content/themes/codecov/assets/brand/sentry-cobranding/logos/codecov-by-sentry-logo.svg" height="30" alt="Codecov" /></a> - -Thanks to [Codecov](https://about.codecov.io/for/open-source/) for providing the code coverage platform that helps us improve our test coverage. - -<a href="https://crowdin.com/"><img src="https://user-images.githubusercontent.com/20679825/230709597-1299a011-171a-4294-a91e-355a9b37c672.svg" height="30" alt="Crowdin" /></a> - -Thanks to [Crowdin](https://crowdin.com/) for providing the localization platform that helps us translate Misskey into many languages. - -<a href="https://hub.docker.com/"><img src="https://user-images.githubusercontent.com/20679825/230148221-f8e73a32-a49b-47c3-9029-9a15c3824f92.png" height="30" alt="Docker" /></a> - -Thanks to [Docker](https://hub.docker.com/) for providing the container platform that helps us run Misskey in production. +<div align="center"> +<a href="https://misskey-hub.net"> + <img src="./assets/title_float.svg" alt="Misskey logo" style="border-radius:50%" width="400"/> +</a> + +**🌎 **[Misskey](https://misskey-hub.net/)** is an open source, decentralized social media platform that's free forever! 🚀** + + +--- +</div> + +# 当フォークについて + +当フォークは PrisMisskey.space で使用しているフォークになります。 +このコードを一部でも使用する場合はクレジット表示をお願いします。 From e133a6b6a4bee78c2598deaad087712b2e8e26ed Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Mon, 6 Nov 2023 18:15:29 +0900 Subject: [PATCH 250/501] =?UTF-8?q?=E3=81=84=E3=81=A1=E3=81=8A=E3=81=86?= =?UTF-8?q?=E5=8B=95=E3=81=8F=E3=82=88=E3=81=86=E3=81=AB=E3=81=AF=E3=81=AA?= =?UTF-8?q?=E3=81=A3=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + misskey-assets | 2 +- .../backend/src/core/NoteCreateService.ts | 3 +- packages/backend/src/core/QueueModule.ts | 13 +- packages/backend/src/core/QueueService.ts | 3 +- packages/backend/src/di-symbols.ts | 1 + packages/backend/src/models/NoteSchedule.ts | 29 ++ packages/backend/src/models/_.ts | 3 + .../backend/src/queue/QueueProcessorModule.ts | 2 + .../src/queue/QueueProcessorService.ts | 12 + packages/backend/src/queue/const.ts | 1 + .../ScheduleNotePostProcessorService.ts | 30 ++ packages/backend/src/queue/types.ts | 38 +- .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../server/api/endpoints/admin/queue/stats.ts | 3 +- .../api/endpoints/notes/create-schedule.ts | 367 ++++++++++++++++++ .../src/server/web/ClientServerService.ts | 13 +- .../frontend/src/components/MkPostForm.vue | 19 +- .../src/components/MkScheduleEditor.vue | 58 +++ 21 files changed, 597 insertions(+), 8 deletions(-) create mode 100644 packages/backend/src/models/NoteSchedule.ts create mode 100644 packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/create-schedule.ts create mode 100644 packages/frontend/src/components/MkScheduleEditor.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index fc6653b05b..2771364f74 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -593,6 +593,7 @@ export interface Locale { "enableInfiniteScroll": string; "visibility": string; "poll": string; + "schedule": string; "useCw": string; "enablePlayer": string; "disablePlayer": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 67a57f994c..99e4636925 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -590,6 +590,7 @@ invisibleNote: "非公開の投稿" enableInfiniteScroll: "自動でもっと見る" visibility: "公開範囲" poll: "アンケート" +schedule: "予約" useCw: "内容を隠す" enablePlayer: "プレイヤーを開く" disablePlayer: "プレイヤーを閉じる" diff --git a/misskey-assets b/misskey-assets index 0179793ec8..cf3ce27b2e 160000 --- a/misskey-assets +++ b/misskey-assets @@ -1 +1 @@ -Subproject commit 0179793ec891856d6f37a3be16ba4c22f67a81b5 +Subproject commit cf3ce27b2eb8417233072e3d6d2fb7c5356c2364 diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index acd11a9fa7..da384f7de4 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -57,6 +57,7 @@ import { FeaturedService } from '@/core/FeaturedService.js'; import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; +import { MiNoteSchedule } from '@/models/_.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -131,6 +132,7 @@ type Option = { renote?: MiNote | null; files?: MiDriveFile[] | null; poll?: IPoll | null; + schedule?: MiNoteSchedule | null; localOnly?: boolean | null; reactionAcceptance?: MiNote['reactionAcceptance']; cw?: string | null; @@ -367,7 +369,6 @@ export class NoteCreateService implements OnApplicationShutdown { data.visibleUsers.push(await this.usersRepository.findOneByOrFail({ id: data.reply!.userId })); } } - const note = await this.insertNote(user, data, tags, emojis, mentionedUsers); setImmediate('post created', { signal: this.#shutdownController.signal }).then( diff --git a/packages/backend/src/core/QueueModule.ts b/packages/backend/src/core/QueueModule.ts index 4444dc9787..abd35afee2 100644 --- a/packages/backend/src/core/QueueModule.ts +++ b/packages/backend/src/core/QueueModule.ts @@ -10,10 +10,11 @@ import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { QUEUE, baseQueueOptions } from '@/queue/const.js'; import type { Provider } from '@nestjs/common'; -import type { DeliverJobData, InboxJobData, EndedPollNotificationJobData, WebhookDeliverJobData, RelationshipJobData } from '../queue/types.js'; +import type { DeliverJobData, InboxJobData, EndedPollNotificationJobData, WebhookDeliverJobData, RelationshipJobData, ScheduleNotePostJobData } from '../queue/types.js'; export type SystemQueue = Bull.Queue<Record<string, unknown>>; export type EndedPollNotificationQueue = Bull.Queue<EndedPollNotificationJobData>; +export type ScheduleNotePostQueue = Bull.Queue<ScheduleNotePostJobData>; export type DeliverQueue = Bull.Queue<DeliverJobData>; export type InboxQueue = Bull.Queue<InboxJobData>; export type DbQueue = Bull.Queue; @@ -33,6 +34,12 @@ const $endedPollNotification: Provider = { inject: [DI.config], }; +const $scheduleNotePost: Provider = { + provide: 'queue:scheduleNotePost', + useFactory: (config: Config) => new Bull.Queue(QUEUE.SCHEDULE_NOTE_POST, baseQueueOptions(config, QUEUE.SCHEDULE_NOTE_POST)), + inject: [DI.config], +}; + const $deliver: Provider = { provide: 'queue:deliver', useFactory: (config: Config) => new Bull.Queue(QUEUE.DELIVER, baseQueueOptions(config, QUEUE.DELIVER)), @@ -75,6 +82,7 @@ const $webhookDeliver: Provider = { providers: [ $system, $endedPollNotification, + $scheduleNotePost, $deliver, $inbox, $db, @@ -85,6 +93,7 @@ const $webhookDeliver: Provider = { exports: [ $system, $endedPollNotification, + $scheduleNotePost, $deliver, $inbox, $db, @@ -97,6 +106,7 @@ export class QueueModule implements OnApplicationShutdown { constructor( @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:scheduleNotePost') public scheduleNotePostQueue: ScheduleNotePostQueue, @Inject('queue:deliver') public deliverQueue: DeliverQueue, @Inject('queue:inbox') public inboxQueue: InboxQueue, @Inject('queue:db') public dbQueue: DbQueue, @@ -117,6 +127,7 @@ export class QueueModule implements OnApplicationShutdown { await Promise.all([ this.systemQueue.close(), this.endedPollNotificationQueue.close(), + this.scheduleNotePostQueue.close(), this.deliverQueue.close(), this.inboxQueue.close(), this.dbQueue.close(), diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts index be378a899b..9b06884392 100644 --- a/packages/backend/src/core/QueueService.ts +++ b/packages/backend/src/core/QueueService.ts @@ -12,7 +12,7 @@ import type { Config } from '@/config.js'; import { DI } from '@/di-symbols.js'; import { bindThis } from '@/decorators.js'; import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; -import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue } from './QueueModule.js'; +import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, RelationshipQueue, SystemQueue, WebhookDeliverQueue, ScheduleNotePostQueue } from './QueueModule.js'; import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js'; import type httpSignature from '@peertube/http-signature'; import type * as Bull from 'bullmq'; @@ -25,6 +25,7 @@ export class QueueService { @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:scheduleNotePost') public ScheduleNotePostQueue: ScheduleNotePostQueue, @Inject('queue:deliver') public deliverQueue: DeliverQueue, @Inject('queue:inbox') public inboxQueue: InboxQueue, @Inject('queue:db') public dbQueue: DbQueue, diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 8411cb8229..545c3183fb 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -15,6 +15,7 @@ export const DI = { //#region Repositories usersRepository: Symbol('usersRepository'), notesRepository: Symbol('notesRepository'), + noteScheduleRepository: Symbol('noteScheduleRepository'), announcementsRepository: Symbol('announcementsRepository'), announcementReadsRepository: Symbol('announcementReadsRepository'), appsRepository: Symbol('appsRepository'), diff --git a/packages/backend/src/models/NoteSchedule.ts b/packages/backend/src/models/NoteSchedule.ts new file mode 100644 index 0000000000..557133e8fa --- /dev/null +++ b/packages/backend/src/models/NoteSchedule.ts @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; +import { noteVisibilities } from '@/types.js'; +import { MiNote } from '@/models/Note.js'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; +import { MiChannel } from './Channel.js'; +import type { MiDriveFile } from './DriveFile.js'; + +@Entity('note_schedule') +export class MiNoteSchedule { + @PrimaryColumn(id()) + public noteId: MiNote['id']; + + @Column('string') + public note:{ apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote }; + + @Column('string') + public user: MiUser & {host: null;uri: null;}; + + @Column('timestamp with time zone', { + nullable: true, + }) + public expiresAt: Date; +} diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index d7c327f164..bc15ce2402 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -68,6 +68,7 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; +import { MiNoteSchedule } from './NoteSchedule.js'; import type { Repository } from 'typeorm'; export { @@ -104,6 +105,7 @@ export { MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, + MiNoteSchedule, MiPage, MiPageLike, MiPasswordResetRequest, @@ -171,6 +173,7 @@ export type NoteFavoritesRepository = Repository<MiNoteFavorite>; export type NoteReactionsRepository = Repository<MiNoteReaction>; export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting>; export type NoteUnreadsRepository = Repository<MiNoteUnread>; +export type NoteScheduleRepository = Repository<MiNoteSchedule>; export type PagesRepository = Repository<MiPage>; export type PageLikesRepository = Repository<MiPageLike>; export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest>; diff --git a/packages/backend/src/queue/QueueProcessorModule.ts b/packages/backend/src/queue/QueueProcessorModule.ts index e6327002c5..d17920efa4 100644 --- a/packages/backend/src/queue/QueueProcessorModule.ts +++ b/packages/backend/src/queue/QueueProcessorModule.ts @@ -10,6 +10,7 @@ import { QueueLoggerService } from './QueueLoggerService.js'; import { QueueProcessorService } from './QueueProcessorService.js'; import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js'; +import { ScheduleNotePostProcessorService } from './processors/ScheduleNotePostProcessorService.js'; import { InboxProcessorService } from './processors/InboxProcessorService.js'; import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMutingsProcessorService.js'; @@ -71,6 +72,7 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor RelationshipProcessorService, WebhookDeliverProcessorService, EndedPollNotificationProcessorService, + ScheduleNotePostProcessorService, DeliverProcessorService, InboxProcessorService, AggregateRetentionProcessorService, diff --git a/packages/backend/src/queue/QueueProcessorService.ts b/packages/backend/src/queue/QueueProcessorService.ts index 5201bfed8e..572fb59ba3 100644 --- a/packages/backend/src/queue/QueueProcessorService.ts +++ b/packages/backend/src/queue/QueueProcessorService.ts @@ -11,6 +11,7 @@ import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { WebhookDeliverProcessorService } from './processors/WebhookDeliverProcessorService.js'; import { EndedPollNotificationProcessorService } from './processors/EndedPollNotificationProcessorService.js'; +import { ScheduleNotePostProcessorService } from './processors/ScheduleNotePostProcessorService.js'; import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; import { InboxProcessorService } from './processors/InboxProcessorService.js'; import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; @@ -78,6 +79,7 @@ export class QueueProcessorService implements OnApplicationShutdown { private relationshipQueueWorker: Bull.Worker; private objectStorageQueueWorker: Bull.Worker; private endedPollNotificationQueueWorker: Bull.Worker; + private schedulerNotePostQueueWorker: Bull.Worker; constructor( @Inject(DI.config) @@ -86,6 +88,7 @@ export class QueueProcessorService implements OnApplicationShutdown { private queueLoggerService: QueueLoggerService, private webhookDeliverProcessorService: WebhookDeliverProcessorService, private endedPollNotificationProcessorService: EndedPollNotificationProcessorService, + private scheduleNotePostProcessorService: ScheduleNotePostProcessorService, private deliverProcessorService: DeliverProcessorService, private inboxProcessorService: InboxProcessorService, private deleteDriveFilesProcessorService: DeleteDriveFilesProcessorService, @@ -320,6 +323,13 @@ export class QueueProcessorService implements OnApplicationShutdown { .on('stalled', (jobId) => objectStorageLogger.warn(`stalled id=${jobId}`)); //#endregion + //#region schedule note post + this.schedulerNotePostQueueWorker = new Bull.Worker(QUEUE.SCHEDULE_NOTE_POST, (job) => this.scheduleNotePostProcessorService.process(job), { + ...baseQueueOptions(this.config, QUEUE.SCHEDULE_NOTE_POST), + autorun: false, + }); + //#endregion + //#region ended poll notification this.endedPollNotificationQueueWorker = new Bull.Worker(QUEUE.ENDED_POLL_NOTIFICATION, (job) => this.endedPollNotificationProcessorService.process(job), { ...baseQueueOptions(this.config, QUEUE.ENDED_POLL_NOTIFICATION), @@ -339,6 +349,7 @@ export class QueueProcessorService implements OnApplicationShutdown { this.relationshipQueueWorker.run(), this.objectStorageQueueWorker.run(), this.endedPollNotificationQueueWorker.run(), + this.schedulerNotePostQueueWorker.run(), ]); } @@ -353,6 +364,7 @@ export class QueueProcessorService implements OnApplicationShutdown { this.relationshipQueueWorker.close(), this.objectStorageQueueWorker.close(), this.endedPollNotificationQueueWorker.close(), + this.schedulerNotePostQueueWorker.close(), ]); } diff --git a/packages/backend/src/queue/const.ts b/packages/backend/src/queue/const.ts index 87d075304d..fb08da9b1d 100644 --- a/packages/backend/src/queue/const.ts +++ b/packages/backend/src/queue/const.ts @@ -11,6 +11,7 @@ export const QUEUE = { INBOX: 'inbox', SYSTEM: 'system', ENDED_POLL_NOTIFICATION: 'endedPollNotification', + SCHEDULE_NOTE_POST: 'scheduleNotePost', DB: 'db', RELATIONSHIP: 'relationship', OBJECT_STORAGE: 'objectStorage', diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts new file mode 100644 index 0000000000..b4e3e0686f --- /dev/null +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import type Logger from '@/logger.js'; +import { bindThis } from '@/decorators.js'; +import { NoteCreateService } from '@/core/NoteCreateService.js'; +import { QueueLoggerService } from '../QueueLoggerService.js'; +import type * as Bull from 'bullmq'; +import type { ScheduleNotePostJobData } from '../types.js'; + +@Injectable() +export class ScheduleNotePostProcessorService { + private logger: Logger; + + constructor( + private noteCreateService: NoteCreateService, + private queueLoggerService: QueueLoggerService, + ) { + this.logger = this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); + } + + @bindThis + public async process(job: Bull.Job<ScheduleNotePostJobData>): Promise<void> { + job.data.note.createdAt = new Date(); + await this.noteCreateService.create(job.data.user, job.data.note); + } +} diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 9330c01528..9a0608dab1 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -6,9 +6,13 @@ import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiNote } from '@/models/Note.js'; -import type { MiUser } from '@/models/User.js'; +import type { MiLocalUser, MiUser } from '@/models/User.js'; import type { MiWebhook } from '@/models/Webhook.js'; import type { IActivity } from '@/core/activitypub/type.js'; +import { IPoll } from '@/models/Poll.js'; +import { MiNoteSchedule } from '@/models/NoteSchedule.js'; +import { MiChannel } from '@/models/Channel.js'; +import { MiApp } from '@/models/App.js'; import type httpSignature from '@peertube/http-signature'; export type DeliverJobData = { @@ -104,6 +108,38 @@ export type EndedPollNotificationJobData = { noteId: MiNote['id']; }; +export type ScheduleNotePostJobData = { + note: { + name?: string | null; + text?: string | null; + reply?: MiNote | null; + renote?: MiNote | null; + files?: MiDriveFile[] | null; + poll?: IPoll | null; + schedule?: MiNoteSchedule | null; + localOnly?: boolean | null; + reactionAcceptance?: MiNote['reactionAcceptance']; + cw?: string | null; + visibility?: string; + visibleUsers?: MinimumUser[] | null; + channel?: MiChannel | null; + apMentions?: MinimumUser[] | null; + apHashtags?: string[] | null; + apEmojis?: string[] | null; + uri?: string | null; + url?: string | null; + app?: MiApp | null; + }; + user: MiUser & {host: null, uri: null}; +} + +type MinimumUser = { + id: MiUser['id']; + host: MiUser['host']; + username: MiUser['username']; + uri: MiUser['uri']; +}; + export type WebhookDeliverJobData = { type: string; content: unknown; diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 23067a9b26..4addba5aa0 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -263,6 +263,7 @@ import * as ep___notes_children from './endpoints/notes/children.js'; import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; +import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; @@ -621,6 +622,7 @@ const $notes_children: Provider = { provide: 'ep:notes/children', useClass: ep__ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }; const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; +const $notes_schedule_create: Provider = { provide: 'ep:notes/create-schedule', useClass: ep___notes_schedule_create.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; @@ -983,6 +985,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_clips, $notes_conversation, $notes_create, + $notes_schedule_create, $notes_delete, $notes_favorites_create, $notes_favorites_delete, @@ -1339,6 +1342,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_clips, $notes_conversation, $notes_create, + $notes_schedule_create, $notes_delete, $notes_favorites_create, $notes_favorites_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index af798fd166..1e3c0bb0e0 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -263,6 +263,7 @@ import * as ep___notes_children from './endpoints/notes/children.js'; import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; +import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; @@ -619,6 +620,7 @@ const eps = [ ['notes/clips', ep___notes_clips], ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], + ['notes/create-schedule', ep___notes_schedule_create], ['notes/delete', ep___notes_delete], ['notes/favorites/create', ep___notes_favorites_create], ['notes/favorites/delete', ep___notes_favorites_delete], diff --git a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts index 901195e9a5..2f09e70b62 100644 --- a/packages/backend/src/server/api/endpoints/admin/queue/stats.ts +++ b/packages/backend/src/server/api/endpoints/admin/queue/stats.ts @@ -5,7 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js'; +import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue, ScheduleNotePostQueue } from '@/core/QueueModule.js'; export const meta = { tags: ['admin'], @@ -48,6 +48,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:scheduleNotePost') public scheduleNotePostQueue: ScheduleNotePostQueue, @Inject('queue:deliver') public deliverQueue: DeliverQueue, @Inject('queue:inbox') public inboxQueue: InboxQueue, @Inject('queue:db') public dbQueue: DbQueue, diff --git a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts b/packages/backend/src/server/api/endpoints/notes/create-schedule.ts new file mode 100644 index 0000000000..3fbfaf36fa --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/create-schedule.ts @@ -0,0 +1,367 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { DataSource, In } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { MiUser } from '@/models/User.js'; +import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/_.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiChannel } from '@/models/Channel.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { NoteCreateService } from '@/core/NoteCreateService.js'; +import { DI } from '@/di-symbols.js'; +import { isPureRenote } from '@/misc/is-pure-renote.js'; +import { QueueService } from '@/core/QueueService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + + prohibitMoved: true, + + limit: { + duration: ms('1hour'), + max: 300, + }, + + kind: 'write:notes', + + errors: { + noSuchRenoteTarget: { + message: 'No such renote target.', + code: 'NO_SUCH_RENOTE_TARGET', + id: 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4', + }, + + cannotReRenote: { + message: 'You can not Renote a pure Renote.', + code: 'CANNOT_RENOTE_TO_A_PURE_RENOTE', + id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a', + }, + + cannotRenoteDueToVisibility: { + message: 'You can not Renote due to target visibility.', + code: 'CANNOT_RENOTE_DUE_TO_VISIBILITY', + id: 'be9529e9-fe72-4de0-ae43-0b363c4938af', + }, + + noSuchReplyTarget: { + message: 'No such reply target.', + code: 'NO_SUCH_REPLY_TARGET', + id: '749ee0f6-d3da-459a-bf02-282e2da4292c', + }, + + cannotReplyToPureRenote: { + message: 'You can not reply to a pure Renote.', + code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', + id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15', + }, + + cannotCreateAlreadyExpiredPoll: { + message: 'Poll is already expired.', + code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', + id: '04da457d-b083-4055-9082-955525eda5a5', + }, + + cannotCreateAlreadyExpiredSchedule: { + message: 'Schedule is already expired.', + code: 'CANNOT_CREATE_ALREADY_EXPIRED_SCHEDULE', + id: '8a9bfb90-fc7e-4878-a3e8-d97faaf5fb07', + }, + + noSuchChannel: { + message: 'No such channel.', + code: 'NO_SUCH_CHANNEL', + id: 'b1653923-5453-4edc-b786-7c4f39bb0bbb', + }, + noSuchSchedule: { + message: 'No such schedule.', + code: 'NO_SUCH_SCHEDULE', + id: '44dee229-8da1-4a61-856d-e3a4bbc12032', + }, + youHaveBeenBlocked: { + message: 'You have been blocked by this user.', + code: 'YOU_HAVE_BEEN_BLOCKED', + id: 'b390d7e1-8a5e-46ed-b625-06271cafd3d3', + }, + + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, + + cannotRenoteOutsideOfChannel: { + message: 'Cannot renote outside of channel.', + code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', + id: '33510210-8452-094c-6227-4a6c05d99f00', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, + visibleUserIds: { type: 'array', uniqueItems: true, items: { + type: 'string', format: 'misskey:id', + } }, + cw: { type: 'string', nullable: true, minLength: 1, maxLength: 100 }, + localOnly: { type: 'boolean', default: false }, + reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, + noExtractMentions: { type: 'boolean', default: false }, + noExtractHashtags: { type: 'boolean', default: false }, + noExtractEmojis: { type: 'boolean', default: false }, + replyId: { type: 'string', format: 'misskey:id', nullable: true }, + renoteId: { type: 'string', format: 'misskey:id', nullable: true }, + channelId: { type: 'string', format: 'misskey:id', nullable: true }, + + // anyOf内にバリデーションを書いても最初の一つしかチェックされない + // See https://github.com/misskey-dev/misskey/pull/10082 + text: { + type: 'string', + minLength: 1, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: true, + }, + fileIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + mediaIds: { + type: 'array', + uniqueItems: true, + minItems: 1, + maxItems: 16, + items: { type: 'string', format: 'misskey:id' }, + }, + poll: { + type: 'object', + nullable: true, + properties: { + choices: { + type: 'array', + uniqueItems: true, + minItems: 2, + maxItems: 10, + items: { type: 'string', minLength: 1, maxLength: 50 }, + }, + multiple: { type: 'boolean' }, + expiresAt: { type: 'integer', nullable: true }, + expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, + }, + required: ['choices'], + }, + schedule: { + type: 'object', + nullable: false, + properties: { + expiresAt: { type: 'integer', nullable: false }, + }, + }, + }, + // (re)note with text, files and poll are optional + anyOf: [ + { required: ['text'] }, + { required: ['renoteId'] }, + { required: ['fileIds'] }, + { required: ['mediaIds'] }, + { required: ['poll'] }, + { required: ['schedule'] }, + ], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.db) + private db: DataSource, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + @Inject(DI.blockingsRepository) + private blockingsRepository: BlockingsRepository, + + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + + @Inject(DI.channelsRepository) + private channelsRepository: ChannelsRepository, + + private noteEntityService: NoteEntityService, + private queueService: QueueService, + private noteCreateService: NoteCreateService, + ) { + super(meta, paramDef, async (ps, me) => { + let visibleUsers: MiUser[] = []; + if (ps.visibleUserIds) { + visibleUsers = await this.usersRepository.findBy({ + id: In(ps.visibleUserIds), + }); + } + + let files: MiDriveFile[] = []; + const fileIds = ps.fileIds ?? ps.mediaIds ?? null; + if (fileIds != null) { + files = await this.driveFilesRepository.createQueryBuilder('file') + .where('file.userId = :userId AND file.id IN (:...fileIds)', { + userId: me.id, + fileIds, + }) + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') + .setParameters({ fileIds }) + .getMany(); + + if (files.length !== fileIds.length) { + throw new ApiError(meta.errors.noSuchFile); + } + } + + let renote: MiNote | null = null; + if (ps.renoteId != null) { + // Fetch renote to note + renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); + + if (renote == null) { + throw new ApiError(meta.errors.noSuchRenoteTarget); + } else if (isPureRenote(renote)) { + throw new ApiError(meta.errors.cannotReRenote); + } + + // Check blocking + if (renote.userId !== me.id) { + const blockExist = await this.blockingsRepository.exist({ + where: { + blockerId: renote.userId, + blockeeId: me.id, + }, + }); + if (blockExist) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } + + if (renote.visibility === 'followers' && renote.userId !== me.id) { + // 他人のfollowers noteはreject + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + } else if (renote.visibility === 'specified') { + // specified / direct noteはreject + throw new ApiError(meta.errors.cannotRenoteDueToVisibility); + } + + if (renote.channelId && renote.channelId !== ps.channelId) { + // チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック + // リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する + const renoteChannel = await this.channelsRepository.findOneById(renote.channelId); + if (renoteChannel == null) { + // リノートしたいノートが書き込まれているチャンネルが無い + throw new ApiError(meta.errors.noSuchChannel); + } else if (!renoteChannel.allowRenoteToExternal) { + // リノート作成のリクエストだが、対象チャンネルがリノート禁止だった場合 + throw new ApiError(meta.errors.cannotRenoteOutsideOfChannel); + } + } + } + + let reply: MiNote | null = null; + if (ps.replyId != null) { + // Fetch reply + reply = await this.notesRepository.findOneBy({ id: ps.replyId }); + + if (reply == null) { + throw new ApiError(meta.errors.noSuchReplyTarget); + } else if (isPureRenote(reply)) { + throw new ApiError(meta.errors.cannotReplyToPureRenote); + } + + // Check blocking + if (reply.userId !== me.id) { + const blockExist = await this.blockingsRepository.exist({ + where: { + blockerId: reply.userId, + blockeeId: me.id, + }, + }); + if (blockExist) { + throw new ApiError(meta.errors.youHaveBeenBlocked); + } + } + } + + if (ps.poll) { + if (typeof ps.poll.expiresAt === 'number') { + if (ps.poll.expiresAt < Date.now()) { + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + } + } else if (typeof ps.poll.expiredAfter === 'number') { + ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter; + } + } + + let channel: MiChannel | null = null; + if (ps.channelId != null) { + channel = await this.channelsRepository.findOneBy({ id: ps.channelId, isArchived: false }); + + if (channel == null) { + throw new ApiError(meta.errors.noSuchChannel); + } + } + if (ps.schedule) { + if (typeof ps.schedule.expiresAt === 'number') { + if (ps.schedule.expiresAt < Date.now()) { + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredSchedule); + } + } + } + + const note = { + files: files, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + text: ps.text ?? undefined, + reply, + renote, + cw: ps.cw, + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + visibility: ps.visibility, + visibleUsers, + channel, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, + }; + + if (ps.schedule && ps.schedule.expiresAt) { + const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); + await this.queueService.ScheduleNotePostQueue.add(String(delay), { + note: note, + user: me, + }, { + delay, + removeOnComplete: true, + }); + } + + return ''; + }); + } +} diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 7a2a52a982..8c2719fc6e 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -24,7 +24,16 @@ import { getNoteSummary } from '@/misc/get-note-summary.js'; import { DI } from '@/di-symbols.js'; import * as Acct from '@/misc/acct.js'; import { MetaService } from '@/core/MetaService.js'; -import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, ObjectStorageQueue, SystemQueue, WebhookDeliverQueue } from '@/core/QueueModule.js'; +import type { + DbQueue, + DeliverQueue, + EndedPollNotificationQueue, + InboxQueue, + ObjectStorageQueue, + ScheduleNotePostQueue, + SystemQueue, + WebhookDeliverQueue, +} from '@/core/QueueModule.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { PageEntityService } from '@/core/entities/PageEntityService.js'; @@ -98,6 +107,7 @@ export class ClientServerService { @Inject('queue:system') public systemQueue: SystemQueue, @Inject('queue:endedPollNotification') public endedPollNotificationQueue: EndedPollNotificationQueue, + @Inject('queue:scheduleNotePost') public scheduleNotePostQueue: ScheduleNotePostQueue, @Inject('queue:deliver') public deliverQueue: DeliverQueue, @Inject('queue:inbox') public inboxQueue: InboxQueue, @Inject('queue:db') public dbQueue: DbQueue, @@ -214,6 +224,7 @@ export class ClientServerService { queues: [ this.systemQueue, this.endedPollNotificationQueue, + this.scheduleNotePostQueue, this.deliverQueue, this.inboxQueue, this.dbQueue, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index c0fd1c14d7..8e2e9f6d8a 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -73,6 +73,7 @@ SPDX-License-Identifier: AGPL-3.0-only <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/> <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> + <MkScheduleEditor v-if="schedule" v-model="schedule" @destroyed="schedule = null"/> <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :user="postAccount ?? $i"/> <div v-if="showingOptions" style="padding: 8px 16px;"> </div> @@ -80,6 +81,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.footerLeft"> <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> + <button v-tooltip="i18n.ts.schedule" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: schedule }]" @click="toggleSchedule"><i class="ti ti-calendar-event"></i></button> <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> @@ -124,6 +126,7 @@ import { deepClone } from '@/scripts/clone.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement } from '@/scripts/achievements.js'; +import MkScheduleEditor from '@/components/MkScheduleEditor.vue'; const modal = inject('modal'); @@ -176,6 +179,9 @@ let poll = $ref<{ expiresAt: string | null; expiredAfter: string | null; } | null>(null); +let schedule = $ref<{ + expiresAt: string | null; +}| null>(null); let useCw = $ref(false); let showPreview = $ref(defaultStore.state.showPreview); watch($$(showPreview), () => defaultStore.set('showPreview', showPreview)); @@ -395,6 +401,16 @@ function togglePoll() { } } +function toggleSchedule() { + if (schedule) { + schedule = null; + } else { + schedule = { + expiresAt: null, + }; + } +} + function addTag(tag: string) { insertTextAtCursor(textareaEl, ` #${tag} `); } @@ -734,6 +750,7 @@ async function post(ev?: MouseEvent) { replyId: props.reply ? props.reply.id : undefined, renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, channelId: props.channel ? props.channel.id : undefined, + schedule: schedule, poll: poll, cw: useCw ? cw ?? '' : null, localOnly: localOnly, @@ -762,7 +779,7 @@ async function post(ev?: MouseEvent) { } posting = true; - os.api('notes/create', postData, token).then(() => { + os.api(postData.schedule ? 'notes/create-schedule' : 'notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted = true; } else { diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue new file mode 100644 index 0000000000..f6c1bdde46 --- /dev/null +++ b/packages/frontend/src/components/MkScheduleEditor.vue @@ -0,0 +1,58 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div style="padding: 8px 16px"> + <section> + <MkInput v-model="atDate" small type="date" class="input"> + <template #label>{{ i18n.ts._poll.deadlineDate }}</template> + </MkInput> + <MkInput v-model="atTime" small type="time" class="input"> + <template #label>{{ i18n.ts._poll.deadlineTime }}</template> + </MkInput> + </section> +</div> +</template> + +<script lang="ts" setup> +import { ref, watch } from 'vue'; +import MkInput from './MkInput.vue'; +import { formatDateTimeString } from '@/scripts/format-time-string.js'; +import { addTime } from '@/scripts/time.js'; +import { i18n } from '@/i18n.js'; + +const props = defineProps<{ + modelValue: { + expiresAt: string; + }; +}>(); +const emit = defineEmits<{ + (ev: 'update:modelValue', v: { + expiresAt: string; + }): void; +}>(); + +const atDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd')); +const atTime = ref('00:00'); +if ( props.modelValue && props.modelValue.expiresAt) { + atDate.value = atTime.value = props.modelValue.expiresAt; +} + +function get() { + const calcAt = () => { + return new Date(`${atDate.value} ${atTime.value}`).getTime(); + }; + + return { + ...( + props.modelValue ? { expiresAt: calcAt() } : {} + ), + }; +} + +watch([atDate, atTime], () => emit('update:modelValue', get()), { + deep: true, +}); +</script> From 387faf55cf8b98918bc6c83fd8377d75408d7d1a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 8 Nov 2023 15:33:46 +0900 Subject: [PATCH 251/501] =?UTF-8?q?db=E3=81=AB=E4=BF=9D=E5=AD=98=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- package.json | 2 +- .../migration/1699337454434-schedulenote.js | 11 +++ packages/backend/src/models/NoteSchedule.ts | 15 ++-- .../backend/src/models/RepositoryModule.ts | 77 ++++++++++++++++++- packages/backend/src/postgres.ts | 2 + .../ScheduleNotePostProcessorService.ts | 20 ++++- packages/backend/src/queue/types.ts | 23 +----- .../api/endpoints/notes/create-schedule.ts | 52 +++++++++++-- 8 files changed, 160 insertions(+), 42 deletions(-) create mode 100644 packages/backend/migration/1699337454434-schedulenote.js diff --git a/package.json b/package.json index 0ed73f56b3..79d1e33a58 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.0", + "version": "2023.11.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/migration/1699337454434-schedulenote.js b/packages/backend/migration/1699337454434-schedulenote.js new file mode 100644 index 0000000000..213da993b0 --- /dev/null +++ b/packages/backend/migration/1699337454434-schedulenote.js @@ -0,0 +1,11 @@ +export class Schedulenote1699337454434 { + name = 'Schedulenote1699337454434' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "note_schedule" ("id" character varying(32) NOT NULL, "note" jsonb NOT NULL, "userId" character varying(260) NOT NULL, CONSTRAINT "PK_3a1ae2db41988f4994268218436" PRIMARY KEY ("id"))`); + } + + async down(queryRunner) { + await queryRunner.query(`DROP TABLE "note_schedule"`); + } +} diff --git a/packages/backend/src/models/NoteSchedule.ts b/packages/backend/src/models/NoteSchedule.ts index 557133e8fa..aec1bb52b9 100644 --- a/packages/backend/src/models/NoteSchedule.ts +++ b/packages/backend/src/models/NoteSchedule.ts @@ -14,16 +14,13 @@ import type { MiDriveFile } from './DriveFile.js'; @Entity('note_schedule') export class MiNoteSchedule { @PrimaryColumn(id()) - public noteId: MiNote['id']; + public id: string; - @Column('string') - public note:{ apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote }; + @Column('jsonb') + public note:{createdAt?: Date | undefined ; apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote }; - @Column('string') - public user: MiUser & {host: null;uri: null;}; - - @Column('timestamp with time zone', { - nullable: true, + @Column('varchar', { + length: 260, }) - public expiresAt: Date; + public userId: MiUser['id']; } diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 866fdfe6d4..61fac96349 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,74 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook } from './_.js'; +import { + MiAbuseUserReport, + MiAccessToken, + MiAd, + MiAnnouncement, + MiAnnouncementRead, + MiAntenna, + MiApp, + MiAuthSession, + MiAvatarDecoration, + MiBlocking, + MiChannel, + MiChannelFavorite, + MiChannelFollowing, + MiClip, + MiClipFavorite, + MiClipNote, + MiDriveFile, + MiDriveFolder, + MiEmoji, + MiFlash, + MiFlashLike, + MiFollowRequest, + MiFollowing, + MiGalleryLike, + MiGalleryPost, + MiHashtag, + MiInstance, + MiMeta, + MiModerationLog, + MiMuting, + MiNote, + MiNoteFavorite, + MiNoteReaction, + MiNoteThreadMuting, + MiNoteUnread, + MiPage, + MiPageLike, + MiPasswordResetRequest, + MiPoll, + MiPollVote, + MiPromoNote, + MiPromoRead, + MiRegistrationTicket, + MiRegistryItem, + MiRelay, + MiRenoteMuting, + MiRetentionAggregation, + MiRole, + MiRoleAssignment, + MiSignin, + MiSwSubscription, + MiUsedUsername, + MiUser, + MiUserIp, + MiUserKeypair, + MiUserList, + MiUserListFavorite, + MiUserListMembership, + MiUserMemo, + MiUserNotePining, + MiUserPending, + MiUserProfile, + MiUserPublickey, + MiUserSecurityKey, + MiWebhook, + MiNoteSchedule, +} from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -21,6 +88,12 @@ const $notesRepository: Provider = { inject: [DI.db], }; +const $noteScheduleRepository: Provider = { + provide: DI.noteScheduleRepository, + useFactory: (db: DataSource) => db.getRepository(MiNoteSchedule), + inject: [DI.db], +}; + const $announcementsRepository: Provider = { provide: DI.announcementsRepository, useFactory: (db: DataSource) => db.getRepository(MiAnnouncement), @@ -405,6 +478,7 @@ const $userMemosRepository: Provider = { providers: [ $usersRepository, $notesRepository, + $noteScheduleRepository, $announcementsRepository, $announcementReadsRepository, $appsRepository, @@ -472,6 +546,7 @@ const $userMemosRepository: Provider = { exports: [ $usersRepository, $notesRepository, + $noteScheduleRepository, $announcementsRepository, $announcementReadsRepository, $appsRepository, diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index cd611839a4..002b5c30aa 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -76,6 +76,7 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserMemo } from '@/models/UserMemo.js'; +import { MiNoteSchedule } from '@/models/NoteSchedule.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -149,6 +150,7 @@ export const entities = [ MiRenoteMuting, MiBlocking, MiNote, + MiNoteSchedule, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts index b4e3e0686f..6e4432f9c3 100644 --- a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -7,6 +7,8 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; +import type { NoteScheduleRepository, UsersRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; import type { ScheduleNotePostJobData } from '../types.js'; @@ -16,6 +18,12 @@ export class ScheduleNotePostProcessorService { private logger: Logger; constructor( + @Inject(DI.noteScheduleRepository) + private noteScheduleRepository: NoteScheduleRepository, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private noteCreateService: NoteCreateService, private queueLoggerService: QueueLoggerService, ) { @@ -24,7 +32,15 @@ export class ScheduleNotePostProcessorService { @bindThis public async process(job: Bull.Job<ScheduleNotePostJobData>): Promise<void> { - job.data.note.createdAt = new Date(); - await this.noteCreateService.create(job.data.user, job.data.note); + this.noteScheduleRepository.findOneBy({ id: job.data.scheduleNoteId }).then(async (data) => { + if (!data) { + this.logger.warn(`Schedule note ${job.data.scheduleNoteId} not found`); + } else { + data.note.createdAt = new Date(); + const me = await this.usersRepository.findOneByOrFail({ id: data.userId }); + await this.noteCreateService.create(me, data.note); + await this.noteScheduleRepository.remove(data); + } + }); } } diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 9a0608dab1..cc69c77d08 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -109,28 +109,7 @@ export type EndedPollNotificationJobData = { }; export type ScheduleNotePostJobData = { - note: { - name?: string | null; - text?: string | null; - reply?: MiNote | null; - renote?: MiNote | null; - files?: MiDriveFile[] | null; - poll?: IPoll | null; - schedule?: MiNoteSchedule | null; - localOnly?: boolean | null; - reactionAcceptance?: MiNote['reactionAcceptance']; - cw?: string | null; - visibility?: string; - visibleUsers?: MinimumUser[] | null; - channel?: MiChannel | null; - apMentions?: MinimumUser[] | null; - apHashtags?: string[] | null; - apEmojis?: string[] | null; - uri?: string | null; - url?: string | null; - app?: MiApp | null; - }; - user: MiUser & {host: null, uri: null}; + scheduleNoteId: MiNote['id']; } type MinimumUser = { diff --git a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts b/packages/backend/src/server/api/endpoints/notes/create-schedule.ts index 3fbfaf36fa..d3e57d6048 100644 --- a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts +++ b/packages/backend/src/server/api/endpoints/notes/create-schedule.ts @@ -7,7 +7,14 @@ import ms from 'ms'; import { DataSource, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { MiUser } from '@/models/User.js'; -import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/_.js'; +import type { + UsersRepository, + NotesRepository, + BlockingsRepository, + DriveFilesRepository, + ChannelsRepository, + NoteScheduleRepository, +} from '@/models/_.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiNote } from '@/models/Note.js'; import type { MiChannel } from '@/models/Channel.js'; @@ -18,6 +25,8 @@ import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { QueueService } from '@/core/QueueService.js'; +import { MiNoteSchedule } from '@/models/_.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -194,6 +203,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.noteScheduleRepository) + private noteScheduleRepository: NoteScheduleRepository, + @Inject(DI.blockingsRepository) private blockingsRepository: BlockingsRepository, @@ -203,9 +215,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, - private noteEntityService: NoteEntityService, private queueService: QueueService, - private noteCreateService: NoteCreateService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { let visibleUsers: MiUser[] = []; @@ -328,8 +339,28 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } } - - const note = { + type NoteType = { + createdAt?: Date | undefined; + apEmojis: any[] | undefined; + visibility: any; + apMentions: any[] | undefined; + visibleUsers: MiUser[]; + channel: null | MiChannel; + poll: { + multiple: any; + choices: any; + expiresAt: Date | null; + } | undefined; + renote: null | MiNote; + localOnly: any; + cw: any; + apHashtags: any[] | undefined; + reactionAcceptance: any; + files: MiDriveFile[]; + text: any; + reply: null | MiNote; + }; + const note:NoteType = { files: files, poll: ps.poll ? { choices: ps.poll.choices, @@ -351,10 +382,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }; if (ps.schedule && ps.schedule.expiresAt) { + me.token = null; + const noteId = this.idService.gen(new Date().getTime()); + await this.noteScheduleRepository.insert({ + id: noteId, + note: note, + userId: me.id, + }); + const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); await this.queueService.ScheduleNotePostQueue.add(String(delay), { - note: note, - user: me, + scheduleNoteId: noteId, }, { delay, removeOnComplete: true, From 14b48f87d83543d50ecd131025fb6bc371e0e158 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 8 Nov 2023 17:16:25 +0900 Subject: [PATCH 252/501] . Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 79d1e33a58..0ed73f56b3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.2", + "version": "2023.11.0", "codename": "nasubi", "repository": { "type": "git", From 540f531b6d10e5419e63012ad63a637d4d7d084a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 9 Nov 2023 16:09:51 +0900 Subject: [PATCH 253/501] =?UTF-8?q?=E4=BA=88=E7=B4=84=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=81=AE=E4=B8=80=E8=A6=A7=E8=A1=A8=E7=A4=BA=E3=80=81=E5=89=8A?= =?UTF-8?q?=E9=99=A4=E3=82=92=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=20db=E3=81=AB=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E3=82=92=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 3 +- locales/ja-JP.yml | 3 +- .../migration/1699337454434-schedulenote.js | 11 ---- .../migration/1699437894737-schedulenote.js | 12 ++++ packages/backend/src/models/NoteSchedule.ts | 4 ++ .../backend/src/server/api/EndpointsModule.ts | 8 +++ packages/backend/src/server/api/endpoints.ts | 4 ++ .../api/endpoints/notes/create-schedule.ts | 4 +- .../api/endpoints/notes/delete-schedule.ts | 49 +++++++++++++++ .../api/endpoints/notes/list-schedule.ts | 61 +++++++++++++++++++ .../frontend/src/components/MkNoteHeader.vue | 4 +- .../frontend/src/components/MkNoteSimple.vue | 33 ++++++++-- .../frontend/src/components/MkPostForm.vue | 9 ++- .../components/MkSchedulePostListDialog.vue | 50 +++++++++++++++ packages/frontend/src/os.ts | 8 ++- 15 files changed, 238 insertions(+), 25 deletions(-) delete mode 100644 packages/backend/migration/1699337454434-schedulenote.js create mode 100644 packages/backend/migration/1699437894737-schedulenote.js create mode 100644 packages/backend/src/server/api/endpoints/notes/delete-schedule.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/list-schedule.ts create mode 100644 packages/frontend/src/components/MkSchedulePostListDialog.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index 2771364f74..63bcb16e41 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -593,8 +593,9 @@ export interface Locale { "enableInfiniteScroll": string; "visibility": string; "poll": string; - "schedule": string; + "schedulePost": string; "useCw": string; + "schedulePostList": string; "enablePlayer": string; "disablePlayer": string; "expandTweet": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 99e4636925..be378180dc 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -590,8 +590,9 @@ invisibleNote: "非公開の投稿" enableInfiniteScroll: "自動でもっと見る" visibility: "公開範囲" poll: "アンケート" -schedule: "予約" +schedulePost: "予約投稿" useCw: "内容を隠す" +schedulePostList: "予約投稿一覧" enablePlayer: "プレイヤーを開く" disablePlayer: "プレイヤーを閉じる" expandTweet: "ポストを展開する" diff --git a/packages/backend/migration/1699337454434-schedulenote.js b/packages/backend/migration/1699337454434-schedulenote.js deleted file mode 100644 index 213da993b0..0000000000 --- a/packages/backend/migration/1699337454434-schedulenote.js +++ /dev/null @@ -1,11 +0,0 @@ -export class Schedulenote1699337454434 { - name = 'Schedulenote1699337454434' - - async up(queryRunner) { - await queryRunner.query(`CREATE TABLE "note_schedule" ("id" character varying(32) NOT NULL, "note" jsonb NOT NULL, "userId" character varying(260) NOT NULL, CONSTRAINT "PK_3a1ae2db41988f4994268218436" PRIMARY KEY ("id"))`); - } - - async down(queryRunner) { - await queryRunner.query(`DROP TABLE "note_schedule"`); - } -} diff --git a/packages/backend/migration/1699437894737-schedulenote.js b/packages/backend/migration/1699437894737-schedulenote.js new file mode 100644 index 0000000000..d0188a45ee --- /dev/null +++ b/packages/backend/migration/1699437894737-schedulenote.js @@ -0,0 +1,12 @@ +export class Schedulenote1699437894737 { + name = 'Schedulenote1699437894737' + + async up(queryRunner) { + await queryRunner.query(`CREATE TABLE "note_schedule" ("id" character varying(32) NOT NULL, "note" jsonb NOT NULL, "userId" character varying(260) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_3a1ae2db41988f4994268218436" PRIMARY KEY ("id"))`); + await queryRunner.query(`CREATE INDEX "IDX_e798958c40009bf0cdef4f28b5" ON "note_schedule" ("userId") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP TABLE "note_schedule"`); + } +} diff --git a/packages/backend/src/models/NoteSchedule.ts b/packages/backend/src/models/NoteSchedule.ts index aec1bb52b9..f622893ecf 100644 --- a/packages/backend/src/models/NoteSchedule.ts +++ b/packages/backend/src/models/NoteSchedule.ts @@ -19,8 +19,12 @@ export class MiNoteSchedule { @Column('jsonb') public note:{createdAt?: Date | undefined ; apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote }; + @Index() @Column('varchar', { length: 260, }) public userId: MiUser['id']; + + @Column('timestamp with time zone') + public expiresAt: Date; } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 4addba5aa0..f7dea3d907 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -264,7 +264,9 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js'; +import * as ep___notes_schedule_list from './endpoints/notes/list-schedule.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_schedule_delete from './endpoints/notes/delete-schedule.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -623,7 +625,9 @@ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; const $notes_schedule_create: Provider = { provide: 'ep:notes/create-schedule', useClass: ep___notes_schedule_create.default }; +const $notes_schedule_list: Provider = { provide: 'ep:notes/list-schedule', useClass: ep___notes_schedule_list.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; +const $notes_schedule_delete: Provider = { provide: 'ep:notes/delete-schedule', useClass: ep___notes_schedule_delete.default }; const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; @@ -986,7 +990,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_schedule_create, + $notes_schedule_list, $notes_delete, + $notes_schedule_delete, $notes_favorites_create, $notes_favorites_delete, $notes_featured, @@ -1343,7 +1349,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_schedule_create, + $notes_schedule_list, $notes_delete, + $notes_schedule_delete, $notes_favorites_create, $notes_favorites_delete, $notes_featured, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 1e3c0bb0e0..386b139256 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -264,7 +264,9 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js'; +import * as ep___notes_schedule_list from './endpoints/notes/list-schedule.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; +import * as ep___notes_schedule_delete from './endpoints/notes/delete-schedule.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -621,7 +623,9 @@ const eps = [ ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], ['notes/create-schedule', ep___notes_schedule_create], + ['notes/list-schedule', ep___notes_schedule_list], ['notes/delete', ep___notes_delete], + ['notes/delete-schedule', ep___notes_schedule_delete], ['notes/favorites/create', ep___notes_favorites_create], ['notes/favorites/delete', ep___notes_favorites_delete], ['notes/featured', ep___notes_featured], diff --git a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts b/packages/backend/src/server/api/endpoints/notes/create-schedule.ts index d3e57d6048..e9854639a8 100644 --- a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts +++ b/packages/backend/src/server/api/endpoints/notes/create-schedule.ts @@ -197,9 +197,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.db) - private db: DataSource, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -388,6 +385,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- id: noteId, note: note, userId: me.id, + expiresAt: new Date(ps.schedule.expiresAt), }); const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); diff --git a/packages/backend/src/server/api/endpoints/notes/delete-schedule.ts b/packages/backend/src/server/api/endpoints/notes/delete-schedule.ts new file mode 100644 index 0000000000..f9398576d3 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/delete-schedule.ts @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import type { NoteScheduleRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + + limit: { + duration: ms('1hour'), + max: 300, + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: '490be23f-8c1f-4796-819f-94cb4f9d1630', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + }, + required: ['noteId'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.noteScheduleRepository) + private noteScheduleRepository: NoteScheduleRepository, + ) { + super(meta, paramDef, async (ps, me) => { + await this.noteScheduleRepository.delete({ id: ps.noteId }); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/notes/list-schedule.ts b/packages/backend/src/server/api/endpoints/notes/list-schedule.ts new file mode 100644 index 0000000000..8cd670e421 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/list-schedule.ts @@ -0,0 +1,61 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import type { NoteScheduleRepository } from '@/models/_.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, + limit: { + duration: ms('1hour'), + max: 300, + }, + + errors: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.noteScheduleRepository) + private noteScheduleRepository: NoteScheduleRepository, + private userEntityService: UserEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const scheduleNotes = await this.noteScheduleRepository.findBy({ userId: me.id }); + const user = await this.userEntityService.pack(me, me); + scheduleNotes.forEach((item: any) => { + item.note.user = user; + item.note.createdAt = new Date(item.expiresAt); + item.note.isSchedule = true; + item.note.id = item.id; + }); + return scheduleNotes; + }); + } +} diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue index b2236b99c2..0d54c3838d 100644 --- a/packages/frontend/src/components/MkNoteHeader.vue +++ b/packages/frontend/src/components/MkNoteHeader.vue @@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="mock"> <MkTime :time="note.createdAt" colored/> </div> + <MkTime v-else-if="note.isSchedule" mode="absolute" :time="note.createdAt" colored/> <MkA v-else :to="notePage(note)"> <MkTime :time="note.createdAt" colored/> </MkA> @@ -42,7 +43,8 @@ import { notePage } from '@/filters/note.js'; import { userPage } from '@/filters/user.js'; defineProps<{ - note: Misskey.entities.Note; + note: Misskey.entities.Note & {isSchedule? : boolean}; + scheduled?: boolean; }>(); const mock = inject<boolean>('mock', false); diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 28b00af246..a42f674be0 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="$style.root"> +<div v-show="!isDeleted" :class="$style.root" :tabindex="!isDeleted ? '-1' : undefined"> <MkAvatar :class="$style.avatar" :user="note.user" link preview/> <div :class="$style.main"> <MkNoteHeader :class="$style.header" :note="note" :mini="true"/> @@ -16,23 +16,40 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-show="note.cw == null || showContent"> <MkSubNoteContent :class="$style.text" :note="note"/> </div> + <div v-if="note.isSchedule" style="margin-top: 10px;"> + <MkButton :class="$style.button" inline @click="editScheduleNote(note.id)">{{ i18n.ts.edit }}</MkButton> + <MkButton :class="$style.button" inline danger @click="deleteScheduleNote()">{{ i18n.ts.delete }}</MkButton> + </div> </div> </div> </div> </template> <script lang="ts" setup> -import { } from 'vue'; import * as Misskey from 'misskey-js'; +import { ref } from 'vue'; +import { i18n } from '../i18n.js'; import MkNoteHeader from '@/components/MkNoteHeader.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkCwButton from '@/components/MkCwButton.vue'; -import { $i } from '@/account.js'; - +import MkButton from '@/components/MkButton.vue'; +import * as os from '@/os.js'; +const isDeleted = ref(false); const props = defineProps<{ - note: Misskey.entities.Note; + note: Misskey.entities.Note & {isSchedule? : boolean}; }>(); +async function deleteScheduleNote() { + await os.apiWithDialog('notes/delete-schedule', { noteId: props.note.id }) + .then(() => { + isDeleted.value = true; + }); +} + +function editScheduleNote(id) { + +} + const showContent = $ref(false); </script> @@ -42,8 +59,12 @@ const showContent = $ref(false); margin: 0; padding: 0; font-size: 0.95em; + border-bottom: solid 0.5px var(--divider); +} +.button{ + margin-right: var(--margin); + margin-bottom: var(--margin); } - .avatar { flex-shrink: 0; display: block; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 8e2e9f6d8a..66a7812448 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -19,6 +19,8 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </div> <div :class="$style.headerRight"> + <button v-tooltip="i18n.ts.schedulePost" class="_button" :class="[$style.headerRightItem, { [$style.headerRightButtonActive]: schedule }]" @click="toggleSchedule"><i class="ti ti-calendar-time"></i></button> + <button v-tooltip="i18n.ts.schedulePostList" class="_button" :class="[$style.headerRightItem]" @click="listSchedulePost"><i class="ti ti-calendar-event"></i></button> <template v-if="!(channel != null && fixed)"> <button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility"> <span v-if="visibility === 'public'"><i class="ti ti-world"></i></span> @@ -81,7 +83,6 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.footerLeft"> <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> - <button v-tooltip="i18n.ts.schedule" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: schedule }]" @click="toggleSchedule"><i class="ti ti-calendar-event"></i></button> <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> @@ -127,6 +128,7 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement } from '@/scripts/achievements.js'; import MkScheduleEditor from '@/components/MkScheduleEditor.vue'; +import { listSchedulePost } from '@/os.js'; const modal = inject('modal'); @@ -1061,6 +1063,10 @@ defineExpose({ background: none; } + &.headerRightButtonActive { + color: var(--accent); + } + &.danger { color: #ff2a2a; } @@ -1214,6 +1220,7 @@ defineExpose({ grid-template-columns: repeat(auto-fill, minmax(42px, 1fr)); grid-auto-rows: 40px; direction: rtl; + } .footerButton { diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue new file mode 100644 index 0000000000..90c3769c82 --- /dev/null +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -0,0 +1,50 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkModalWindow + ref="dialogEl" + :withOkButton="false" + @click="cancel()" + @close="cancel()" + @closed="$emit('closed')" +> + <template #header> 予約投稿一覧</template> + <div v-for="item in notes"> + <MkSpacer :marginMin="14" :marginMax="16"> + <MkNoteSimple scheduled="true" :note="item.note"/> + </MkSpacer> + </div> +</MkModalWindow> +</template> + +<script lang="ts" setup> +import { onMounted, ref } from 'vue'; +import * as Misskey from 'misskey-js'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import * as os from '@/os.js'; +import MkNoteSimple from '@/components/MkNoteSimple.vue'; +import MkSignin from '@/components/MkSignin.vue'; +const emit = defineEmits<{ + (ev: 'ok', selected: Misskey.entities.UserDetailed): void; + (ev: 'cancel'): void; + (ev: 'closed'): void; +}>(); + +let dialogEl = $ref(); +const notes = ref([]); +const cancel = () => { + emit('cancel'); + dialogEl.close(); +}; + +onMounted(async () => { + notes.value = await os.api('notes/list-schedule'); +}); + +</script> + +<style lang="scss" module> +</style> diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 8093335a28..f878e62db0 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -431,7 +431,13 @@ export async function selectUser(opts: { includeSelf?: boolean } = {}) { }, 'closed'); }); } - +export async function listSchedulePost() { + return new Promise((resolve, reject) => { + popup(defineAsyncComponent(() => import('@/components/MkSchedulePostListDialog.vue')), { + }, { + }, 'closed'); + }); +} export async function selectDriveFile(multiple: boolean): Promise<Misskey.entities.DriveFile[]> { return new Promise((resolve, reject) => { popup(defineAsyncComponent(() => import('@/components/MkDriveSelectDialog.vue')), { From 12b7ec30461eee0c80abf7ecc965ed904bb0edee Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 9 Nov 2023 16:12:34 +0900 Subject: [PATCH 254/501] =?UTF-8?q?=E5=A4=89=E6=9B=B4=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../src/queue/processors/ScheduleNotePostProcessorService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts index 6e4432f9c3..d1312bea8e 100644 --- a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -27,7 +27,7 @@ export class ScheduleNotePostProcessorService { private noteCreateService: NoteCreateService, private queueLoggerService: QueueLoggerService, ) { - this.logger = this.queueLoggerService.logger.createSubLogger('ended-poll-notification'); + this.logger = this.queueLoggerService.logger.createSubLogger('schedule-note-post'); } @bindThis From d6fb3c3342da111520eda6b18837f17fb3b50b5e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 9 Nov 2023 18:35:47 +0900 Subject: [PATCH 255/501] =?UTF-8?q?=E5=9E=8B=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=A8=E3=81=A1=E3=82=87=E3=81=A3=E3=81=A8?= =?UTF-8?q?=E3=81=97=E3=81=9F=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../api/endpoints/notes/list-schedule.ts | 65 +++++++++++++++---- .../frontend/src/components/MkPostForm.vue | 3 +- 2 files changed, 56 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/list-schedule.ts b/packages/backend/src/server/api/endpoints/notes/list-schedule.ts index 8cd670e421..6efe8b6382 100644 --- a/packages/backend/src/server/api/endpoints/notes/list-schedule.ts +++ b/packages/backend/src/server/api/endpoints/notes/list-schedule.ts @@ -20,7 +20,31 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - ref: 'Note', + properties: { + id: { type: 'string', optional: false, nullable: false }, + note: { + type: 'object', + optional: false, nullable: false, + properties: { + id: { type: 'string', optional: false, nullable: false }, + text: { type: 'string', optional: false, nullable: false }, + files: { type: 'array', optional: false, nullable: false, items: { type: 'any' } }, + localOnly: { type: 'boolean', optional: false, nullable: false }, + visibility: { type: 'string', optional: false, nullable: false }, + visibleUsers: { type: 'array', optional: false, nullable: false, items: { type: 'any' } }, + reactionAcceptance: { type: 'string', optional: false, nullable: false }, + user: { + type: 'object', + optional: false, nullable: false, + ref: 'User', + }, + createdAt: { type: 'string', optional: false, nullable: false }, + isSchedule: { type: 'boolean', optional: false, nullable: false }, + }, + }, + userId: { type: 'string', optional: false, nullable: false }, + expiresAt: { type: 'string', optional: false, nullable: false }, + }, }, }, limit: { @@ -33,10 +57,6 @@ export const meta = { } as const; export const paramDef = { - type: 'object', - properties: { - }, - required: [], } as const; @Injectable() @@ -49,13 +69,36 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- super(meta, paramDef, async (ps, me) => { const scheduleNotes = await this.noteScheduleRepository.findBy({ userId: me.id }); const user = await this.userEntityService.pack(me, me); - scheduleNotes.forEach((item: any) => { - item.note.user = user; - item.note.createdAt = new Date(item.expiresAt); - item.note.isSchedule = true; - item.note.id = item.id; + const scheduleNotesPack: { + id: string; + note: { + id: string; + text: string; + files: any[]; + localOnly: boolean; + visibility: string; + visibleUsers: any[]; + reactionAcceptance: string; + user: any; + createdAt: string; + isSchedule: boolean; + }; + userId: string; + expiresAt: string; + }[] = scheduleNotes.map((item: any) => { + return { + ...item, + note: { + ...item.note, + user: user, + createdAt: new Date(item.expiresAt), + isSchedule: true, + id: item.id, + }, + }; }); - return scheduleNotes; + + return scheduleNotesPack; }); } } diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 66a7812448..6e2705f918 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -563,6 +563,7 @@ function removeVisibleUser(user) { function clear() { text = ''; + schedule = null; files = []; poll = null; quoteId = null; @@ -802,7 +803,7 @@ async function post(ev?: MouseEvent) { if (notesCount === 1) { claimAchievement('notes1'); } - + poll = null; const text = postData.text ?? ''; const lowerCase = text.toLowerCase(); if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { From 4406af58f136442a2c4d75ad07b0080bddcafcc8 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 19:12:49 +0900 Subject: [PATCH 256/501] `/api/notes/` -> `/api/notes/schedule/` --- .../backend/src/server/api/EndpointsModule.ts | 16 +++++++------- packages/backend/src/server/api/endpoints.ts | 12 +++++----- .../create.ts} | 2 +- .../delete.ts} | 0 .../{list-schedule.ts => schedule/list.ts} | 0 .../frontend/src/components/MkNoteSimple.vue | 2 +- .../frontend/src/components/MkPostForm.vue | 2 +- .../components/MkSchedulePostListDialog.vue | 2 +- packages/misskey-js/etc/misskey-js.api.md | 22 ++++++++++++++++++- packages/misskey-js/src/api.types.ts | 3 +++ 10 files changed, 42 insertions(+), 19 deletions(-) rename packages/backend/src/server/api/endpoints/notes/{create-schedule.ts => schedule/create.ts} (99%) rename packages/backend/src/server/api/endpoints/notes/{delete-schedule.ts => schedule/delete.ts} (100%) rename packages/backend/src/server/api/endpoints/notes/{list-schedule.ts => schedule/list.ts} (100%) diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index f7dea3d907..cb9a17fdb6 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -263,10 +263,10 @@ import * as ep___notes_children from './endpoints/notes/children.js'; import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; -import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js'; -import * as ep___notes_schedule_list from './endpoints/notes/list-schedule.js'; +import * as ep___notes_schedule_create from './endpoints/notes/schedule/create.js'; +import * as ep___notes_schedule_delete from './endpoints/notes/schedule/delete.js'; +import * as ep___notes_schedule_list from './endpoints/notes/schedule/list.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; -import * as ep___notes_schedule_delete from './endpoints/notes/delete-schedule.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -624,10 +624,10 @@ const $notes_children: Provider = { provide: 'ep:notes/children', useClass: ep__ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }; const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; -const $notes_schedule_create: Provider = { provide: 'ep:notes/create-schedule', useClass: ep___notes_schedule_create.default }; -const $notes_schedule_list: Provider = { provide: 'ep:notes/list-schedule', useClass: ep___notes_schedule_list.default }; +const $notes_schedule_create: Provider = { provide: 'ep:notes/schedule/create', useClass: ep___notes_schedule_create.default }; +const $notes_schedule_delete: Provider = { provide: 'ep:notes/schedule/delete', useClass: ep___notes_schedule_delete.default }; +const $notes_schedule_list: Provider = { provide: 'ep:notes/schedule/list', useClass: ep___notes_schedule_list.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; -const $notes_schedule_delete: Provider = { provide: 'ep:notes/delete-schedule', useClass: ep___notes_schedule_delete.default }; const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; @@ -990,9 +990,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_schedule_create, + $notes_schedule_delete, $notes_schedule_list, $notes_delete, - $notes_schedule_delete, $notes_favorites_create, $notes_favorites_delete, $notes_featured, @@ -1349,9 +1349,9 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_conversation, $notes_create, $notes_schedule_create, + $notes_schedule_delete, $notes_schedule_list, $notes_delete, - $notes_schedule_delete, $notes_favorites_create, $notes_favorites_delete, $notes_featured, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 386b139256..6010589093 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -263,10 +263,10 @@ import * as ep___notes_children from './endpoints/notes/children.js'; import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; -import * as ep___notes_schedule_create from './endpoints/notes/create-schedule.js'; -import * as ep___notes_schedule_list from './endpoints/notes/list-schedule.js'; +import * as ep___notes_schedule_create from './endpoints/notes/schedule/create.js'; +import * as ep___notes_schedule_delete from './endpoints/notes/schedule/delete.js'; +import * as ep___notes_schedule_list from './endpoints/notes/schedule/list.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; -import * as ep___notes_schedule_delete from './endpoints/notes/delete-schedule.js'; import * as ep___notes_favorites_create from './endpoints/notes/favorites/create.js'; import * as ep___notes_favorites_delete from './endpoints/notes/favorites/delete.js'; import * as ep___notes_featured from './endpoints/notes/featured.js'; @@ -622,10 +622,10 @@ const eps = [ ['notes/clips', ep___notes_clips], ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], - ['notes/create-schedule', ep___notes_schedule_create], - ['notes/list-schedule', ep___notes_schedule_list], + ['notes/schedule/create', ep___notes_schedule_create], + ['notes/schedule/delete', ep___notes_schedule_delete], + ['notes/schedule/list', ep___notes_schedule_list], ['notes/delete', ep___notes_delete], - ['notes/delete-schedule', ep___notes_schedule_delete], ['notes/favorites/create', ep___notes_favorites_create], ['notes/favorites/delete', ep___notes_favorites_delete], ['notes/featured', ep___notes_featured], diff --git a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts similarity index 99% rename from packages/backend/src/server/api/endpoints/notes/create-schedule.ts rename to packages/backend/src/server/api/endpoints/notes/schedule/create.ts index e9854639a8..37c6bd36a6 100644 --- a/packages/backend/src/server/api/endpoints/notes/create-schedule.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts @@ -27,7 +27,7 @@ import { isPureRenote } from '@/misc/is-pure-renote.js'; import { QueueService } from '@/core/QueueService.js'; import { MiNoteSchedule } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; -import { ApiError } from '../../error.js'; +import { ApiError } from '../../../error.js'; export const meta = { tags: ['notes'], diff --git a/packages/backend/src/server/api/endpoints/notes/delete-schedule.ts b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts similarity index 100% rename from packages/backend/src/server/api/endpoints/notes/delete-schedule.ts rename to packages/backend/src/server/api/endpoints/notes/schedule/delete.ts diff --git a/packages/backend/src/server/api/endpoints/notes/list-schedule.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts similarity index 100% rename from packages/backend/src/server/api/endpoints/notes/list-schedule.ts rename to packages/backend/src/server/api/endpoints/notes/schedule/list.ts diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index a42f674be0..769a42fa06 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -40,7 +40,7 @@ const props = defineProps<{ }>(); async function deleteScheduleNote() { - await os.apiWithDialog('notes/delete-schedule', { noteId: props.note.id }) + await os.apiWithDialog('notes/schedule/delete', { noteId: props.note.id }) .then(() => { isDeleted.value = true; }); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index cea8352c2c..4fa145c924 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -786,7 +786,7 @@ async function post(ev?: MouseEvent) { } posting = true; - os.api(postData.schedule ? 'notes/create-schedule' : 'notes/create', postData, token).then(() => { + os.api(postData.schedule ? 'notes/schedule/create' : 'notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted = true; } else { diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue index 90c3769c82..caa84d862f 100644 --- a/packages/frontend/src/components/MkSchedulePostListDialog.vue +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -41,7 +41,7 @@ const cancel = () => { }; onMounted(async () => { - notes.value = await os.api('notes/list-schedule'); + notes.value = await os.api('notes/schedule/list'); }); </script> diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 87922ba791..4164435adb 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1759,6 +1759,26 @@ export type Endpoints = { }; res: null; }; + 'notes/schedule/create': { + req: Partial<Note> & { + schedule: { + expiresAt: number; + }; + }; + res: { + createdNote: Note; + }; + }; + 'notes/schedule/delete': { + req: { + noteId: Note['id']; + }; + res: null; + }; + 'notes/schedule/list': { + req: TODO; + res: Note[]; + }; 'notes/favorites/create': { req: { noteId: Note['id']; @@ -3035,7 +3055,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts -// src/api.types.ts:632:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/api.types.ts:635:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts // src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index 54b175fcf1..30a507480a 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -509,6 +509,9 @@ export type Endpoints = { }; }; res: { createdNote: Note }; }; 'notes/delete': { req: { noteId: Note['id']; }; res: null; }; + 'notes/schedule/create': { req: Partial<Note> & { schedule: { expiresAt: number; } }; res: { createdNote: Note }; }; + 'notes/schedule/delete': { req: { noteId: Note['id']; }; res: null; }; + 'notes/schedule/list': { req: TODO; res: Note[]; }; 'notes/favorites/create': { req: { noteId: Note['id']; }; res: null; }; 'notes/favorites/delete': { req: { noteId: Note['id']; }; res: null; }; 'notes/featured': { req: TODO; res: Note[]; }; From 3439a29c142bd7f47d4331eebfa7d0bdfae0e362 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 21:20:33 +0900 Subject: [PATCH 257/501] (refactor) better types --- .../backend/src/core/NoteCreateService.ts | 36 +---------- packages/backend/src/models/NoteSchedule.ts | 7 +-- .../api/endpoints/notes/schedule/create.ts | 60 ++++++++----------- packages/backend/src/types.ts | 39 ++++++++++++ 4 files changed, 67 insertions(+), 75 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index da384f7de4..6fb024db53 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -15,19 +15,16 @@ import { extractHashtags } from '@/misc/extract-hashtags.js'; import type { IMentionedRemoteUsers } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js'; import type { ChannelFollowingsRepository, ChannelsRepository, FollowingsRepository, InstancesRepository, MiFollowing, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserListMembershipsRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; -import type { MiDriveFile } from '@/models/DriveFile.js'; -import type { MiApp } from '@/models/App.js'; import { concat } from '@/misc/prelude/array.js'; import { IdService } from '@/core/IdService.js'; import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js'; -import type { IPoll } from '@/models/Poll.js'; import { MiPoll } from '@/models/Poll.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { checkWordMute } from '@/misc/check-word-mute.js'; -import type { MiChannel } from '@/models/Channel.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { MemorySingleCache } from '@/misc/cache.js'; import type { MiUserProfile } from '@/models/UserProfile.js'; +import type { MiNoteCreateOption as Option, MiMinimumUser as MinimumUser } from '@/types.js'; import { RelayService } from '@/core/RelayService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { DI } from '@/di-symbols.js'; @@ -57,7 +54,6 @@ import { FeaturedService } from '@/core/FeaturedService.js'; import { FunoutTimelineService } from '@/core/FunoutTimelineService.js'; import { UtilityService } from '@/core/UtilityService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; -import { MiNoteSchedule } from '@/models/_.js'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; @@ -117,36 +113,6 @@ class NotificationManager { } } -type MinimumUser = { - id: MiUser['id']; - host: MiUser['host']; - username: MiUser['username']; - uri: MiUser['uri']; -}; - -type Option = { - createdAt?: Date | null; - name?: string | null; - text?: string | null; - reply?: MiNote | null; - renote?: MiNote | null; - files?: MiDriveFile[] | null; - poll?: IPoll | null; - schedule?: MiNoteSchedule | null; - localOnly?: boolean | null; - reactionAcceptance?: MiNote['reactionAcceptance']; - cw?: string | null; - visibility?: string; - visibleUsers?: MinimumUser[] | null; - channel?: MiChannel | null; - apMentions?: MinimumUser[] | null; - apHashtags?: string[] | null; - apEmojis?: string[] | null; - uri?: string | null; - url?: string | null; - app?: MiApp | null; -}; - @Injectable() export class NoteCreateService implements OnApplicationShutdown { #shutdownController = new AbortController(); diff --git a/packages/backend/src/models/NoteSchedule.ts b/packages/backend/src/models/NoteSchedule.ts index f622893ecf..565d6a6211 100644 --- a/packages/backend/src/models/NoteSchedule.ts +++ b/packages/backend/src/models/NoteSchedule.ts @@ -4,12 +4,9 @@ */ import { Entity, Index, JoinColumn, Column, PrimaryColumn, ManyToOne } from 'typeorm'; -import { noteVisibilities } from '@/types.js'; -import { MiNote } from '@/models/Note.js'; +import type { MiNoteCreateOption } from '@/types.js'; import { id } from './util/id.js'; import { MiUser } from './User.js'; -import { MiChannel } from './Channel.js'; -import type { MiDriveFile } from './DriveFile.js'; @Entity('note_schedule') export class MiNoteSchedule { @@ -17,7 +14,7 @@ export class MiNoteSchedule { public id: string; @Column('jsonb') - public note:{createdAt?: Date | undefined ; apEmojis: any[] | undefined; visibility: any; apMentions: any[] | undefined; visibleUsers: MiUser[]; channel: null | MiChannel; poll: { multiple: any; choices: any; expiresAt: Date | null } | undefined; renote: null | MiNote; localOnly: any; cw: any; apHashtags: any[] | undefined; reactionAcceptance: any; files: MiDriveFile[]; text: any; reply: null | MiNote }; + public note: MiNoteCreateOption; @Index() @Column('varchar', { diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts index 37c6bd36a6..2d0b6145c7 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts @@ -7,6 +7,9 @@ import ms from 'ms'; import { DataSource, In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { MiChannel } from '@/models/Channel.js'; import type { UsersRepository, NotesRepository, @@ -15,17 +18,13 @@ import type { ChannelsRepository, NoteScheduleRepository, } from '@/models/_.js'; -import type { MiDriveFile } from '@/models/DriveFile.js'; -import type { MiNote } from '@/models/Note.js'; -import type { MiChannel } from '@/models/Channel.js'; +import type { MiNoteCreateOption } from '@/types.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { NoteCreateService } from '@/core/NoteCreateService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { QueueService } from '@/core/QueueService.js'; -import { MiNoteSchedule } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../../error.js'; @@ -86,6 +85,12 @@ export const meta = { id: '8a9bfb90-fc7e-4878-a3e8-d97faaf5fb07', }, + specifyScheduleDate: { + message: 'Please specify schedule date.', + code: 'PLEASE_SPECIFY_SCHEDULE_DATE', + id: 'c93a6ad6-f7e2-4156-a0c2-3d03529e5e0f', + }, + noSuchChannel: { message: 'No such channel.', code: 'NO_SUCH_CHANNEL', @@ -212,6 +217,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.channelsRepository) private channelsRepository: ChannelsRepository, + private noteEntityService: NoteEntityService, private queueService: QueueService, private idService: IdService, ) { @@ -336,38 +342,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } } - type NoteType = { - createdAt?: Date | undefined; - apEmojis: any[] | undefined; - visibility: any; - apMentions: any[] | undefined; - visibleUsers: MiUser[]; - channel: null | MiChannel; - poll: { - multiple: any; - choices: any; - expiresAt: Date | null; - } | undefined; - renote: null | MiNote; - localOnly: any; - cw: any; - apHashtags: any[] | undefined; - reactionAcceptance: any; - files: MiDriveFile[]; - text: any; - reply: null | MiNote; - }; - const note:NoteType = { - files: files, + const note: MiNoteCreateOption = { + files, poll: ps.poll ? { choices: ps.poll.choices, multiple: ps.poll.multiple ?? false, expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, } : undefined, - text: ps.text ?? undefined, + text: ps.text ?? null, reply, renote, - cw: ps.cw, + cw: ps.cw ?? null, localOnly: ps.localOnly, reactionAcceptance: ps.reactionAcceptance, visibility: ps.visibility, @@ -380,9 +365,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.schedule && ps.schedule.expiresAt) { me.token = null; - const noteId = this.idService.gen(new Date().getTime()); + const scheduleNoteId = this.idService.gen(new Date().getTime()); await this.noteScheduleRepository.insert({ - id: noteId, + id: scheduleNoteId, note: note, userId: me.id, expiresAt: new Date(ps.schedule.expiresAt), @@ -390,14 +375,19 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); await this.queueService.ScheduleNotePostQueue.add(String(delay), { - scheduleNoteId: noteId, + scheduleNoteId, }, { delay, removeOnComplete: true, }); - } - return ''; + return { + scheduleNoteId, + scheduledNote: note, + }; + } else { + throw new ApiError(meta.errors.specifyScheduleDate); + } }); } } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index e6dfeb6f8c..ab644bfc03 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -3,6 +3,14 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import type { MiDriveFile } from '@/models/DriveFile.js'; +import type { IPoll } from '@/models/Poll.js'; +import type { MiChannel } from '@/models/Channel.js'; +import type { MiApp } from '@/models/App.js'; +import type { MiUser } from '@/models/User.js'; +import type { MiNote } from '@/models/Note.js'; +import type { MiNoteSchedule } from '@/models/NoteSchedule.js'; + /** * note - 通知オンにしているユーザーが投稿した * follow - フォローされた @@ -239,6 +247,37 @@ export type ModerationLogPayloads = { }; }; +export type MiMinimumUser = { + id: MiUser['id']; + host: MiUser['host']; + username: MiUser['username']; + uri: MiUser['uri']; +}; + +export type MiNoteCreateOption = { + createdAt?: Date | null; + name?: string | null; + text?: string | null; + reply?: MiNote | null; + renote?: MiNote | null; + files?: MiDriveFile[] | null; + poll?: IPoll | null; + schedule?: MiNoteSchedule | null; + localOnly?: boolean | null; + reactionAcceptance?: MiNote['reactionAcceptance']; + cw?: string | null; + visibility?: string; + visibleUsers?: MiMinimumUser[] | null; + channel?: MiChannel | null; + apMentions?: MiMinimumUser[] | null; + apHashtags?: string[] | null; + apEmojis?: string[] | null; + uri?: string | null; + url?: string | null; + app?: MiApp | null; +}; + + export type Serialized<T> = { [K in keyof T]: T[K] extends Date From 5fa8735cc480d5f332ae086716fbb53f30c78482 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 21:20:58 +0900 Subject: [PATCH 258/501] tweak ui --- locales/index.d.ts | 8 ++- locales/ja-JP.yml | 8 ++- packages/frontend/src/components/MkMenu.vue | 4 +- .../frontend/src/components/MkPostForm.vue | 59 +++++++++++++++---- .../src/components/MkScheduleEditor.vue | 9 +-- .../components/MkSchedulePostListDialog.vue | 5 +- 6 files changed, 73 insertions(+), 20 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 63bcb16e41..23eb3ec419 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -595,7 +595,6 @@ export interface Locale { "poll": string; "schedulePost": string; "useCw": string; - "schedulePostList": string; "enablePlayer": string; "disablePlayer": string; "expandTweet": string; @@ -2469,6 +2468,13 @@ export interface Locale { }; }; }; + "_schedulePost": { + "list": string; + "postDate": string; + "postTime": string; + "localTime": string; + "addSchedule": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index be378180dc..69c834d11d 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -592,7 +592,6 @@ visibility: "公開範囲" poll: "アンケート" schedulePost: "予約投稿" useCw: "内容を隠す" -schedulePostList: "予約投稿一覧" enablePlayer: "プレイヤーを開く" disablePlayer: "プレイヤーを閉じる" expandTweet: "ポストを展開する" @@ -2356,3 +2355,10 @@ _externalResourceInstaller: _themeInstallFailed: title: "テーマのインストールに失敗しました" description: "テーマのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。" + +_schedulePost: + list: "予約投稿一覧" + postDate: "日付" + postTime: "時刻" + localTime: "端末に設定されているタイムゾーンの時刻で投稿されます。" + addSchedule: "予約設定" diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 9457bf385f..6ed2fa6d25 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -421,9 +421,9 @@ onBeforeUnmount(() => { .indicator { position: absolute; top: 5px; - left: 13px; + right: 18px; color: var(--indicator); - font-size: 12px; + font-size: 8px; animation: blink 1s infinite; } diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 4fa145c924..bebaf36a17 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -19,8 +19,6 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </div> <div :class="$style.headerRight"> - <button v-tooltip="i18n.ts.schedulePost" class="_button" :class="[$style.headerRightItem, { [$style.headerRightButtonActive]: schedule }]" @click="toggleSchedule"><i class="ti ti-calendar-time"></i></button> - <button v-tooltip="i18n.ts.schedulePostList" class="_button" :class="[$style.headerRightItem]" @click="listSchedulePost"><i class="ti ti-calendar-event"></i></button> <template v-if="!(channel != null && fixed)"> <button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility"> <span v-if="visibility === 'public'"><i class="ti ti-world"></i></span> @@ -38,17 +36,13 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="!localOnly"><i class="ti ti-rocket"></i></span> <span v-else><i class="ti ti-rocket-off"></i></span> </button> - <button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" class="_button" :class="[$style.headerRightItem, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" @click="toggleReactionAcceptance"> - <span v-if="reactionAcceptance === 'likeOnly'"><i class="ti ti-heart"></i></span> - <span v-else-if="reactionAcceptance === 'likeOnlyForRemote'"><i class="ti ti-heart-plus"></i></span> - <span v-else><i class="ti ti-icons"></i></span> - </button> + <button v-tooltip="i18n.ts.otherSettings" class="_button" :class="[$style.headerRightItem]" @click="openOtherSettingsMenu"><i class="ti ti-dots"></i></button> <button v-click-anime class="_button" :class="$style.submit" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> <div :class="$style.submitInner"> <template v-if="posted"></template> <template v-else-if="posting"><MkEllipsis/></template> <template v-else>{{ submitText }}</template> - <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : 'ti ti-send'"></i> + <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : schedule ? 'ti ti-clock-hour-4' : 'ti ti-send'"></i> </div> </button> </div> @@ -242,7 +236,9 @@ const submitText = $computed((): string => { ? i18n.ts.quote : props.reply ? i18n.ts.reply - : i18n.ts.note; + : schedule + ? i18n.ts._schedulePost.addSchedule + : i18n.ts.note; }); const textLength = $computed((): number => { @@ -753,7 +749,7 @@ async function post(ev?: MouseEvent) { replyId: props.reply ? props.reply.id : undefined, renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, channelId: props.channel ? props.channel.id : undefined, - schedule: schedule, + schedule, poll: poll, cw: useCw ? cw ?? '' : null, localOnly: localOnly, @@ -778,6 +774,10 @@ async function post(ev?: MouseEvent) { } } + if (postData.schedule?.expiresAt && typeof postData.schedule.expiresAt === 'string') { + postData.schedule.expiresAt = parseInt(postData.schedule.expiresAt); + } + let token = undefined; if (postAccount) { @@ -902,6 +902,45 @@ function openAccountMenu(ev: MouseEvent) { }, ev); } +function openOtherSettingsMenu(ev: MouseEvent) { + let reactionAcceptanceIcon: string; + switch (reactionAcceptance) { + case 'likeOnly': + reactionAcceptanceIcon = 'ti ti-heart'; + break; + case 'likeOnlyForRemote': + reactionAcceptanceIcon = 'ti ti-heart-plus'; + break; + default: + reactionAcceptanceIcon = 'ti ti-icons'; + break; + } + + os.popupMenu([{ + type: 'button', + text: i18n.ts.reactionAcceptance, + icon: reactionAcceptanceIcon, + action: toggleReactionAcceptance, + }, { + type: 'button', + text: i18n.ts.schedulePost, + icon: 'ti ti-calendar-time', + indicate: (schedule != null), + action: toggleSchedule, + }, null, { + type: 'button', + text: i18n.ts._schedulePost.list, + icon: 'ti ti-calendar-event', + action: () => { + // 投稿フォームが二重に出ないようにとじておく + emit('cancel'); + listSchedulePost(); + }, + }], ev.currentTarget ?? ev.target, { + align: 'right', + }); +} + onMounted(() => { if (props.autofocus) { focus(); diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue index f6c1bdde46..d53451f5bb 100644 --- a/packages/frontend/src/components/MkScheduleEditor.vue +++ b/packages/frontend/src/components/MkScheduleEditor.vue @@ -5,12 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div style="padding: 8px 16px"> - <section> + <section class="_gaps_s"> <MkInput v-model="atDate" small type="date" class="input"> - <template #label>{{ i18n.ts._poll.deadlineDate }}</template> + <template #label>{{ i18n.ts._schedulePost.postDate }}</template> </MkInput> <MkInput v-model="atTime" small type="time" class="input"> - <template #label>{{ i18n.ts._poll.deadlineTime }}</template> + <template #label>{{ i18n.ts._schedulePost.postTime }}</template> + <template #caption>{{ i18n.ts._schedulePost.localTime }}</template> </MkInput> </section> </div> @@ -53,6 +54,6 @@ function get() { } watch([atDate, atTime], () => emit('update:modelValue', get()), { - deep: true, + immediate: true, }); </script> diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue index caa84d862f..cf5a4f4d31 100644 --- a/packages/frontend/src/components/MkSchedulePostListDialog.vue +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only @close="cancel()" @closed="$emit('closed')" > - <template #header> 予約投稿一覧</template> + <template #header>{{ i18n.ts._schedulePost.list }}</template> <div v-for="item in notes"> <MkSpacer :marginMin="14" :marginMax="16"> <MkNoteSimple scheduled="true" :note="item.note"/> @@ -26,7 +26,8 @@ import * as Misskey from 'misskey-js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import * as os from '@/os.js'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; -import MkSignin from '@/components/MkSignin.vue'; +import { i18n } from '@/i18n.js'; + const emit = defineEmits<{ (ev: 'ok', selected: Misskey.entities.UserDetailed): void; (ev: 'cancel'): void; From fab470d64d5a8b419653a5440af471a791961ac5 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 21:25:22 +0900 Subject: [PATCH 259/501] fix lint --- packages/backend/src/types.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index ab644bfc03..7fcc39cda8 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -277,7 +277,6 @@ export type MiNoteCreateOption = { app?: MiApp | null; }; - export type Serialized<T> = { [K in keyof T]: T[K] extends Date From 54fa83f1c5e23a5a5df25d9fd08540127a979331 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 21:29:19 +0900 Subject: [PATCH 260/501] =?UTF-8?q?(add)=20navbar:=20=E4=BA=88=E7=B4=84?= =?UTF-8?q?=E6=8A=95=E7=A8=BF=E4=B8=80=E8=A6=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/navbar.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index f0ed773f82..99a02671eb 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -171,4 +171,11 @@ export const navbarItemDef = reactive({ show: computed(() => $i != null), to: `/@${$i?.username}`, }, + scheduledNotes: { + title: i18n.ts._schedulePost.list, + icon: 'ti ti-calendar-event', + action: (ev) => { + os.listSchedulePost(); + }, + }, }); From a0e9ebfb83af2164fb7b6cd032a9ce7c5cf77403 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 21:34:18 +0900 Subject: [PATCH 261/501] NoteSchedule -> ScheduledNote --- packages/backend/src/di-symbols.ts | 2 +- packages/backend/src/models/RepositoryModule.ts | 12 ++++++------ .../src/models/{NoteSchedule.ts => ScheduledNote.ts} | 2 +- packages/backend/src/models/_.ts | 6 +++--- packages/backend/src/postgres.ts | 4 ++-- .../processors/ScheduleNotePostProcessorService.ts | 10 +++++----- packages/backend/src/queue/types.ts | 2 +- .../server/api/endpoints/notes/schedule/create.ts | 8 ++++---- .../server/api/endpoints/notes/schedule/delete.ts | 8 ++++---- .../src/server/api/endpoints/notes/schedule/list.ts | 8 ++++---- packages/backend/src/types.ts | 4 ++-- 11 files changed, 33 insertions(+), 33 deletions(-) rename packages/backend/src/models/{NoteSchedule.ts => ScheduledNote.ts} (95%) diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index 545c3183fb..afa919dfa9 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -15,7 +15,7 @@ export const DI = { //#region Repositories usersRepository: Symbol('usersRepository'), notesRepository: Symbol('notesRepository'), - noteScheduleRepository: Symbol('noteScheduleRepository'), + scheduledNotesRepository: Symbol('scheduledNotesRepository'), announcementsRepository: Symbol('announcementsRepository'), announcementReadsRepository: Symbol('announcementReadsRepository'), appsRepository: Symbol('appsRepository'), diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 61fac96349..1d3d0c873f 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -71,7 +71,7 @@ import { MiUserPublickey, MiUserSecurityKey, MiWebhook, - MiNoteSchedule, + MiScheduledNote, } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -88,9 +88,9 @@ const $notesRepository: Provider = { inject: [DI.db], }; -const $noteScheduleRepository: Provider = { - provide: DI.noteScheduleRepository, - useFactory: (db: DataSource) => db.getRepository(MiNoteSchedule), +const $scheduledNotesRepository: Provider = { + provide: DI.scheduledNotesRepository, + useFactory: (db: DataSource) => db.getRepository(MiScheduledNote), inject: [DI.db], }; @@ -478,7 +478,7 @@ const $userMemosRepository: Provider = { providers: [ $usersRepository, $notesRepository, - $noteScheduleRepository, + $scheduledNotesRepository, $announcementsRepository, $announcementReadsRepository, $appsRepository, @@ -546,7 +546,7 @@ const $userMemosRepository: Provider = { exports: [ $usersRepository, $notesRepository, - $noteScheduleRepository, + $scheduledNotesRepository, $announcementsRepository, $announcementReadsRepository, $appsRepository, diff --git a/packages/backend/src/models/NoteSchedule.ts b/packages/backend/src/models/ScheduledNote.ts similarity index 95% rename from packages/backend/src/models/NoteSchedule.ts rename to packages/backend/src/models/ScheduledNote.ts index 565d6a6211..7099f1f366 100644 --- a/packages/backend/src/models/NoteSchedule.ts +++ b/packages/backend/src/models/ScheduledNote.ts @@ -9,7 +9,7 @@ import { id } from './util/id.js'; import { MiUser } from './User.js'; @Entity('note_schedule') -export class MiNoteSchedule { +export class MiScheduledNote { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index bc15ce2402..10f5982ce5 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -68,7 +68,7 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; -import { MiNoteSchedule } from './NoteSchedule.js'; +import { MiScheduledNote } from './ScheduledNote.js'; import type { Repository } from 'typeorm'; export { @@ -105,7 +105,7 @@ export { MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, - MiNoteSchedule, + MiScheduledNote, MiPage, MiPageLike, MiPasswordResetRequest, @@ -173,7 +173,7 @@ export type NoteFavoritesRepository = Repository<MiNoteFavorite>; export type NoteReactionsRepository = Repository<MiNoteReaction>; export type NoteThreadMutingsRepository = Repository<MiNoteThreadMuting>; export type NoteUnreadsRepository = Repository<MiNoteUnread>; -export type NoteScheduleRepository = Repository<MiNoteSchedule>; +export type ScheduledNotesRepository = Repository<MiScheduledNote>; export type PagesRepository = Repository<MiPage>; export type PageLikesRepository = Repository<MiPageLike>; export type PasswordResetRequestsRepository = Repository<MiPasswordResetRequest>; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 002b5c30aa..47af990231 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -76,7 +76,7 @@ import { MiRoleAssignment } from '@/models/RoleAssignment.js'; import { MiFlash } from '@/models/Flash.js'; import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserMemo } from '@/models/UserMemo.js'; -import { MiNoteSchedule } from '@/models/NoteSchedule.js'; +import { MiScheduledNote } from '@/models/ScheduledNote.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -150,7 +150,7 @@ export const entities = [ MiRenoteMuting, MiBlocking, MiNote, - MiNoteSchedule, + MiScheduledNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts index d1312bea8e..821645b7bb 100644 --- a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import type Logger from '@/logger.js'; import { bindThis } from '@/decorators.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; -import type { NoteScheduleRepository, UsersRepository } from '@/models/_.js'; +import type { ScheduledNotesRepository, UsersRepository } from '@/models/_.js'; import { DI } from '@/di-symbols.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type * as Bull from 'bullmq'; @@ -18,8 +18,8 @@ export class ScheduleNotePostProcessorService { private logger: Logger; constructor( - @Inject(DI.noteScheduleRepository) - private noteScheduleRepository: NoteScheduleRepository, + @Inject(DI.scheduledNotesRepository) + private scheduledNotesRepository: ScheduledNotesRepository, @Inject(DI.usersRepository) private usersRepository: UsersRepository, @@ -32,14 +32,14 @@ export class ScheduleNotePostProcessorService { @bindThis public async process(job: Bull.Job<ScheduleNotePostJobData>): Promise<void> { - this.noteScheduleRepository.findOneBy({ id: job.data.scheduleNoteId }).then(async (data) => { + this.scheduledNotesRepository.findOneBy({ id: job.data.scheduleNoteId }).then(async (data) => { if (!data) { this.logger.warn(`Schedule note ${job.data.scheduleNoteId} not found`); } else { data.note.createdAt = new Date(); const me = await this.usersRepository.findOneByOrFail({ id: data.userId }); await this.noteCreateService.create(me, data.note); - await this.noteScheduleRepository.remove(data); + await this.scheduledNotesRepository.remove(data); } }); } diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index cc69c77d08..178ffaf1ed 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -10,7 +10,7 @@ import type { MiLocalUser, MiUser } from '@/models/User.js'; import type { MiWebhook } from '@/models/Webhook.js'; import type { IActivity } from '@/core/activitypub/type.js'; import { IPoll } from '@/models/Poll.js'; -import { MiNoteSchedule } from '@/models/NoteSchedule.js'; +import { MiScheduledNote } from '@/models/ScheduledNote.js'; import { MiChannel } from '@/models/Channel.js'; import { MiApp } from '@/models/App.js'; import type httpSignature from '@peertube/http-signature'; diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts index 2d0b6145c7..44deb7bb9b 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts @@ -16,7 +16,7 @@ import type { BlockingsRepository, DriveFilesRepository, ChannelsRepository, - NoteScheduleRepository, + ScheduledNotesRepository, } from '@/models/_.js'; import type { MiNoteCreateOption } from '@/types.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; @@ -205,8 +205,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, - @Inject(DI.noteScheduleRepository) - private noteScheduleRepository: NoteScheduleRepository, + @Inject(DI.scheduledNotesRepository) + private scheduledNotesRepository: ScheduledNotesRepository, @Inject(DI.blockingsRepository) private blockingsRepository: BlockingsRepository, @@ -366,7 +366,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.schedule && ps.schedule.expiresAt) { me.token = null; const scheduleNoteId = this.idService.gen(new Date().getTime()); - await this.noteScheduleRepository.insert({ + await this.scheduledNotesRepository.insert({ id: scheduleNoteId, note: note, userId: me.id, diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts index f9398576d3..1e2b5b91a7 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts @@ -5,7 +5,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { NoteScheduleRepository } from '@/models/_.js'; +import type { ScheduledNotesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -39,11 +39,11 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.noteScheduleRepository) - private noteScheduleRepository: NoteScheduleRepository, + @Inject(DI.scheduledNotesRepository) + private scheduledNotesRepository: ScheduledNotesRepository, ) { super(meta, paramDef, async (ps, me) => { - await this.noteScheduleRepository.delete({ id: ps.noteId }); + await this.scheduledNotesRepository.delete({ id: ps.noteId }); }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 6efe8b6382..9fa2f79593 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -7,7 +7,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; -import type { NoteScheduleRepository } from '@/models/_.js'; +import type { ScheduledNotesRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; export const meta = { @@ -62,12 +62,12 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.noteScheduleRepository) - private noteScheduleRepository: NoteScheduleRepository, + @Inject(DI.scheduledNotesRepository) + private scheduledNotesRepository: ScheduledNotesRepository, private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { - const scheduleNotes = await this.noteScheduleRepository.findBy({ userId: me.id }); + const scheduleNotes = await this.scheduledNotesRepository.findBy({ userId: me.id }); const user = await this.userEntityService.pack(me, me); const scheduleNotesPack: { id: string; diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 7fcc39cda8..606abf7522 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -9,7 +9,7 @@ import type { MiChannel } from '@/models/Channel.js'; import type { MiApp } from '@/models/App.js'; import type { MiUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; -import type { MiNoteSchedule } from '@/models/NoteSchedule.js'; +import type { MiScheduledNote } from '@/models/ScheduledNote.js'; /** * note - 通知オンにしているユーザーが投稿した @@ -262,7 +262,7 @@ export type MiNoteCreateOption = { renote?: MiNote | null; files?: MiDriveFile[] | null; poll?: IPoll | null; - schedule?: MiNoteSchedule | null; + schedule?: MiScheduledNote | null; localOnly?: boolean | null; reactionAcceptance?: MiNote['reactionAcceptance']; cw?: string | null; From f65e85c31958e184bfc29fbd15129c949d80b61b Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 22:22:26 +0900 Subject: [PATCH 262/501] =?UTF-8?q?(enhance)=20=E4=BA=88=E7=B4=84=E7=99=BB?= =?UTF-8?q?=E9=8C=B2=E5=AE=8C=E4=BA=86=E3=81=97=E3=81=9F=E3=81=93=E3=81=A8?= =?UTF-8?q?=E3=82=92=E3=82=8F=E3=81=8B=E3=82=8A=E3=82=84=E3=81=99=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/frontend/src/components/MkPostForm.vue | 8 ++++++++ 3 files changed, 10 insertions(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index 23eb3ec419..00dc9eb9e7 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2474,6 +2474,7 @@ export interface Locale { "postTime": string; "localTime": string; "addSchedule": string; + "willBePostedAtX": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 69c834d11d..003e26f792 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2362,3 +2362,4 @@ _schedulePost: postTime: "時刻" localTime: "端末に設定されているタイムゾーンの時刻で投稿されます。" addSchedule: "予約設定" + willBePostedAtX: "このノートは{date}に投稿されます。" diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index bebaf36a17..d3645d35d6 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -111,6 +111,7 @@ import { formatTimeString } from '@/scripts/format-time-string.js'; import { Autocomplete } from '@/scripts/autocomplete.js'; import * as os from '@/os.js'; import { selectFiles } from '@/scripts/select-file.js'; +import { dateTimeFormat } from '@/scripts/intl-const.js'; import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n.js'; @@ -808,6 +809,13 @@ async function post(ev?: MouseEvent) { claimAchievement('notes1'); } poll = null; + + if (postData.schedule?.expiresAt) { + const d = new Date(postData.schedule.expiresAt); + const str = dateTimeFormat.format(d); + os.toast(i18n.t('_schedulePost.willBePostedAtX', { date: str })); + } + const text = postData.text ?? ''; const lowerCase = text.toLowerCase(); if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { From 1995400dac7695264a8696a866bc2acab595f958 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 22:24:41 +0900 Subject: [PATCH 263/501] change msg --- locales/ja-JP.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 003e26f792..459692f74e 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2362,4 +2362,4 @@ _schedulePost: postTime: "時刻" localTime: "端末に設定されているタイムゾーンの時刻で投稿されます。" addSchedule: "予約設定" - willBePostedAtX: "このノートは{date}に投稿されます。" + willBePostedAtX: "{date}に投稿予約しました。" From 2bc15c09ed88242b7c9244cd683b797f2c120b2a Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 23:31:16 +0900 Subject: [PATCH 264/501] =?UTF-8?q?(enhance)=20`notes/create`=20=E3=81=A7?= =?UTF-8?q?=E4=BA=88=E7=B4=84=E6=8A=95=E7=A8=BF=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScheduleNotePostProcessorService.ts | 4 +- packages/backend/src/queue/types.ts | 2 +- .../src/server/api/endpoints/notes/create.ts | 90 +++++++++++++++++-- .../api/endpoints/notes/schedule/delete.ts | 12 ++- .../api/endpoints/notes/schedule/list.ts | 1 + .../frontend/src/components/MkNoteSimple.vue | 4 +- .../frontend/src/components/MkPostForm.vue | 6 +- packages/misskey-js/etc/misskey-js.api.md | 14 ++- packages/misskey-js/src/api.types.ts | 12 ++- 9 files changed, 122 insertions(+), 23 deletions(-) diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts index 821645b7bb..9fd80fc521 100644 --- a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -32,9 +32,9 @@ export class ScheduleNotePostProcessorService { @bindThis public async process(job: Bull.Job<ScheduleNotePostJobData>): Promise<void> { - this.scheduledNotesRepository.findOneBy({ id: job.data.scheduleNoteId }).then(async (data) => { + this.scheduledNotesRepository.findOneBy({ id: job.data.scheduledNoteId }).then(async (data) => { if (!data) { - this.logger.warn(`Schedule note ${job.data.scheduleNoteId} not found`); + this.logger.warn(`Schedule note ${job.data.scheduledNoteId} not found`); } else { data.note.createdAt = new Date(); const me = await this.usersRepository.findOneByOrFail({ id: data.userId }); diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts index 178ffaf1ed..b6895816c6 100644 --- a/packages/backend/src/queue/types.ts +++ b/packages/backend/src/queue/types.ts @@ -109,7 +109,7 @@ export type EndedPollNotificationJobData = { }; export type ScheduleNotePostJobData = { - scheduleNoteId: MiNote['id']; + scheduledNoteId: MiNote['id']; } type MinimumUser = { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index df02d3acb7..8a04ec540f 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -7,7 +7,8 @@ import ms from 'ms'; import { In } from 'typeorm'; import { Inject, Injectable } from '@nestjs/common'; import type { MiUser } from '@/models/User.js'; -import type { UsersRepository, NotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/_.js'; +import type { UsersRepository, NotesRepository, ScheduledNotesRepository, BlockingsRepository, DriveFilesRepository, ChannelsRepository } from '@/models/_.js'; +import type { MiNoteCreateOption } from '@/types.js'; import type { MiDriveFile } from '@/models/DriveFile.js'; import type { MiNote } from '@/models/Note.js'; import type { MiChannel } from '@/models/Channel.js'; @@ -15,6 +16,8 @@ import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; +import { QueueService } from '@/core/QueueService.js'; +import { IdService } from '@/core/IdService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { ApiError } from '../../error.js'; @@ -39,9 +42,17 @@ export const meta = { properties: { createdNote: { type: 'object', - optional: false, nullable: false, + optional: false, nullable: true, ref: 'Note', }, + scheduledNoteId: { + type: 'string', + optional: true, nullable: true, + }, + scheduledNote: { + type: 'object', + optional: true, nullable: true, + }, }, }, @@ -105,6 +116,22 @@ export const meta = { code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', id: '33510210-8452-094c-6227-4a6c05d99f00', }, + + cannotCreateAlreadyExpiredSchedule: { + message: 'Schedule is already expired.', + code: 'CANNOT_CREATE_ALREADY_EXPIRED_SCHEDULE', + id: '8a9bfb90-fc7e-4878-a3e8-d97faaf5fb07', + }, + specifyScheduleDate: { + message: 'Please specify schedule date.', + code: 'PLEASE_SPECIFY_SCHEDULE_DATE', + id: 'c93a6ad6-f7e2-4156-a0c2-3d03529e5e0f', + }, + noSuchSchedule: { + message: 'No such schedule.', + code: 'NO_SUCH_SCHEDULE', + id: '44dee229-8da1-4a61-856d-e3a4bbc12032', + }, }, } as const; @@ -164,6 +191,13 @@ export const paramDef = { }, required: ['choices'], }, + schedule: { + type: 'object', + nullable: true, + properties: { + expiresAt: { type: 'integer', nullable: false }, + }, + }, }, // (re)note with text, files and poll are optional anyOf: [ @@ -172,6 +206,7 @@ export const paramDef = { { required: ['fileIds'] }, { required: ['mediaIds'] }, { required: ['poll'] }, + { required: ['schedule'] }, ], } as const; @@ -184,6 +219,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.scheduledNotesRepository) + private scheduledNotesRepository: ScheduledNotesRepository, + @Inject(DI.blockingsRepository) private blockingsRepository: BlockingsRepository, @@ -195,6 +233,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private noteEntityService: NoteEntityService, private noteCreateService: NoteCreateService, + + private queueService: QueueService, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { let visibleUsers: MiUser[] = []; @@ -311,8 +352,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } - // 投稿を作成 - const note = await this.noteCreateService.create(me, { + const note: MiNoteCreateOption = { createdAt: new Date(), files: files, poll: ps.poll ? { @@ -332,11 +372,45 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- apMentions: ps.noExtractMentions ? [] : undefined, apHashtags: ps.noExtractHashtags ? [] : undefined, apEmojis: ps.noExtractEmojis ? [] : undefined, - }); - - return { - createdNote: await this.noteEntityService.pack(note, me), }; + + if (ps.schedule) { + if (!ps.schedule.expiresAt) { + throw new ApiError(meta.errors.specifyScheduleDate); + } + + me.token = null; + const scheduledNoteId = this.idService.gen(new Date().getTime()); + await this.scheduledNotesRepository.insert({ + id: scheduledNoteId, + note: note, + userId: me.id, + expiresAt: new Date(ps.schedule.expiresAt), + }); + + const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); + await this.queueService.ScheduleNotePostQueue.add(String(delay), { + scheduledNoteId, + }, { + jobId: scheduledNoteId, + delay, + removeOnComplete: true, + }); + + return { + scheduledNoteId, + scheduledNote: note, + + // ↓互換性のため(微妙) + createdNote: null, + }; + } else { + // 投稿を作成 + const createdNoteRaw = await this.noteCreateService.create(me, note); + return { + createdNote: await this.noteEntityService.pack(createdNoteRaw, me), + }; + } }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts index 1e2b5b91a7..e108016e80 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/delete.ts @@ -6,6 +6,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import type { ScheduledNotesRepository } from '@/models/_.js'; +import { QueueService } from '@/core/QueueService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -31,9 +32,9 @@ export const meta = { export const paramDef = { type: 'object', properties: { - noteId: { type: 'string', format: 'misskey:id' }, + scheduledNoteId: { type: 'string', format: 'misskey:id' }, }, - required: ['noteId'], + required: ['scheduledNoteId'], } as const; @Injectable() @@ -41,9 +42,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.scheduledNotesRepository) private scheduledNotesRepository: ScheduledNotesRepository, + + private queueService: QueueService, ) { super(meta, paramDef, async (ps, me) => { - await this.scheduledNotesRepository.delete({ id: ps.noteId }); + await this.scheduledNotesRepository.delete({ id: ps.scheduledNoteId }); + if (ps.scheduledNoteId) { + await this.queueService.ScheduleNotePostQueue.remove(ps.scheduledNoteId); + } }); } } diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 9fa2f79593..3b8c029983 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -93,6 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- user: user, createdAt: new Date(item.expiresAt), isSchedule: true, + // ↓TODO: NoteのIDに予約投稿IDを入れたくない(本来別ものなため) id: item.id, }, }; diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 769a42fa06..1535609a05 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -40,7 +40,9 @@ const props = defineProps<{ }>(); async function deleteScheduleNote() { - await os.apiWithDialog('notes/schedule/delete', { noteId: props.note.id }) + if (!props.note.isSchedule) return; + // スケジュールつきノートの場合は、ノートIDのフィールドに予約投稿ID(scheduledNoteId)が入るので注意!!!! + await os.apiWithDialog('notes/schedule/delete', { scheduledNoteId: props.note.id }) .then(() => { isDeleted.value = true; }); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index d3645d35d6..83de95d880 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -751,7 +751,7 @@ async function post(ev?: MouseEvent) { renoteId: props.renote ? props.renote.id : quoteId ? quoteId : undefined, channelId: props.channel ? props.channel.id : undefined, schedule, - poll: poll, + poll, cw: useCw ? cw ?? '' : null, localOnly: localOnly, visibility: visibility, @@ -783,11 +783,11 @@ async function post(ev?: MouseEvent) { if (postAccount) { const storedAccounts = await getAccounts(); - token = storedAccounts.find(x => x.id === postAccount.id)?.token; + token = storedAccounts.find(x => x.id === postAccount?.id)?.token; } posting = true; - os.api(postData.schedule ? 'notes/schedule/create' : 'notes/create', postData, token).then(() => { + os.api('notes/create', postData, token).then(() => { if (props.freezeAfterPosted) { posted = true; } else { diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 4164435adb..6319c49ebe 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1748,6 +1748,9 @@ export type Endpoints = { expiresAt?: null | number; expiredAfter?: null | number; }; + schedule?: null | { + expiresAt?: null | number; + }; }; res: { createdNote: Note; @@ -1771,13 +1774,18 @@ export type Endpoints = { }; 'notes/schedule/delete': { req: { - noteId: Note['id']; + scheduledNoteId: Note['id']; }; res: null; }; 'notes/schedule/list': { req: TODO; - res: Note[]; + res: { + id: Note['id']; + userId: User['id']; + expiresAt: number; + note: Note; + }[]; }; 'notes/favorites/create': { req: { @@ -3055,7 +3063,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts -// src/api.types.ts:635:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/api.types.ts:643:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts // src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index 30a507480a..12030be37e 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -507,11 +507,19 @@ export type Endpoints = { expiresAt?: null | number; expiredAfter?: null | number; }; + schedule?: null | { + expiresAt?: null | number; + }; }; res: { createdNote: Note }; }; 'notes/delete': { req: { noteId: Note['id']; }; res: null; }; 'notes/schedule/create': { req: Partial<Note> & { schedule: { expiresAt: number; } }; res: { createdNote: Note }; }; - 'notes/schedule/delete': { req: { noteId: Note['id']; }; res: null; }; - 'notes/schedule/list': { req: TODO; res: Note[]; }; + 'notes/schedule/delete': { req: { scheduledNoteId: Note['id']; }; res: null; }; + 'notes/schedule/list': { req: TODO; res: { + id: Note['id']; + userId: User['id']; + expiresAt: number; + note: Note; + }[]; }; 'notes/favorites/create': { req: { noteId: Note['id']; }; res: null; }; 'notes/favorites/delete': { req: { noteId: Note['id']; }; res: null; }; 'notes/featured': { req: TODO; res: Note[]; }; From 22766fc7a2bd69731c1756790eb9c084c3316a2f Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 9 Nov 2023 23:31:57 +0900 Subject: [PATCH 265/501] =?UTF-8?q?`notes/schedule/create`=20=E3=82=92?= =?UTF-8?q?=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/EndpointsModule.ts | 4 - packages/backend/src/server/api/endpoints.ts | 2 - .../api/endpoints/notes/schedule/create.ts | 393 ------------------ packages/misskey-js/etc/misskey-js.api.md | 12 +- packages/misskey-js/src/api.types.ts | 1 - 5 files changed, 1 insertion(+), 411 deletions(-) delete mode 100644 packages/backend/src/server/api/endpoints/notes/schedule/create.ts diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index cb9a17fdb6..09126c46da 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -263,7 +263,6 @@ import * as ep___notes_children from './endpoints/notes/children.js'; import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; -import * as ep___notes_schedule_create from './endpoints/notes/schedule/create.js'; import * as ep___notes_schedule_delete from './endpoints/notes/schedule/delete.js'; import * as ep___notes_schedule_list from './endpoints/notes/schedule/list.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; @@ -624,7 +623,6 @@ const $notes_children: Provider = { provide: 'ep:notes/children', useClass: ep__ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes_clips.default }; const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; -const $notes_schedule_create: Provider = { provide: 'ep:notes/schedule/create', useClass: ep___notes_schedule_create.default }; const $notes_schedule_delete: Provider = { provide: 'ep:notes/schedule/delete', useClass: ep___notes_schedule_delete.default }; const $notes_schedule_list: Provider = { provide: 'ep:notes/schedule/list', useClass: ep___notes_schedule_list.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; @@ -989,7 +987,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_clips, $notes_conversation, $notes_create, - $notes_schedule_create, $notes_schedule_delete, $notes_schedule_list, $notes_delete, @@ -1348,7 +1345,6 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $notes_clips, $notes_conversation, $notes_create, - $notes_schedule_create, $notes_schedule_delete, $notes_schedule_list, $notes_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 6010589093..6a98fb21ed 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -263,7 +263,6 @@ import * as ep___notes_children from './endpoints/notes/children.js'; import * as ep___notes_clips from './endpoints/notes/clips.js'; import * as ep___notes_conversation from './endpoints/notes/conversation.js'; import * as ep___notes_create from './endpoints/notes/create.js'; -import * as ep___notes_schedule_create from './endpoints/notes/schedule/create.js'; import * as ep___notes_schedule_delete from './endpoints/notes/schedule/delete.js'; import * as ep___notes_schedule_list from './endpoints/notes/schedule/list.js'; import * as ep___notes_delete from './endpoints/notes/delete.js'; @@ -622,7 +621,6 @@ const eps = [ ['notes/clips', ep___notes_clips], ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], - ['notes/schedule/create', ep___notes_schedule_create], ['notes/schedule/delete', ep___notes_schedule_delete], ['notes/schedule/list', ep___notes_schedule_list], ['notes/delete', ep___notes_delete], diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts b/packages/backend/src/server/api/endpoints/notes/schedule/create.ts deleted file mode 100644 index 44deb7bb9b..0000000000 --- a/packages/backend/src/server/api/endpoints/notes/schedule/create.ts +++ /dev/null @@ -1,393 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and other misskey contributors - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import ms from 'ms'; -import { DataSource, In } from 'typeorm'; -import { Inject, Injectable } from '@nestjs/common'; -import type { MiUser } from '@/models/User.js'; -import type { MiNote } from '@/models/Note.js'; -import type { MiDriveFile } from '@/models/DriveFile.js'; -import type { MiChannel } from '@/models/Channel.js'; -import type { - UsersRepository, - NotesRepository, - BlockingsRepository, - DriveFilesRepository, - ChannelsRepository, - ScheduledNotesRepository, -} from '@/models/_.js'; -import type { MiNoteCreateOption } from '@/types.js'; -import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { DI } from '@/di-symbols.js'; -import { isPureRenote } from '@/misc/is-pure-renote.js'; -import { QueueService } from '@/core/QueueService.js'; -import { IdService } from '@/core/IdService.js'; -import { ApiError } from '../../../error.js'; - -export const meta = { - tags: ['notes'], - - requireCredential: true, - - prohibitMoved: true, - - limit: { - duration: ms('1hour'), - max: 300, - }, - - kind: 'write:notes', - - errors: { - noSuchRenoteTarget: { - message: 'No such renote target.', - code: 'NO_SUCH_RENOTE_TARGET', - id: 'b5c90186-4ab0-49c8-9bba-a1f76c282ba4', - }, - - cannotReRenote: { - message: 'You can not Renote a pure Renote.', - code: 'CANNOT_RENOTE_TO_A_PURE_RENOTE', - id: 'fd4cc33e-2a37-48dd-99cc-9b806eb2031a', - }, - - cannotRenoteDueToVisibility: { - message: 'You can not Renote due to target visibility.', - code: 'CANNOT_RENOTE_DUE_TO_VISIBILITY', - id: 'be9529e9-fe72-4de0-ae43-0b363c4938af', - }, - - noSuchReplyTarget: { - message: 'No such reply target.', - code: 'NO_SUCH_REPLY_TARGET', - id: '749ee0f6-d3da-459a-bf02-282e2da4292c', - }, - - cannotReplyToPureRenote: { - message: 'You can not reply to a pure Renote.', - code: 'CANNOT_REPLY_TO_A_PURE_RENOTE', - id: '3ac74a84-8fd5-4bb0-870f-01804f82ce15', - }, - - cannotCreateAlreadyExpiredPoll: { - message: 'Poll is already expired.', - code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', - id: '04da457d-b083-4055-9082-955525eda5a5', - }, - - cannotCreateAlreadyExpiredSchedule: { - message: 'Schedule is already expired.', - code: 'CANNOT_CREATE_ALREADY_EXPIRED_SCHEDULE', - id: '8a9bfb90-fc7e-4878-a3e8-d97faaf5fb07', - }, - - specifyScheduleDate: { - message: 'Please specify schedule date.', - code: 'PLEASE_SPECIFY_SCHEDULE_DATE', - id: 'c93a6ad6-f7e2-4156-a0c2-3d03529e5e0f', - }, - - noSuchChannel: { - message: 'No such channel.', - code: 'NO_SUCH_CHANNEL', - id: 'b1653923-5453-4edc-b786-7c4f39bb0bbb', - }, - noSuchSchedule: { - message: 'No such schedule.', - code: 'NO_SUCH_SCHEDULE', - id: '44dee229-8da1-4a61-856d-e3a4bbc12032', - }, - youHaveBeenBlocked: { - message: 'You have been blocked by this user.', - code: 'YOU_HAVE_BEEN_BLOCKED', - id: 'b390d7e1-8a5e-46ed-b625-06271cafd3d3', - }, - - noSuchFile: { - message: 'Some files are not found.', - code: 'NO_SUCH_FILE', - id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', - }, - - cannotRenoteOutsideOfChannel: { - message: 'Cannot renote outside of channel.', - code: 'CANNOT_RENOTE_OUTSIDE_OF_CHANNEL', - id: '33510210-8452-094c-6227-4a6c05d99f00', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, - visibleUserIds: { type: 'array', uniqueItems: true, items: { - type: 'string', format: 'misskey:id', - } }, - cw: { type: 'string', nullable: true, minLength: 1, maxLength: 100 }, - localOnly: { type: 'boolean', default: false }, - reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, - noExtractMentions: { type: 'boolean', default: false }, - noExtractHashtags: { type: 'boolean', default: false }, - noExtractEmojis: { type: 'boolean', default: false }, - replyId: { type: 'string', format: 'misskey:id', nullable: true }, - renoteId: { type: 'string', format: 'misskey:id', nullable: true }, - channelId: { type: 'string', format: 'misskey:id', nullable: true }, - - // anyOf内にバリデーションを書いても最初の一つしかチェックされない - // See https://github.com/misskey-dev/misskey/pull/10082 - text: { - type: 'string', - minLength: 1, - maxLength: MAX_NOTE_TEXT_LENGTH, - nullable: true, - }, - fileIds: { - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, - mediaIds: { - type: 'array', - uniqueItems: true, - minItems: 1, - maxItems: 16, - items: { type: 'string', format: 'misskey:id' }, - }, - poll: { - type: 'object', - nullable: true, - properties: { - choices: { - type: 'array', - uniqueItems: true, - minItems: 2, - maxItems: 10, - items: { type: 'string', minLength: 1, maxLength: 50 }, - }, - multiple: { type: 'boolean' }, - expiresAt: { type: 'integer', nullable: true }, - expiredAfter: { type: 'integer', nullable: true, minimum: 1 }, - }, - required: ['choices'], - }, - schedule: { - type: 'object', - nullable: false, - properties: { - expiresAt: { type: 'integer', nullable: false }, - }, - }, - }, - // (re)note with text, files and poll are optional - anyOf: [ - { required: ['text'] }, - { required: ['renoteId'] }, - { required: ['fileIds'] }, - { required: ['mediaIds'] }, - { required: ['poll'] }, - { required: ['schedule'] }, - ], -} as const; - -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export - constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, - - @Inject(DI.scheduledNotesRepository) - private scheduledNotesRepository: ScheduledNotesRepository, - - @Inject(DI.blockingsRepository) - private blockingsRepository: BlockingsRepository, - - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - - @Inject(DI.channelsRepository) - private channelsRepository: ChannelsRepository, - - private noteEntityService: NoteEntityService, - private queueService: QueueService, - private idService: IdService, - ) { - super(meta, paramDef, async (ps, me) => { - let visibleUsers: MiUser[] = []; - if (ps.visibleUserIds) { - visibleUsers = await this.usersRepository.findBy({ - id: In(ps.visibleUserIds), - }); - } - - let files: MiDriveFile[] = []; - const fileIds = ps.fileIds ?? ps.mediaIds ?? null; - if (fileIds != null) { - files = await this.driveFilesRepository.createQueryBuilder('file') - .where('file.userId = :userId AND file.id IN (:...fileIds)', { - userId: me.id, - fileIds, - }) - .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') - .setParameters({ fileIds }) - .getMany(); - - if (files.length !== fileIds.length) { - throw new ApiError(meta.errors.noSuchFile); - } - } - - let renote: MiNote | null = null; - if (ps.renoteId != null) { - // Fetch renote to note - renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); - - if (renote == null) { - throw new ApiError(meta.errors.noSuchRenoteTarget); - } else if (isPureRenote(renote)) { - throw new ApiError(meta.errors.cannotReRenote); - } - - // Check blocking - if (renote.userId !== me.id) { - const blockExist = await this.blockingsRepository.exist({ - where: { - blockerId: renote.userId, - blockeeId: me.id, - }, - }); - if (blockExist) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } - - if (renote.visibility === 'followers' && renote.userId !== me.id) { - // 他人のfollowers noteはreject - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); - } else if (renote.visibility === 'specified') { - // specified / direct noteはreject - throw new ApiError(meta.errors.cannotRenoteDueToVisibility); - } - - if (renote.channelId && renote.channelId !== ps.channelId) { - // チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック - // リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する - const renoteChannel = await this.channelsRepository.findOneById(renote.channelId); - if (renoteChannel == null) { - // リノートしたいノートが書き込まれているチャンネルが無い - throw new ApiError(meta.errors.noSuchChannel); - } else if (!renoteChannel.allowRenoteToExternal) { - // リノート作成のリクエストだが、対象チャンネルがリノート禁止だった場合 - throw new ApiError(meta.errors.cannotRenoteOutsideOfChannel); - } - } - } - - let reply: MiNote | null = null; - if (ps.replyId != null) { - // Fetch reply - reply = await this.notesRepository.findOneBy({ id: ps.replyId }); - - if (reply == null) { - throw new ApiError(meta.errors.noSuchReplyTarget); - } else if (isPureRenote(reply)) { - throw new ApiError(meta.errors.cannotReplyToPureRenote); - } - - // Check blocking - if (reply.userId !== me.id) { - const blockExist = await this.blockingsRepository.exist({ - where: { - blockerId: reply.userId, - blockeeId: me.id, - }, - }); - if (blockExist) { - throw new ApiError(meta.errors.youHaveBeenBlocked); - } - } - } - - if (ps.poll) { - if (typeof ps.poll.expiresAt === 'number') { - if (ps.poll.expiresAt < Date.now()) { - throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); - } - } else if (typeof ps.poll.expiredAfter === 'number') { - ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter; - } - } - - let channel: MiChannel | null = null; - if (ps.channelId != null) { - channel = await this.channelsRepository.findOneBy({ id: ps.channelId, isArchived: false }); - - if (channel == null) { - throw new ApiError(meta.errors.noSuchChannel); - } - } - if (ps.schedule) { - if (typeof ps.schedule.expiresAt === 'number') { - if (ps.schedule.expiresAt < Date.now()) { - throw new ApiError(meta.errors.cannotCreateAlreadyExpiredSchedule); - } - } - } - const note: MiNoteCreateOption = { - files, - poll: ps.poll ? { - choices: ps.poll.choices, - multiple: ps.poll.multiple ?? false, - expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, - } : undefined, - text: ps.text ?? null, - reply, - renote, - cw: ps.cw ?? null, - localOnly: ps.localOnly, - reactionAcceptance: ps.reactionAcceptance, - visibility: ps.visibility, - visibleUsers, - channel, - apMentions: ps.noExtractMentions ? [] : undefined, - apHashtags: ps.noExtractHashtags ? [] : undefined, - apEmojis: ps.noExtractEmojis ? [] : undefined, - }; - - if (ps.schedule && ps.schedule.expiresAt) { - me.token = null; - const scheduleNoteId = this.idService.gen(new Date().getTime()); - await this.scheduledNotesRepository.insert({ - id: scheduleNoteId, - note: note, - userId: me.id, - expiresAt: new Date(ps.schedule.expiresAt), - }); - - const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); - await this.queueService.ScheduleNotePostQueue.add(String(delay), { - scheduleNoteId, - }, { - delay, - removeOnComplete: true, - }); - - return { - scheduleNoteId, - scheduledNote: note, - }; - } else { - throw new ApiError(meta.errors.specifyScheduleDate); - } - }); - } -} diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 6319c49ebe..fa5faadcbc 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -1762,16 +1762,6 @@ export type Endpoints = { }; res: null; }; - 'notes/schedule/create': { - req: Partial<Note> & { - schedule: { - expiresAt: number; - }; - }; - res: { - createdNote: Note; - }; - }; 'notes/schedule/delete': { req: { scheduledNoteId: Note['id']; @@ -3063,7 +3053,7 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts // src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts -// src/api.types.ts:643:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/api.types.ts:642:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts // src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts index 12030be37e..f1a7179be9 100644 --- a/packages/misskey-js/src/api.types.ts +++ b/packages/misskey-js/src/api.types.ts @@ -512,7 +512,6 @@ export type Endpoints = { }; }; res: { createdNote: Note }; }; 'notes/delete': { req: { noteId: Note['id']; }; res: null; }; - 'notes/schedule/create': { req: Partial<Note> & { schedule: { expiresAt: number; } }; res: { createdNote: Note }; }; 'notes/schedule/delete': { req: { scheduledNoteId: Note['id']; }; res: null; }; 'notes/schedule/list': { req: TODO; res: { id: Note['id']; From 9bc6d8024fcab8855fb3e2aa86bb1d74d853eb70 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Sat, 11 Nov 2023 16:42:12 +0900 Subject: [PATCH 266/501] =?UTF-8?q?=E3=83=9A=E3=83=BC=E3=82=B8=E3=83=8D?= =?UTF-8?q?=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=AF=E3=82=A8=E3=83=AA?= =?UTF-8?q?=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/api/endpoints/notes/schedule/list.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 3b8c029983..166f3fa3bb 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -9,6 +9,7 @@ import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; import type { ScheduledNotesRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { QueryService } from '@/core/QueryService.js'; export const meta = { tags: ['notes'], @@ -57,6 +58,12 @@ export const meta = { } as const; export const paramDef = { + type: 'object', + properties: { + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + }, } as const; @Injectable() @@ -64,10 +71,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.scheduledNotesRepository) private scheduledNotesRepository: ScheduledNotesRepository, + private userEntityService: UserEntityService, + private queryService: QueryService, ) { super(meta, paramDef, async (ps, me) => { - const scheduleNotes = await this.scheduledNotesRepository.findBy({ userId: me.id }); + const query = this.queryService.makePaginationQuery(this.scheduledNotesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) + .andWhere('note.userId = :userId', { userId: me.id }); + + const scheduleNotes = await query.limit(ps.limit).getMany(); const user = await this.userEntityService.pack(me, me); const scheduleNotesPack: { id: string; From 8608df20b2ac2732232ca034159b3b190566d128 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Sat, 11 Nov 2023 17:10:23 +0900 Subject: [PATCH 267/501] =?UTF-8?q?=E3=83=95=E3=83=AD=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=A8=E3=83=B3=E3=83=89=E3=82=82MkPagination=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ScheduleNotePostProcessorService.ts | 6 +-- .../components/MkSchedulePostListDialog.vue | 42 ++++++++++++------- 2 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts index 9fd80fc521..40fc3f3e54 100644 --- a/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts +++ b/packages/backend/src/queue/processors/ScheduleNotePostProcessorService.ts @@ -24,13 +24,13 @@ export class ScheduleNotePostProcessorService { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - private noteCreateService: NoteCreateService, - private queueLoggerService: QueueLoggerService, + private noteCreateService: NoteCreateService, + private queueLoggerService: QueueLoggerService, ) { this.logger = this.queueLoggerService.logger.createSubLogger('schedule-note-post'); } - @bindThis + @bindThis public async process(job: Bull.Job<ScheduleNotePostJobData>): Promise<void> { this.scheduledNotesRepository.findOneBy({ id: job.data.scheduledNoteId }).then(async (data) => { if (!data) { diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue index cf5a4f4d31..3ab9b7f683 100644 --- a/packages/frontend/src/components/MkSchedulePostListDialog.vue +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -12,39 +12,51 @@ SPDX-License-Identifier: AGPL-3.0-only @closed="$emit('closed')" > <template #header>{{ i18n.ts._schedulePost.list }}</template> - <div v-for="item in notes"> - <MkSpacer :marginMin="14" :marginMax="16"> - <MkNoteSimple scheduled="true" :note="item.note"/> - </MkSpacer> - </div> + <MkSpacer :marginMin="14" :marginMax="16"> + <MkPagination :pagination="pagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </template> + + <template #default="{ items }"> + <div class="_gaps"> + <MkNoteSimple v-for="item in items" :key="item.id" :scheduled="true" :note="item.note"/> + </div> + </template> + </MkPagination> + </MkSpacer> </MkModalWindow> </template> <script lang="ts" setup> -import { onMounted, ref } from 'vue'; +import { ref } from 'vue'; import * as Misskey from 'misskey-js'; +import type { Paging } from '@/components/MkPagination.vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; -import * as os from '@/os.js'; +import MkPagination from '@/components/MkPagination.vue'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; import { i18n } from '@/i18n.js'; +import { infoImageUrl } from '@/instance.js'; const emit = defineEmits<{ (ev: 'ok', selected: Misskey.entities.UserDetailed): void; (ev: 'cancel'): void; - (ev: 'closed'): void; + (ev: 'c-losed'): void; }>(); -let dialogEl = $ref(); -const notes = ref([]); +const dialogEl = ref(); const cancel = () => { emit('cancel'); - dialogEl.close(); + dialogEl.value.close(); }; -onMounted(async () => { - notes.value = await os.api('notes/schedule/list'); -}); - +const pagination: Paging = { + endpoint: 'notes/schedule/list', + limit: 10, +}; </script> <style lang="scss" module> From 7694d17b2e0c4cc0ade106db07171ae5dc35d610 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Sat, 11 Nov 2023 18:53:25 +0900 Subject: [PATCH 268/501] =?UTF-8?q?(add)=20=E3=83=AD=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E5=88=B6=E5=BE=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/backend/src/core/RoleService.ts | 3 +++ .../src/server/api/endpoints/notes/create.ts | 13 +++++++++++ .../api/endpoints/notes/schedule/list.ts | 1 + .../frontend/src/components/MkPostForm.vue | 6 ++--- packages/frontend/src/const.ts | 1 + packages/frontend/src/navbar.ts | 1 + .../frontend/src/pages/admin/roles.editor.vue | 22 ++++++++++++++++++- packages/frontend/src/pages/admin/roles.vue | 8 +++++++ 10 files changed, 53 insertions(+), 4 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 00dc9eb9e7..e671747c26 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1665,6 +1665,7 @@ export interface Locale { "gtlAvailable": string; "ltlAvailable": string; "canPublicNote": string; + "canScheduleNote": string; "canInvite": string; "inviteLimit": string; "inviteLimitCycle": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 459692f74e..454fd32aec 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1574,6 +1574,7 @@ _role: gtlAvailable: "グローバルタイムラインの閲覧" ltlAvailable: "ローカルタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" + canScheduleNote: "予約投稿の許可" canInvite: "サーバー招待コードの発行" inviteLimit: "招待コードの作成可能数" inviteLimitCycle: "招待コードの発行間隔" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index d6a414694a..d70665ab89 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -27,6 +27,7 @@ export type RolePolicies = { gtlAvailable: boolean; ltlAvailable: boolean; canPublicNote: boolean; + canScheduleNote: boolean; canInvite: boolean; inviteLimit: number; inviteLimitCycle: number; @@ -53,6 +54,7 @@ export const DEFAULT_POLICIES: RolePolicies = { gtlAvailable: true, ltlAvailable: true, canPublicNote: true, + canScheduleNote: true, canInvite: false, inviteLimit: 0, inviteLimitCycle: 60 * 24 * 7, @@ -303,6 +305,7 @@ export class RoleService implements OnApplicationShutdown { gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)), ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)), canPublicNote: calc('canPublicNote', vs => vs.some(v => v === true)), + canScheduleNote: calc('canScheduleNote', vs => vs.some(v => v === true)), canInvite: calc('canInvite', vs => vs.some(v => v === true)), inviteLimit: calc('inviteLimit', vs => Math.max(...vs)), inviteLimitCycle: calc('inviteLimitCycle', vs => Math.max(...vs)), diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 8a04ec540f..698b57ade9 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -18,6 +18,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; import { QueueService } from '@/core/QueueService.js'; import { IdService } from '@/core/IdService.js'; +import { RoleService } from '@/core/RoleService.js'; import { DI } from '@/di-symbols.js'; import { isPureRenote } from '@/misc/is-pure-renote.js'; import { ApiError } from '../../error.js'; @@ -132,6 +133,12 @@ export const meta = { code: 'NO_SUCH_SCHEDULE', id: '44dee229-8da1-4a61-856d-e3a4bbc12032', }, + rolePermissionDenied: { + message: 'You are not assigned to a required role.', + code: 'ROLE_PERMISSION_DENIED', + kind: 'permission', + id: '7f86f06f-7e15-4057-8561-f4b6d4ac755a', + }, }, } as const; @@ -234,6 +241,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private noteEntityService: NoteEntityService, private noteCreateService: NoteCreateService, + private roleService: RoleService, private queueService: QueueService, private idService: IdService, ) { @@ -375,6 +383,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }; if (ps.schedule) { + const canCreateScheduledNote = (await this.roleService.getUserPolicies(me.id)).canScheduleNote; + if (!canCreateScheduledNote) { + throw new ApiError(meta.errors.rolePermissionDenied); + } + if (!ps.schedule.expiresAt) { throw new ApiError(meta.errors.specifyScheduleDate); } diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 166f3fa3bb..0362805465 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -15,6 +15,7 @@ export const meta = { tags: ['notes'], requireCredential: true, + requireRolePolicy: 'canScheduleNote', res: { type: 'array', optional: false, nullable: false, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 2458de5a5e..e43c540ee2 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -929,13 +929,13 @@ function openOtherSettingsMenu(ev: MouseEvent) { text: i18n.ts.reactionAcceptance, icon: reactionAcceptanceIcon, action: toggleReactionAcceptance, - }, { + }, ($i.policies?.canScheduleNote) ? { type: 'button', text: i18n.ts.schedulePost, icon: 'ti ti-calendar-time', indicate: (schedule != null), action: toggleSchedule, - }, null, { + } : undefined, ...(($i.policies?.canScheduleNote) ? [ null, { type: 'button', text: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', @@ -944,7 +944,7 @@ function openOtherSettingsMenu(ev: MouseEvent) { emit('cancel'); listSchedulePost(); }, - }], ev.currentTarget ?? ev.target, { + }] : [])], ev.currentTarget ?? ev.target, { align: 'right', }); } diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index b3071fd924..dd135b14cd 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -61,6 +61,7 @@ export const ROLE_POLICIES = [ 'gtlAvailable', 'ltlAvailable', 'canPublicNote', + 'canScheduleNote', 'canInvite', 'inviteLimit', 'inviteLimitCycle', diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index 99a02671eb..c0e0d40cf6 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -174,6 +174,7 @@ export const navbarItemDef = reactive({ scheduledNotes: { title: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', + show: computed(() => $i && $i.policies?.canScheduleNote), action: (ev) => { os.listSchedulePost(); }, diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 1db99e61f4..ace76753ce 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,6 +160,26 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canScheduleNote, 'canScheduleNote'])"> + <template #label>{{ i18n.ts._role._options.canScheduleNote }}</template> + <template #suffix> + <span v-if="role.policies.canScheduleNote.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.canScheduleNote.value ? i18n.ts.yes : i18n.ts.no }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canScheduleNote)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.canScheduleNote.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkSwitch v-model="role.policies.canScheduleNote.value" :disabled="role.policies.canScheduleNote.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + <MkRange v-model="role.policies.canScheduleNote.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix> @@ -549,7 +569,7 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkRange from '@/components/MkRange.vue'; import FormSlot from '@/components/form/slot.vue'; import { i18n } from '@/i18n.js'; -import { ROLE_POLICIES } from '@/const'; +import { ROLE_POLICIES } from '@/const.js'; import { instance } from '@/instance.js'; import { deepClone } from '@/scripts/clone.js'; diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index d3f3773564..fc47e0ba3e 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -48,6 +48,14 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canScheduleNote, 'canScheduleNote'])"> + <template #label>{{ i18n.ts._role._options.canScheduleNote }}</template> + <template #suffix>{{ policies.canScheduleNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canScheduleNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> From 271c872c97f215ef5d8e0be62251dd422a52e5b1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 14 Nov 2023 17:27:52 +0900 Subject: [PATCH 269/501] expiresAt to scheduledAt Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../backend/migration/1699949373507-schedulenote2.js | 11 +++++++++++ packages/backend/src/models/ScheduledNote.ts | 2 +- .../backend/src/server/api/endpoints/notes/create.ts | 8 ++++---- .../src/server/api/endpoints/notes/schedule/list.ts | 6 +++--- packages/frontend/src/components/MkPostForm.vue | 10 +++++----- packages/frontend/src/components/MkScheduleEditor.vue | 10 +++++----- 6 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 packages/backend/migration/1699949373507-schedulenote2.js diff --git a/packages/backend/migration/1699949373507-schedulenote2.js b/packages/backend/migration/1699949373507-schedulenote2.js new file mode 100644 index 0000000000..b8d8a6394b --- /dev/null +++ b/packages/backend/migration/1699949373507-schedulenote2.js @@ -0,0 +1,11 @@ +export class Schedulenote21699949373507 { + name = 'Schedulenote21699949373507' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note_schedule" RENAME COLUMN "expiresAt" TO "scheduledAt"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note_schedule" RENAME COLUMN "scheduledAt" TO "expiresAt"`); + } +} diff --git a/packages/backend/src/models/ScheduledNote.ts b/packages/backend/src/models/ScheduledNote.ts index 7099f1f366..513ad2f845 100644 --- a/packages/backend/src/models/ScheduledNote.ts +++ b/packages/backend/src/models/ScheduledNote.ts @@ -23,5 +23,5 @@ export class MiScheduledNote { public userId: MiUser['id']; @Column('timestamp with time zone') - public expiresAt: Date; + public scheduledAt: Date; } diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 698b57ade9..20f1cfd4e7 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -202,7 +202,7 @@ export const paramDef = { type: 'object', nullable: true, properties: { - expiresAt: { type: 'integer', nullable: false }, + scheduledAt: { type: 'integer', nullable: false }, }, }, }, @@ -388,7 +388,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.rolePermissionDenied); } - if (!ps.schedule.expiresAt) { + if (!ps.schedule.scheduledAt) { throw new ApiError(meta.errors.specifyScheduleDate); } @@ -398,10 +398,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- id: scheduledNoteId, note: note, userId: me.id, - expiresAt: new Date(ps.schedule.expiresAt), + scheduledAt: new Date(ps.schedule.scheduledAt), }); - const delay = new Date(ps.schedule.expiresAt).getTime() - Date.now(); + const delay = new Date(ps.schedule.scheduledAt).getTime() - Date.now(); await this.queueService.ScheduleNotePostQueue.add(String(delay), { scheduledNoteId, }, { diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 0362805465..83c8a2597d 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -45,7 +45,7 @@ export const meta = { }, }, userId: { type: 'string', optional: false, nullable: false }, - expiresAt: { type: 'string', optional: false, nullable: false }, + scheduledAt: { type: 'string', optional: false, nullable: false }, }, }, }, @@ -97,14 +97,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- isSchedule: boolean; }; userId: string; - expiresAt: string; + scheduledAt: string; }[] = scheduleNotes.map((item: any) => { return { ...item, note: { ...item.note, user: user, - createdAt: new Date(item.expiresAt), + createdAt: new Date(item.scheduledAt), isSchedule: true, // ↓TODO: NoteのIDに予約投稿IDを入れたくない(本来別ものなため) id: item.id, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index e43c540ee2..8f0737b146 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -177,7 +177,7 @@ let poll = $ref<{ expiredAfter: string | null; } | null>(null); let schedule = $ref<{ - expiresAt: string | null; + scheduledAt: string | null; }| null>(null); let useCw = $ref(false); let showPreview = $ref(defaultStore.state.showPreview); @@ -405,7 +405,7 @@ function toggleSchedule() { schedule = null; } else { schedule = { - expiresAt: null, + scheduledAt: null, }; } } @@ -775,8 +775,8 @@ async function post(ev?: MouseEvent) { } } - if (postData.schedule?.expiresAt && typeof postData.schedule.expiresAt === 'string') { - postData.schedule.expiresAt = parseInt(postData.schedule.expiresAt); + if (postData.schedule?.scheduledAt && typeof postData.schedule.scheduledAt === 'string') { + postData.schedule.scheduledAt = parseInt(postData.schedule.scheduledAt); } let token = undefined; @@ -935,7 +935,7 @@ function openOtherSettingsMenu(ev: MouseEvent) { icon: 'ti ti-calendar-time', indicate: (schedule != null), action: toggleSchedule, - } : undefined, ...(($i.policies?.canScheduleNote) ? [ null, { + } : undefined, ...(($i.policies?.canScheduleNote) ? [null, { type: 'button', text: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue index d53451f5bb..f6336ddf69 100644 --- a/packages/frontend/src/components/MkScheduleEditor.vue +++ b/packages/frontend/src/components/MkScheduleEditor.vue @@ -26,19 +26,19 @@ import { i18n } from '@/i18n.js'; const props = defineProps<{ modelValue: { - expiresAt: string; + scheduledAt: string; }; }>(); const emit = defineEmits<{ (ev: 'update:modelValue', v: { - expiresAt: string; + scheduledAt: string; }): void; }>(); const atDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd')); const atTime = ref('00:00'); -if ( props.modelValue && props.modelValue.expiresAt) { - atDate.value = atTime.value = props.modelValue.expiresAt; +if ( props.modelValue && props.modelValue.scheduledAt) { + atDate.value = atTime.value = props.modelValue.scheduledAt; } function get() { @@ -48,7 +48,7 @@ function get() { return { ...( - props.modelValue ? { expiresAt: calcAt() } : {} + props.modelValue ? { scheduledAt: calcAt() } : {} ), }; } From 380c8c32831ccda0b69db9f5683ac85b8fb6ec8f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 14 Nov 2023 18:39:21 +0900 Subject: [PATCH 270/501] =?UTF-8?q?scheduledAt=E3=82=92ISO8601=E3=81=A7?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/src/server/api/endpoints/notes/create.ts | 2 +- packages/frontend/src/components/MkPostForm.vue | 4 ---- packages/frontend/src/components/MkScheduleEditor.vue | 4 ++-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 20f1cfd4e7..3655fba23a 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -202,7 +202,7 @@ export const paramDef = { type: 'object', nullable: true, properties: { - scheduledAt: { type: 'integer', nullable: false }, + scheduledAt: { type: 'string', nullable: false }, }, }, }, diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 8f0737b146..804bdbe49a 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -775,10 +775,6 @@ async function post(ev?: MouseEvent) { } } - if (postData.schedule?.scheduledAt && typeof postData.schedule.scheduledAt === 'string') { - postData.schedule.scheduledAt = parseInt(postData.schedule.scheduledAt); - } - let token = undefined; if (postAccount) { diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue index f6336ddf69..b5083ad050 100644 --- a/packages/frontend/src/components/MkScheduleEditor.vue +++ b/packages/frontend/src/components/MkScheduleEditor.vue @@ -43,12 +43,12 @@ if ( props.modelValue && props.modelValue.scheduledAt) { function get() { const calcAt = () => { - return new Date(`${atDate.value} ${atTime.value}`).getTime(); + return new Date(`${atDate.value}T${atTime.value}`).toISOString(); }; return { ...( - props.modelValue ? { scheduledAt: calcAt() } : {} + props.modelValue ? { scheduledAt: calcAt() } : '' ), }; } From 29e2f886aa8791582a1ea0542373c6a71970eaca Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 14 Nov 2023 18:48:13 +0900 Subject: [PATCH 271/501] =?UTF-8?q?id=E3=81=AE=E3=81=A8=E3=81=93=E3=81=ABs?= =?UTF-8?q?cheduledAt=E3=81=8B=E3=82=89=E7=94=9F=E6=88=90=E3=81=97?= =?UTF-8?q?=E3=81=9Fid=E3=82=92=E5=85=A5=E3=82=8C=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../backend/src/server/api/endpoints/notes/schedule/list.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 83c8a2597d..9374e0b669 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -10,6 +10,7 @@ import { DI } from '@/di-symbols.js'; import type { ScheduledNotesRepository } from '@/models/_.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { QueryService } from '@/core/QueryService.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['notes'], @@ -73,6 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.scheduledNotesRepository) private scheduledNotesRepository: ScheduledNotesRepository, + private idService: IdService, private userEntityService: UserEntityService, private queryService: QueryService, ) { @@ -107,7 +109,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- createdAt: new Date(item.scheduledAt), isSchedule: true, // ↓TODO: NoteのIDに予約投稿IDを入れたくない(本来別ものなため) - id: item.id, + id: this.idService.gen(item.scheduledAt.getTime()), }, }; }); From ea96b4cb71b5bb6424d6febf85be0b2c2dc5846a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 14 Nov 2023 18:54:49 +0900 Subject: [PATCH 272/501] =?UTF-8?q?scheduledAt=E3=81=AB=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=81=A8=E3=81=8D=E3=81=AE=E5=A4=89=E6=9B=B4=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/components/MkPostForm.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 804bdbe49a..031b108db4 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -806,8 +806,8 @@ async function post(ev?: MouseEvent) { } poll = null; - if (postData.schedule?.expiresAt) { - const d = new Date(postData.schedule.expiresAt); + if (postData.schedule?.scheduledAt) { + const d = new Date(postData.schedule.scheduledAt); const str = dateTimeFormat.format(d); os.toast(i18n.t('_schedulePost.willBePostedAtX', { date: str })); } From 66bb2e15c8b4e031ef1f71adc3bf99a2209f18a5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 15 Nov 2023 13:46:17 +0900 Subject: [PATCH 273/501] emoji request --- .../api/endpoints/admin/emoji/add-draft.ts | 78 +++++++++++-------- 1 file changed, 45 insertions(+), 33 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index aae6f6df39..d91ddfca96 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -6,6 +6,7 @@ import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; import { MetaService } from '@/core/MetaService.js'; +import {DriveService} from "@/core/DriveService.js"; export const meta = { tags: ['admin'], @@ -53,11 +54,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private metaService: MetaService, private customEmojiService: CustomEmojiService, private moderationLogService: ModerationLogService, + private driveService: DriveService, ) { super(meta, paramDef, async (ps, me) => { - const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + let driveFile; + let tmp = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (tmp == null) throw new ApiError(meta.errors.noSuchFile); - if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + try { + driveFile = await this.driveService.uploadFromUrl({ url: tmp.url , user: null, force: true }); + } catch (e) { + throw new ApiError(); + } const emoji = await this.customEmojiService.add({ driveFile, @@ -72,40 +80,44 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { roleIdsThatCanBeUsedThisEmojiAsReaction: [], }); const {ApiBase,EmojiBotToken,DiscordWebhookUrl} = (await this.metaService.fetch()) - const data_disc = {"username": "絵文字追加通知ちゃん", - 'content': - '絵文字名 : :'+ ps.name +':\n' + - 'カテゴリ : ' + ps.category + '\n'+ - 'ライセンス : '+ ps.license + '\n'+ - 'タグ : '+ps.aliases+ '\n'+ - '追加したユーザー : ' + '@'+me.username + '\n' + + if (EmojiBotToken){ + const data_Miss = { + 'i': EmojiBotToken, + 'text': + '絵文字名 : :' + ps.name + ':\n' + + 'カテゴリ : ' + ps.category + '\n' + + 'ライセンス : ' + ps.license + '\n' + + 'タグ : ' + ps.aliases + '\n' + + '追加したユーザー : ' + '@' + me.username + '\n' + }; + await fetch(ApiBase+'/notes/create', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body:JSON.stringify( data_Miss) + }) } - const data_Miss = { - 'i': EmojiBotToken, - 'text': - '絵文字名 : :' + ps.name + ':\n' + - 'カテゴリ : ' + ps.category + '\n' + - 'ライセンス : ' + ps.license + '\n' + - 'タグ : ' + ps.aliases + '\n' + - '追加したユーザー : ' + '@' + me.username + '\n' - }; + if (DiscordWebhookUrl){ + const data_disc = {"username": "絵文字追加通知ちゃん", + 'content': + '絵文字名 : :'+ ps.name +':\n' + + 'カテゴリ : ' + ps.category + '\n'+ + 'ライセンス : '+ ps.license + '\n'+ + 'タグ : '+ps.aliases+ '\n'+ + '追加したユーザー : ' + '@'+me.username + '\n' + } + await fetch(DiscordWebhookUrl, { + 'method':'post', + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data_disc), + }) + } - await fetch(ApiBase+'/notes/create', { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body:JSON.stringify( data_Miss) - }) - - await fetch(DiscordWebhookUrl, { - 'method':'post', - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data_disc), - }) return { id: emoji.id, From 108714267d54be6940a2e4654ecfbb17c8f607f2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Wed, 15 Nov 2023 14:26:07 +0900 Subject: [PATCH 274/501] Update changelog Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69cb64c243..1c3809104d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### General - Feat: コントロールパネルの「照会」から、入力されたメールアドレスを持つユーザーを検索できるようになりました +- Feat: 絵文字の申請機能が追加されました ### Client - Fix: アイコンデコレーションが複数の場所で見切れている問題を修正 From 4e3aacc113327c591c5979d133b0d50d1045b6d2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 19 Nov 2023 17:55:34 +0900 Subject: [PATCH 275/501] bug fix --- packages/frontend/src/components/MkMenu.vue | 4 ++-- packages/frontend/src/components/MkSwitch.vue | 8 +++++--- packages/frontend/src/components/MkTimeline.vue | 13 +++++++------ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue index 7cbe98d830..96e6d34be1 100644 --- a/packages/frontend/src/components/MkMenu.vue +++ b/packages/frontend/src/components/MkMenu.vue @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="item.indicate" :class="$style.indicator"><i class="_indicatorCircle"></i></span> </button> <button v-else-if="item.type === 'switch'" role="menuitemcheckbox" :tabindex="i" class="_button" :class="[$style.item, $style.switch, { [$style.switchDisabled]: item.disabled } , { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @click="switchItem(item)" @mouseenter.passive="onItemMouseEnter(item)" @mouseleave.passive="onItemMouseLeave(item)"> - <MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)"/> + <MkSwitchButton :class="$style.switchButton" :checked="item.ref" :disabled="item.disabled" @toggle="switchItem(item)" model-value/> <span :class="$style.switchText">{{ item.text }}</span> </button> <button v-else-if="item.type === 'parent'" class="_button" role="menuitem" :tabindex="i" :class="[$style.item, $style.parent, { [$style.childShowing]: childShowingItem === item } , { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]" @mouseenter="preferClick ? null : showChildren(item, $event)" @click="!preferClick ? null : showChildren(item, $event)"> @@ -69,7 +69,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { isTouchUsing } from '@/scripts/touch.js'; import {defaultStore} from '@/store.js' - +import MkSwitchButton from '@/components/MkSwitch.button.vue'; let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const childrenCache = new WeakMap<MenuParent, MenuItem[]>(); diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index db71f010e7..1ad6ac1040 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div :class="[$style.root, { [$style.disabled]: disabled && gamingType === '', [$style.checked]: checked && gamingType === '' }]"> +<div :class="[$style.root, { [$style.disabled]: disabled , [$style.checked]: checked }]"> <input ref="input" type="checkbox" @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only @keydown.enter="toggle" > <XButton :checked="checked" :disabled="disabled" @toggle="toggle"/> - <span :class="$style.body,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}"> + <span :class="[$style.body,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"> <!-- TODO: 無名slotの方は廃止 --> <span :class="$style.label"> <span @click="toggle"> @@ -30,7 +30,9 @@ SPDX-License-Identifier: AGPL-3.0-only import {toRefs, Ref, ref, computed, watch} from 'vue'; import XButton from '@/components/MkSwitch.button.vue'; import {defaultStore} from "@/store.js"; -let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); +const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); + +console.log(gamingType.value) const props = defineProps<{ modelValue: boolean | Ref<boolean>; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index e45cf2f34f..34089096d9 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -107,12 +107,6 @@ function connectChannel() { withFiles: props.onlyFiles ? true : undefined, }); } else if (props.src === 'media') { - endpoint = 'notes/hybrid-timeline'; - query = { - withFiles: true, - withRenotes: props.withRenotes, - withReplies: props.withReplies, - }; connection = stream.useChannel('hybridTimeline', { withFiles: true, withRenotes: props.withRenotes, @@ -198,6 +192,13 @@ function updatePaginationQuery() { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, }; + }else if(props.src === 'media'){ + endpoint = 'notes/hybrid-timeline'; + query = { + withFiles: true, + withRenotes: props.withRenotes, + withReplies: props.withReplies, + }; } else if (props.src === 'mentions') { endpoint = 'notes/mentions'; query = null; From ea80860de77762a9c052488758bcc5bb34dc3f79 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 19 Nov 2023 22:12:31 +0900 Subject: [PATCH 276/501] bug fix --- packages/frontend/src/components/MkSwitch.vue | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index 1ad6ac1040..f384d9ea3d 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only @keydown.enter="toggle" > <XButton :checked="checked" :disabled="disabled" @toggle="toggle"/> - <span :class="[$style.body,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}]"> + <span :class="$style.body,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light'}"> <!-- TODO: 無名slotの方は廃止 --> <span :class="$style.label"> <span @click="toggle"> @@ -106,22 +106,6 @@ const toggle = () => { display: block; transition: inherit; color: var(--fg); - &.gamingDark { - background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); - background-size: 1800% 1800%; - -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; - -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; - animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite; - - } - &.gamingLight{ - background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); - background-size: 1800% 1800% !important; - -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.25, 0.25, 1) infinite !important; - - } } .label { From a2ea072deb685d4e28f7a8a9619ff78e220dd5c5 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 23 Nov 2023 15:57:10 +0900 Subject: [PATCH 277/501] fix api extractor --- packages/misskey-js/etc/misskey-js.api.md | 24 +++-------------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index 4dbe946d2e..fa5faadcbc 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -345,18 +345,6 @@ export type Endpoints = { }; res: null; }; - 'admin/unset-user-avatar': { - req: { - userId: User['id']; - }; - res: null; - }; - 'admin/unset-user-banner': { - req: { - userId: User['id']; - }; - res: null; - }; 'admin/delete-logs': { req: NoParams; res: null; @@ -2703,16 +2691,10 @@ type ModerationLog = { } | { type: 'resolveAbuseReport'; info: ModerationLogPayloads['resolveAbuseReport']; -} | { - type: 'unsetUserAvatar'; - info: ModerationLogPayloads['unsetUserAvatar']; -} | { - type: 'unsetUserBanner'; - info: ModerationLogPayloads['unsetUserBanner']; }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; @@ -3070,8 +3052,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // Warnings were encountered during analysis: // // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts -// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts -// src/api.types.ts:634:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts +// src/api.types.ts:642:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts // src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts From 6c3d6ba955912c7f4953b5df20e0e571972095b4 Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 23 Nov 2023 17:20:37 +0900 Subject: [PATCH 278/501] =?UTF-8?q?id=E3=81=AE=E6=B7=B7=E5=90=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../src/server/api/endpoints/notes/create.ts | 5 +- .../api/endpoints/notes/schedule/list.ts | 55 ++----------------- .../frontend/src/components/MkNoteSimple.vue | 18 ++++-- 5 files changed, 24 insertions(+), 56 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 851f2e7ea7..60f09d8117 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2493,6 +2493,7 @@ export interface Locale { "localTime": string; "addSchedule": string; "willBePostedAtX": string; + "deleteAreYouSure": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 02888994e9..16c9fc5888 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2381,3 +2381,4 @@ _schedulePost: localTime: "端末に設定されているタイムゾーンの時刻で投稿されます。" addSchedule: "予約設定" willBePostedAtX: "{date}に投稿予約しました。" + deleteAreYouSure: "予約投稿を削除しますか?" diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 97b7c31d6c..1b05c51fd2 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -311,7 +311,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (renote.channelId && renote.channelId !== ps.channelId) { // チャンネルのノートに対しリノート要求がきたとき、チャンネル外へのリノート可否をチェック // リノートのユースケースのうち、チャンネル内→チャンネル外は少数だと考えられるため、JOINはせず必要な時に都度取得する - const renoteChannel = await this.channelsRepository.findOneById(renote.channelId); + const renoteChannel = await this.channelsRepository.findOneBy({ id: renote.channelId }); if (renoteChannel == null) { // リノートしたいノートが書き込まれているチャンネルが無い throw new ApiError(meta.errors.noSuchChannel); @@ -391,6 +391,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }; if (ps.schedule) { + // 予約投稿 const canCreateScheduledNote = (await this.roleService.getUserPolicies(me.id)).canScheduleNote; if (!canCreateScheduledNote) { throw new ApiError(meta.errors.rolePermissionDenied); @@ -410,7 +411,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); const delay = new Date(ps.schedule.scheduledAt).getTime() - Date.now(); - await this.queueService.ScheduleNotePostQueue.add(String(delay), { + await this.queueService.ScheduleNotePostQueue.add(delay.toString(), { scheduledNoteId, }, { jobId: scheduledNoteId, diff --git a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts index 9374e0b669..9f89cf2a7f 100644 --- a/packages/backend/src/server/api/endpoints/notes/schedule/list.ts +++ b/packages/backend/src/server/api/endpoints/notes/schedule/list.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { DI } from '@/di-symbols.js'; @@ -23,37 +22,8 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - properties: { - id: { type: 'string', optional: false, nullable: false }, - note: { - type: 'object', - optional: false, nullable: false, - properties: { - id: { type: 'string', optional: false, nullable: false }, - text: { type: 'string', optional: false, nullable: false }, - files: { type: 'array', optional: false, nullable: false, items: { type: 'any' } }, - localOnly: { type: 'boolean', optional: false, nullable: false }, - visibility: { type: 'string', optional: false, nullable: false }, - visibleUsers: { type: 'array', optional: false, nullable: false, items: { type: 'any' } }, - reactionAcceptance: { type: 'string', optional: false, nullable: false }, - user: { - type: 'object', - optional: false, nullable: false, - ref: 'User', - }, - createdAt: { type: 'string', optional: false, nullable: false }, - isSchedule: { type: 'boolean', optional: false, nullable: false }, - }, - }, - userId: { type: 'string', optional: false, nullable: false }, - scheduledAt: { type: 'string', optional: false, nullable: false }, - }, }, }, - limit: { - duration: ms('1hour'), - max: 300, - }, errors: { }, @@ -84,32 +54,17 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const scheduleNotes = await query.limit(ps.limit).getMany(); const user = await this.userEntityService.pack(me, me); - const scheduleNotesPack: { - id: string; - note: { - id: string; - text: string; - files: any[]; - localOnly: boolean; - visibility: string; - visibleUsers: any[]; - reactionAcceptance: string; - user: any; - createdAt: string; - isSchedule: boolean; - }; - userId: string; - scheduledAt: string; - }[] = scheduleNotes.map((item: any) => { + const scheduleNotesPack = scheduleNotes.map((item) => { return { ...item, + scheduledAt: new Date(item.scheduledAt).toISOString(), note: { ...item.note, user: user, - createdAt: new Date(item.scheduledAt), + createdAt: new Date(item.scheduledAt).toISOString(), isSchedule: true, - // ↓TODO: NoteのIDに予約投稿IDを入れたくない(本来別ものなため) - id: this.idService.gen(item.scheduledAt.getTime()), + id: null, + scheduledNoteId: item.id, }, }; }); diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index dd01cbab31..e47429113f 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -36,13 +36,23 @@ import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; const isDeleted = ref(false); const props = defineProps<{ - note: Misskey.entities.Note & {isSchedule? : boolean}; + note: Misskey.entities.Note & { + id: string | null; + isSchedule?: boolean; + scheduledNoteId?: string; + }; }>(); async function deleteScheduleNote() { - if (!props.note.isSchedule) return; - // スケジュールつきノートの場合は、ノートIDのフィールドに予約投稿ID(scheduledNoteId)が入るので注意!!!! - await os.apiWithDialog('notes/schedule/delete', { scheduledNoteId: props.note.id }) + if (!props.note.isSchedule || !props.note.scheduledNoteId) return; + + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts._schedulePost.deleteAreYouSure, + }); + if (canceled) return; + + await os.apiWithDialog('notes/schedule/delete', { scheduledNoteId: props.note.scheduledNoteId }) .then(() => { isDeleted.value = true; }); From c95cbbc9b622381dbd3b4d9abba50cf9ce13ce6f Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Thu, 23 Nov 2023 17:22:28 +0900 Subject: [PATCH 279/501] fix api extractor --- packages/misskey-js/etc/misskey-js.api.md | 24 ++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md index fa5faadcbc..f9ad3b7422 100644 --- a/packages/misskey-js/etc/misskey-js.api.md +++ b/packages/misskey-js/etc/misskey-js.api.md @@ -345,6 +345,18 @@ export type Endpoints = { }; res: null; }; + 'admin/unset-user-avatar': { + req: { + userId: User['id']; + }; + res: null; + }; + 'admin/unset-user-banner': { + req: { + userId: User['id']; + }; + res: null; + }; 'admin/delete-logs': { req: NoParams; res: null; @@ -2691,10 +2703,16 @@ type ModerationLog = { } | { type: 'resolveAbuseReport'; info: ModerationLogPayloads['resolveAbuseReport']; +} | { + type: 'unsetUserAvatar'; + info: ModerationLogPayloads['unsetUserAvatar']; +} | { + type: 'unsetUserBanner'; + info: ModerationLogPayloads['unsetUserBanner']; }); // @public (undocumented) -export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration"]; +export const moderationLogTypes: readonly ["updateServerSettings", "suspend", "unsuspend", "updateUserNote", "addCustomEmoji", "updateCustomEmoji", "deleteCustomEmoji", "assignRole", "unassignRole", "createRole", "updateRole", "deleteRole", "clearQueue", "promoteQueue", "deleteDriveFile", "deleteNote", "createGlobalAnnouncement", "createUserAnnouncement", "updateGlobalAnnouncement", "updateUserAnnouncement", "deleteGlobalAnnouncement", "deleteUserAnnouncement", "resetPassword", "suspendRemoteInstance", "unsuspendRemoteInstance", "markSensitiveDriveFile", "unmarkSensitiveDriveFile", "resolveAbuseReport", "createInvitation", "createAd", "updateAd", "deleteAd", "createAvatarDecoration", "updateAvatarDecoration", "deleteAvatarDecoration", "unsetUserAvatar", "unsetUserBanner"]; // @public (undocumented) export const mutedNoteReasons: readonly ["word", "manual", "spam", "other"]; @@ -3052,8 +3070,8 @@ type UserSorting = '+follower' | '-follower' | '+createdAt' | '-createdAt' | '+u // Warnings were encountered during analysis: // // src/api.types.ts:16:32 - (ae-forgotten-export) The symbol "TODO" needs to be exported by the entry point index.d.ts -// src/api.types.ts:18:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts -// src/api.types.ts:642:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts +// src/api.types.ts:20:25 - (ae-forgotten-export) The symbol "NoParams" needs to be exported by the entry point index.d.ts +// src/api.types.ts:644:18 - (ae-forgotten-export) The symbol "ShowUserReq" needs to be exported by the entry point index.d.ts // src/entities.ts:116:2 - (ae-forgotten-export) The symbol "notificationTypes_2" needs to be exported by the entry point index.d.ts // src/entities.ts:627:2 - (ae-forgotten-export) The symbol "ModerationLogPayloads" needs to be exported by the entry point index.d.ts // src/streaming.types.ts:33:4 - (ae-forgotten-export) The symbol "FIXME" needs to be exported by the entry point index.d.ts From 359a4a83a0cb1518510210181098a9635695a330 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Fri, 24 Nov 2023 22:46:54 +0900 Subject: [PATCH 280/501] bug fix --- packages/backend/src/models/json-schema/user.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index 289bfc4afd..b26e40ae57 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -143,7 +143,8 @@ export const packedUserLiteSchema = { }, }, }, -} as const; +} +} as const export const packedUserDetailedNotMeOnlySchema = { type: 'object', From 28ee4d47c0b240db6eb30e14291e44862b1f7484 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 26 Nov 2023 05:18:59 +0900 Subject: [PATCH 281/501] =?UTF-8?q?Feat:=20=E4=BA=88=E7=B4=84=E6=8A=95?= =?UTF-8?q?=E7=A8=BF=E3=81=AE=E5=89=8A=E9=99=A4=E3=81=97=E3=81=A6=E7=B7=A8?= =?UTF-8?q?=E9=9B=86=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../frontend/src/components/MkNoteSimple.vue | 24 +++++++++++++++++-- .../frontend/src/components/MkPostForm.vue | 5 ++++ .../src/components/MkScheduleEditor.vue | 6 +++-- .../components/MkSchedulePostListDialog.vue | 13 +++++----- 6 files changed, 40 insertions(+), 10 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 60f09d8117..69b01f0a84 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2494,6 +2494,7 @@ export interface Locale { "addSchedule": string; "willBePostedAtX": string; "deleteAreYouSure": string; + "deleteAndEditConfirm": string; }; } declare const locales: { diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 16c9fc5888..755d2715b0 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2382,3 +2382,4 @@ _schedulePost: addSchedule: "予約設定" willBePostedAtX: "{date}に投稿予約しました。" deleteAreYouSure: "予約投稿を削除しますか?" + deleteAndEditConfirm: "予約投稿を削除して編集しますか?" diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index e47429113f..8a130e95f5 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSubNoteContent :class="$style.text" :note="note"/> </div> <div v-if="note.isSchedule" style="margin-top: 10px;"> - <MkButton :class="$style.button" inline @click="editScheduleNote(note.id)">{{ i18n.ts.edit }}</MkButton> + <MkButton :class="$style.button" inline @click="editScheduleNote()">{{ i18n.ts.deleteAndEdit }}</MkButton> <MkButton :class="$style.button" inline danger @click="deleteScheduleNote()">{{ i18n.ts.delete }}</MkButton> </div> </div> @@ -43,6 +43,10 @@ const props = defineProps<{ }; }>(); +const emit = defineEmits<{ + (ev: 'editScheduleNote'): void; +}>(); + async function deleteScheduleNote() { if (!props.note.isSchedule || !props.note.scheduledNoteId) return; @@ -58,8 +62,24 @@ async function deleteScheduleNote() { }); } -function editScheduleNote(id) { +async function editScheduleNote() { + if (!props.note.isSchedule || !props.note.scheduledNoteId) return; + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts._schedulePost.deleteAndEditConfirm, + }); + + if (canceled) return; + + await os.api('notes/schedule/delete', { scheduledNoteId: props.note.scheduledNoteId }) + .then(() => { + isDeleted.value = true; + }); + + await os.post({ initialNote: props.note, renote: props.note.renote, reply: props.note.reply, channel: props.note.channel }); + + emit('editScheduleNote'); } const showContent = $ref(false); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 031b108db4..6f3fc7f3be 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -983,6 +983,11 @@ onMounted(() => { files = init.files; cw = init.cw; useCw = init.cw != null; + if (init.isSchedule) { + schedule = { + scheduledAt: init.createdAt, + }; + } if (init.poll) { poll = { choices: init.poll.choices.map(x => x.text), diff --git a/packages/frontend/src/components/MkScheduleEditor.vue b/packages/frontend/src/components/MkScheduleEditor.vue index b5083ad050..2035dcc450 100644 --- a/packages/frontend/src/components/MkScheduleEditor.vue +++ b/packages/frontend/src/components/MkScheduleEditor.vue @@ -37,8 +37,10 @@ const emit = defineEmits<{ const atDate = ref(formatDateTimeString(addTime(new Date(), 1, 'day'), 'yyyy-MM-dd')); const atTime = ref('00:00'); -if ( props.modelValue && props.modelValue.scheduledAt) { - atDate.value = atTime.value = props.modelValue.scheduledAt; +if ( props.modelValue.scheduledAt) { + const date = new Date(props.modelValue.scheduledAt); + atDate.value = formatDateTimeString(date, 'yyyy-MM-dd'); + atTime.value = formatDateTimeString(date, 'HH:mm'); } function get() { diff --git a/packages/frontend/src/components/MkSchedulePostListDialog.vue b/packages/frontend/src/components/MkSchedulePostListDialog.vue index 3ab9b7f683..23bcb6d25e 100644 --- a/packages/frontend/src/components/MkSchedulePostListDialog.vue +++ b/packages/frontend/src/components/MkSchedulePostListDialog.vue @@ -9,11 +9,10 @@ SPDX-License-Identifier: AGPL-3.0-only :withOkButton="false" @click="cancel()" @close="cancel()" - @closed="$emit('closed')" > <template #header>{{ i18n.ts._schedulePost.list }}</template> <MkSpacer :marginMin="14" :marginMax="16"> - <MkPagination :pagination="pagination"> + <MkPagination ref="paginationEl" :pagination="pagination"> <template #empty> <div class="_fullinfo"> <img :src="infoImageUrl" class="_ghost"/> @@ -23,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #default="{ items }"> <div class="_gaps"> - <MkNoteSimple v-for="item in items" :key="item.id" :scheduled="true" :note="item.note"/> + <MkNoteSimple v-for="item in items" :key="item.id" :scheduled="true" :note="item.note" @editScheduleNote="listUpdate"/> </div> </template> </MkPagination> @@ -42,9 +41,7 @@ import { i18n } from '@/i18n.js'; import { infoImageUrl } from '@/instance.js'; const emit = defineEmits<{ - (ev: 'ok', selected: Misskey.entities.UserDetailed): void; (ev: 'cancel'): void; - (ev: 'c-losed'): void; }>(); const dialogEl = ref(); @@ -52,11 +49,15 @@ const cancel = () => { emit('cancel'); dialogEl.value.close(); }; - +const paginationEl = ref(); const pagination: Paging = { endpoint: 'notes/schedule/list', limit: 10, }; + +function listUpdate() { + paginationEl.value.reload(); +} </script> <style lang="scss" module> From 5b7984a23b20a41ec57d786fef199b5e1775d097 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 26 Nov 2023 16:54:42 +0900 Subject: [PATCH 282/501] Revert "Fix: mute" This reverts commit bbf1ba6a --- packages/frontend/src/components/MkNote.vue | 2 -- packages/frontend/src/pages/settings/general.vue | 2 ++ .../frontend/src/pages/settings/mute-block.word-mute.vue | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index f513a0b0f8..d1e76ff641 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -150,8 +150,6 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </I18n> </div> - <div v-else-if="hideMutedNotes" /> - </template> <script lang="ts" setup> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index b51cd80a71..fe89ce21be 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -49,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch> + <MkSwitch v-model="hideMutedNotes">{{ i18n.ts.hideMutedNotes }}</MkSwitch> <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor}}</MkSwitch> <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> <template #label>{{ i18n.ts._visibility.home }}</template> @@ -257,6 +258,7 @@ const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showC const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); +const hideMutedNotes = computed(defaultStore.makeGetterSetter('hideMutedNotes')); const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue index e0f0cdfde4..9e0f92030f 100644 --- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue +++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue @@ -11,13 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> </MkTextarea> </div> - <MkSwitch v-model="hideMutedNotes">{{ i18n.ts._wordMute.hideMutedNotes }}</MkSwitch> <MkButton primary inline :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </template> <script lang="ts" setup> -import {computed, ref, watch} from 'vue'; +import { ref, watch } from 'vue'; import MkTextarea from '@/components/MkTextarea.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; @@ -30,7 +29,6 @@ const props = defineProps<{ const emit = defineEmits<{ (ev: 'save', value: (string[] | string)[]): void; }>(); -import MkSwitch from "@/components/MkSwitch.vue"; const render = (mutedWords) => mutedWords.map(x => { if (Array.isArray(x)) { @@ -40,9 +38,10 @@ const render = (mutedWords) => mutedWords.map(x => { } }).join('\n'); +const tab = ref('soft'); const mutedWords = ref(render($i!.mutedWords)); const changed = ref(false); -const hideMutedNotes = computed(defaultStore.makeGetterSetter('hideMutedNotes')); + watch(mutedWords, () => { changed.value = true; }); From 5fe20bbe969bb040f6a8ff7d85de99f3e4e690c9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 26 Nov 2023 18:21:16 +0900 Subject: [PATCH 283/501] fix mute --- .../src/pages/settings/mute-block.vue | 344 +++++++++--------- .../pages/settings/mute-block.word-mute.vue | 111 +++--- 2 files changed, 227 insertions(+), 228 deletions(-) diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index 4883ca0df4..e3bbf38ae1 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -4,125 +4,125 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <MkFolder> - <template #icon><i class="ti ti-message-off"></i></template> - <template #label>{{ i18n.ts.wordMute }}</template> + <div class="_gaps_m"> + <MkFolder> + <template #icon><i class="ti ti-message-off"></i></template> + <template #label>{{ i18n.ts.wordMute }}</template> - <XWordMute :muted="$i!.mutedWords" @save="saveMutedWords"/> - </MkFolder> + <XWordMute :muted="$i!.mutedWords" @save="saveMutedWords"/> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-message-off"></i></template> - <template #label>{{ i18n.ts.hardWordMute }}</template> + <MkFolder> + <template #icon><i class="ti ti-message-off"></i></template> + <template #label>{{ i18n.ts.hardWordMute }}</template> - <XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/> - </MkFolder> + <XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-planet-off"></i></template> - <template #label>{{ i18n.ts.instanceMute }}</template> + <MkFolder> + <template #icon><i class="ti ti-planet-off"></i></template> + <template #label>{{ i18n.ts.instanceMute }}</template> - <XInstanceMute/> - </MkFolder> + <XInstanceMute/> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-repeat-off"></i></template> - <template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> + <MkFolder> + <template #icon><i class="ti ti-repeat-off"></i></template> + <template #label>{{ i18n.ts.mutedUsers }} ({{ i18n.ts.renote }})</template> - <MkPagination :pagination="renoteMutingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="renoteMutingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> - <MkUserCardMini :user="item.mutee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub"> - <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> - </div> - </div> - </div> - </template> - </MkPagination> - </MkFolder> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedRenoteMuteItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> + <MkUserCardMini :user="item.mutee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleRenoteMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unrenoteMute(item.mutee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedRenoteMuteItems.includes(item.id)" :class="$style.userItemSub"> + <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + </div> + </div> + </div> + </template> + </MkPagination> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-eye-off"></i></template> - <template #label>{{ i18n.ts.mutedUsers }}</template> + <MkFolder> + <template #icon><i class="ti ti-eye-off"></i></template> + <template #label>{{ i18n.ts.mutedUsers }}</template> - <MkPagination :pagination="mutingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="mutingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> - <MkUserCardMini :user="item.mutee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub"> - <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> - </div> - </div> - </div> - </template> - </MkPagination> - </MkFolder> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.mutee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedMuteItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.mutee)"> + <MkUserCardMini :user="item.mutee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleMuteItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unmute(item.mutee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedMuteItems.includes(item.id)" :class="$style.userItemSub"> + <div>Muted at: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> + </div> + </div> + </template> + </MkPagination> + </MkFolder> - <MkFolder> - <template #icon><i class="ti ti-ban"></i></template> - <template #label>{{ i18n.ts.blockedUsers }}</template> + <MkFolder> + <template #icon><i class="ti ti-ban"></i></template> + <template #label>{{ i18n.ts.blockedUsers }}</template> - <MkPagination :pagination="blockingPagination"> - <template #empty> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.noUsers }}</div> - </div> - </template> + <MkPagination :pagination="blockingPagination"> + <template #empty> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.noUsers }}</div> + </div> + </template> - <template #default="{ items }"> - <div class="_gaps_s"> - <div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]"> - <div :class="$style.userItemMain"> - <MkA :class="$style.userItemMainBody" :to="userPage(item.blockee)"> - <MkUserCardMini :user="item.blockee"/> - </MkA> - <button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> - <button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button> - </div> - <div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub"> - <div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div> - <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> - <div v-else>Period: {{ i18n.ts.indefinitely }}</div> - </div> - </div> - </div> - </template> - </MkPagination> - </MkFolder> -</div> + <template #default="{ items }"> + <div class="_gaps_s"> + <div v-for="item in items" :key="item.blockee.id" :class="[$style.userItem, { [$style.userItemOpend]: expandedBlockItems.includes(item.id) }]"> + <div :class="$style.userItemMain"> + <MkA :class="$style.userItemMainBody" :to="userPage(item.blockee)"> + <MkUserCardMini :user="item.blockee"/> + </MkA> + <button class="_button" :class="$style.userToggle" @click="toggleBlockItem(item)"><i :class="$style.chevron" class="ti ti-chevron-down"></i></button> + <button class="_button" :class="$style.remove" @click="unblock(item.blockee, $event)"><i class="ti ti-x"></i></button> + </div> + <div v-if="expandedBlockItems.includes(item.id)" :class="$style.userItemSub"> + <div>Blocked at: <MkTime :time="item.createdAt" mode="detail"/></div> + <div v-if="item.expiresAt">Period: {{ new Date(item.expiresAt).toLocaleString() }}</div> + <div v-else>Period: {{ i18n.ts.indefinitely }}</div> + </div> + </div> + </div> + </template> + </MkPagination> + </MkFolder> + </div> </template> <script lang="ts" setup> @@ -140,18 +140,18 @@ import { $i } from '@/account.js'; import MkFolder from '@/components/MkFolder.vue'; const renoteMutingPagination = { - endpoint: 'renote-mute/list' as const, - limit: 10, + endpoint: 'renote-mute/list' as const, + limit: 10, }; const mutingPagination = { - endpoint: 'mute/list' as const, - limit: 10, + endpoint: 'mute/list' as const, + limit: 10, }; const blockingPagination = { - endpoint: 'blocking/list' as const, - limit: 10, + endpoint: 'blocking/list' as const, + limit: 10, }; let expandedRenoteMuteItems = $ref([]); @@ -159,68 +159,68 @@ let expandedMuteItems = $ref([]); let expandedBlockItems = $ref([]); async function unrenoteMute(user, ev) { - os.popupMenu([{ - text: i18n.ts.renoteUnmute, - icon: 'ti ti-x', - action: async () => { - await os.apiWithDialog('renote-mute/delete', { userId: user.id }); - //role.users = role.users.filter(u => u.id !== user.id); - }, - }], ev.currentTarget ?? ev.target); + os.popupMenu([{ + text: i18n.ts.renoteUnmute, + icon: 'ti ti-x', + action: async () => { + await os.apiWithDialog('renote-mute/delete', { userId: user.id }); + //role.users = role.users.filter(u => u.id !== user.id); + }, + }], ev.currentTarget ?? ev.target); } async function unmute(user, ev) { - os.popupMenu([{ - text: i18n.ts.unmute, - icon: 'ti ti-x', - action: async () => { - await os.apiWithDialog('mute/delete', { userId: user.id }); - //role.users = role.users.filter(u => u.id !== user.id); - }, - }], ev.currentTarget ?? ev.target); + os.popupMenu([{ + text: i18n.ts.unmute, + icon: 'ti ti-x', + action: async () => { + await os.apiWithDialog('mute/delete', { userId: user.id }); + //role.users = role.users.filter(u => u.id !== user.id); + }, + }], ev.currentTarget ?? ev.target); } async function unblock(user, ev) { - os.popupMenu([{ - text: i18n.ts.unblock, - icon: 'ti ti-x', - action: async () => { - await os.apiWithDialog('blocking/delete', { userId: user.id }); - //role.users = role.users.filter(u => u.id !== user.id); - }, - }], ev.currentTarget ?? ev.target); + os.popupMenu([{ + text: i18n.ts.unblock, + icon: 'ti ti-x', + action: async () => { + await os.apiWithDialog('blocking/delete', { userId: user.id }); + //role.users = role.users.filter(u => u.id !== user.id); + }, + }], ev.currentTarget ?? ev.target); } async function toggleRenoteMuteItem(item) { - if (expandedRenoteMuteItems.includes(item.id)) { - expandedRenoteMuteItems = expandedRenoteMuteItems.filter(x => x !== item.id); - } else { - expandedRenoteMuteItems.push(item.id); - } + if (expandedRenoteMuteItems.includes(item.id)) { + expandedRenoteMuteItems = expandedRenoteMuteItems.filter(x => x !== item.id); + } else { + expandedRenoteMuteItems.push(item.id); + } } async function toggleMuteItem(item) { - if (expandedMuteItems.includes(item.id)) { - expandedMuteItems = expandedMuteItems.filter(x => x !== item.id); - } else { - expandedMuteItems.push(item.id); - } + if (expandedMuteItems.includes(item.id)) { + expandedMuteItems = expandedMuteItems.filter(x => x !== item.id); + } else { + expandedMuteItems.push(item.id); + } } async function toggleBlockItem(item) { - if (expandedBlockItems.includes(item.id)) { - expandedBlockItems = expandedBlockItems.filter(x => x !== item.id); - } else { - expandedBlockItems.push(item.id); - } + if (expandedBlockItems.includes(item.id)) { + expandedBlockItems = expandedBlockItems.filter(x => x !== item.id); + } else { + expandedBlockItems.push(item.id); + } } async function saveMutedWords(mutedWords: (string | string[])[]) { - await os.api('i/update', { mutedWords }); + await os.api('i/update', { mutedWords }); } async function saveHardMutedWords(hardMutedWords: (string | string[])[]) { - await os.api('i/update', { hardMutedWords }); + await os.api('i/update', { hardMutedWords }); } const headerActions = $computed(() => []); @@ -228,47 +228,47 @@ const headerActions = $computed(() => []); const headerTabs = $computed(() => []); definePageMetadata({ - title: i18n.ts.muteAndBlock, - icon: 'ti ti-ban', + title: i18n.ts.muteAndBlock, + icon: 'ti ti-ban', }); </script> <style lang="scss" module> .userItemMain { - display: flex; + display: flex; } .userItemSub { - padding: 6px 12px; - font-size: 85%; - color: var(--fgTransparentWeak); + padding: 6px 12px; + font-size: 85%; + color: var(--fgTransparentWeak); } .userItemMainBody { - flex: 1; - min-width: 0; - margin-right: 8px; + flex: 1; + min-width: 0; + margin-right: 8px; - &:hover { - text-decoration: none; - } + &:hover { + text-decoration: none; + } } .userToggle, .remove { - width: 32px; - height: 32px; - align-self: center; + width: 32px; + height: 32px; + align-self: center; } .chevron { - display: block; - transition: transform 0.1s ease-out; + display: block; + transition: transform 0.1s ease-out; } .userItem.userItemOpend { - .chevron { - transform: rotateX(180deg); - } + .chevron { + transform: rotateX(180deg); + } } </style> diff --git a/packages/frontend/src/pages/settings/mute-block.word-mute.vue b/packages/frontend/src/pages/settings/mute-block.word-mute.vue index 9e0f92030f..0e8bc702dd 100644 --- a/packages/frontend/src/pages/settings/mute-block.word-mute.vue +++ b/packages/frontend/src/pages/settings/mute-block.word-mute.vue @@ -4,15 +4,15 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="_gaps_m"> - <div> - <MkTextarea v-model="mutedWords"> - <span>{{ i18n.ts._wordMute.muteWords }}</span> - <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> - </MkTextarea> - </div> - <MkButton primary inline :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> -</div> + <div class="_gaps_m"> + <div> + <MkTextarea v-model="mutedWords"> + <span>{{ i18n.ts._wordMute.muteWords }}</span> + <template #caption>{{ i18n.ts._wordMute.muteWordsDescription }}<br>{{ i18n.ts._wordMute.muteWordsDescription2 }}</template> + </MkTextarea> + </div> + <MkButton primary inline :disabled="!changed" @click="save()"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + </div> </template> <script lang="ts" setup> @@ -23,71 +23,70 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; const props = defineProps<{ - muted: (string[] | string)[]; + muted: (string[] | string)[]; }>(); const emit = defineEmits<{ - (ev: 'save', value: (string[] | string)[]): void; + (ev: 'save', value: (string[] | string)[]): void; }>(); const render = (mutedWords) => mutedWords.map(x => { - if (Array.isArray(x)) { - return x.join(' '); - } else { - return x; - } + if (Array.isArray(x)) { + return x.join(' '); + } else { + return x; + } }).join('\n'); -const tab = ref('soft'); -const mutedWords = ref(render($i!.mutedWords)); +const mutedWords = ref(render(props.muted)); const changed = ref(false); watch(mutedWords, () => { - changed.value = true; + changed.value = true; }); async function save() { - const parseMutes = (mutes) => { - // split into lines, remove empty lines and unnecessary whitespace - let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== ''); + const parseMutes = (mutes) => { + // split into lines, remove empty lines and unnecessary whitespace + let lines = mutes.trim().split('\n').map(line => line.trim()).filter(line => line !== ''); - // check each line if it is a RegExp or not - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - const regexp = line.match(/^\/(.+)\/(.*)$/); - if (regexp) { - // check that the RegExp is valid - try { - new RegExp(regexp[1], regexp[2]); - // note that regex lines will not be split by spaces! - } catch (err: any) { - // invalid syntax: do not save, do not reset changed flag - os.alert({ - type: 'error', - title: i18n.ts.regexpError, - text: i18n.t('regexpErrorDescription', { tab: 'word mute', line: i + 1 }) + '\n' + err.toString(), - }); - // re-throw error so these invalid settings are not saved - throw err; - } - } else { - lines[i] = line.split(' '); - } - } + // check each line if it is a RegExp or not + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + const regexp = line.match(/^\/(.+)\/(.*)$/); + if (regexp) { + // check that the RegExp is valid + try { + new RegExp(regexp[1], regexp[2]); + // note that regex lines will not be split by spaces! + } catch (err: any) { + // invalid syntax: do not save, do not reset changed flag + os.alert({ + type: 'error', + title: i18n.ts.regexpError, + text: i18n.t('regexpErrorDescription', { tab: 'word mute', line: i + 1 }) + '\n' + err.toString(), + }); + // re-throw error so these invalid settings are not saved + throw err; + } + } else { + lines[i] = line.split(' '); + } + } - return lines; - }; + return lines; + }; - let parsed; - try { - parsed = parseMutes(mutedWords.value); - } catch (err) { - // already displayed error message in parseMutes - return; - } + let parsed; + try { + parsed = parseMutes(mutedWords.value); + } catch (err) { + // already displayed error message in parseMutes + return; + } - emit('save', parsed); + emit('save', parsed); - changed.value = false; + changed.value = false; } </script> From e3761231b12cba3f62b282576d0f6e6581d01369 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 26 Nov 2023 18:45:22 +0900 Subject: [PATCH 284/501] fix mute --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c7589ae9dc..c560604511 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.11.1-PrisMisskey.1", + "version": "2023.11.1-PrisMisskey.2", "codename": "nasubi", "repository": { "type": "git", From 09cfba4547a299a41702e0e3f2fe20fd5ef3023a Mon Sep 17 00:00:00 2001 From: kakkokari-gtyih <daisho7308+f@gmail.com> Date: Mon, 27 Nov 2023 21:35:22 +0900 Subject: [PATCH 285/501] fix design --- packages/frontend/src/components/MkNoteSimple.vue | 4 ++-- packages/frontend/src/components/MkPostForm.vue | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 8a130e95f5..ee3be089a6 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSubNoteContent :class="$style.text" :note="note"/> </div> <div v-if="note.isSchedule" style="margin-top: 10px;"> - <MkButton :class="$style.button" inline @click="editScheduleNote()">{{ i18n.ts.deleteAndEdit }}</MkButton> - <MkButton :class="$style.button" inline danger @click="deleteScheduleNote()">{{ i18n.ts.delete }}</MkButton> + <MkButton :class="$style.button" inline @click="editScheduleNote()"><i class="ti ti-pencil"></i> {{ i18n.ts.deleteAndEdit }}</MkButton> + <MkButton :class="$style.button" inline danger @click="deleteScheduleNote()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> </div> </div> </div> diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 6f3fc7f3be..6fcef55a6e 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -68,8 +68,10 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/> - <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> - <MkScheduleEditor v-if="schedule" v-model="schedule" @destroyed="schedule = null"/> + <div :class="$style.postOptionsRoot"> + <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> + <MkScheduleEditor v-if="schedule" v-model="schedule" @destroyed="schedule = null"/> + </div> <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :user="postAccount ?? $i"/> <div v-if="showingOptions" style="padding: 8px 16px;"> </div> @@ -1212,6 +1214,15 @@ defineExpose({ border-bottom: solid 0.5px var(--divider); } +.postOptionsRoot { + >* { + border-bottom: solid 0.5px var(--divider); + } + >:last-child { + border-bottom: none; + } +} + .hashtags { z-index: 1; padding-top: 8px; From da83bc315a1c23d345f325e036b6b02b624d8896 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 9 Dec 2023 19:05:27 +0900 Subject: [PATCH 286/501] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AE=E3=83=9B=E3=83=BC=E3=83=A0=E9=80=9A=E7=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 3 ++- locales/ja-JP.yml | 1 + .../src/server/api/endpoints/admin/emoji/add-draft.ts | 2 ++ .../frontend/src/components/MkEmojiEditDialog.vue | 9 ++++++++- packages/frontend/src/pages/settings/general.vue | 11 ++--------- packages/frontend/src/store.ts | 8 -------- 6 files changed, 15 insertions(+), 19 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 25c640495e..20a7ad1a0c 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -282,6 +282,7 @@ export interface Locale { "messaging": string; "upload": string; "keepOriginalUploading": string; + "isNotifyIsHome": string; "keepOriginalUploadingDescription": string; "fromDrive": string; "fromUrl": string; @@ -1716,8 +1717,8 @@ export interface Locale { "gtlAvailable": string; "ltlAvailable": string; "canPublicNote": string; - "canScheduleNote": string; "canEditNote": string; + "canScheduleNote": string; "canInvite": string; "inviteLimit": string; "inviteLimitCycle": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 94a1790dae..9482c743e8 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -279,6 +279,7 @@ saved: "保存しました" messaging: "チャット" upload: "アップロード" keepOriginalUploading: "オリジナル画像を保持" +isNotifyIsHome: "ホーム投稿で通知する" keepOriginalUploadingDescription: "画像をアップロードする時にオリジナル版を保持します。オフにするとアップロード時にブラウザでWeb公開用画像を生成します。" fromDrive: "ドライブから" fromUrl: "URLから" diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts index d91ddfca96..978663be6b 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts @@ -38,6 +38,7 @@ export const paramDef = { fileId: { type: 'string', format: 'misskey:id' }, isSensitive: { type: 'boolean' }, localOnly: { type: 'boolean' }, + isNotifyIsHome: { type: 'boolean', default: false }, }, required: ['name', 'fileId'], } as const; @@ -84,6 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (EmojiBotToken){ const data_Miss = { 'i': EmojiBotToken, + 'visibility': ps.isNotifyIsHome ? 'home' : 'public', 'text': '絵文字名 : :' + ps.name + ':\n' + 'カテゴリ : ' + ps.category + '\n' + diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index e7f7b7c076..0e08122ec0 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -66,9 +66,13 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkSwitch v-if="!isRequest" v-model="draft" :disabled="isRequest"> + <MkSwitch v-model="isNotifyIsHome"> + {{ i18n.ts.isNotifyIsHome }} + </MkSwitch> + <MkSwitch v-if="!isRequest" v-model="draft" > {{ i18n.ts.draft }} </MkSwitch> + </div> </MkSpacer> <div :class="$style.footer"> @@ -116,6 +120,7 @@ let file = $ref<Misskey.entities.DriveFile>(); let chooseFile: DriveFile|null = $ref(null); let draft = $ref(props.emoji ? props.emoji.draft : false); let isRequest = $ref(props.isRequest); +let isNotifyIsHome = $ref(false); let url; watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { @@ -148,6 +153,7 @@ async function add() { aliases: aliases.split(' '), license: license === '' ? null : license, fileId: chooseFile.id, + isNotifyIsHome: isNotifyIsHome, }); emit('done', { @@ -220,6 +226,7 @@ async function done() { isSensitive, localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), + isNotifyIsHome, }; if (file) { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index fe89ce21be..a842460cab 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -49,7 +49,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch> - <MkSwitch v-model="hideMutedNotes">{{ i18n.ts.hideMutedNotes }}</MkSwitch> <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor}}</MkSwitch> <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> <template #label>{{ i18n.ts._visibility.home }}</template> @@ -139,10 +138,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="useSystemFont">{{ i18n.ts.useSystemFont }}</MkSwitch> <MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch> <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> - <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> - <MkSwitch :disabled="enableUltimateDataSaverMode || enableCellularWithUltimateDataSaver" v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> - <MkSwitch v-model="enableUltimateDataSaverMode">{{ i18n.ts.UltimateDataSaver }}</MkSwitch> - <MkSwitch v-model="enableCellularWithUltimateDataSaver">{{ i18n.ts.cellularWithUltimateDataSaver }}</MkSwitch> + <MkSwitch v-model="enableDataSaverMode">{{ i18n.ts.dataSaver }}</MkSwitch> + <MkSwitch v-model="enableCellularWithDataSaver">{{ i18n.ts.cellularWithDataSaver }}</MkSwitch> <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave}}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> <MkSwitch v-model="enablehanntenn">{{ i18n.ts.hanntenn }}<template #caption>{{ i18n.ts.hanntennInfo }} </template></MkSwitch> @@ -258,7 +255,6 @@ const showClipButtonInNoteFooter = computed(defaultStore.makeGetterSetter('showC const reactionsDisplaySize = computed(defaultStore.makeGetterSetter('reactionsDisplaySize')); const limitWidthOfReaction = computed(defaultStore.makeGetterSetter('limitWidthOfReaction')); const collapseRenotes = computed(defaultStore.makeGetterSetter('collapseRenotes')); -const hideMutedNotes = computed(defaultStore.makeGetterSetter('hideMutedNotes')); const reduceAnimation = computed(defaultStore.makeGetterSetter('animation', v => !v, v => !v)); const useBlurEffectForModal = computed(defaultStore.makeGetterSetter('useBlurEffectForModal')); const useBlurEffect = computed(defaultStore.makeGetterSetter('useBlurEffect')); @@ -271,8 +267,6 @@ const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('dis const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); const enableCellularWithDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithDataSaver')); -const enableUltimateDataSaverMode = computed(defaultStore.makeGetterSetter('enableUltimateDataSaverMode')) -const enableCellularWithUltimateDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithUltimateDataSaver')); const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); const enableDataSaverMode = computed(defaultStore.makeGetterSetter('enableDataSaverMode')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); @@ -301,7 +295,6 @@ const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimel const showGlobalTimeline = computed(defaultStore.makeGetterSetter('showGlobalTimeline')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) const FeaturedOrNote = computed(defaultStore.makeGetterSetter('FeaturedOrNote')) -const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 367948edd5..07bbc5499d 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -234,18 +234,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - enableUltimateDataSaverMode: { - where: 'device', - default: false, - }, enableCellularWithDataSaver: { where: 'device', default: false, }, - enableCellularWithUltimateDataSaver: { - where: 'device', - default: false, - }, disableShowingAnimatedImages: { where: 'device', default: window.matchMedia('(prefers-reduced-motion)').matches, From e4b090b20bb745b7d3cbffdcdb5991fbd02bbbe8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 10 Dec 2023 01:32:00 +0900 Subject: [PATCH 287/501] =?UTF-8?q?=E6=8D=A8=E3=81=A6=E3=82=81=E3=81=82?= =?UTF-8?q?=E3=81=A9=E5=BC=BE=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/EmailService.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index f31cec2b3a..29d575b564 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -183,7 +183,22 @@ export class EmailService { } else { validated = { valid: true, reason: null }; } - + if (meta.enableActiveEmailValidation) { + const dispose = await this.httpRequestService.send('https://raw.githubusercontent.com/mattyatea/disposable-email-domains/master/disposable_email_blocklist.conf', { + method: 'GET', + }); + const dispoes_2 = await this.httpRequestService.send('https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains_strict.txt', { + method: 'GET', + }); + const disposableEmailDomains = (await dispose.text()).split('\n'); + const disposableEmailDomains_2 = (await dispoes_2.text()).split('\n'); + disposableEmailDomains.push(...disposableEmailDomains_2); + const domain = emailAddress.split('@')[1]; + console.log(domain) + if (disposableEmailDomains.includes(domain)) { + validated = { valid: false, reason: 'disposable' }; + } + } const available = exist === 0 && validated.valid; return { From eb88ac308edee45765136afc8dffada9b01e2db2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 10 Dec 2023 01:39:13 +0900 Subject: [PATCH 288/501] =?UTF-8?q?=E6=8D=A8=E3=81=A6=E3=82=81=E3=81=82?= =?UTF-8?q?=E3=81=A9=E5=BC=BE=E3=81=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/src/core/EmailService.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 29d575b564..bc92cc5fe9 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -191,8 +191,6 @@ export class EmailService { method: 'GET', }); const disposableEmailDomains = (await dispose.text()).split('\n'); - const disposableEmailDomains_2 = (await dispoes_2.text()).split('\n'); - disposableEmailDomains.push(...disposableEmailDomains_2); const domain = emailAddress.split('@')[1]; console.log(domain) if (disposableEmailDomains.includes(domain)) { From 6849d510ac81f25dfc02bcc872fa0d65d12a5f63 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 10 Dec 2023 09:17:24 +0900 Subject: [PATCH 289/501] =?UTF-8?q?Feat:=20=E8=A4=87=E6=95=B0=E3=83=8E?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E9=80=9A=E5=A0=B1=E6=A9=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../1702149469508-abusenoteselect.js | 11 ++ .../backend/src/core/GlobalEventService.ts | 1 + .../entities/AbuseUserReportEntityService.ts | 1 + .../backend/src/models/AbuseUserReport.ts | 5 + .../api/endpoints/users/report-abuse.ts | 19 ++- .../frontend/src/components/MkAbuseReport.vue | 130 +++++++++++------- .../src/components/MkAbuseReportWindow.vue | 95 +++++++++++-- .../frontend/src/scripts/get-note-menu.ts | 2 +- 10 files changed, 197 insertions(+), 69 deletions(-) create mode 100644 packages/backend/migration/1702149469508-abusenoteselect.js diff --git a/locales/index.d.ts b/locales/index.d.ts index 846a6d503d..da976aca93 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -682,6 +682,7 @@ export interface Locale { "forwardReport": string; "forwardReportIsAnonymous": string; "send": string; + "reportedNote": string; "abuseMarkAsResolved": string; "openInNewTab": string; "openInSideView": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 0d84440bc8..9c26c03397 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -679,6 +679,7 @@ reporterOrigin: "通報元" forwardReport: "リモートサーバーに通報を転送する" forwardReportIsAnonymous: "リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。" send: "送信" +reportedNote: "通報されたノート" abuseMarkAsResolved: "対応済みにする" openInNewTab: "新しいタブで開く" openInSideView: "サイドビューで開く" diff --git a/packages/backend/migration/1702149469508-abusenoteselect.js b/packages/backend/migration/1702149469508-abusenoteselect.js new file mode 100644 index 0000000000..eb9d5b34d8 --- /dev/null +++ b/packages/backend/migration/1702149469508-abusenoteselect.js @@ -0,0 +1,11 @@ +export class Abusenoteselect1702149469508 { + name = 'Abusenoteselect1702149469508' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "notes" jsonb NOT NULL DEFAULT '[]'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "notes"`); + } +} diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index d175f21f2f..f16c18c2d6 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -157,6 +157,7 @@ export interface AdminEventTypes { targetUserId: MiUser['id'], reporterId: MiUser['id'], comment: string; + notes: any[]; }; } //#endregion diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 97de891ece..627dddd3bc 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -33,6 +33,7 @@ export class AbuseUserReportEntityService { id: report.id, createdAt: this.idService.parse(report.id).date.toISOString(), comment: report.comment, + notes: report.notes, resolved: report.resolved, reporterId: report.reporterId, targetUserId: report.targetUserId, diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index 593c44f66b..995fbc4018 100644 --- a/packages/backend/src/models/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -60,6 +60,11 @@ export class MiAbuseUserReport { }) public comment: string; + @Column('jsonb', { + default: [], + }) + public notes: any[]; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 3bcf44cc42..d54b01a6f1 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -3,14 +3,17 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import { setImmediate } from 'node:timers/promises'; import sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; -import type { AbuseUserReportsRepository } from '@/models/_.js'; +import { In } from 'typeorm'; +import type { AbuseUserReportsRepository, NotesRepository } from '@/models/_.js'; import { IdService } from '@/core/IdService.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MetaService } from '@/core/MetaService.js'; import { EmailService } from '@/core/EmailService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { RoleService } from '@/core/RoleService.js'; @@ -21,7 +24,7 @@ export const meta = { requireCredential: true, - description: 'File a report.', + description: 'User a report.', errors: { noSuchUser: { @@ -49,6 +52,7 @@ export const paramDef = { properties: { userId: { type: 'string', format: 'misskey:id' }, comment: { type: 'string', minLength: 1, maxLength: 2048 }, + noteIds: { type: 'array', items: { type: 'string', format: 'misskey:id' } }, }, required: ['userId', 'comment'], } as const; @@ -59,11 +63,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private idService: IdService, private metaService: MetaService, private emailService: EmailService, private getterService: GetterService, private roleService: RoleService, + private noteEntityService: NoteEntityService, private globalEventService: GlobalEventService, ) { super(meta, paramDef, async (ps, me) => { @@ -81,6 +89,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.cannotReportAdmin); } + const notes = ps.noteIds ? await this.notesRepository.find({ + where: { id: In(ps.noteIds) }, + }) : []; + const filteredNotes = notes.filter(note => note.userId === user.id); const report = await this.abuseUserReportsRepository.insert({ id: this.idService.gen(), targetUserId: user.id, @@ -88,6 +100,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- reporterId: me.id, reporterHost: null, comment: ps.comment, + notes: ps.noteIds ? await this.noteEntityService.packMany(filteredNotes) : [], }).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0])); // Publish event to moderators @@ -100,9 +113,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- targetUserId: report.targetUserId, reporterId: report.reporterId, comment: report.comment, + notes: report.notes, }); } - const meta = await this.metaService.fetch(); if (meta.email) { this.emailService.sendEmail(meta.email, 'New abuse report', diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index ce7e134b70..4c8a334b23 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -4,13 +4,13 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="bcekxzvu _margin _panel"> - <div class="target"> - <MkA v-user-preview="report.targetUserId" class="info" :to="`/admin/user/${report.targetUserId}`"> - <MkAvatar class="avatar" :user="report.targetUser" indicator/> - <div class="names"> - <MkUserName class="name" :user="report.targetUser"/> - <MkAcct class="acct" :user="report.targetUser" style="display: block;"/> +<div :class="$style.root"> + <div :class="$style.target"> + <MkA v-user-preview="report.targetUserId" :class="$style.info" :to="`/admin/user/${report.targetUserId}`"> + <MkAvatar :class="$style.avatar" :user="report.targetUser" indicator/> + <div :class="$style.name"> + <MkUserName :class="$style.names" :user="report.targetUser"/> + <MkAcct :class="$style.names" :user="report.targetUser" style="display: block;"/> </div> </MkA> <MkKeyValue> @@ -18,9 +18,15 @@ SPDX-License-Identifier: AGPL-3.0-only <template #value>{{ dateString(report.targetUser.createdAt) }} (<MkTime :time="report.targetUser.createdAt"/>)</template> </MkKeyValue> </div> - <div class="detail"> + <div :class="$style.detail"> <div> <Mfm :text="report.comment"/> + <MkFolder v-if="report.notes.length !== 0" :class="$style.notes"> + <template #label>{{ i18n.ts.reportedNote }}</template> + <div v-for="note in report.notes" :class="$style.notes"> + <MkNoteSimple :note="note"/> + </div> + </MkFolder> </div> <hr/> <div>{{ i18n.ts.reporter }}: <MkA :to="`/admin/user/${report.reporter.id}`" class="_link">@{{ report.reporter.username }}</MkA></div> @@ -42,15 +48,28 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; +import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { dateString } from '@/filters/date.js'; - +import MkFolder from '@/components/MkFolder.vue'; +import MkNoteSimple from '@/components/MkNoteSimple.vue'; const props = defineProps<{ - report: any; + report: { + id: string; + createdAt:string; + targetUserId:Misskey.entities.User['id']; + targetUser:Misskey.entities.User & {createdAt:string;}; + reporter:Misskey.entities.User; + assignee:Misskey.entities.User['id']; + comment:string; + notes:Misskey.entities.Note['id'][]; + forwarded:boolean; + resolved:boolean; + }; }>(); const emit = defineEmits<{ @@ -69,47 +88,56 @@ function resolve() { } </script> -<style lang="scss" scoped> -.bcekxzvu { +<style lang="scss" module> +.root { display: flex; - - > .target { - width: 35%; - box-sizing: border-box; - text-align: left; - padding: 24px; - border-right: solid 1px var(--divider); - - > .info { - display: flex; - box-sizing: border-box; - align-items: center; - padding: 14px; - border-radius: 8px; - --c: rgb(255 196 0 / 15%); - background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); - background-size: 16px 16px; - - > .avatar { - width: 42px; - height: 42px; - } - - > .names { - margin-left: 0.3em; - padding: 0 8px; - flex: 1; - - > .name { - font-weight: bold; - } - } - } - } - - > .detail { - flex: 1; - padding: 24px; - } + margin: var(--margin) 0; + background: var(--panel); + border-radius: var(--radius); + overflow: clip; } + +.notes { + margin: var(--margin) 0; + padding: 0; +} + +.target { + width: 35%; + box-sizing: border-box; + text-align: left; + padding: 24px; + border-right: solid 1px var(--divider); +} +.info { + display: flex; + box-sizing: border-box; + align-items: center; + padding: 14px; + border-radius: 8px; + --c: rgb(255 196 0 / 15%); + background-image: linear-gradient(45deg, var(--c) 16.67%, transparent 16.67%, transparent 50%, var(--c) 50%, var(--c) 66.67%, transparent 66.67%, transparent 100%); + background-size: 16px 16px; +} + +.avatar { + width: 42px; + height: 42px; +} + +.names { + margin-left: 0.3em; + padding: 0 8px; + flex: 1; +} + +.name { + font-weight: bold; +} + +.detail { + flex: 1; + padding: 24px; +} + </style> diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 7814681ea2..1177eb81bf 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<MkWindow ref="uiWindow" :initialWidth="400" :initialHeight="500" :canResize="true" @closed="emit('closed')"> +<MkWindow ref="uiWindow" :initialWidth="400" :initialHeight="500" :canResize="true" style="overflow-x: clip;" @closed="emit('closed')"> <template #header> <i class="ti ti-exclamation-circle" style="margin-right: 0.5em;"></i> <I18n :src="i18n.ts.reportAbuseOf" tag="span"> @@ -13,19 +13,45 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </I18n> </template> - <MkSpacer :marginMin="20" :marginMax="28"> - <div class="_gaps_m" :class="$style.root"> - <div class=""> - <MkTextarea v-model="comment"> - <template #label>{{ i18n.ts.details }}</template> - <template #caption>{{ i18n.ts.fillAbuseReportDescription }}</template> - </MkTextarea> - </div> - <div class=""> - <MkButton primary full :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> - </div> - </div> - </MkSpacer> + <Transition + mode="out-in" + :enterActiveClass="$style.transition_x_enterActive" + :leaveActiveClass="$style.transition_x_leaveActive" + :enterFromClass="$style.transition_x_enterFrom" + :leaveToClass="$style.transition_x_leaveTo" + > + <template v-if="page === 0"> + <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_gaps_m" :class="$style.root"> + <MkPagination v-slot="{items}" :key="user.id" :pagination="Pagination"> + <div v-for="item in items" :key="item.id" :class="$style.note"> + <MkSwitch v-model="item.isAbuseReport" @update:modelValue="pushAbuseReportNote($event,item.id)"></MkSwitch> + <MkAvatar :user="item.user" preview/> + <MkNoteSimple :note="item"/> + </div> + </MkPagination> + <div class="_buttonsCenter"> + <MkButton primary rounded gradate @click="page++">{{ i18n.ts.continue }} <i class="ti ti-arrow-right"></i></MkButton> + </div> + </div> + </MkSpacer> + </template> + + <template v-else-if="page === 1"> + <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_gaps_m" :class="$style.root"> + <MkTextarea v-model="comment"> + <template #label>{{ i18n.ts.details }}</template> + <template #caption>{{ i18n.ts.fillAbuseReportDescription }}</template> + </MkTextarea> + <div class="_buttonsCenter"> + <MkButton @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> + <MkButton primary :disabled="comment.length === 0" @click="send">{{ i18n.ts.send }}</MkButton> + </div> + </div> + </MkSpacer> + </template> + </Transition> </MkWindow> </template> @@ -37,23 +63,46 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; +import MkPagination from '@/components/MkPagination.vue'; +import MkNoteSimple from '@/components/MkNoteSimple.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; const props = defineProps<{ user: Misskey.entities.User; initialComment?: string; + initialNoteId?: Misskey.entities.Note['id']; }>(); +const Pagination = { + endpoint: 'users/notes' as const, + limit: 10, + params: { + userId: props.user.id, + }, +}; + const emit = defineEmits<{ (ev: 'closed'): void; }>(); +const abuseNotesId = ref(props.initialNoteId ? [props.initialNoteId] : []); +const page = ref(0); const uiWindow = shallowRef<InstanceType<typeof MkWindow>>(); const comment = ref(props.initialComment ?? ''); +function pushAbuseReportNote(v, id) { + if (v) { + abuseNotesId.value.push(id); + } else { + abuseNotesId.value = abuseNotesId.value.filter(noteId => noteId !== id); + } +} + function send() { os.apiWithDialog('users/report-abuse', { userId: props.user.id, comment: comment.value, + noteIds: abuseNotesId.value, }, undefined).then(res => { os.alert({ type: 'success', @@ -69,4 +118,22 @@ function send() { .root { --root-margin: 16px; } +.transition_x_enterActive, +.transition_x_leaveActive { + transition: opacity 0.3s cubic-bezier(0,0,.35,1), transform 0.3s cubic-bezier(0,0,.35,1); +} +.transition_x_enterFrom { + opacity: 0; + transform: translateX(50px); +} +.transition_x_leaveTo { + opacity: 0; + transform: translateX(-50px); +} +.note{ + display: flex; + margin: var(--margin) 0; + align-items: center; + +} </style> diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 14ada9b7f0..4f467213b8 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -102,7 +102,7 @@ export function getAbuseNoteMenu(note: misskey.entities.Note, text: string): Men const u = note.url ?? note.uri ?? `${url}/notes/${note.id}`; os.popup(defineAsyncComponent(() => import('@/components/MkAbuseReportWindow.vue')), { user: note.user, - initialComment: `Note: ${u}\n-----\n`, + initialNoteId: note.id, }, {}, 'closed'); }, }; From 5623960efaec32882d2ef1b309f7dd01e1cf5bc9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 14 Dec 2023 21:06:50 +0900 Subject: [PATCH 290/501] bug fix --- locales/index.d.ts | 20 +-- packages/frontend/src/account.ts | 2 +- packages/frontend/src/components/MkButton.vue | 143 +++++++++++++++++- .../src/components/MkEmojiEditDialog.vue | 10 +- .../src/components/MkEmojiPicker.section.vue | 1 + .../frontend/src/components/MkEmojiPicker.vue | 2 +- .../frontend/src/components/MkNoteSimple.vue | 1 - .../src/components/MkNotifyButton.vue | 8 +- .../frontend/src/components/MkPostForm.vue | 8 +- .../src/components/global/MkEmojiKitchen.vue | 4 +- packages/frontend/src/pages/about.emojis.vue | 6 +- packages/frontend/src/pages/about.vue | 2 +- packages/frontend/src/pages/admin/index.vue | 2 +- .../src/pages/admin/other-settings.vue | 18 +-- packages/frontend/src/pages/timeline.vue | 4 +- .../src/ui/_common_/stream-indicator.vue | 1 - 16 files changed, 183 insertions(+), 49 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index bb797ed710..8b8f1feb96 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2563,6 +2563,16 @@ export interface Locale { }; }; }; + "_schedulePost": { + "list": string; + "postDate": string; + "postTime": string; + "localTime": string; + "addSchedule": string; + "willBePostedAtX": string; + "deleteAreYouSure": string; + "deleteAndEditConfirm": string; + }; "_dataSaver": { "_media": { "title": string; @@ -2581,16 +2591,6 @@ export interface Locale { "description": string; }; }; - "_schedulePost": { - "list": string; - "postDate": string; - "postTime": string; - "localTime": string; - "addSchedule": string; - "willBePostedAtX": string; - "deleteAreYouSure": string; - "deleteAndEditConfirm": string; - }; } declare const locales: { [lang: string]: Locale; diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 0e4e4b50ff..a6af298024 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -284,7 +284,7 @@ export async function openAccountMenu(opts: { text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, - }, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { + }, { type: 'divider' }, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { type: 'parent' as const, icon: 'ti ti-plus', text: i18n.ts.addAccount, diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 9197b4f38d..b5a14a2a39 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -65,7 +65,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { nextTick, onMounted, shallowRef } from 'vue'; +import { nextTick, onMounted, shallowRef, computed, ref, watch } from 'vue'; +import {defaultStore} from "@/store.js"; const props = defineProps<{ type?: 'button' | 'submit' | 'reset'; @@ -92,8 +93,8 @@ const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); // gamingをrefで初期化する let gaming = ref(''); // 0-off , 1-dark , 2-light - // gaming.valueに新しい値を代入する + if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { @@ -113,7 +114,7 @@ watch(darkMode, () => { }) watch(gamingMode, () => { - if (darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate ) { + if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { gaming.value = 'light'; @@ -121,7 +122,6 @@ watch(gamingMode, () => { gaming.value = ''; } }) - const emit = defineEmits<{ (ev: 'click', payload: MouseEvent): void; }>(); @@ -228,7 +228,59 @@ function onMousedown(evt: MouseEvent): void { font-weight: bold; color: var(--fgOnAccent) !important; background: var(--accent); + &.gamingLight { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + color: white !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + &:not(:disabled):hover { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + color: white !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &:not(:disabled):active { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + color: white !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; + } + } + + &.gamingDark { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + color: black; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + + &:not(:disabled):hover { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% ; + color: black; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + } + + &:not(:disabled):active { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + color: black; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + } + } &:not(:disabled):hover { background: var(--X8); } @@ -285,6 +337,59 @@ function onMousedown(evt: MouseEvent): void { &:not(:disabled):active { background: linear-gradient(90deg, var(--X8), var(--X8)); } + &.gamingLight { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + color: white !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + + &:not(:disabled):hover { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800%; + color: white !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + } + + &:not(:disabled):active { + background: linear-gradient(270deg, #c06161, #c0a567, #b6ba69, #81bc72, #63c3be, #8bacd6, #9f8bd6, #d18bd6, #d883b4); + background-size: 1800% 1800% !important; + color: white !important; + -webkit-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite; + -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; + animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; + } + } + + &.gamingDark { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800%; + color: black; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + + &:not(:disabled):hover { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% ; + color: black; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + } + + &:not(:disabled):active { + background: linear-gradient(270deg, #e7a2a2, #e3cfa2, #ebefa1, #b3e7a6, #a6ebe7, #aec5e3, #cabded, #e0b9e3, #f4bddd); + background-size: 1800% 1800% !important; + color: black; + -webkit-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite ; + -moz-animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + animation: AnimationDark var(--gamingspeed) cubic-bezier(0, 0.45, 0.30, 1) infinite; + } + } } &.danger { @@ -352,4 +457,34 @@ function onMousedown(evt: MouseEvent): void { z-index: 1; pointer-events: none; } +@-webkit-keyframes AnimationLight { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@-moz-keyframes AnimationLight { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@keyframes AnimationLight { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@-webkit-keyframes AnimationDark { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@-moz-keyframes AnimationDark { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} +@keyframes AnimationDark { + 0%{background-position:0% 50%} + 50%{background-position:100% 50%} + 100%{background-position:0% 50%} +} </style> diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 1a0f670dc7..4413ef3650 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -148,11 +148,11 @@ function ok() { async function add() { const ret = await os.api('admin/emoji/add-draft', { name: name, - category: category, + category: category.value, aliases: aliases.value.split(' '), license: license.value === '' ? null : license.value, fileId: chooseFile.value.id, - isNotifyIsHome: isNotifyIsHome, + isNotifyIsHome: isNotifyIsHome.value, }); emit('done', { @@ -199,7 +199,7 @@ async function update() { aliases: aliases.value.split(' ').filter(x => x !== ''), license: license.value === '' ? null : license.value, fileId: chooseFile.value?.id, - draft: draft, + draft: draft.value, }); emit('done', { @@ -222,10 +222,10 @@ async function done() { aliases: aliases.value.split(' ').filter(x => x !== ''), license: license.value === '' ? null : license.value, isSensitive: isSensitive.value, - draft: draft, + draft: draft.value, localOnly: localOnly.value, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id), - isNotifyIsHome, + isNotifyIsHome: isNotifyIsHome.value, }; if (file.value) { diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 1295f5782c..1459f24a13 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -51,6 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only @pointerenter="computeButtonTitle" @click="emit('chosen', emoji, $event)" > + jhkjh <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> </button> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index d0791e5554..7723b530fb 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -327,7 +327,7 @@ watch(q, () => { searchResultUnicode.value = Array.from(searchUnicode()); }); -function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { +function filterAvailable(emoji: Misskey.entities.EmojiSimple): null | boolean { return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); } diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index c17bd05e35..c26fcea911 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -28,7 +28,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { ref } from 'vue'; import { i18n } from '../i18n.js'; import MkNoteHeader from '@/components/MkNoteHeader.vue'; import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; diff --git a/packages/frontend/src/components/MkNotifyButton.vue b/packages/frontend/src/components/MkNotifyButton.vue index b25d79ee6b..fd9767846e 100644 --- a/packages/frontend/src/components/MkNotifyButton.vue +++ b/packages/frontend/src/components/MkNotifyButton.vue @@ -62,8 +62,8 @@ const props = withDefaults(defineProps<{ large: false, }); -let isFollowing = $ref(props.user.isFollowing); -let notify = $ref(props.user.notify); +let isFollowing = ref(props.user.isFollowing); +let notify = ref(props.user.notify); const connection = useStream().useChannel('main'); if (props.user.isFollowing == null) { @@ -80,12 +80,12 @@ if (props.user.notify == null) { function onFollowChange(user: Misskey.entities.UserDetailed) { if (user.id === props.user.id) { - isFollowing = user.isFollowing; + isFollowing.value = user.isFollowing; } } function onNotifyChange(user: Misskey.entities.UserDetailed) { if (user.id === props.user.id) { - notify = user.notify; + notify.value = user.notify; console.log(props.user.notify) } } diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 7cf1e34445..5acb424dc1 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -99,7 +99,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { inject, watch, nextTick, onMounted, defineAsyncComponent , computed, ref , provide, shallowRef, ref, computed } from 'vue'; +import { inject, watch, nextTick, onMounted, defineAsyncComponent , provide, shallowRef, ref, computed } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; @@ -891,10 +891,10 @@ async function insertEmoji(ev: MouseEvent) { ); } function insertMfm(){ - insertTextAtCursor(textareaEl, '$'); + insertTextAtCursor(textareaEl.value, '$'); } function insertRuby() { - insertTextAtCursor(textareaEl, '$[ruby 本文 上につくやつ]'); + insertTextAtCursor(textareaEl.value, '$[ruby 本文 上につくやつ]'); } function showActions(ev) { os.popupMenu(postFormActions.map(action => ({ @@ -953,7 +953,7 @@ function openOtherSettingsMenu(ev: MouseEvent) { icon: 'ti ti-calendar-time', indicate: (schedule != null), action: toggleSchedule, - } : undefined, ...(($i.policies?.canScheduleNote) ? [null, { + } : undefined, ...(($i.policies?.canScheduleNote) ? [{ type: 'divider' }, { type: 'button', text: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', diff --git a/packages/frontend/src/components/global/MkEmojiKitchen.vue b/packages/frontend/src/components/global/MkEmojiKitchen.vue index 1ce71b36b2..a507a5a509 100644 --- a/packages/frontend/src/components/global/MkEmojiKitchen.vue +++ b/packages/frontend/src/components/global/MkEmojiKitchen.vue @@ -4,7 +4,7 @@ </template> <script lang="ts" setup> -import { computed } from 'vue'; +import {computed, ref} from 'vue'; const props = defineProps<{ name: string; @@ -17,7 +17,7 @@ const rawUrl = computed(() => props.url); const url = computed(() => rawUrl.value); const alt = computed(() => props.name); -let errored = $ref(url.value == null); +let errored = ref(url.value == null); </script> <style lang="scss" module> diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index a79be897cf..e22c4423cb 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -67,14 +67,14 @@ import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; -import { customEmojis, customEmojiCategories } from '@/custom-emojis.js'; +import {customEmojis, customEmojiCategories, getCustomEmojiTags} from '@/custom-emojis.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata'; -let tab = $ref('emojis'); -const headerActions = $computed(() => []); +let tab = ref('emojis'); +const headerActions = computed(() => []); const customEmojiTags = getCustomEmojiTags(); const q = ref(''); diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index ab4ce20cbc..985a75377e 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {computed, ref, watch, ref } from 'vue'; +import {computed, ref, watch } from 'vue'; import XEmojis from './about.emojis.vue'; import XFederation from './about.federation.vue'; import { version, host } from '@/config.js'; diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index b4b422e798..d25561af79 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {computed, onActivated, onMounted, onUnmounted, provide, ref, watch, ref, computed } from 'vue'; +import {computed, onActivated, onMounted, onUnmounted, provide, ref, watch } from 'vue'; import { i18n } from '@/i18n.js'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index 8afb4b5e7b..b94d2b3737 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -69,18 +69,18 @@ const enableServerMachineStats = ref<boolean>(false); const enableIdenticonGeneration = ref<boolean>(false); const enableChartsForRemoteUser = ref<boolean>(false); const enableChartsForFederatedInstances = ref<boolean>(false); -let DiscordWebhookUrl: string | null = $ref(null); -let EmojiBotToken: string | null = $ref(null); -let ApiBase:string | null = $ref(null) +let DiscordWebhookUrl = ref(null); +let EmojiBotToken= ref(null); +let ApiBase= ref(null) async function init() { const meta = await os.api('admin/meta'); enableServerMachineStats.value = meta.enableServerMachineStats; enableIdenticonGeneration.value = meta.enableIdenticonGeneration; enableChartsForRemoteUser.value = meta.enableChartsForRemoteUser; enableChartsForFederatedInstances.value = meta.enableChartsForFederatedInstances; - DiscordWebhookUrl = meta.DiscordWebhookUrl; - EmojiBotToken = meta.EmojiBotToken; - ApiBase = meta.ApiBase; + DiscordWebhookUrl.value = meta.DiscordWebhookUrl; + EmojiBotToken.value = meta.EmojiBotToken; + ApiBase.value = meta.ApiBase; } function save() { @@ -89,9 +89,9 @@ function save() { enableIdenticonGeneration: enableIdenticonGeneration.value, enableChartsForRemoteUser: enableChartsForRemoteUser.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, - DiscordWebhookUrl, - EmojiBotToken, - ApiBase + DiscordWebhookUrl:DiscordWebhookUrl.value, + EmojiBotToken:EmojiBotToken.value, + ApiBase:ApiBase.value }).then(() => { fetchInstance(); }); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 3d62972ce0..555df52029 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -67,7 +67,7 @@ const src = computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src const withRenotes = ref(true); const withReplies = ref($i ? defaultStore.state.tlWithReplies : false); const onlyFiles = ref(false); -const isShowMediaTimeline = $ref(defaultStore.state.showMediaTimeline) +const isShowMediaTimeline = ref(defaultStore.state.showMediaTimeline) watch(src, () => queue.value = 0); @@ -226,7 +226,7 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList title: i18n.ts._timelines.local, icon: 'ti ti-planet', iconOnly: true, -}, ...(isShowMediaTimeline ? [{ +}, ...(isShowMediaTimeline.value ? [{ key: 'media', title: i18n.ts._timelines.media, icon: 'ti ti-photo', diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue index ed3ff257ee..7eb70d7e1d 100644 --- a/packages/frontend/src/ui/_common_/stream-indicator.vue +++ b/packages/frontend/src/ui/_common_/stream-indicator.vue @@ -27,7 +27,6 @@ const hasDisconnected = ref(false); let timeoutId = ref<number>(); function onDisconnected() { - if (isReloading) return; window.clearTimeout(timeoutId.value); timeoutId.value = window.setTimeout(() => { hasDisconnected.value = true; From 1375a6fbdbd2b15f8b3b3f0ab40e7b18352984bc Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 09:06:51 +0900 Subject: [PATCH 291/501] =?UTF-8?q?fix:=20=E3=82=AF=E3=82=BD=E3=83=95?= =?UTF-8?q?=E3=82=A9=E3=83=AB=E3=83=80=E6=94=B9=E5=A4=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkEmojiPicker.section.vue | 131 ++- .../frontend/src/components/MkEmojiPicker.vue | 964 +++++++++--------- 2 files changed, 542 insertions(+), 553 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 1459f24a13..5875b678a1 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -4,59 +4,58 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> -<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) --> -<section v-if="!hasChildSection" v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);"> - <header class="_acrylic" @click="shown = !shown"> - <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-icons"></i>:{{ emojis.length }}) - </header> - <div v-if="shown" class="body"> - <button - v-for="emoji in emojis" - :key="emoji" - :data-emoji="emoji" - class="_button item" - @pointerenter="computeButtonTitle" - @click="emit('chosen', emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> -</section> -<!-- フォルダの中にはカスタム絵文字やフォルダがある --> -<section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);"> - <header class="_acrylic" @click="shown = !shown"> - <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }}) - </header> - <div v-if="shown" style="padding-left: 9px;"> - <MkEmojiPickerSection - v-for="child in customEmojiTree" - :key="`custom:${child.value}`" - :initialShown="initialShown" - :emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))" - :hasChildSection="child.children.length !== 0" - :customEmojiTree="child.children" - @chosen="nestedChosen" - > - {{ child.value || i18n.ts.other }} - </MkEmojiPickerSection> - </div> - <div v-if="shown" class="body"> - <button - v-for="emoji in emojis" - :key="emoji" - :data-emoji="emoji" - class="_button item" - @pointerenter="computeButtonTitle" - @click="emit('chosen', emoji, $event)" - > - jhkjh - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> -</section> + <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> + <!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) --> + <section v-if="!hasChildSection" style="border-radius: 6px; padding-top: 9px;"> + <header class="_acrylic" @click="shown = !shown" style="border-radius: 6px;"> + <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) + </header> + <div v-if="shown" class="body"> + <button + v-for="emoji in emojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + <!-- フォルダの中にはカスタム絵文字やフォルダがある --> + <section v-else style="border-radius: 6px; padding-top: 9px;"> + <header class="_acrylic" @click="shown = !shown"> + <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (フォルダー) + </header> + <div v-if="shown" style="padding-left: 9px; "> + <MkEmojiPickerSection + v-for="child in customEmojiTree" + :key="`custom:${child.value}`" + :initialShown="initialShown" + :emojis="computed(() => customEmojis.filter(e => e.category === child.category || e.category === child.category+'/'+child.category).map(e => `:${e.name}:`))" + :hasChildSection="child.children.length !== 0" + :customEmojiTree="child.children" + @chosen="nestedChosen" + > + {{ child.value || i18n.ts.other }} + </MkEmojiPickerSection> + </div> + <div v-if="shown" class="body"> + <button + v-for="emoji in emojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> </template> <script lang="ts" setup> @@ -68,32 +67,18 @@ import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue'; const props = defineProps<{ category?: string[]; - emojis: string[] | Ref<string[]>; - initialShown?: boolean; - hasChildSection?: boolean; - customEmojiTree?: CustomEmojiFolderTree[]; + emojis: string[] | Ref<string[]>; + initialShown?: boolean; + hasChildSection?: boolean; + customEmojiTree?: CustomEmojiFolderTree[]; }>(); - +console.log(props.customEmojiTree); const emit = defineEmits<{ (ev: 'chosen', v: string, event: MouseEvent): void; }>(); -const toggleShown = (index) => { - shown_fold.value[index] = !shown_fold.value[index]; - -}; - -const toggleShown_fol = () => { - for (let i = 0; i < shown_fold.value.length; i++) { - shown_fold.value[i] = false; - } - shown_fol.value = !shown_fol.value; -}; - const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value); -const shown_fold = ref(Array(props.category === undefined ? 0 : props.category.length).fill(false)); const shown = ref(!!props.initialShown); -const shown_fol = ref(!!props.initialShown); /** @see MkEmojiPicker.vue */ function computeButtonTitle(ev: MouseEvent): void { @@ -103,6 +88,6 @@ function computeButtonTitle(ev: MouseEvent): void { } function nestedChosen(emoji: any, ev?: MouseEvent) { - emit('chosen', emoji, ev); + emit('chosen', emoji, ev); } </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 84b375f8b2..ce2e83f042 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -4,36 +4,36 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> - <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> - <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> - <div ref="emojisEl" class="emojis" tabindex="-1"> - <section class="result"> - <div v-if="searchResultCustom.length > 0" class="body"> - <button - v-for="emoji in searchResultCustom" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji class="emoji" :name="emoji.name"/> - </button> - </div> - <div v-if="searchResultUnicode.length > 0" class="body"> - <button - v-for="emoji in searchResultUnicode" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkEmoji class="emoji" :emoji="emoji.char"/> - </button> - </div> - </section> + <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> + <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> + <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> + <div ref="emojisEl" class="emojis" tabindex="-1"> + <section class="result"> + <div v-if="searchResultCustom.length > 0" class="body"> + <button + v-for="emoji in searchResultCustom" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji class="emoji" :name="emoji.name"/> + </button> + </div> + <div v-if="searchResultUnicode.length > 0" class="body"> + <button + v-for="emoji in searchResultUnicode" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkEmoji class="emoji" :emoji="emoji.char"/> + </button> + </div> + </section> <div v-if="tab === 'index'" class="group index"> <section v-if="showPinned && pinned.length > 0"> @@ -53,49 +53,49 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </section> - <section> - <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> - <div class="body"> - <button - v-for="emoji in recentlyUsedEmojis" - :key="emoji" - class="_button item" - :data-emoji="emoji" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> - </section> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> - <XSection - v-for="child in customEmojiFolderRoot.children" - :key="`custom:${child.value}`" - :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value).filter(filterAvailable).map(e => `:${e.name}:`))" - :hasChildSection="child.children.length !== 0" - :customEmojiTree="child.children" - @chosen="chosen" - > - {{ child.value || i18n.ts.other }} - </XSection> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.emoji }}</header> - <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> - </div> - </div> - <div class="tabs"> - <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> - </div> -</div> + <section> + <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> + <div class="body"> + <button + v-for="emoji in recentlyUsedEmojis" + :key="emoji" + class="_button item" + :data-emoji="emoji" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> + <XSection + v-for="child in customEmojiFolderRoot.children" + :key="`custom:${child.value}`" + :initialShown="false" + :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !e.category || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" + :hasChildSection="child.children.length !== 0" + :customEmojiTree="child.children" + @chosen="chosen" + > + {{ child.value || i18n.ts.other }} + </XSection> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.emoji }}</header> + <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> + </div> + </div> + <div class="tabs"> + <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> + </div> + </div> </template> <script lang="ts" setup> @@ -103,12 +103,12 @@ import { ref, shallowRef, computed, watch, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; import XSection from '@/components/MkEmojiPicker.section.vue'; import { - emojilist, - emojiCharByCategory, - UnicodeEmojiDef, - unicodeEmojiCategories as categories, - getEmojiName, - CustomEmojiFolderTree, + emojilist, + emojiCharByCategory, + UnicodeEmojiDef, + unicodeEmojiCategories as categories, + getEmojiName, + CustomEmojiFolderTree, } from '@/scripts/emojilist.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import * as os from '@/os.js'; @@ -120,18 +120,18 @@ import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-e import { $i } from '@/account.js'; const props = withDefaults(defineProps<{ - showPinned?: boolean; - pinnedEmojis?: string[]; - maxHeight?: number; - asDrawer?: boolean; - asWindow?: boolean; - asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう + showPinned?: boolean; + pinnedEmojis?: string[]; + maxHeight?: number; + asDrawer?: boolean; + asWindow?: boolean; + asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう }>(), { - showPinned: true, + showPinned: true, }); const emit = defineEmits<{ - (ev: 'chosen', v: string): void; + (ev: 'chosen', v: string): void; }>(); const searchEl = shallowRef<HTMLInputElement>(); @@ -156,517 +156,521 @@ const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); const customEmojiFolderRoot: CustomEmojiFolderTree = { value: '', category: '', children: [] }; function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree { - const parts = input.split('/').map(p => p.trim()); - let currentNode: CustomEmojiFolderTree = root; + const parts = input.split('/').map(p => p.trim()); // スラッシュで区切って配列にしてる + let currentNode: CustomEmojiFolderTree = root; // currentNode は root - for (const part of parts) { - let existingNode = currentNode.children.find((node) => node.value === part); + if (parts.length === 1 && parts[0] !== '') { // parts が 1 つで空じゃなかったら + parts.push(parts[0]) // parts に parts[0] を追加 (test category だったら test/test category になる) + } - if (!existingNode) { - const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] }; - currentNode.children.push(newNode); - existingNode = newNode; - } + for (const part of parts) { // parts を順番に見ていく + let existingNode = currentNode.children.find((node) => node.value === part); // currentNode の children から part と同じ value を持つ node を探す - currentNode = existingNode; - } + if (!existingNode) { // なかったら + const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] }; // 新しい node を作る - return currentNode; + currentNode.children.push(newNode); // currentNode の children に newNode を追加 + existingNode = newNode; // existingNode に newNode を代入 + } + + currentNode = existingNode; // currentNode に existingNode を代入 + } + return currentNode; } customEmojiCategories.value.forEach(ec => { - if (ec !== null) { - parseAndMergeCategories(ec, customEmojiFolderRoot); - } + if (ec !== null) { + parseAndMergeCategories(ec, customEmojiFolderRoot); + } }); parseAndMergeCategories('', customEmojiFolderRoot); watch(q, () => { - if (emojisEl.value) emojisEl.value.scrollTop = 0; + if (emojisEl.value) emojisEl.value.scrollTop = 0; - if (q.value === '') { - searchResultCustom.value = []; - searchResultUnicode.value = []; - return; - } + if (q.value === '') { + searchResultCustom.value = []; + searchResultUnicode.value = []; + return; + } - const newQ = q.value.replace(/:/g, '').toLowerCase(); + const newQ = q.value.replace(/:/g, '').toLowerCase(); - const searchCustom = () => { - const max = 100; - const emojis = customEmojis.value; - const matches = new Set<Misskey.entities.EmojiSimple>(); + const searchCustom = () => { + const max = 100; + const emojis = customEmojis.value; + const matches = new Set<Misskey.entities.EmojiSimple>(); - const exactMatch = emojis.find(emoji => emoji.name === newQ); - if (exactMatch) matches.add(exactMatch); + const exactMatch = emojis.find(emoji => emoji.name === newQ); + if (exactMatch) matches.add(exactMatch); - if (newQ.includes(' ')) { // AND検索 - const keywords = newQ.split(' '); + if (newQ.includes(' ')) { // AND検索 + const keywords = newQ.split(' '); - // 名前にキーワードが含まれている - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + // 名前にキーワードが含まれている + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - // 名前またはエイリアスにキーワードが含まれている - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } else { - for (const emoji of emojis) { - if (emoji.name.startsWith(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + // 名前またはエイリアスにキーワードが含まれている + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } else { + for (const emoji of emojis) { + if (emoji.name.startsWith(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.aliases.some(alias => alias.startsWith(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias.startsWith(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.name.includes(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.name.includes(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.aliases.some(alias => alias.includes(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias.includes(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } - return matches; - }; + return matches; + }; - const searchUnicode = () => { - const max = 100; - const emojis = emojilist; - const matches = new Set<UnicodeEmojiDef>(); + const searchUnicode = () => { + const max = 100; + const emojis = emojilist; + const matches = new Set<UnicodeEmojiDef>(); - const exactMatch = emojis.find(emoji => emoji.name === newQ); - if (exactMatch) matches.add(exactMatch); + const exactMatch = emojis.find(emoji => emoji.name === newQ); + if (exactMatch) matches.add(exactMatch); - if (newQ.includes(' ')) { // AND検索 - const keywords = newQ.split(' '); + if (newQ.includes(' ')) { // AND検索 + const keywords = newQ.split(' '); - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (keywords.every(keyword => index[emoji.char].some(k => k.includes(keyword)))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } - } else { - for (const emoji of emojis) { - if (emoji.name.startsWith(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (keywords.every(keyword => index[emoji.char].some(k => k.includes(keyword)))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } + } else { + for (const emoji of emojis) { + if (emoji.name.startsWith(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (index[emoji.char].some(k => k.startsWith(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (index[emoji.char].some(k => k.startsWith(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } - for (const emoji of emojis) { - if (emoji.name.includes(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.name.includes(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (index[emoji.char].some(k => k.includes(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } - } + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (index[emoji.char].some(k => k.includes(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } + } - return matches; - }; + return matches; + }; - searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); - searchResultUnicode.value = Array.from(searchUnicode()); + searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); + searchResultUnicode.value = Array.from(searchUnicode()); }); function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { - return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); + return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); } function focus() { - if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { - searchEl.value?.focus({ - preventScroll: true, - }); - } + if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { + searchEl.value?.focus({ + preventScroll: true, + }); + } } function reset() { - if (emojisEl.value) emojisEl.value.scrollTop = 0; - q.value = ''; + if (emojisEl.value) emojisEl.value.scrollTop = 0; + q.value = ''; } function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): string { - return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; + return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; } /** @see MkEmojiPicker.section.vue */ function computeButtonTitle(ev: MouseEvent): void { - const elm = ev.target as HTMLElement; - const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + const elm = ev.target as HTMLElement; + const emoji = elm.dataset.emoji as string; + elm.title = getEmojiName(emoji) ?? emoji; } function chosen(emoji: any, ev?: MouseEvent) { - const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; - if (el) { - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); - } + const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } - const key = getKey(emoji); - emit('chosen', key); + const key = getKey(emoji); + emit('chosen', key); - // 最近使った絵文字更新 - if (!pinned.value?.includes(key)) { - let recents = defaultStore.state.recentlyUsedEmojis; - recents = recents.filter((emoji: any) => emoji !== key); - recents.unshift(key); - defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); - } + // 最近使った絵文字更新 + if (!pinned.value?.includes(key)) { + let recents = defaultStore.state.recentlyUsedEmojis; + recents = recents.filter((emoji: any) => emoji !== key); + recents.unshift(key); + defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); + } } function input(): void { - // Using custom input event instead of v-model to respond immediately on - // Android, where composition happens on all languages - // (v-model does not update during composition) - q.value = searchEl.value?.value.trim() ?? ''; + // Using custom input event instead of v-model to respond immediately on + // Android, where composition happens on all languages + // (v-model does not update during composition) + q.value = searchEl.value?.value.trim() ?? ''; } function paste(event: ClipboardEvent): void { - const pasted = event.clipboardData?.getData('text') ?? ''; - if (done(pasted)) { - event.preventDefault(); - } + const pasted = event.clipboardData?.getData('text') ?? ''; + if (done(pasted)) { + event.preventDefault(); + } } function onEnter(ev: KeyboardEvent) { - if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; - done(); + if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; + done(); } function done(query?: string): boolean | void { - if (query == null) query = q.value; - if (query == null || typeof query !== 'string') return; + if (query == null) query = q.value; + if (query == null || typeof query !== 'string') return; - const q2 = query.replace(/:/g, ''); - const exactMatchCustom = customEmojisMap.get(q2); - if (exactMatchCustom) { - chosen(exactMatchCustom); - return true; - } - const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2); - if (exactMatchUnicode) { - chosen(exactMatchUnicode); - return true; - } - if (searchResultCustom.value.length > 0) { - chosen(searchResultCustom.value[0]); - return true; - } - if (searchResultUnicode.value.length > 0) { - chosen(searchResultUnicode.value[0]); - return true; - } + const q2 = query.replace(/:/g, ''); + const exactMatchCustom = customEmojisMap.get(q2); + if (exactMatchCustom) { + chosen(exactMatchCustom); + return true; + } + const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2); + if (exactMatchUnicode) { + chosen(exactMatchUnicode); + return true; + } + if (searchResultCustom.value.length > 0) { + chosen(searchResultCustom.value[0]); + return true; + } + if (searchResultUnicode.value.length > 0) { + chosen(searchResultUnicode.value[0]); + return true; + } } onMounted(() => { - focus(); + focus(); }); defineExpose({ - focus, - reset, + focus, + reset, }); </script> <style lang="scss" scoped> .omfetrab { - $pad: 8px; + $pad: 8px; - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; - &.s1 { - --eachSize: 40px; - } + &.s1 { + --eachSize: 40px; + } - &.s2 { - --eachSize: 45px; - } + &.s2 { + --eachSize: 45px; + } - &.s3 { - --eachSize: 50px; - } + &.s3 { + --eachSize: 50px; + } - &.w1 { - width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr; - } + &.w1 { + width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr; + } - &.w2 { - width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w2 { + width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w3 { - width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w3 { + width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w4 { - width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w4 { + width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w5 { - width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w5 { + width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.h1 { - height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); - } + &.h1 { + height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); + } - &.h2 { - height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); - } + &.h2 { + height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); + } - &.h3 { - height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); - } + &.h3 { + height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); + } - &.h4 { - height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); - } + &.h4 { + height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); + } - &.asDrawer { - width: 100% !important; + &.asDrawer { + width: 100% !important; - > .emojis { - ::v-deep(section) { - > header { - height: 32px; - line-height: 32px; - padding: 0 12px; - font-size: 15px; - } + > .emojis { + ::v-deep(section) { + > header { + height: 32px; + line-height: 32px; + padding: 0 12px; + font-size: 15px; + } - > .body { - display: grid; - grid-template-columns: var(--columns); - font-size: 30px; + > .body { + display: grid; + grid-template-columns: var(--columns); + font-size: 30px; - > .item { - aspect-ratio: 1 / 1; - width: auto; - height: auto; - min-width: 0; - } - } - } - } - } + > .item { + aspect-ratio: 1 / 1; + width: auto; + height: auto; + min-width: 0; + } + } + } + } + } - &.asWindow { - width: 100% !important; - height: 100% !important; + &.asWindow { + width: 100% !important; + height: 100% !important; - > .emojis { - ::v-deep(section) { - > .body { - display: grid; - grid-template-columns: var(--columns); - font-size: 30px; + > .emojis { + ::v-deep(section) { + > .body { + display: grid; + grid-template-columns: var(--columns); + font-size: 30px; - > .item { - aspect-ratio: 1 / 1; - width: auto; - height: auto; - min-width: 0; - } - } - } - } - } + > .item { + aspect-ratio: 1 / 1; + width: auto; + height: auto; + min-width: 0; + } + } + } + } + } - > .search { - width: 100%; - padding: 12px; - box-sizing: border-box; - font-size: 1em; - outline: none; - border: none; - background: transparent; - color: var(--fg); + > .search { + width: 100%; + padding: 12px; + box-sizing: border-box; + font-size: 1em; + outline: none; + border: none; + background: transparent; + color: var(--fg); - &:not(:focus):not(.filled) { - margin-bottom: env(safe-area-inset-bottom, 0px); - } + &:not(:focus):not(.filled) { + margin-bottom: env(safe-area-inset-bottom, 0px); + } - &:not(.filled) { - order: 1; - z-index: 2; - box-shadow: 0px -1px 0 0px var(--divider); - } - } + &:not(.filled) { + order: 1; + z-index: 2; + box-shadow: 0px -1px 0 0px var(--divider); + } + } - > .tabs { - display: flex; - display: none; + > .tabs { + display: flex; + display: none; - > .tab { - flex: 1; - height: 38px; - border-top: solid 0.5px var(--divider); + > .tab { + flex: 1; + height: 38px; + border-top: solid 0.5px var(--divider); - &.active { - border-top: solid 1px var(--accent); - color: var(--accent); - } - } - } + &.active { + border-top: solid 1px var(--accent); + color: var(--accent); + } + } + } - > .emojis { - height: 100%; - overflow-y: auto; - overflow-x: hidden; + > .emojis { + height: 100%; + overflow-y: auto; + overflow-x: hidden; - scrollbar-width: none; + scrollbar-width: none; - &::-webkit-scrollbar { - display: none; - } + &::-webkit-scrollbar { + display: none; + } - > .group { - &:not(.index) { - padding: 4px 0 8px 0; - border-top: solid 0.5px var(--divider); - } + > .group { + &:not(.index) { + padding: 4px 0 8px 0; + border-top: solid 0.5px var(--divider); + } - > header { - /*position: sticky; - top: 0; - left: 0;*/ - height: 32px; - line-height: 32px; - z-index: 2; - padding: 0 8px; - font-size: 12px; - } - } + > header { + /*position: sticky; +top: 0; +left: 0;*/ + height: 32px; + line-height: 32px; + z-index: 2; + padding: 0 8px; + font-size: 12px; + } + } - ::v-deep(section) { - > header { - position: sticky; - top: 0; - left: 0; - height: 32px; - line-height: 32px; - z-index: 1; - padding: 0 8px; - font-size: 12px; - cursor: pointer; + ::v-deep(section) { + > header { + position: sticky; + top: 0; + left: 0; + height: 32px; + line-height: 32px; + z-index: 1; + padding: 0 8px; + font-size: 12px; + cursor: pointer; - &:hover { - color: var(--accent); - } - } + &:hover { + color: var(--accent); + } + } - > .body { - position: relative; - padding: $pad; + > .body { + position: relative; + padding: $pad; - > .item { - position: relative; - padding: 0; - width: var(--eachSize); - height: var(--eachSize); - contain: strict; - border-radius: 4px; - font-size: 24px; + > .item { + position: relative; + padding: 0; + width: var(--eachSize); + height: var(--eachSize); + contain: strict; + border-radius: 4px; + font-size: 24px; - &:focus-visible { - outline: solid 2px var(--focus); - z-index: 1; - } + &:focus-visible { + outline: solid 2px var(--focus); + z-index: 1; + } - &:hover { - background: rgba(0, 0, 0, 0.05); - } + &:hover { + background: rgba(0, 0, 0, 0.05); + } - &:active { - background: var(--accent); - box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); - } + &:active { + background: var(--accent); + box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); + } - > .emoji { - height: 1.25em; - vertical-align: -.25em; - pointer-events: none; - width: 100%; - object-fit: contain; - } - } - } + > .emoji { + height: 1.25em; + vertical-align: -.25em; + pointer-events: none; + width: 100%; + object-fit: contain; + } + } + } - &.result { - border-bottom: solid 0.5px var(--divider); + &.result { + border-bottom: solid 0.5px var(--divider); - &:empty { - display: none; - } - } - } - } + &:empty { + display: none; + } + } + } + } } </style> From 2a9ddf2bc8674858136d2f9bd7fa85ec9e8e022a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 10:44:01 +0900 Subject: [PATCH 292/501] =?UTF-8?q?=E5=A4=9A=E5=88=86=E5=8B=95=E3=81=8F?= =?UTF-8?q?=E3=81=A8=E6=80=9D=E3=81=86=E3=81=8B=E3=82=89=E3=83=AA=E3=83=AA?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=81=97=E3=82=88=E3=81=86=E3=81=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 +- package.json | 2 +- .../backend/1703294653915-requestemoji.js | 11 + packages/backend/src/models/Meta.ts | 5 + .../api/endpoints/admin/emoji/add-draft.ts | 129 ----------- .../api/endpoints/admin/emoji/add-request.ts | 84 +++++-- .../src/server/api/endpoints/admin/meta.ts | 1 + .../server/api/endpoints/admin/update-meta.ts | 4 + .../src/components/MkCustomEmojiEditDraft.vue | 214 ------------------ .../src/components/MkCustomEmojiEditLocal.vue | 11 +- .../src/components/MkEmojiEditDialog.vue | 80 ++++--- .../src/components/MkNoteDetailed.vue | 1 - packages/frontend/src/components/MkSwitch.vue | 2 - packages/frontend/src/pages/about.emojis.vue | 34 +-- .../src/pages/admin/other-settings.vue | 6 + .../src/pages/custom-emojis-manager.vue | 6 +- 16 files changed, 161 insertions(+), 433 deletions(-) create mode 100644 packages/backend/1703294653915-requestemoji.js delete mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts delete mode 100644 packages/frontend/src/components/MkCustomEmojiEditDraft.vue diff --git a/locales/index.d.ts b/locales/index.d.ts index 71176daf8d..6d5bcd0c59 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1076,11 +1076,11 @@ export interface Locale { "hiddenTagsDescription": string; "notesSearchNotAvailable": string; "license": string; - "draft": string; - "undrafted": string; "requestPending": string; "approval": string; "requestingEmojis": string; + "draft": string; + "undrafted": string; "unfavoriteConfirm": string; "myClips": string; "drivecleaner": string; diff --git a/package.json b/package.json index d39b800a18..d9af23c2e4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.12.0", + "version": "2023.12.0-PrisMisskey.1", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/1703294653915-requestemoji.js b/packages/backend/1703294653915-requestemoji.js new file mode 100644 index 0000000000..358e77bf07 --- /dev/null +++ b/packages/backend/1703294653915-requestemoji.js @@ -0,0 +1,11 @@ +export class Requestemoji1703294653915 { + name = 'Requestemoji1703294653915' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "requestEmojiAllOk" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "requestEmojiAllOk"`); + } +} diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 4a81c39680..6a483d51d4 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -549,4 +549,9 @@ export class MiMeta { default: 0, }) public notesPerOneAd: number; + + @Column('boolean', { + default: false, + }) + public requestEmojiAllOk: boolean; } diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts deleted file mode 100644 index 978663be6b..0000000000 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-draft.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Inject, Injectable } from '@nestjs/common'; -import { Endpoint } from '@/server/api/endpoint-base.js'; -import type { DriveFilesRepository } from '@/models/_.js'; -import { DI } from '@/di-symbols.js'; -import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { ApiError } from '../../../error.js'; -import { MetaService } from '@/core/MetaService.js'; -import {DriveService} from "@/core/DriveService.js"; -export const meta = { - tags: ['admin'], - - requireCredential: true, - requireRolePolicy: 'canRequestCustomEmojis', - - errors: { - noSuchFile: { - message: 'No such file.', - code: 'NO_SUCH_FILE', - id: 'fc46b5a4-6b92-4c33-ac66-b806659bb5cf', - }, - }, -} as const; - -export const paramDef = { - type: 'object', - properties: { - name: { type: 'string', pattern: '^[a-zA-Z0-9_]+$' }, - category: { - type: 'string', - nullable: true, - description: 'Use `null` to reset the category.', - }, - aliases: { type: 'array', items: { - type: 'string', - } }, - license: { type: 'string', nullable: true }, - fileId: { type: 'string', format: 'misskey:id' }, - isSensitive: { type: 'boolean' }, - localOnly: { type: 'boolean' }, - isNotifyIsHome: { type: 'boolean', default: false }, - }, - required: ['name', 'fileId'], -} as const; - -// TODO: ロジックをサービスに切り出す - -// eslint-disable-next-line import/no-default-export -@Injectable() -export default class extends Endpoint<typeof meta, typeof paramDef> { - - constructor( - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - private metaService: MetaService, - private customEmojiService: CustomEmojiService, - private moderationLogService: ModerationLogService, - private driveService: DriveService, - ) { - super(meta, paramDef, async (ps, me) => { - let driveFile; - let tmp = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); - if (tmp == null) throw new ApiError(meta.errors.noSuchFile); - - try { - driveFile = await this.driveService.uploadFromUrl({ url: tmp.url , user: null, force: true }); - } catch (e) { - throw new ApiError(); - } - - const emoji = await this.customEmojiService.add({ - driveFile, - name: ps.name, - category: ps.category ?? null, - aliases: ps.aliases ?? [], - license: ps.license ?? null, - host: null, - draft: false, - isSensitive: ps.isSensitive ?? false, - localOnly: ps.localOnly ?? false, - roleIdsThatCanBeUsedThisEmojiAsReaction: [], - }); - const {ApiBase,EmojiBotToken,DiscordWebhookUrl} = (await this.metaService.fetch()) - - if (EmojiBotToken){ - const data_Miss = { - 'i': EmojiBotToken, - 'visibility': ps.isNotifyIsHome ? 'home' : 'public', - 'text': - '絵文字名 : :' + ps.name + ':\n' + - 'カテゴリ : ' + ps.category + '\n' + - 'ライセンス : ' + ps.license + '\n' + - 'タグ : ' + ps.aliases + '\n' + - '追加したユーザー : ' + '@' + me.username + '\n' - }; - await fetch(ApiBase+'/notes/create', { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body:JSON.stringify( data_Miss) - }) - } - - if (DiscordWebhookUrl){ - const data_disc = {"username": "絵文字追加通知ちゃん", - 'content': - '絵文字名 : :'+ ps.name +':\n' + - 'カテゴリ : ' + ps.category + '\n'+ - 'ライセンス : '+ ps.license + '\n'+ - 'タグ : '+ps.aliases+ '\n'+ - '追加したユーザー : ' + '@'+me.username + '\n' - } - await fetch(DiscordWebhookUrl, { - 'method':'post', - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(data_disc), - }) - } - - - return { - id: emoji.id, - }; - }); - } -} diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts index 609297f667..3818cd044a 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts @@ -5,6 +5,8 @@ import { DI } from '@/di-symbols.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { ModerationLogService } from '@/core/ModerationLogService.js'; import { ApiError } from '../../../error.js'; +import {MetaService} from "@/core/MetaService.js"; +import {DriveService} from "@/core/DriveService.js"; export const meta = { tags: ['admin'], @@ -54,9 +56,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { constructor( @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - + private metaService: MetaService, private customEmojiService: CustomEmojiService, - + private driveService: DriveService, private moderationLogService: ModerationLogService, ) { super(meta, paramDef, async (ps, me) => { @@ -64,25 +66,81 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const isRequestDuplicate = await this.customEmojiService.checkRequestDuplicate(ps.name); if (isDuplicate || isRequestDuplicate) throw new ApiError(meta.errors.duplicateName); - const driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); - + let driveFile; + driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); - const emoji = await this.customEmojiService.request({ - driveFile, - name: ps.name, - category: ps.category ?? null, - aliases: ps.aliases ?? [], - license: ps.license ?? null, - isSensitive: ps.isSensitive ?? false, - localOnly: ps.localOnly ?? false, - }); + if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + const {ApiBase,EmojiBotToken,DiscordWebhookUrl,requestEmojiAllOk} = (await this.metaService.fetch()) + let emoji; + if (requestEmojiAllOk){ + emoji = await this.customEmojiService.add({ + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + host: null, + draft: false, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + roleIdsThatCanBeUsedThisEmojiAsReaction: [], + }); + }else{ + emoji = await this.customEmojiService.request({ + driveFile, + name: ps.name, + category: ps.category ?? null, + aliases: ps.aliases ?? [], + license: ps.license ?? null, + isSensitive: ps.isSensitive ?? false, + localOnly: ps.localOnly ?? false, + }); + } + await this.moderationLogService.log(me, 'addCustomEmoji', { emojiId: emoji.id, emoji: emoji, }); + if (EmojiBotToken){ + const data_Miss = { + 'i': EmojiBotToken, + 'visibility': ps.isNotifyIsHome ? 'home' : 'public', + 'text': + '絵文字名 : :' + ps.name + ':\n' + + 'カテゴリ : ' + ps.category + '\n' + + 'ライセンス : ' + ps.license + '\n' + + 'タグ : ' + ps.aliases + '\n' + + '追加したユーザー : ' + '@' + me.username + '\n' + }; + await fetch(ApiBase+'/notes/create', { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body:JSON.stringify( data_Miss) + }) + } + + if (DiscordWebhookUrl){ + const data_disc = {"username": "絵文字追加通知ちゃん", + 'content': + '絵文字名 : :'+ ps.name +':\n' + + 'カテゴリ : ' + ps.category + '\n'+ + 'ライセンス : '+ ps.license + '\n'+ + 'タグ : '+ps.aliases+ '\n'+ + '追加したユーザー : ' + '@'+me.username + '\n' + } + await fetch(DiscordWebhookUrl, { + 'method':'post', + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data_disc), + }) + } return { id: emoji.id, }; diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index 9fe23c7ace..cdb920b910 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -455,6 +455,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- turnstileSiteKey: instance.turnstileSiteKey, swPublickey: instance.swPublicKey, themeColor: instance.themeColor, + requestEmojiAllOk: instance.requestEmojiAllOk, mascotImageUrl: instance.mascotImageUrl, bannerUrl: instance.bannerUrl, serverErrorImageUrl: instance.serverErrorImageUrl, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 3b7e6ad224..faa84fba7c 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -103,6 +103,7 @@ export const paramDef = { privacyPolicyUrl: { type: 'string', nullable: true }, useObjectStorage: { type: 'boolean' }, objectStorageBaseUrl: { type: 'string', nullable: true }, + requestEmojiAllOk: { type: 'boolean', nullable: true }, objectStorageBucket: { type: 'string', nullable: true }, objectStoragePrefix: { type: 'string', nullable: true }, objectStorageEndpoint: { type: 'string', nullable: true }, @@ -221,6 +222,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.infoImageUrl = ps.infoImageUrl; } + if (ps.requestEmojiAllOk !== undefined) { + set.requestEmojiAllOk = ps.requestEmojiAllOk; + } if (ps.notFoundImageUrl !== undefined) { set.notFoundImageUrl = ps.notFoundImageUrl; } diff --git a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue b/packages/frontend/src/components/MkCustomEmojiEditDraft.vue deleted file mode 100644 index 34d033119d..0000000000 --- a/packages/frontend/src/components/MkCustomEmojiEditDraft.vue +++ /dev/null @@ -1,214 +0,0 @@ -<template> -<MkPagination ref="emojisDraftPaginationComponent" :pagination="paginationDraft"> - <template #empty><span>{{ i18n.ts.noCustomEmojis }}</span></template> - <template #default="{items}"> - <div class="ldhfsamy"> - <template v-for="emoji in items" :key="emoji.id"> - <div class="emoji _panel"> - <div class="img"> - <div class="imgLight"><img :src="emoji.url" :alt="emoji.name"/></div> - <div class="imgDark"><img :src="emoji.url" :alt="emoji.name"/></div> - </div> - <div class="info"> - <div class="name">{{ i18n.ts.name }}: {{ emoji.name }}</div> - <div class="category">{{ i18n.ts.category }}:{{ emoji.category }}</div> - <div class="aliases">{{ i18n.ts.tags }}:{{ emoji.aliases.join(' ') }}</div> - <div class="license">{{ i18n.ts.license }}:{{ emoji.license }}</div> - </div> - <div class="edit-button"> - <MkButton primary class="edit" @click="editDraft(emoji)"> - {{ i18n.ts.edit }} - </MkButton> - <MkButton class="draft" @click="undrafted(emoji)"> - {{ i18n.ts.undrafted }} - </MkButton> - <MkButton danger class="delete" @click="deleteDraft(emoji)"> - {{ i18n.ts.delete }} - </MkButton> - </div> - </div> - </template> - </div> - </template> -</MkPagination> -</template> - -<script lang="ts" setup> -import { computed, defineAsyncComponent, ref, shallowRef } from 'vue'; -import MkPagination from '@/components/MkPagination.vue'; -import * as os from '@/os'; -import { i18n } from '@/i18n'; -import MkButton from '@/components/MkButton.vue'; - -const emojisDraftPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); - -const query = ref(null); - -const paginationDraft = { - endpoint: 'admin/emoji/list' as const, - limit: 30, - params: computed(() => ({ - query: (query.value && query.value !== '') ? query.value : null, - draft: true, - })), -}; - -const editDraft = (emoji) => { - os.popup(defineAsyncComponent(() => import('@/components/MkEmojiEditDialog.vue')), { - emoji: emoji, - isRequest: false, - }, { - done: result => { - if (result.updated) { - emojisDraftPaginationComponent.value.updateItem(result.updated.id, (oldEmoji: any) => ({ - ...oldEmoji, - ...result.updated, - })); - emojisDraftPaginationComponent.value.reload(); - } else if (result.deleted) { - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - emojisDraftPaginationComponent.value.reload(); - } - }, - }, 'closed'); -}; - -async function undrafted(emoji) { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.t('undraftAreYouSure', { x: emoji.name }), - }); - if (canceled) return; - - await os.api('admin/emoji/update', { - id: emoji.id, - name: emoji.name, - category: emoji.category, - aliases: emoji.aliases, - license: emoji.license, - draft: false, - isSensitive: emoji.isSensitive, - localOnly: emoji.localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: emoji.roleIdsThatCanBeUsedThisEmojiAsReaction, - }); - - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - emojisDraftPaginationComponent.value.reload(); -} - -async function deleteDraft(emoji) { - const { canceled } = await os.confirm({ - type: 'warning', - text: i18n.t('removeAreYouSure', { x: emoji.name }), - }); - if (canceled) return; - - os.api('admin/emoji/delete', { - id: emoji.id, - }).then(() => { - emojisDraftPaginationComponent.value.removeItem((item) => item.id === emoji.id); - emojisDraftPaginationComponent.value.reload(); - }); -} -</script> - -<style lang="scss" scoped> -.empty { - margin: var(--margin); -} - -.ldhfsamy { - > .emoji { - align-items: center; - padding: 11px; - text-align: left; - border: solid 1px var(--panel); - margin: 10px; - - > .img { - display: grid; - grid-row: 1; - grid-column: 1/ span 2; - grid-template-columns: 50% 50%; - place-content: center; - place-items: center; - - > .imgLight { - display: grid; - grid-column: 1; - background-color: #fff; - margin-bottom: 12px; - > img { - max-height: 64px; - max-width: 100%; - } - } - - > .imgDark { - display: grid; - grid-column: 2; - background-color: #000; - margin-bottom: 12px; - > img { - max-height: 64px; - max-width: 100%; - } - } - } - - > .info { - display: grid; - grid-row: 2; - grid-template-rows: 30px 30px 30px; - - > .name { - grid-row: 1; - text-overflow: ellipsis; - overflow: hidden; - } - - > .category { - grid-row: 2; - text-overflow: ellipsis; - overflow: hidden; - } - - > .aliases { - grid-row: 3; - text-overflow: ellipsis; - overflow: hidden; - } - - > .license { - grid-row: 4; - text-overflow: ellipsis; - overflow: hidden; - } - } - - > .edit-button { - display: grid; - grid-template-rows: 42px; - margin-top: 6px; - - > .edit { - grid-row: 1; - width: 100%; - margin: 6px 0; - } - - > .draft { - grid-row: 2; - width: 100%; - margin: 6px 0; - } - - > .delete { - grid-row: 3; - width: 100%; - margin: 6px 0; - } - } - } -} -</style> diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index 8d9484b8e7..e7d8e86ee5 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -48,8 +48,8 @@ import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkSwitch from '@/components/MkSwitch.vue'; -import * as os from '@/os'; -import { i18n } from '@/i18n'; +import * as os from '@/os.js'; +import { i18n } from '@/i18n.js'; const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); @@ -96,13 +96,6 @@ const setlocalOnlyBulk = async () => { }); emojisPaginationComponent.value.reload(); }; -const selectAll = () => { - if (selectedEmojis.value.length > 0) { - selectedEmojis.value = []; - } else { - selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); - } -}; const toggleSelect = (emoji) => { diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index a2335d8d16..ef60a60225 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -63,8 +63,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo> </div> </MkFolder> - <MkSwitch v-model="isSensitive">isSensitive</MkSwitch> - <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> <MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> <MkSwitch v-model="isNotifyIsHome"> @@ -86,7 +84,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch } from 'vue'; +import {computed, ref, watch} from 'vue'; import * as Misskey from 'misskey-js'; import { DriveFile } from 'misskey-js/built/entities.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; @@ -106,25 +104,24 @@ const props = defineProps<{ isRequest: boolean, }>(); -let dialog = $ref(null); -let name: string = $ref(props.emoji ? props.emoji.name : ''); -let category: string = $ref(props.emoji ? props.emoji.category : ''); -let aliases: string = $ref(props.emoji ? props.emoji.aliases.join(' ') : ''); -let license: string = $ref(props.emoji ? (props.emoji.license ?? '') : ''); -let isSensitive = $ref(props.emoji ? props.emoji.isSensitive : false); -let localOnly = $ref(props.emoji ? props.emoji.localOnly : false); -let roleIdsThatCanBeUsedThisEmojiAsReaction = $ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); -let rolesThatCanBeUsedThisEmojiAsReaction = $ref([]); -let file = $ref<Misskey.entities.DriveFile>(); -let chooseFile: DriveFile|null = $ref(null); -let isRequest = $ref(props.isRequest ?? false); -watch($$(roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { - rolesThatCanBeUsedThisEmojiAsReaction = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); +let dialog = ref(null); +let name = ref(props.emoji ? props.emoji.name : ''); +let category = ref(props.emoji ? props.emoji.category : ''); +let aliases = ref(props.emoji ? props.emoji.aliases.join(' ') : ''); +let license = ref(props.emoji ? (props.emoji.license ?? '') : ''); +let isSensitive = ref(props.emoji ? props.emoji.isSensitive : false); +let localOnly = ref(props.emoji ? props.emoji.localOnly : false); +let roleIdsThatCanBeUsedThisEmojiAsReaction = ref((props.emoji && props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction) ? props.emoji.roleIdsThatCanBeUsedThisEmojiAsReaction : []); +let rolesThatCanBeUsedThisEmojiAsReaction = ref([]); +let file = ref<Misskey.entities.DriveFile>(); +let isRequest = ref(props.isRequest ?? false); +watch((roleIdsThatCanBeUsedThisEmojiAsReaction), async () => { + rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null); }, { immediate: true }); - -const imgUrl = computed(() => file ? file.url : props.emoji && !isRequest ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); +const isNotifyIsHome = ref(props.emoji ? props.emoji.isNotifyIsHome : false); +const imgUrl = computed(() => file.value ? file.value.url : props.emoji && !isRequest.value ? `/emoji/${props.emoji.name}.webp` : props.emoji && props.emoji.url ? props.emoji.url : null); const validation = computed(() => { - return name.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; + return name.value.match(/^[a-zA-Z0-9_]+$/) && imgUrl.value != null; }); const emit = defineEmits<{ (ev: 'done', v: { deleted?: boolean; updated?: any; created?: any }): void, @@ -132,45 +129,44 @@ const emit = defineEmits<{ }>(); async function changeImage(ev) { - file = await selectFile(ev.currentTarget ?? ev.target, null); - const candidate = file.name.replace(/\.(.+)$/, ''); + file.value = await selectFile(ev.currentTarget ?? ev.target, null); + const candidate = file.value.name.replace(/\.(.+)$/, ''); if (candidate.match(/^[a-z0-9_]+$/)) { - name = candidate; + name.value = candidate; } } async function addRole() { const roles = await os.api('admin/roles/list'); - const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id); + const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id); const { canceled, result: role } = await os.select({ items: roles.filter(r => r.isPublic).filter(r => !currentRoleIds.includes(r.id)).map(r => ({ text: r.name, value: r })), }); if (canceled) return; - rolesThatCanBeUsedThisEmojiAsReaction.push(role); + rolesThatCanBeUsedThisEmojiAsReaction.value.push(role); } async function removeRole(role, ev) { - rolesThatCanBeUsedThisEmojiAsReaction = rolesThatCanBeUsedThisEmojiAsReaction.filter(x => x.id !== role.id); + rolesThatCanBeUsedThisEmojiAsReaction.value = rolesThatCanBeUsedThisEmojiAsReaction.value.filter(x => x.id !== role.id); } async function done() { const params = { - name, - category: category === '' ? null : category, - aliases: aliases.replace(' ', ' ').split(' ').filter(x => x !== ''), - license: license === '' ? null : license, - Request: isRequest, - isSensitive, - localOnly, - roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.map(x => x.id), - }; - - if (file) { - params.fileId = file.id; - } + name: name.value, + category: category.value === '' ? null : category.value, + aliases: aliases.value.replace(' ', ' ').split(' ').filter(x => x !== ''), + license: license.value === '' ? null : license.value, + Request: isRequest.value, + isSensitive: isSensitive.value, + localOnly: localOnly.value, + roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id), + }; + if (file.value) { + params.fileId = file.value.id; + } if (props.emoji) { if (isRequest) { await os.apiWithDialog('admin/emoji/update-request', { @@ -191,7 +187,7 @@ async function done() { }, }); - dialog.close(); + dialog.value.close(); } else { const created = isRequest ? await os.apiWithDialog('admin/emoji/add-request', params) @@ -201,7 +197,7 @@ async function done() { created: created, }); - dialog.close(); + dialog.value.close(); } } @@ -218,7 +214,7 @@ async function del() { emit('done', { deleted: true, }); - dialog.close(); + dialog.value.close(); }); } </script> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 6f671b7310..8827949200 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -244,7 +244,6 @@ if (darkMode.value && gamingMode.value == true) { } watch(darkMode, () => { - console.log(gaming) if (darkMode.value && gamingMode.value == true) { gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { diff --git a/packages/frontend/src/components/MkSwitch.vue b/packages/frontend/src/components/MkSwitch.vue index ed9ec10176..6f36acfc6e 100644 --- a/packages/frontend/src/components/MkSwitch.vue +++ b/packages/frontend/src/components/MkSwitch.vue @@ -32,8 +32,6 @@ import XButton from '@/components/MkSwitch.button.vue'; import {defaultStore} from "@/store.js"; const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); -console.log(gamingType.value) - const props = defineProps<{ modelValue: boolean | Ref<boolean>; disabled?: boolean; diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 150aab340c..37fb7934a9 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer v-if="tab === 'emojis'" :contentMax="1000" :marginMin="20"> <MkButton v-if="$i && ($i.isModerator || $i.policies.canManageCustomEmojis)" primary link to="/custom-emojis-manager">{{ i18n.ts.manageCustomEmojis }}</MkButton> - <MkButton v-if="$i && (!$i.isModerator && $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> + <MkButton v-if="$i && (!$i.isModerator || $i.policies.canRequestCustomEmojis)" primary @click="edit">{{ i18n.ts.requestCustomEmojis }}</MkButton> <div class="query" style="margin-top: 10px;"> <MkInput v-model="q" class="" :placeholder="i18n.ts.search" autocapitalize="off"> @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch, defineAsyncComponent, ref } from 'vue'; +import {watch, defineAsyncComponent, ref, computed} from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; @@ -57,10 +57,10 @@ import * as os from '@/os'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata'; -let tab = $ref('emojis'); -const headerActions = $computed(() => []); +let tab = ref('emojis'); +const headerActions = computed(() => []); -const headerTabs = $computed(() => [{ +const headerTabs = computed(() => [{ key: 'emojis', title: i18n.ts.list, }, { @@ -70,29 +70,29 @@ const headerTabs = $computed(() => [{ definePageMetadata(ref({})); -let q = $ref(''); -let searchEmojis = $ref<Misskey.entities.CustomEmoji[]>(null); -let selectedTags = $ref(new Set()); +let q = ref(''); +let searchEmojis = ref<Misskey.entities.CustomEmoji[]>(null); +let selectedTags = ref(new Set()); const requestEmojis = await os.apiGet('emoji-requests'); function search() { - if ((q === '' || q == null) && selectedTags.size === 0) { - searchEmojis = null; + if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) { + searchEmojis.value = null; return; } - if (selectedTags.size === 0) { - const queryarry = q.match(/\:([a-z0-9_]*)\:/g); + if (selectedTags.value.size === 0) { + const queryarry = q.value.match(/\:([a-z0-9_]*)\:/g); if (queryarry) { - searchEmojis = customEmojis.value.filter(emoji => + searchEmojis.value = customEmojis.value.filter(emoji => queryarry.includes(`:${emoji.name}:`), ); } else { - searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q)); + searchEmojis.value = customEmojis.value.filter(emoji => emoji.name.includes(q.value) || emoji.aliases.includes(q)); } } else { - searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t))); + searchEmojis.value = customEmojis.value.filter(emoji => (emoji.name.includes(q.value) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t))); } } @@ -106,11 +106,11 @@ const edit = () => { }, 'closed'); }; -watch($$(q), () => { +watch((q), () => { search(); }); -watch($$(selectedTags), () => { +watch((selectedTags), () => { search(); }, { deep: true }); diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index b94d2b3737..c2249e2d98 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -48,6 +48,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ti ti-key"></i></template> <template #label>ApiBase</template> </MkInput> + <MkSwitch v-model="requestEmojiAllOk"> +絵文字の申請全部許可 + </MkSwitch> </div> </FormSuspense> </MkSpacer> @@ -69,6 +72,7 @@ const enableServerMachineStats = ref<boolean>(false); const enableIdenticonGeneration = ref<boolean>(false); const enableChartsForRemoteUser = ref<boolean>(false); const enableChartsForFederatedInstances = ref<boolean>(false); +const requestEmojiAllOk = ref(false) let DiscordWebhookUrl = ref(null); let EmojiBotToken= ref(null); let ApiBase= ref(null) @@ -78,6 +82,7 @@ async function init() { enableIdenticonGeneration.value = meta.enableIdenticonGeneration; enableChartsForRemoteUser.value = meta.enableChartsForRemoteUser; enableChartsForFederatedInstances.value = meta.enableChartsForFederatedInstances; + requestEmojiAllOk.value = meta.requestEmojiAllOk; DiscordWebhookUrl.value = meta.DiscordWebhookUrl; EmojiBotToken.value = meta.EmojiBotToken; ApiBase.value = meta.ApiBase; @@ -88,6 +93,7 @@ function save() { enableServerMachineStats: enableServerMachineStats.value, enableIdenticonGeneration: enableIdenticonGeneration.value, enableChartsForRemoteUser: enableChartsForRemoteUser.value, + requestEmojiAllOk: requestEmojiAllOk.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, DiscordWebhookUrl:DiscordWebhookUrl.value, EmojiBotToken:EmojiBotToken.value, diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue index 9ae68bf899..fc0fb961a4 100644 --- a/packages/frontend/src/pages/custom-emojis-manager.vue +++ b/packages/frontend/src/pages/custom-emojis-manager.vue @@ -37,7 +37,7 @@ import { definePageMetadata } from '@/scripts/page-metadata'; const tab = ref('request'); const add = async (ev: MouseEvent) => { - os.popup(defineAsyncComponent(() => import('./emoji-edit-dialog.vue')), { + os.popup(defineAsyncComponent(() => import('../components/MkEmojiEditDialog.vue')), { }, { done: result => { //TODO: emitにして追加を反映 @@ -91,7 +91,7 @@ const menu = (ev: MouseEvent) => { }], ev.currentTarget ?? ev.target); }; -const headerActions = $computed(() => [{ +const headerActions = computed(() => [{ asFullButton: true, icon: 'ti ti-plus', text: i18n.ts.addEmoji, @@ -101,7 +101,7 @@ const headerActions = $computed(() => [{ handler: menu, }]); -const headerTabs = $computed(() => [{ +const headerTabs = computed(() => [{ key: 'request', title: i18n.ts.requestingEmojis, }, { From 56fb028855097b610b87e578aff2bbfca3086f4e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 10:44:13 +0900 Subject: [PATCH 293/501] =?UTF-8?q?=E5=A4=9A=E5=88=86=E5=8B=95=E3=81=8F?= =?UTF-8?q?=E3=81=A8=E6=80=9D=E3=81=86=E3=81=8B=E3=82=89=E3=83=AA=E3=83=AA?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=81=97=E3=82=88=E3=81=86=E3=81=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/server/api/endpoints/admin/emoji/add-request.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts index 3818cd044a..b10f7d088d 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts @@ -67,9 +67,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { if (isDuplicate || isRequestDuplicate) throw new ApiError(meta.errors.duplicateName); let driveFile; - driveFile = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); - if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); + let tmp = await this.driveFilesRepository.findOneBy({ id: ps.fileId }); + if (tmp == null) throw new ApiError(meta.errors.noSuchFile); + try { + driveFile = await this.driveService.uploadFromUrl({ url: tmp.url , user: null, force: true }); + } catch (e) { + throw new ApiError(); + } if (driveFile == null) throw new ApiError(meta.errors.noSuchFile); const {ApiBase,EmojiBotToken,DiscordWebhookUrl,requestEmojiAllOk} = (await this.metaService.fetch()) let emoji; From a03814feaeca3796cd6f0ae9f5e6ea572a0c27d3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 10:52:18 +0900 Subject: [PATCH 294/501] =?UTF-8?q?=E5=A4=9A=E5=88=86=E5=8B=95=E3=81=8F?= =?UTF-8?q?=E3=81=A8=E6=80=9D=E3=81=86=E3=81=8B=E3=82=89=E3=83=AA=E3=83=AA?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=81=97=E3=82=88=E3=81=86=E3=81=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/backend/{ => migration}/1703294653915-requestemoji.js | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/backend/{ => migration}/1703294653915-requestemoji.js (100%) diff --git a/packages/backend/1703294653915-requestemoji.js b/packages/backend/migration/1703294653915-requestemoji.js similarity index 100% rename from packages/backend/1703294653915-requestemoji.js rename to packages/backend/migration/1703294653915-requestemoji.js From 7491a4af7b2303ecb84789062bc4bea1912396bd Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 11:01:14 +0900 Subject: [PATCH 295/501] (isRequest) -> (isRequest.value) --- packages/frontend/src/components/MkEmojiEditDialog.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index ef60a60225..6fccc7c614 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -168,7 +168,7 @@ async function done() { params.fileId = file.value.id; } if (props.emoji) { - if (isRequest) { + if (isRequest.value) { await os.apiWithDialog('admin/emoji/update-request', { id: props.emoji.id, ...params, From 1f1908870c215a0dab489bb9da05ef367f76074d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 11:06:27 +0900 Subject: [PATCH 296/501] =?UTF-8?q?=E3=83=90=E3=82=B0fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/server/api/endpoints/admin/emoji/update.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts index d2d0bbc9b1..ce250760df 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/update.ts @@ -65,7 +65,7 @@ export const paramDef = { } }, Request: { type: 'boolean' }, }, - required: ['id', 'name', 'draft', 'aliases'], + required: ['id', 'name', 'aliases'], } as const; @Injectable() @@ -102,7 +102,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- isSensitive: ps.isSensitive, localOnly: ps.localOnly, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction, - draft: ps.draft, + draft: false, }, me);} else { const file = await this.driveFileEntityService.getFromUrl(emoji.originalUrl); if (file === null) throw new ApiError(meta.errors.noSuchFile); From e5f81c02a1bb51225513be6682f1a79d487797ab Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 11:46:57 +0900 Subject: [PATCH 297/501] =?UTF-8?q?=E3=83=95=E3=82=A9=E3=83=AB=E3=83=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkEmojiPicker.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index ce2e83f042..40776ff653 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="child in customEmojiFolderRoot.children" :key="`custom:${child.value}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !e.category || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" :hasChildSection="child.children.length !== 0" :customEmojiTree="child.children" @chosen="chosen" @@ -158,8 +158,9 @@ const customEmojiFolderRoot: CustomEmojiFolderTree = { value: '', category: '', function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree { const parts = input.split('/').map(p => p.trim()); // スラッシュで区切って配列にしてる let currentNode: CustomEmojiFolderTree = root; // currentNode は root - - if (parts.length === 1 && parts[0] !== '') { // parts が 1 つで空じゃなかったら + let includesPart = customEmojis.value.some(emoji => emoji.category.includes(parts[0]+'/')) ; + console.log(includesPart) + if (parts.length === 1 && parts[0] !== '' && includesPart) { // parts が 1 つで空じゃなかったら parts.push(parts[0]) // parts に parts[0] を追加 (test category だったら test/test category になる) } From 868cd2860d81aed29568992eb55abad9a0ca4988 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 23 Dec 2023 23:14:51 +0900 Subject: [PATCH 298/501] bug fix --- .../src/components/MkCustomEmojiEditLocal.vue | 37 +++++++------------ .../src/components/MkEmojiEditDialog.vue | 2 +- packages/frontend/src/pages/emojis.emoji.vue | 2 +- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue index e7d8e86ee5..9a29622827 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditLocal.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditLocal.vue @@ -13,7 +13,7 @@ <MkButton inline @click="addTagBulk">Add tag</MkButton> <MkButton inline @click="removeTagBulk">Remove tag</MkButton> <MkButton inline @click="setLisenceBulk">Set Lisence</MkButton> - <MkButton inline @click="setisSensitiveBulk">Set isSensitive</MkButton> + <MkButton inline @click="isSensitiveBulk">Set isSensitive</MkButton> <MkButton inline @click="setlocalOnlyBulk">Set localOnly</MkButton> <MkButton inline danger @click="delBulk">Delete</MkButton> </div> @@ -22,14 +22,14 @@ <template #default="{items}"> <div :class="$style.root"> <div v-for="emoji in items" :key="emoji.id"> - <button v-if="emoji.request" class="_panel _button" :class="[{ selected: selectedEmojis.includes(emoji.id) },$style.emoji,$style.emojirequest]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <button v-if="emoji.request" class="_panel _button" :class="[{ [$style.selected]: selectedEmojis.includes(emoji.id) },$style.emoji,$style.emojirequest]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> <img :src="emoji.url" class="img" :alt="emoji.name"/> <div class="body"> <div class="name _monospace">{{ emoji.name }}</div> <div class="info">{{ emoji.category }}</div> </div> </button> - <button v-else class="_panel _button" :class="[{ selected: selectedEmojis.includes(emoji.id) },$style.emoji]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> + <button v-else class="_panel _button" :class="[{ [$style.selected]: selectedEmojis.includes(emoji.id) },$style.emoji]" @click="selectMode ? toggleSelect(emoji) : edit(emoji)"> <img :src="emoji.url" :class="$style.img" :alt="emoji.name"/> <div :class="$style.body"> <div :class="$style.name" class="_monospace">{{ emoji.name }}</div> @@ -66,11 +66,12 @@ const pagination = { }; const selectAll = () => { - if (selectedEmojis.value.length > 0) { - selectedEmojis.value = []; - } else { - selectedEmojis.value = emojisPaginationComponent.value.items.map(item => item.id); - } + + if (selectedEmojis.value.length > 0) { + selectedEmojis.value = []; + } else { + selectedEmojis.value = Array.from(emojisPaginationComponent.value.items.values(), item => item.id); + } }; const setisSensitiveBulk = async () => { const { canceled, result } = await os.switch1({ @@ -99,6 +100,7 @@ const setlocalOnlyBulk = async () => { const toggleSelect = (emoji) => { + console.log(selectedEmojis.value) if (selectedEmojis.value.includes(emoji.id)) { selectedEmojis.value = selectedEmojis.value.filter(x => x !== emoji.id); } else { @@ -149,18 +151,6 @@ const setLisenceBulk = async () => { emojisPaginationComponent.value.reload(); }; -const isLocalBulk = async () => { - const { canceled, result } = await os.inputText({ - title: 'License', - }); - if (canceled) return; - await os.apiWithDialog('admin/emoji/set-islocal-bulk', { - ids: selectedEmojis.value, - isLocal: result, - }); - emojisPaginationComponent.value.reload(); -}; - const isSensitiveBulk = async () => { const { canceled, result } = await os.inputText({ title: 'License', @@ -240,9 +230,10 @@ const delBulk = async () => { border-color: var(--inputBorderHover); } - &.selected { - border-color: var(--accent); - } + +} +.selected { + border-color: var(--accent); } .img { width: 42px; diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 6fccc7c614..7419563311 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -189,7 +189,7 @@ async function done() { dialog.value.close(); } else { - const created = isRequest + const created = isRequest.value ? await os.apiWithDialog('admin/emoji/add-request', params) : await os.apiWithDialog('admin/emoji/add', params); diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index 8a53f54e69..a71a372e41 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -50,7 +50,7 @@ function menu(ev) { text: i18n.ts.info, icon: 'ti ti-info-circle', action: () => { - os.apiGet('emoji-requests', { name: props.emoji.name }).then(res => { + os.apiGet('emoji', { name: props.emoji.name }).then(res => { os.alert({ type: 'info', text: `License: ${res.license}`, From 77ce5d2da3ea4b34473269667847fb2aeee3c58d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Dec 2023 00:15:55 +0900 Subject: [PATCH 299/501] bug fix --- packages/frontend/src/components/MkEmojiPicker.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 40776ff653..555380d800 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -76,7 +76,7 @@ SPDX-License-Identifier: AGPL-3.0-only v-for="child in customEmojiFolderRoot.children" :key="`custom:${child.value}`" :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" + :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" :hasChildSection="child.children.length !== 0" :customEmojiTree="child.children" @chosen="chosen" From a1284757ffba1a03925f8607fb544f4e3ba5d5ba Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 24 Dec 2023 00:18:39 +0900 Subject: [PATCH 300/501] bug fix --- packages/frontend/src/components/MkEmojiPicker.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 555380d800..c452aad918 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -158,7 +158,7 @@ const customEmojiFolderRoot: CustomEmojiFolderTree = { value: '', category: '', function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree { const parts = input.split('/').map(p => p.trim()); // スラッシュで区切って配列にしてる let currentNode: CustomEmojiFolderTree = root; // currentNode は root - let includesPart = customEmojis.value.some(emoji => emoji.category.includes(parts[0]+'/')) ; + let includesPart = customEmojis.value.some(emoji => emoji.category !== null && emoji.category.includes(parts[0]+'/')) ; console.log(includesPart) if (parts.length === 1 && parts[0] !== '' && includesPart) { // parts が 1 つで空じゃなかったら parts.push(parts[0]) // parts に parts[0] を追加 (test category だったら test/test category になる) From a294b5fd0bad4d0b1d783313a5346ef2de170ec9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Tue, 26 Dec 2023 23:41:22 +0900 Subject: [PATCH 301/501] bug fix --- packages/frontend/src/pages/admin/index.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index f480390327..ef2985307f 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch,ComputedRef, Ref, onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue'; +import { ComputedRef, Ref, onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue'; import { i18n } from '@/i18n.js'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; From e8ad65dc8a09082a79c4b0683f9724de39c9bddb Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 30 Dec 2023 08:44:46 +0900 Subject: [PATCH 302/501] bug fix --- packages/frontend/src/components/MkPostForm.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 52bf5f7852..a140722ab3 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -137,6 +137,8 @@ import { miLocalStorage } from '@/local-storage.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; +import MkScheduleEditor from '@/components/MkScheduleEditor.vue'; +import { listSchedulePost } from '@/os.js'; const modal = inject('modal'); let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); @@ -406,7 +408,9 @@ function addMissingMention() { } } } - +function insertRuby() { + insertTextAtCursor(textareaEl.value, '$[ruby 本文 上につくやつ]'); +} function togglePoll() { if (poll.value) { poll.value = null; From 1456d4904221af7d777612e3b05da14f0641e6b1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 30 Dec 2023 08:53:47 +0900 Subject: [PATCH 303/501] bug fix --- .../api/endpoints/admin/emoji/add-request.ts | 2 +- .../src/server/api/endpoints/admin/emoji/add.ts | 3 +-- packages/frontend/src/pages/settings/general.vue | 3 --- packages/frontend/src/pages/user/home.vue | 15 +++++---------- packages/frontend/src/store.ts | 4 ---- 5 files changed, 7 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts index b10f7d088d..147073c2b4 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add-request.ts @@ -44,6 +44,7 @@ export const paramDef = { isSensitive: { type: 'boolean', nullable: true }, localOnly: { type: 'boolean', nullable: true }, fileId: { type: 'string', format: 'misskey:id' }, + isNotifyIsHome: { type: 'boolean', nullable: true }, }, required: ['name', 'fileId'], } as const; @@ -86,7 +87,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { aliases: ps.aliases ?? [], license: ps.license ?? null, host: null, - draft: false, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, roleIdsThatCanBeUsedThisEmojiAsReaction: [], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts index 8c1f61f40d..85e3dfc7ea 100644 --- a/packages/backend/src/server/api/endpoints/admin/emoji/add.ts +++ b/packages/backend/src/server/api/endpoints/admin/emoji/add.ts @@ -58,7 +58,7 @@ export const paramDef = { }, }, }, - required: ['name', 'fileId', 'draft'], + required: ['name', 'fileId'], } as const; // TODO: ロジックをサービスに切り出す @@ -87,7 +87,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- license: ps.license ?? null, isSensitive: ps.isSensitive ?? false, localOnly: ps.localOnly ?? false, - draft: false, roleIdsThatCanBeUsedThisEmojiAsReaction: ps.roleIdsThatCanBeUsedThisEmojiAsReaction ?? [], }, me); diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 925d8d88fc..a41ba9cc92 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -36,7 +36,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </MkFolder> <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> - <MkSwitch v-model="FeaturedOrNote">{{ i18n.ts.FeaturedOrNote}}<template #caption>{{ i18n.ts.FeaturedOrNoteInfo }} </template></MkSwitch> <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> </div> </FormSection> @@ -325,7 +324,6 @@ const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')) const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showGlobalTimeline = computed(defaultStore.makeGetterSetter('showGlobalTimeline')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) -const FeaturedOrNote = computed(defaultStore.makeGetterSetter('FeaturedOrNote')) const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); @@ -392,7 +390,6 @@ watch([ showMediaTimeline, showVisibilityColor, enableonlyAndWithSave, - FeaturedOrNote, showGlobalTimeline, disableStreamingTimeline, enableSeasonalScreenEffect, diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index aa88473dfc..96cfb2c3e7 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -148,17 +148,10 @@ SPDX-License-Identifier: AGPL-3.0-only <XActivity :key="user.id" :user="user"/> </MkLazy> </template> - <div v-if="!defaultStore.state.FeaturedOrNote"> - <div v-if="!disableNotes"> - <MkLazy> - <XTimeline :user="user"/> - </MkLazy> + <div> + <div style="margin-bottom: 8px;">{{ i18n.ts._sfx.note }}</div> + <MkNotes :class="$style.tl" :noGap="true" :pagination="Notes"/> </div> - </div> - <div v-else> - <div style="margin-bottom: 8px;">{{ i18n.ts._sfx.note }}</div> - <MkNotes :class="$style.tl" :noGap="true" :pagination="Notes"/> - </div> </div> </div> <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> @@ -195,6 +188,8 @@ import {isFollowingVisibleForMe, isFollowersVisibleForMe} from '@/scripts/isFfVi import MkNotifyButton from "@/components/MkNotifyButton.vue"; import MkRemoteInfoUpdate from "@/components/MkRemoteInfoUpdate.vue"; import {defaultStore} from '@/store.js'; +import MkNotes from "@/components/MkNotes.vue"; +import MkLazy from "@/components/global/MkLazy.vue"; function calcAge(birthdate: string): number { const date = new Date(birthdate); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 6077810d76..d5adf47c28 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -478,10 +478,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - FeaturedOrNote: { - where: 'device', - default: false - }, tlWithReplies: { where: 'device', default: false, From 74e45b13eba75b3150f9aeb8d5a1f04e0afa6ac2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 31 Dec 2023 18:19:59 +0900 Subject: [PATCH 304/501] =?UTF-8?q?Feat:=20GDPR=E3=83=A2=E3=83=BC=E3=83=89?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../migration/1703704097603-GDPRMode.js | 11 +++++++++ .../migration/1704005554275-abusenoteIds.js | 11 +++++++++ .../backend/src/core/GlobalEventService.ts | 1 + .../entities/AbuseUserReportEntityService.ts | 23 +++++++++++++++++-- .../backend/src/models/AbuseUserReport.ts | 5 ++++ packages/backend/src/models/Meta.ts | 5 ++++ .../src/server/api/endpoints/admin/meta.ts | 5 ++++ .../server/api/endpoints/admin/update-meta.ts | 5 ++++ .../api/endpoints/users/report-abuse.ts | 10 ++++---- .../frontend/src/components/MkAbuseReport.vue | 6 +++-- .../frontend/src/pages/admin/moderation.vue | 7 ++++++ 13 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 packages/backend/migration/1703704097603-GDPRMode.js create mode 100644 packages/backend/migration/1704005554275-abusenoteIds.js diff --git a/locales/index.d.ts b/locales/index.d.ts index 56792f856a..669f8b48e9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -876,6 +876,7 @@ export interface Locale { "on": string; "off": string; "emailRequiredForSignup": string; + "enableGDPRMode": string; "unread": string; "filter": string; "controlPanel": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4169b42be0..1d7607b280 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -873,6 +873,7 @@ itsOff: "オフになっています" on: "オン" off: "オフ" emailRequiredForSignup: "アカウント登録にメールアドレスを必須にする" +enableGDPRMode: "GDPRモードを有効にする" unread: "未読" filter: "フィルタ" controlPanel: "コントロールパネル" diff --git a/packages/backend/migration/1703704097603-GDPRMode.js b/packages/backend/migration/1703704097603-GDPRMode.js new file mode 100644 index 0000000000..86a7e5d85f --- /dev/null +++ b/packages/backend/migration/1703704097603-GDPRMode.js @@ -0,0 +1,11 @@ +export class GDPRMode1703704097603 { + name = 'GDPRMode1703704097603' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "enableGDPRMode" boolean NOT NULL DEFAULT false`); + } + + async down(queryRunner) {; + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGDPRMode"`); + } +} diff --git a/packages/backend/migration/1704005554275-abusenoteIds.js b/packages/backend/migration/1704005554275-abusenoteIds.js new file mode 100644 index 0000000000..e52f8850b7 --- /dev/null +++ b/packages/backend/migration/1704005554275-abusenoteIds.js @@ -0,0 +1,11 @@ +export class AbusenoteId1704005554275 { + name = 'AbusenoteId1704005554275' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD "noteIds" jsonb NOT NULL DEFAULT '[]'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP COLUMN "noteIds"`); + } +} diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index f16c18c2d6..800ce47b1f 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -158,6 +158,7 @@ export interface AdminEventTypes { reporterId: MiUser['id'], comment: string; notes: any[]; + noteIds: string[]; }; } //#endregion diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 627dddd3bc..23a513dd76 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -4,12 +4,14 @@ */ import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { AbuseUserReportsRepository } from '@/models/_.js'; +import type { AbuseUserReportsRepository, NotesRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js'; import { bindThis } from '@/decorators.js'; import { IdService } from '@/core/IdService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { UserEntityService } from './UserEntityService.js'; @Injectable() @@ -18,7 +20,11 @@ export class AbuseUserReportEntityService { @Inject(DI.abuseUserReportsRepository) private abuseUserReportsRepository: AbuseUserReportsRepository, + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, private idService: IdService, ) { } @@ -28,12 +34,25 @@ export class AbuseUserReportEntityService { src: MiAbuseUserReport['id'] | MiAbuseUserReport, ) { const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); + const notes = (report.notes.length === 0) ? report.notes : []; + if (report.noteIds && report.noteIds.length > 0) { + for (const x of report.noteIds) { + const exists = await this.notesRepository.countBy({ id: x }); + if (exists === 0) { + notes.push('deleted'); + continue; + } + notes.push(await this.noteEntityService.pack(x)); + } + } + + console.log(report.notes.length, null, notes); return await awaitAll({ id: report.id, createdAt: this.idService.parse(report.id).date.toISOString(), comment: report.comment, - notes: report.notes, + notes, resolved: report.resolved, reporterId: report.reporterId, targetUserId: report.targetUserId, diff --git a/packages/backend/src/models/AbuseUserReport.ts b/packages/backend/src/models/AbuseUserReport.ts index 995fbc4018..a30cdbada5 100644 --- a/packages/backend/src/models/AbuseUserReport.ts +++ b/packages/backend/src/models/AbuseUserReport.ts @@ -65,6 +65,11 @@ export class MiAbuseUserReport { }) public notes: any[]; + @Column('jsonb', { + default: [], + }) + public noteIds: string[] | null; + //#region Denormalized fields @Index() @Column('varchar', { diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 84ca762492..818557dab3 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -541,4 +541,9 @@ export class MiMeta { default: 0, }) public notesPerOneAd: number; + + @Column('boolean', { + default: false, + }) + public enableGDPRMode: boolean; } diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index febc4ab1b1..4ee549bf1f 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -416,6 +416,10 @@ export const meta = { type: 'string', optional: false, nullable: false, }, + enableGDPRMode: { + type: 'boolean', + optional: false, nullable: false, + }, }, }, } as const; @@ -534,6 +538,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- perUserHomeTimelineCacheMax: instance.perUserHomeTimelineCacheMax, perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, notesPerOneAd: instance.notesPerOneAd, + enableGDPRMode: instance.enableGDPRMode, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 5a215696fb..dc0001a9d9 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -138,6 +138,7 @@ export const paramDef = { type: 'string', }, }, + enableGDPRMode: { type: 'boolean' }, }, required: [], } as const; @@ -209,6 +210,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.infoImageUrl !== undefined) { set.infoImageUrl = ps.infoImageUrl; } + console.log(ps.enableGDPRMode); + if (ps.enableGDPRMode !== undefined) { + set.enableGDPRMode = ps.enableGDPRMode; + } if (ps.notFoundImageUrl !== undefined) { set.notFoundImageUrl = ps.notFoundImageUrl; diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index a7edee9e03..5769f03b2f 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -25,7 +25,7 @@ export const meta = { requireCredential: true, kind: 'write:report-abuse', - description: 'User a report.', + description: 'File a report.', errors: { noSuchUser: { @@ -91,9 +91,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } const notes = ps.noteIds ? await this.notesRepository.find({ - where: { id: In(ps.noteIds) }, + where: { id: In(ps.noteIds), userId: user.id }, }) : []; - const filteredNotes = notes.filter(note => note.userId === user.id); + const report = await this.abuseUserReportsRepository.insert({ id: this.idService.gen(), targetUserId: user.id, @@ -101,7 +101,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- reporterId: me.id, reporterHost: null, comment: ps.comment, - notes: ps.noteIds ? await this.noteEntityService.packMany(filteredNotes) : [], + notes: (ps.noteIds && !((await this.metaService.fetch()).enableGDPRMode)) ? await this.noteEntityService.packMany(notes) : [], + noteIds: (ps.noteIds && (await this.metaService.fetch()).enableGDPRMode) ? ps.noteIds : [], }).then(x => this.abuseUserReportsRepository.findOneByOrFail(x.identifiers[0])); // Publish event to moderators @@ -115,6 +116,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- reporterId: report.reporterId, comment: report.comment, notes: report.notes, + noteIds: report.noteIds ?? [], }); } const meta = await this.metaService.fetch(); diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue index 4c8a334b23..abf7a86256 100644 --- a/packages/frontend/src/components/MkAbuseReport.vue +++ b/packages/frontend/src/components/MkAbuseReport.vue @@ -24,7 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFolder v-if="report.notes.length !== 0" :class="$style.notes"> <template #label>{{ i18n.ts.reportedNote }}</template> <div v-for="note in report.notes" :class="$style.notes"> - <MkNoteSimple :note="note"/> + <MkNoteSimple v-if="note !== 'deleted'" :note="note"/> + <div v-else> note is deleted </div> </div> </MkFolder> </div> @@ -62,7 +63,7 @@ const props = defineProps<{ id: string; createdAt:string; targetUserId:Misskey.entities.User['id']; - targetUser:Misskey.entities.User & {createdAt:string;}; + targetUser:Misskey.entities.User & { createdAt:string; }; reporter:Misskey.entities.User; assignee:Misskey.entities.User['id']; comment:string; @@ -109,6 +110,7 @@ function resolve() { padding: 24px; border-right: solid 1px var(--divider); } + .info { display: flex; box-sizing: border-box; diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index f6c0b29403..e61a476373 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -18,6 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.emailRequiredForSignup }}</template> </MkSwitch> + <MkSwitch v-model="enableGDPRMode"> + <template #label>{{ i18n.ts.enableGDPRMode }}</template> + </MkSwitch> + <FormLink to="/admin/server-rules">{{ i18n.ts.serverRules }}</FormLink> <MkInput v-model="tosUrl" type="url"> @@ -79,6 +83,7 @@ const hiddenTags = ref<string>(''); const preservedUsernames = ref<string>(''); const tosUrl = ref<string | null>(null); const privacyPolicyUrl = ref<string | null>(null); +const enableGDPRMode = ref<boolean>(false); async function init() { const meta = await os.api('admin/meta'); @@ -89,6 +94,7 @@ async function init() { preservedUsernames.value = meta.preservedUsernames.join('\n'); tosUrl.value = meta.tosUrl; privacyPolicyUrl.value = meta.privacyPolicyUrl; + enableGDPRMode.value = meta.enableGDPRMode; } function save() { @@ -100,6 +106,7 @@ function save() { sensitiveWords: sensitiveWords.value.split('\n'), hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), + enableGDPRMode: enableGDPRMode.value, }).then(() => { fetchInstance(); }); From c6cccd791dc6886fec701a254af4c3aec0e9fff1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sun, 31 Dec 2023 18:49:18 +0900 Subject: [PATCH 305/501] =?UTF-8?q?=E3=81=AA=E3=82=93=E3=81=8B=E3=81=A1?= =?UTF-8?q?=E3=82=87=E3=81=A3=E3=81=A8=E3=81=97=E3=81=9Fbug=20fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/frontend/src/components/MkAbuseReportWindow.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index 1177eb81bf..f757292ef2 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m" :class="$style.root"> <MkPagination v-slot="{items}" :key="user.id" :pagination="Pagination"> <div v-for="item in items" :key="item.id" :class="$style.note"> - <MkSwitch v-model="item.isAbuseReport" @update:modelValue="pushAbuseReportNote($event,item.id)"></MkSwitch> + <MkSwitch :modelValue="item.id === initialNoteId" @update:modelValue="pushAbuseReportNote($event,item.id)"></MkSwitch> <MkAvatar :user="item.user" preview/> <MkNoteSimple :note="item"/> </div> From 1c108927ea469179950b14885d06e61335213d14 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:25:32 +0900 Subject: [PATCH 306/501] =?UTF-8?q?=E8=89=B2=E3=80=85=E3=83=AA=E3=83=95?= =?UTF-8?q?=E3=82=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/avatar-decorations/create.ts | 2 + .../admin/avatar-decorations/list.ts | 1 + .../src/components/MkEmojiPicker.section.vue | 4 +- .../src/components/MkFoldableSection.vue | 211 +++++++++++------- .../frontend/src/components/MkNoteSimple.vue | 2 +- .../frontend/src/components/MkPollEditor.vue | 6 +- .../frontend/src/components/MkPostForm.vue | 10 +- .../src/pages/admin/instance-block.vue | 10 +- .../frontend/src/pages/settings/general.vue | 120 +++++----- packages/misskey-js/src/autogen/types.ts | 2 + 10 files changed, 208 insertions(+), 160 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts index 4ac74253cc..60afe88bbf 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/create.ts @@ -24,6 +24,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: { type: 'string', } }, + category: { type: 'string', nullable: true }, }, required: ['name', 'description', 'url'], } as const; @@ -39,6 +40,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- description: ps.description, url: ps.url, roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration, + category: ps.category ?? '', }, me); }); } diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts index 33122c3eef..39297de536 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts @@ -95,6 +95,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- name: avatarDecoration.name, description: avatarDecoration.description, url: avatarDecoration.url, + category: avatarDecoration.category, roleIdsThatCanBeUsedThisDecoration: avatarDecoration.roleIdsThatCanBeUsedThisDecoration, })); }); diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 5875b678a1..9391c39066 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -6,8 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> <!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) --> - <section v-if="!hasChildSection" style="border-radius: 6px; padding-top: 9px;"> - <header class="_acrylic" @click="shown = !shown" style="border-radius: 6px;"> + <section v-if="!hasChildSection" style="border-radius: 6px;"> + <header class="_acrylic" @click="shown = !shown" > <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) </header> <div v-if="shown" class="body"> diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index 1ffc95d944..7f2b4e0858 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -4,141 +4,186 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div ref="el" :class="$style.root"> - <header :class="$style.header" class="_button" :style="{ background: bg }" @click="showBody = !showBody"> +<div ref="rootEl" :class="$style.root" role="group" :aria-expanded="opened"> + <header :class="[$style.header, { [$style.opened]: opened }]" class="_button" role="button" data-cy-folder-header @click="toggle"> <div :class="$style.title"><div><slot name="header"></slot></div></div> <div :class="$style.divider"></div> <button class="_button" :class="$style.button"> - <template v-if="showBody"><i class="ti ti-chevron-up"></i></template> + <template v-if="opened"><i class="ti ti-chevron-up"></i></template> <template v-else><i class="ti ti-chevron-down"></i></template> </button> </header> - <Transition - :name="defaultStore.state.animation ? 'folder-toggle' : ''" - @enter="enter" - @afterEnter="afterEnter" - @leave="leave" - @afterLeave="afterLeave" - > - <div v-show="showBody"> - <slot></slot> - </div> - </Transition> + <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null, overflow: maxHeight ? `auto` : null }" :aria-hidden="!opened"> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_toggle_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_toggle_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_toggle_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_toggle_leaveTo : ''" + @enter="enter" + @afterEnter="afterEnter" + @leave="leave" + @afterLeave="afterLeave" + > + <div v-show="opened"> + <slot></slot> + </div> + </Transition> + </div> </div> </template> <script lang="ts" setup> -import { onMounted, ref, shallowRef, watch } from 'vue'; -import tinycolor from 'tinycolor2'; -import { miLocalStorage } from '@/local-storage.js'; +import { nextTick, onMounted, shallowRef, ref } from 'vue'; import { defaultStore } from '@/store.js'; -const miLocalStoragePrefix = 'ui:folder:' as const; - const props = withDefaults(defineProps<{ - expanded?: boolean; - persistKey?: string; + defaultOpen?: boolean; + maxHeight?: number | null; }>(), { - expanded: true, + defaultOpen: true, + maxHeight: null, }); -const el = shallowRef<HTMLDivElement>(); -const bg = ref<string | null>(null); -const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded); - -watch(showBody, () => { - if (props.persistKey) { - miLocalStorage.setItem(`${miLocalStoragePrefix}${props.persistKey}`, showBody.value ? 't' : 'f'); +const getBgColor = (el: HTMLElement) => { + const style = window.getComputedStyle(el); + if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) { + return style.backgroundColor; + } else { + return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; } -}); +}; -function enter(el: Element) { +const rootEl = shallowRef<HTMLElement>(); +const bgSame = ref(false); +const opened = ref(props.defaultOpen); +const openedAtLeastOnce = ref(props.defaultOpen); + +function enter(el) { const elementHeight = el.getBoundingClientRect().height; el.style.height = 0; el.offsetHeight; // reflow - el.style.height = elementHeight + 'px'; + el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px'; } -function afterEnter(el: Element) { +function afterEnter(el) { el.style.height = null; } -function leave(el: Element) { +function leave(el) { const elementHeight = el.getBoundingClientRect().height; el.style.height = elementHeight + 'px'; el.offsetHeight; // reflow el.style.height = 0; } -function afterLeave(el: Element) { +function afterLeave(el) { el.style.height = null; } +function toggle() { + if (!opened.value) { + openedAtLeastOnce.value = true; + } + + nextTick(() => { + opened.value = !opened.value; + }); +} + onMounted(() => { - function getParentBg(el: HTMLElement | null): string { - if (el == null || el.tagName === 'BODY') return 'var(--bg)'; - const bg = el.style.background || el.style.backgroundColor; - if (bg) { - return bg; - } else { - return getParentBg(el.parentElement); - } - } - - const rawBg = getParentBg(el.value); - const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); - _bg.setAlpha(0.85); - bg.value = _bg.toRgbString(); + const computedStyle = getComputedStyle(document.documentElement); + const parentBg = getBgColor(rootEl.value.parentElement); + const myBg = computedStyle.getPropertyValue('--panel'); + bgSame.value = parentBg === myBg; }); </script> <style lang="scss" module> -.folder-toggle-enter-active, .folder-toggle-leave-active { - overflow-y: clip; - transition: opacity 0.5s, height 0.5s !important; +.transition_toggle_enterActive, +.transition_toggle_leaveActive { + overflow-y: clip; + transition: opacity 0.3s, height 0.3s, transform 0.3s !important; } -.folder-toggle-enter-from { - opacity: 0; -} -.folder-toggle-leave-to { - opacity: 0; +.transition_toggle_enterFrom, +.transition_toggle_leaveTo { + opacity: 0; } .root { - position: relative; + display: block; } .header { - display: flex; - position: relative; - z-index: 10; - position: sticky; - top: var(--stickyTop, 0px); - -webkit-backdrop-filter: var(--blur, blur(8px)); - backdrop-filter: var(--blur, blur(20px)); + display: flex; + position: relative; + z-index: 10; + position: sticky; + top: var(--stickyTop, 0px); + -webkit-backdrop-filter: var(--blur, blur(8px)); + backdrop-filter: var(--blur, blur(20px)); + + &.opened { + border-radius: 6px 6px 0 0; + } } +.headerUpper { + display: flex; + align-items: center; +} + +.headerLower { + color: var(--fgTransparentWeak); + font-size: .85em; + padding-left: 4px; +} + +.headerIcon { + margin-right: 0.75em; + flex-shrink: 0; + text-align: center; + opacity: 0.8; + + &:empty { + display: none; + + & + .headerText { + padding-left: 4px; + } + } +} .title { - display: grid; - place-content: center; - margin: 0; - padding: 12px 16px 12px 0; + display: grid; + place-content: center; + margin: 0; + padding: 12px 16px 12px 0; +} +.headerText { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; + padding-right: 12px; +} + +.headerTextSub { + color: var(--fgTransparentWeak); + font-size: .85em; +} + +.headerRight { + margin-left: auto; + color: var(--fgTransparentWeak); + white-space: nowrap; +} + +.headerRightText:not(:empty) { + margin-right: 0.75em; } .divider { - flex: 1; - margin: auto; - height: 1px; - background: var(--divider); -} - -.button { - padding: 12px 0 12px 16px; -} - -@container (max-width: 500px) { - .title { - padding: 8px 10px 8px 0; - } + flex: 1; + margin: auto; + height: 1px; + background: var(--divider); } </style> diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index c26fcea911..49c681b3ee 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -91,7 +91,7 @@ const showContent = ref(false); margin: 0; padding: 0; font-size: 0.95em; - border-bottom: solid 0.5px var(--divider); + } .button{ margin-right: var(--margin); diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue index 43e576d1ab..cb2b9c280d 100644 --- a/packages/frontend/src/components/MkPollEditor.vue +++ b/packages/frontend/src/components/MkPollEditor.vue @@ -149,8 +149,10 @@ watch([choices, multiple, expiration, atDate, atTime, after, unit], () => emit(' <style lang="scss" scoped> .zmdxowus { - padding: 8px 16px; - + margin: 4px 8px; + padding: 4px 8px; + border-radius: 8px; + border: solid 2px var(--divider); > .caution { margin: 0 0 8px 0; font-size: 0.8em; diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 67de693b20..d46ea13b1c 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -1274,23 +1274,21 @@ defineExpose({ .cw { z-index: 1; padding-bottom: 8px; - border-bottom: solid 0.5px var(--divider); + border-bottom: solid 1px var(--divider); } .postOptionsRoot { >* { - border-bottom: solid 0.5px var(--divider); - } - >:last-child { - border-bottom: none; + border-bottom: solid 1px var(--divider); } + } .hashtags { z-index: 1; padding-top: 8px; padding-bottom: 8px; - border-top: solid 0.5px var(--divider); + border-top: solid 1px var(--divider); } .textOuter { diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index 9c504e46b2..7292fade8d 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> <template #header> - <XHeader :actions="headerActions" :tabs="headerTabs"/> + <MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/> </template> <MkSpacer :contentMax="700" :marginMin="16" :marginMax="32"> <FormSuspense :p="init"> @@ -34,8 +34,8 @@ import * as os from '@/os.js'; import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import MkSpacer from "@/components/global/MkSpacer.vue"; -import MkStickyContainer from "@/components/global/MkStickyContainer.vue"; +import MkSpacer from '@/components/global/MkSpacer.vue'; +import MkStickyContainer from '@/components/global/MkStickyContainer.vue'; const blockedHosts = ref<string>(''); const silencedHosts = ref<string>(''); @@ -43,8 +43,8 @@ const tab = ref('block'); async function init() { const meta = await os.api('admin/meta'); - blockedHosts.value = meta.blockedHosts.join('\n'); - silencedHosts.value = meta.silencedHosts.join('\n'); + blockedHosts.value = meta.blockedHosts ? meta.blockedHosts.join('\n') : ''; + silencedHosts.value = meta.silencedHosts ? meta.silencedHosts.join('\n') : ''; } function save() { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index a41ba9cc92..aa678c981f 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -35,32 +35,31 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </MkFolder> - <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline}}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> - <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> + <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline }}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> + <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> </div> </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.displayOfNote }}</template> + <MkFoldableSection :defaultOpen="false" class="item"> + <template #header>{{ i18n.ts.displayOfNote }}</template> <div class="_gaps_m"> <div class="_gaps_s"> <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch> <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch> <MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch> - <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor}}</MkSwitch> - <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> - <template #label>{{ i18n.ts._visibility.home }}</template> - </MkColorInput> - <MkColorInput v-if="showVisibilityColor" v-model="followerColor"> - <template #label>{{ i18n.ts._visibility.followers }}</template> - </MkColorInput> - <MkColorInput v-if="showVisibilityColor" v-model="specifiedColor"> - <template #label>{{ i18n.ts._visibility.specified }}</template> - </MkColorInput> - <MkColorInput v-if="showVisibilityColor" v-model="localOnlyColor"> - <template #label>{{ i18n.ts.localOnly }}</template> - </MkColorInput> + <MkSwitch v-model="showVisibilityColor">{{ i18n.ts.showVisibilityColor }}</MkSwitch> + <MkColorInput v-if="showVisibilityColor" v-model="homeColor"> + <template #label>{{ i18n.ts._visibility.home }}</template> + </MkColorInput> + <MkColorInput v-if="showVisibilityColor" v-model="followerColor"> + <template #label>{{ i18n.ts._visibility.followers }}</template> + </MkColorInput> + <MkColorInput v-if="showVisibilityColor" v-model="specifiedColor"> + <template #label>{{ i18n.ts._visibility.specified }}</template> + </MkColorInput> + <MkColorInput v-if="showVisibilityColor" v-model="localOnlyColor"> + <template #label>{{ i18n.ts.localOnly }}</template> + </MkColorInput> <MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch> <MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch> <MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch> @@ -97,10 +96,9 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="2_3">{{ i18n.t('limitTo', { x: '2:3' }) }}</option> </MkRadios> </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.notificationDisplay }}</template> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false" class="item"> + <template #header>{{ i18n.ts.notificationDisplay }}</template> <div class="_gaps_m"> <MkSwitch v-model="useGroupedNotifications">{{ i18n.ts.useGroupedNotifications }}</MkSwitch> @@ -121,11 +119,9 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton> </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.appearance }}</template> - + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false" class="item"> + <template #header>{{ i18n.ts.appearance }}</template> <div class="_gaps_m"> <div class="_gaps_s"> <MkSwitch v-model="reduceAnimation">{{ i18n.ts.reduceUiAnimation }}</MkSwitch> @@ -139,8 +135,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="disableDrawer">{{ i18n.ts.disableDrawer }}</MkSwitch> <MkSwitch v-model="forceShowAds">{{ i18n.ts.forceShowAds }}</MkSwitch> <MkSwitch v-model="enableGamingMode">{{ i18n.ts.gamingMode }} <template #caption>{{ i18n.ts.gamingModeInfo }} </template></MkSwitch> - <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave}}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> - <MkSwitch v-model="enablehanntenn">{{ i18n.ts.hanntenn }}<template #caption>{{ i18n.ts.hanntennInfo }} </template></MkSwitch> + <MkSwitch v-model="enableonlyAndWithSave">{{ i18n.ts.onlyAndWithSave }}<template #caption>{{ i18n.ts.onlyAndWithSaveInfo }} </template></MkSwitch> + <MkSwitch v-model="enablehanntenn">{{ i18n.ts.hanntenn }}<template #caption>{{ i18n.ts.hanntennInfo }} </template></MkSwitch> <MkSwitch v-model="enableSeasonalScreenEffect">{{ i18n.ts.seasonalScreenEffect }}</MkSwitch> </div> <div> @@ -161,10 +157,9 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="3"><span style="font-size: 17px;">Aa</span></option> </MkRadios> </div> - </FormSection> - - <FormSection> - <template #label>{{ i18n.ts.behavior }}</template> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false" class="item"> + <template #header>{{ i18n.ts.behavior }}</template> <div class="_gaps_m"> <div class="_gaps_s"> @@ -184,10 +179,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.numberOfPageCache }}</template> <template #caption>{{ i18n.ts.numberOfPageCacheDescription }}</template> </MkRange> - <MkRange v-model="numberOfGamingSpeed" :min="1" :max="60" :step="1" easing> - <template #label>{{ i18n.ts.GamingSpeedChange }}</template> - <template #caption>{{ i18n.ts.GamingSpeedChangeInfo }}</template> - </MkRange> + <MkRange v-model="numberOfGamingSpeed" :min="1" :max="60" :step="1" easing> + <template #label>{{ i18n.ts.GamingSpeedChange }}</template> + <template #caption>{{ i18n.ts.GamingSpeedChangeInfo }}</template> + </MkRange> <MkFolder> <template #label>{{ i18n.ts.dataSaver }}</template> @@ -219,7 +214,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> </div> - </FormSection> + </MkFoldableSection> <FormSection> <template #label>{{ i18n.ts.other }}</template> @@ -261,7 +256,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; import { globalEvents } from '@/events.js'; import { claimAchievement } from '@/scripts/achievements.js'; -import MkColorInput from "@/components/MkColorInput.vue"; +import MkColorInput from '@/components/MkColorInput.vue'; +import MkFoldableSection from '@/components/MkFoldableSection.vue'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -323,7 +319,7 @@ const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWit const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showGlobalTimeline = computed(defaultStore.makeGetterSetter('showGlobalTimeline')); -const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')) +const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); @@ -333,29 +329,31 @@ watch(lang, () => { miLocalStorage.removeItem('localeVersion'); }); -document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); +document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value + 's'); + function hexToRgb(hex) { - // 16進数のカラーコードから "#" を除去 - hex = hex.replace(/^#/, ''); + // 16進数のカラーコードから "#" を除去 + hex = hex.replace(/^#/, ''); - // 16進数をRGBに変換 - const r = parseInt(hex.substring(0, 2), 16); - const g = parseInt(hex.substring(2, 4), 16); - const b = parseInt(hex.substring(4, 6), 16); + // 16進数をRGBに変換 + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); - return `${r},${g},${b}`; + return `${r},${g},${b}`; } + document.documentElement.style.setProperty('--homeColor', hexToRgb(homeColor.value)); -document.documentElement.style.setProperty("--followerColor",hexToRgb(followerColor.value)); -document.documentElement.style.setProperty("--specifiedColor",hexToRgb(specifiedColor.value)) -watch([homeColor,specifiedColor,followerColor], ()=>{ - document.documentElement.style.setProperty('--homeColor', hexToRgb(homeColor.value)); - document.documentElement.style.setProperty("--followerColor",hexToRgb(followerColor.value)); - document.documentElement.style.setProperty("--specifiedColor",hexToRgb(specifiedColor.value)) -}) -watch(numberOfGamingSpeed, () =>{ - document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value+'s'); -}) +document.documentElement.style.setProperty('--followerColor', hexToRgb(followerColor.value)); +document.documentElement.style.setProperty('--specifiedColor', hexToRgb(specifiedColor.value)); +watch([homeColor, specifiedColor, followerColor], () => { + document.documentElement.style.setProperty('--homeColor', hexToRgb(homeColor.value)); + document.documentElement.style.setProperty('--followerColor', hexToRgb(followerColor.value)); + document.documentElement.style.setProperty('--specifiedColor', hexToRgb(specifiedColor.value)); +}); +watch(numberOfGamingSpeed, () => { + document.documentElement.style.setProperty('--gamingspeed', numberOfGamingSpeed.value + 's'); +}); watch(fontSize, () => { if (fontSize.value == null) { miLocalStorage.removeItem('fontSize'); @@ -388,9 +386,9 @@ watch([ highlightSensitiveMedia, keepScreenOn, showMediaTimeline, - showVisibilityColor, - enableonlyAndWithSave, - showGlobalTimeline, + showVisibilityColor, + enableonlyAndWithSave, + showGlobalTimeline, disableStreamingTimeline, enableSeasonalScreenEffect, ], async () => { diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts index 94bb263980..978162cd6a 100644 --- a/packages/misskey-js/src/autogen/types.ts +++ b/packages/misskey-js/src/autogen/types.ts @@ -5431,6 +5431,7 @@ export type operations = { updatedAt: string | null; name: string; description: string; + category: string; url: string; roleIdsThatCanBeUsedThisDecoration: string[]; })[]; @@ -14879,6 +14880,7 @@ export type operations = { name: string; description: string; url: string; + category: string; roleIdsThatCanBeUsedThisDecoration: string[]; }[]; }; From 4cdf048a7cd5891579152759c1f0686abb5c47da Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:25:54 +0900 Subject: [PATCH 307/501] =?UTF-8?q?=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E3=82=AB=E3=83=86=E3=82=B4=E3=83=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/src/models/AvatarDecoration.ts | 5 + .../admin/avatar-decorations/update.ts | 2 + .../api/endpoints/get-avatar-decorations.ts | 1 + .../src/components/MkAvatarDecoEditDialog.vue | 164 ++++++++++++++++++ .../frontend/src/pages/avatar-decorations.vue | 74 ++++---- .../settings/avatar-decoration.dialog.vue | 3 + .../src/pages/settings/avatar-decoration.vue | 28 ++- 7 files changed, 228 insertions(+), 49 deletions(-) create mode 100644 packages/frontend/src/components/MkAvatarDecoEditDialog.vue diff --git a/packages/backend/src/models/AvatarDecoration.ts b/packages/backend/src/models/AvatarDecoration.ts index 08ebbdeac1..f658663e68 100644 --- a/packages/backend/src/models/AvatarDecoration.ts +++ b/packages/backend/src/models/AvatarDecoration.ts @@ -31,6 +31,11 @@ export class MiAvatarDecoration { }) public description: string; + @Column('varchar', { + length: 256, + }) + public category: string; + // TODO: 定期ジョブで存在しなくなったロールIDを除去するようにする @Column('varchar', { array: true, length: 128, default: '{}', diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts index 6211345f96..3d424c68de 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/update.ts @@ -30,6 +30,7 @@ export const paramDef = { roleIdsThatCanBeUsedThisDecoration: { type: 'array', items: { type: 'string', } }, + category: { type: 'string', nullable: true }, }, required: ['id'], } as const; @@ -45,6 +46,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- description: ps.description, url: ps.url, roleIdsThatCanBeUsedThisDecoration: ps.roleIdsThatCanBeUsedThisDecoration, + category: ps.category ?? '', }, me); }); } diff --git a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts index dbe1626149..028d647393 100644 --- a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts +++ b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts @@ -75,6 +75,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- name: decoration.name, description: decoration.description, url: decoration.url, + category: decoration.category, roleIdsThatCanBeUsedThisDecoration: decoration.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(role => role.id === roleId)), })); }); diff --git a/packages/frontend/src/components/MkAvatarDecoEditDialog.vue b/packages/frontend/src/components/MkAvatarDecoEditDialog.vue new file mode 100644 index 0000000000..9ef5be1df2 --- /dev/null +++ b/packages/frontend/src/components/MkAvatarDecoEditDialog.vue @@ -0,0 +1,164 @@ +<!-- +SPDX-FileCopyrightText: syuilo and other misskey contributors +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkModalWindow + ref="dialog" + :width="400" + @close="dialog.close()" + @closed="$emit('closed')" +> + <template v-if="avatarDecoration" #header>:{{ avatarDecoration.name }}</template> + <template v-else #header>New create</template> + + <div> + <MkSpacer :marginMin="20" :marginMax="28"> + <div class="_gaps_m"> + <div class="_gaps_m"> + <XDecoration + v-if="avatarDecoration" + :key="avatarDecoration.id" + :decoration="avatarDecoration" + /> + <MkInput v-model="name"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkTextarea v-model="description"> + <template #label>{{ i18n.ts.description }}</template> + </MkTextarea> + <MkInput v-model="url"> + <template #label>{{ i18n.ts.imageUrl }}</template> + </MkInput> + <MkInput v-model="category"> + <template #label>{{ i18n.ts.category }}</template> + </MkInput> + </div> + </div> + </MkSpacer> + <div :class="$style.footer"> + <div :class="$style.footerButtons"> + <MkButton danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + <MkButton primary rounded style="margin: 0 auto;" @click="save"><i class="ti ti-check"></i> {{ props.avatarDecoration ? i18n.ts.update : i18n.ts.create }}</MkButton> + </div> + </div> + </div> +</MkModalWindow> +</template> + +<script lang="ts" setup> +import { computed, ref, watch } from 'vue'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import MkButton from '@/components/MkButton.vue'; +import { i18n } from '@/i18n.js'; +import * as os from '@/os.js'; +import MkInput from '@/components/MkInput.vue'; +import MkTextarea from '@/components/MkTextarea.vue'; +import XDecoration from '@/pages/settings/avatar-decoration.decoration.vue'; +const props = defineProps<{ + avatarDecoration?: { + id: string | null; + name: string; + description: string; + url: string; + category: string; + }; +}>(); +let name = ref(props.avatarDecoration?.name ?? ''); +let category = ref(props.avatarDecoration?.category ?? ''); +let description = ref(props.avatarDecoration?.description ?? ''); +let url = ref(props.avatarDecoration?.url ?? ''); +const emit = defineEmits<{ + (ev: 'del'): void +}>(); + +let dialog = ref<InstanceType<typeof MkModalWindow> | null>(null); + +function del() { + os.confirm({ + type: 'warning', + text: i18n.t('deleteAreYouSure', { x: props.avatarDecoration?.name }), + }).then(({ canceled }) => { + if (canceled) return; + os.api('admin/avatar-decorations/delete', { id: props.avatarDecoration?.id }).then(() => { + + }); + }); + emit('del'); +} + +async function save() { + if (props.avatarDecoration == null) { + await os.apiWithDialog('admin/avatar-decorations/create', { + name: name.value, + description: description.value, + url: url.value, + category: category.value, + }); + } else { + await os.apiWithDialog('admin/avatar-decorations/update', { + id: props.avatarDecoration.id ?? '', + name: name.value, + description: description.value, + url: url.value, + category: category.value, + }); + } + emit('del'); +} + +</script> + +<style lang="scss" module> +.imgs { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: center; +} + +.imgContainer { + padding: 8px; + border-radius: 6px; +} + +.img { + display: block; + height: 64px; + width: 64px; + object-fit: contain; +} + +.roleItem { + display: flex; +} + +.role { + flex: 1; +} + +.roleUnassign { + width: 32px; + height: 32px; + margin-left: 8px; + align-self: center; +} + +.footer { + position: sticky; + bottom: 0; + left: 0; + padding: 12px; + border-top: solid 0.5px var(--divider); + -webkit-backdrop-filter: var(--blur, blur(15px)); + backdrop-filter: var(--blur, blur(15px)); +} + +.footerButtons { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: center; +} +</style> diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 87964ac697..eae411fd0d 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -8,41 +8,26 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="900"> <div class="_gaps"> - <MkFolder v-for="avatarDecoration in avatarDecorations" :key="avatarDecoration.id ?? avatarDecoration._id" :defaultOpen="avatarDecoration.id == null"> - <template #label>{{ avatarDecoration.name }}</template> - <template #caption>{{ avatarDecoration.description }}</template> - - <div class="_gaps_m"> - <MkInput v-model="avatarDecoration.name"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkTextarea v-model="avatarDecoration.description"> - <template #label>{{ i18n.ts.description }}</template> - </MkTextarea> - <MkInput v-model="avatarDecoration.url"> - <template #label>{{ i18n.ts.imageUrl }}</template> - </MkInput> - <div class="buttons _buttons"> - <MkButton class="button" inline primary @click="save(avatarDecoration)"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> - <MkButton v-if="avatarDecoration.id != null" class="button" inline danger @click="del(avatarDecoration)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> - </div> - </div> - </MkFolder> + <div :class="$style.decorations"> + <XDecoration + v-for="avatarDecoration in avatarDecorations" + :key="avatarDecoration.id" + :decoration="avatarDecoration" + @click="openDecorationEdit(avatarDecoration)" + /> + </div> </div> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, defineAsyncComponent } from 'vue'; import * as Misskey from 'misskey-js'; -import MkButton from '@/components/MkButton.vue'; -import MkInput from '@/components/MkInput.vue'; -import MkTextarea from '@/components/MkTextarea.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import MkFolder from '@/components/MkFolder.vue'; +import XDecoration from '@/pages/settings/avatar-decoration.decoration.vue'; const avatarDecorations = ref<Misskey.entities.AdminAvatarDecorationsListResponse>([]); @@ -53,27 +38,27 @@ function add() { name: '', description: '', url: '', + category: '', }); } -function del(avatarDecoration) { - os.confirm({ - type: 'warning', - text: i18n.t('deleteAreYouSure', { x: avatarDecoration.name }), - }).then(({ canceled }) => { - if (canceled) return; - avatarDecorations.value = avatarDecorations.value.filter(x => x !== avatarDecoration); - os.api('admin/avatar-decorations/delete', avatarDecoration); +function openDecorationEdit(avatarDecoration) { + os.popup(defineAsyncComponent(() => import('@/components/MkAvatarDecoEditDialog.vue')), { + avatarDecoration: avatarDecoration, + }, { + del: () => { + window.location.reload(); + }, }); } -async function save(avatarDecoration) { - if (avatarDecoration.id == null) { - await os.apiWithDialog('admin/avatar-decorations/create', avatarDecoration); - load(); - } else { - os.apiWithDialog('admin/avatar-decorations/update', avatarDecoration); - } +function openDecorationCreate() { + os.popup(defineAsyncComponent(() => import('@/components/MkAvatarDecoEditDialog.vue')), { + }, { + del: result => { + window.location.reload(); + }, + }); } function load() { @@ -88,7 +73,7 @@ const headerActions = computed(() => [{ asFullButton: true, icon: 'ti ti-plus', text: i18n.ts.add, - handler: add, + handler: openDecorationCreate, }]); const headerTabs = computed(() => []); @@ -98,3 +83,10 @@ definePageMetadata({ icon: 'ti ti-sparkles', }); </script> +<style module> +.decorations { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + grid-gap: 12px; +} +</style> diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue index 329ab4d47a..544f1dc9ac 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue @@ -20,6 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkAvatar style="width: 64px; height: 64px; margin-bottom: 20px;" :user="$i" :decorations="decorationsForPreview" forceShowDecoration/> </div> <div class="_gaps_s"> + {{ i18n.ts.description }} + <p style="white-space: pre-wrap;">{{ decoration.description }}</p> <MkRange v-model="angle" continuousUpdate :min="-0.5" :max="0.5" :step="0.025" :textConverter="(v) => `${Math.floor(v * 360)}°`"> <template #label>{{ i18n.ts.angle }}</template> </MkRange> @@ -59,6 +61,7 @@ const props = defineProps<{ id: string; url: string; name: string; + description: string; }; }>(); diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 6551fc917e..34920e1ae3 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -29,13 +29,19 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> </div> - <div :class="$style.decorations"> - <XDecoration - v-for="avatarDecoration in avatarDecorations" - :key="avatarDecoration.id" - :decoration="avatarDecoration" - @click="openDecoration(avatarDecoration)" - /> + <div v-for="category in categories"> + <MkFoldableSection> + <template #header> {{ (category !== '') ? category : i18n.ts.other }}</template> + <div :class="$style.decorations"> + <div v-for="avatarDecoration in avatarDecorations.filter(ad => ad.category === category)"> + <XDecoration + :key="avatarDecoration.id" + :decoration="avatarDecoration" + @click="openDecoration(avatarDecoration)" + /> + </div> + </div> + </MkFoldableSection> </div> </div> <div v-else> @@ -54,14 +60,20 @@ import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import MkInfo from '@/components/MkInfo.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; +import MkFoldableSection from '@/components/MkFoldableSection.vue'; const loading = ref(true); -const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]); +const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse & { category:string }>([]); os.api('get-avatar-decorations').then(_avatarDecorations => { avatarDecorations.value = _avatarDecorations; loading.value = false; }); +const categories = computed(() => { + const allCategories = avatarDecorations.value.map(ad => ad.category); + const uniqueCategories = [...new Set(allCategories)]; + return uniqueCategories.sort(); +}); function openDecoration(avatarDecoration, index?: number) { os.popup(defineAsyncComponent(() => import('./avatar-decoration.dialog.vue')), { From b7102f67afa535586d5e9e2d45bb45ae584d1a1b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:26:44 +0900 Subject: [PATCH 308/501] =?UTF-8?q?=E3=82=A2=E3=82=A4=E3=82=B3=E3=83=B3?= =?UTF-8?q?=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E3=82=AB=E3=83=86=E3=82=B4=E3=83=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../migration/1704206095136-avatardecoration.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/backend/migration/1704206095136-avatardecoration.js diff --git a/packages/backend/migration/1704206095136-avatardecoration.js b/packages/backend/migration/1704206095136-avatardecoration.js new file mode 100644 index 0000000000..ca1989d225 --- /dev/null +++ b/packages/backend/migration/1704206095136-avatardecoration.js @@ -0,0 +1,11 @@ +export class Avatardecoration1704206095136 { + name = 'Avatardecoration1704206095136' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "category" character varying(256) NOT NULL`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "avatar_decoration" DROP COLUMN "category"`); + } +} From 4d052272e94bdd85e3082e1a5c8290fe93a0124f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:28:09 +0900 Subject: [PATCH 309/501] 2023.12.2-PrisMisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c2046080c5..3599b75727 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.12.2-PrisMisskey.1", + "version": "2023.12.2-PrisMisskey.2", "codename": "nasubi", "repository": { "type": "git", From 6a06ec134789660e4f8be4ee7d75e79689e95e58 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:32:22 +0900 Subject: [PATCH 310/501] 2023.12.2-PrisMisskey.2 bug fix --- packages/backend/migration/1704206095136-avatardecoration.js | 2 +- packages/backend/src/models/AvatarDecoration.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/backend/migration/1704206095136-avatardecoration.js b/packages/backend/migration/1704206095136-avatardecoration.js index ca1989d225..15e8f5a7c3 100644 --- a/packages/backend/migration/1704206095136-avatardecoration.js +++ b/packages/backend/migration/1704206095136-avatardecoration.js @@ -2,7 +2,7 @@ export class Avatardecoration1704206095136 { name = 'Avatardecoration1704206095136' async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "category" character varying(256) NOT NULL`); + await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "category" character varying(256) NOT NULL DEFAULT '{}'`); } async down(queryRunner) { diff --git a/packages/backend/src/models/AvatarDecoration.ts b/packages/backend/src/models/AvatarDecoration.ts index f658663e68..010cbfe82d 100644 --- a/packages/backend/src/models/AvatarDecoration.ts +++ b/packages/backend/src/models/AvatarDecoration.ts @@ -33,6 +33,7 @@ export class MiAvatarDecoration { @Column('varchar', { length: 256, + default: '', }) public category: string; From 09b1bac3ad13dfdf2eaa15d03efb8b8000fbdbb5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:33:20 +0900 Subject: [PATCH 311/501] bug fix --- packages/backend/migration/1704206095136-avatardecoration.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/migration/1704206095136-avatardecoration.js b/packages/backend/migration/1704206095136-avatardecoration.js index 15e8f5a7c3..797d91a74e 100644 --- a/packages/backend/migration/1704206095136-avatardecoration.js +++ b/packages/backend/migration/1704206095136-avatardecoration.js @@ -2,7 +2,7 @@ export class Avatardecoration1704206095136 { name = 'Avatardecoration1704206095136' async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "category" character varying(256) NOT NULL DEFAULT '{}'`); + await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "category" character varying(256) NOT NULL DEFAULT ''`); } async down(queryRunner) { From 1e8889ecfb2ab8e3120b2b86f76de9c651dce18e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 01:52:25 +0900 Subject: [PATCH 312/501] category --- .../frontend/src/pages/avatar-decorations.vue | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index eae411fd0d..21b1cf71f0 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -7,13 +7,16 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="900"> + <MkSwitch v-model="select">SelectMode</MkSwitch> + <MkButton @click="setCategoryBulk">Set Category</MkButton> <div class="_gaps"> <div :class="$style.decorations"> <XDecoration v-for="avatarDecoration in avatarDecorations" :key="avatarDecoration.id" + :class=" selectItemsId.includes(avatarDecoration.id) ? $style.selected : '' " :decoration="avatarDecoration" - @click="openDecorationEdit(avatarDecoration)" + @click="select ? selectItems(avatarDecoration.id) : openDecorationEdit(avatarDecoration)" /> </div> </div> @@ -22,14 +25,18 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed, defineAsyncComponent } from 'vue'; +import { ref, computed, defineAsyncComponent, watch } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import XDecoration from '@/pages/settings/avatar-decoration.decoration.vue'; +import MkButton from '@/components/MkButton.vue'; +import MkSwitch from '@/components/MkSwitch.vue'; const avatarDecorations = ref<Misskey.entities.AdminAvatarDecorationsListResponse>([]); +const select = ref(false); +const selectItemsId = ref<string[]>([]); function add() { avatarDecorations.value.unshift({ @@ -42,6 +49,15 @@ function add() { }); } +function selectItems(decorationId) { + if (selectItemsId.value.includes(decorationId)) { + const index = selectItemsId.value.indexOf(decorationId); + selectItemsId.value.splice(index, 1); + } else { + selectItemsId.value.push(decorationId); + } +} + function openDecorationEdit(avatarDecoration) { os.popup(defineAsyncComponent(() => import('@/components/MkAvatarDecoEditDialog.vue')), { avatarDecoration: avatarDecoration, @@ -68,6 +84,25 @@ function load() { } load(); +watch(select, () => { + selectItemsId.value = []; +}); + +async function setCategoryBulk() { + const { canceled, result } = await os.inputText({ + title: 'Category', + }); + if (canceled) return; + if (selectItemsId.value.length > 1) { + for (let i = 0; i < selectItemsId.value.length; i++) { + let decorationId = selectItemsId.value[i]; + await os.api('admin/avatar-decorations/update', { + id: decorationId, + category: result, + }); + } + } +} const headerActions = computed(() => [{ asFullButton: true, @@ -89,4 +124,7 @@ definePageMetadata({ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); grid-gap: 12px; } +.selected{ + border: 0.1px solid var(--accent); +} </style> From a4974e3c8ae22cbf4d2d4f88f63fc20ba23547d3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 3 Jan 2024 02:13:25 +0900 Subject: [PATCH 313/501] defaultOpen --- packages/frontend/src/pages/settings/avatar-decoration.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 34920e1ae3..e86fd02318 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-for="category in categories"> - <MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> <template #header> {{ (category !== '') ? category : i18n.ts.other }}</template> <div :class="$style.decorations"> <div v-for="avatarDecoration in avatarDecorations.filter(ad => ad.category === category)"> From 085a93b9fc2170de09a921c98ffe56877b7be598 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 4 Jan 2024 14:16:28 +0900 Subject: [PATCH 314/501] AvatarDecoration federation and more fix --- .../1704343998612-avatardecoration_fed.js | 13 +++++ .../activitypub/models/ApPersonService.ts | 55 +++++++++++++++++-- .../backend/src/models/AvatarDecoration.ts | 5 ++ .../admin/avatar-decorations/list.ts | 4 +- .../api/endpoints/get-avatar-decorations.ts | 5 +- .../src/server/api/endpoints/i/update.ts | 9 ++- packages/frontend/src/components/MkInput.vue | 2 +- .../frontend/src/components/MkPollEditor.vue | 4 +- packages/frontend/src/components/MkSelect.vue | 2 +- 9 files changed, 84 insertions(+), 15 deletions(-) create mode 100644 packages/backend/migration/1704343998612-avatardecoration_fed.js diff --git a/packages/backend/migration/1704343998612-avatardecoration_fed.js b/packages/backend/migration/1704343998612-avatardecoration_fed.js new file mode 100644 index 0000000000..8e182f92f4 --- /dev/null +++ b/packages/backend/migration/1704343998612-avatardecoration_fed.js @@ -0,0 +1,13 @@ +export class AvatardecorationFed1704343998612 { + name = 'AvatardecorationFed1704343998612' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "host" character varying(256)`); + await queryRunner.query(`ALTER TABLE "avatar_decoration" ALTER COLUMN "category" SET DEFAULT ''`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "avatar_decoration" ALTER COLUMN "category" DROP DEFAULT`); + await queryRunner.query(`ALTER TABLE "avatar_decoration" DROP COLUMN "host"`); + } +} diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index bf38d5fd60..c436f9f529 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -38,6 +38,8 @@ import { MetaService } from '@/core/MetaService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import type { AccountMoveService } from '@/core/AccountMoveService.js'; import { checkHttps } from '@/misc/check-https.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -76,6 +78,8 @@ export class ApPersonService implements OnModuleInit { private apLoggerService: ApLoggerService; private accountMoveService: AccountMoveService; private logger: Logger; + private httpRequestService: HttpRequestService; + private avatarDecorationService: AvatarDecorationService; constructor( private moduleRef: ModuleRef, @@ -100,6 +104,7 @@ export class ApPersonService implements OnModuleInit { @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, + ) { } @@ -124,6 +129,8 @@ export class ApPersonService implements OnModuleInit { this.apLoggerService = this.moduleRef.get('ApLoggerService'); this.accountMoveService = this.moduleRef.get('AccountMoveService'); this.logger = this.apLoggerService.logger; + this.httpRequestService = this.moduleRef.get('HttpRequestService'); + this.avatarDecorationService = this.moduleRef.get('AvatarDecorationService'); } private punyHost(url: string): string { @@ -225,14 +232,14 @@ export class ApPersonService implements OnModuleInit { return null; } - private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { + private async resolveAvatarAndBanner(user: MiRemoteUser, host: string | null, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { const [avatar, banner] = await Promise.all([icon, image].map(img => { if (img == null) return null; if (user == null) throw new Error('failed to create user: user is null'); return this.apImageService.resolveImage(user, img).catch(() => null); })); - return { + const returnData: any = { avatarId: avatar?.id ?? null, bannerId: banner?.id ?? null, avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, @@ -240,6 +247,42 @@ export class ApPersonService implements OnModuleInit { avatarBlurhash: avatar?.blurhash ?? null, bannerBlurhash: banner?.blurhash ?? null, }; + + if (host) { + const i = await this.federatedInstanceService.fetch(host); + console.log('avatarDecorationFetch: start'); + if (i.softwareName === 'misskey') { + const remoteUserId = user.uri.split('/users/')[1]; + const userMetaRequest = await this.httpRequestService.send(`https://${i.host}/api/users/show`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + 'userId': remoteUserId, + }), + }); + const res: any = await userMetaRequest.json(); + if (res.avatarDecorations) { + const localDecos = await this.avatarDecorationService.getAll(); + // ローカルのデコレーションとして登録する + for (const deco of res.avatarDecorations) { + if (localDecos.some((v) => v.id === deco.id)) continue; + await this.avatarDecorationService.create({ + id: deco.id, + updatedAt: null, + url: deco.url, + name: `import_${host}_${deco.id}`, + description: `Imported from ${host}`, + host: host, + }); + } + Object.assign(returnData, { avatarDecorations: res.avatarDecorations }); + } + } + } + + return returnData; } /** @@ -380,7 +423,7 @@ export class ApPersonService implements OnModuleInit { //#region アバターとヘッダー画像をフェッチ try { - const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image); + const updates = await this.resolveAvatarAndBanner(user, host, person.icon, person.image); await this.usersRepository.update(user.id, updates); user = { ...user, ...updates }; @@ -442,6 +485,10 @@ export class ApPersonService implements OnModuleInit { const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); const url = getOneApHrefNullable(person.url); + let host = null; + if (url) { + host = new URL(url).host; + } if (url && !checkHttps(url)) { throw new Error('unexpected schema of person url: ' + url); @@ -462,7 +509,7 @@ export class ApPersonService implements OnModuleInit { movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, - ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), + ...(await this.resolveAvatarAndBanner(exist, host, person.icon, person.image).catch(() => ({}))), } as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>; const moving = ((): boolean => { diff --git a/packages/backend/src/models/AvatarDecoration.ts b/packages/backend/src/models/AvatarDecoration.ts index 010cbfe82d..c98d13df30 100644 --- a/packages/backend/src/models/AvatarDecoration.ts +++ b/packages/backend/src/models/AvatarDecoration.ts @@ -36,6 +36,11 @@ export class MiAvatarDecoration { default: '', }) public category: string; + @Column('varchar', { + length: 256, + nullable: true, + }) + public host: string; // TODO: 定期ジョブで存在しなくなったロールIDを除去するようにする @Column('varchar', { diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts index 39297de536..805c1b974d 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts @@ -88,7 +88,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- super(meta, paramDef, async (ps, me) => { const avatarDecorations = await this.avatarDecorationService.getAll(true); - return avatarDecorations.map(avatarDecoration => ({ + const filteredAvatarDecorations = avatarDecorations.filter(avatarDecoration => avatarDecoration.host === null); + console.log(filteredAvatarDecorations); + return filteredAvatarDecorations.map(avatarDecoration => ({ id: avatarDecoration.id, createdAt: this.idService.parse(avatarDecoration.id).date.toISOString(), updatedAt: avatarDecoration.updatedAt?.toISOString() ?? null, diff --git a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts index 028d647393..4fe15d8cf0 100644 --- a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts +++ b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts @@ -70,7 +70,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const decorations = await this.avatarDecorationService.getAll(true); const allRoles = await this.roleService.getRoles(); - return decorations.map(decoration => ({ + // Filter decorations where host is null + const filteredDecorations = decorations.filter(decoration => decoration.host === null); + + return filteredDecorations.map(decoration => ({ id: decoration.id, name: decoration.name, description: decoration.description, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index b258349148..9299f7fa7c 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -294,10 +294,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (typeof ps.preventAiLearning === 'boolean') profileUpdates.preventAiLearning = ps.preventAiLearning; if (typeof ps.isCat === 'boolean' && !ps.isGorilla) { updates.isCat = ps.isCat; - }; + } if (typeof ps.isGorilla === 'boolean' && !ps.isCat) { - updates.isGorilla = ps.isGorilla - }; + updates.isGorilla = ps.isGorilla; + } if (typeof ps.injectFeaturedNote === 'boolean') profileUpdates.injectFeaturedNote = ps.injectFeaturedNote; if (typeof ps.receiveAnnouncementEmail === 'boolean') profileUpdates.receiveAnnouncementEmail = ps.receiveAnnouncementEmail; if (typeof ps.alwaysMarkNsfw === 'boolean') { @@ -342,11 +342,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]); const allRoles = await this.roleService.getRoles(); const decorationIds = decorations - .filter(d => d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id))) + .filter(d => d.host === null && (d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))) .map(d => d.id); if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole); - updates.avatarDecorations = ps.avatarDecorations.filter(d => decorationIds.includes(d.id)).map(d => ({ id: d.id, angle: d.angle ?? 0, diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue index ae797eb7d2..345c0c888a 100644 --- a/packages/frontend/src/components/MkInput.vue +++ b/packages/frontend/src/components/MkInput.vue @@ -163,7 +163,7 @@ onMounted(() => { focus(); } }); - + if (props.mfmAutocomplete) { autocomplete = new Autocomplete(inputEl.value, v, props.mfmAutocomplete === true ? null : props.mfmAutocomplete); } diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue index cb2b9c280d..9a45455775 100644 --- a/packages/frontend/src/components/MkPollEditor.vue +++ b/packages/frontend/src/components/MkPollEditor.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="choices.length < 10" class="add" @click="add">{{ i18n.ts.add }}</MkButton> <MkButton v-else class="add" disabled>{{ i18n.ts._poll.noMore }}</MkButton> <MkSwitch v-model="multiple">{{ i18n.ts._poll.canMultipleVote }}</MkSwitch> - <section> + <section style="margin-bottom: 8px; border-top: solid 1.5px var(--divider);"> <div> <MkSelect v-model="expiration" small> <template #label>{{ i18n.ts._poll.expiration }}</template> @@ -152,7 +152,7 @@ watch([choices, multiple, expiration, atDate, atTime, after, unit], () => emit(' margin: 4px 8px; padding: 4px 8px; border-radius: 8px; - border: solid 2px var(--divider); + border: solid 1.5px var(--divider); > .caution { margin: 0 0 8px 0; font-size: 0.8em; diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue index 33b8a9a86d..dfe1a0a69a 100644 --- a/packages/frontend/src/components/MkSelect.vue +++ b/packages/frontend/src/components/MkSelect.vue @@ -177,7 +177,7 @@ function show(ev: MouseEvent) { <style lang="scss" module> .label { font-size: 0.85em; - padding: 0 0 8px 0; + padding: 8px 0; user-select: none; &:empty { From 334894f7bf95b668c21fbdb0d1afa29f21d726a9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Thu, 4 Jan 2024 23:05:07 +0900 Subject: [PATCH 315/501] =?UTF-8?q?=E4=B8=8A=E9=99=90=E3=82=92=E3=81=A4?= =?UTF-8?q?=E3=81=91=E3=81=9F=E3=82=8A=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- .../backend/src/core/entities/AbuseUserReportEntityService.ts | 4 +++- .../backend/src/server/api/endpoints/users/report-abuse.ts | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts index 23a513dd76..c89ae4464d 100644 --- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts +++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts @@ -34,7 +34,7 @@ export class AbuseUserReportEntityService { src: MiAbuseUserReport['id'] | MiAbuseUserReport, ) { const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src }); - const notes = (report.notes.length === 0) ? report.notes : []; + const notes = []; if (report.noteIds && report.noteIds.length > 0) { for (const x of report.noteIds) { @@ -45,6 +45,8 @@ export class AbuseUserReportEntityService { } notes.push(await this.noteEntityService.pack(x)); } + } else if (report.notes.length > 0) { + notes.push(...(report.notes)); } console.log(report.notes.length, null, notes); diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 5769f03b2f..0d95ebf855 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -53,7 +53,7 @@ export const paramDef = { properties: { userId: { type: 'string', format: 'misskey:id' }, comment: { type: 'string', minLength: 1, maxLength: 2048 }, - noteIds: { type: 'array', items: { type: 'string', format: 'misskey:id' } }, + noteIds: { type: 'array', items: { type: 'string', format: 'misskey:id', maxLength: 16 } }, }, required: ['userId', 'comment'], } as const; From 768f158c105d00fbfcc72b75d8130604ba2845d6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyacocacora0@gmail.com> Date: Sat, 6 Jan 2024 14:00:13 +0900 Subject: [PATCH 316/501] =?UTF-8?q?ANY()=E3=81=AA=E3=81=8F=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: mattyatea <mattyacocacora0@gmail.com> --- packages/backend/src/core/QueryService.ts | 4 ++-- packages/backend/src/core/chart/charts/federation.ts | 10 +++++----- .../server/api/endpoints/drive/files/attached-notes.ts | 3 +-- .../backend/src/server/api/endpoints/hashtags/users.ts | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index f006ed4944..7a2c325715 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -212,8 +212,8 @@ export class QueryService { // または 自分自身 .orWhere('note.userId = :meId') // または 自分宛て - .orWhere(':meId = ANY(note.visibleUserIds)') - .orWhere(':meId = ANY(note.mentions)') + .orWhere(':meId IN (note.visibleUserIds)') + .orWhere(':meId IN (note.mentions)') .orWhere(new Brackets(qb => { qb // または フォロワー宛ての投稿であり、 diff --git a/packages/backend/src/core/chart/charts/federation.ts b/packages/backend/src/core/chart/charts/federation.ts index fc474b002b..c3ab73d69d 100644 --- a/packages/backend/src/core/chart/charts/federation.ts +++ b/packages/backend/src/core/chart/charts/federation.ts @@ -65,21 +65,21 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di this.followingsRepository.createQueryBuilder('following') .select('COUNT(DISTINCT following.followeeHost)') .where('following.followeeHost IS NOT NULL') - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .getRawOne() .then(x => parseInt(x.count, 10)), this.followingsRepository.createQueryBuilder('following') .select('COUNT(DISTINCT following.followerHost)') .where('following.followerHost IS NOT NULL') - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followerHost NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followerHost NOT IN (:...blocked)', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) .andWhere(`following.followerHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .getRawOne() .then(x => parseInt(x.count, 10)), this.followingsRepository.createQueryBuilder('following') .select('COUNT(DISTINCT following.followeeHost)') .where('following.followeeHost IS NOT NULL') - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'following.followeeHost NOT IN (:...blocked)', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) .andWhere(`following.followeeHost NOT IN (${ suspendedInstancesQuery.getQuery() })`) .andWhere(`following.followeeHost IN (${ pubsubSubQuery.getQuery() })`) .setParameters(pubsubSubQuery.getParameters()) @@ -88,7 +88,7 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di this.instancesRepository.createQueryBuilder('instance') .select('COUNT(instance.id)') .where(`instance.host IN (${ subInstancesQuery.getQuery() })`) - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) .andWhere('instance.isSuspended = false') .andWhere('instance.isNotResponding = false') .getRawOne() @@ -96,7 +96,7 @@ export default class FederationChart extends Chart<typeof schema> { // eslint-di this.instancesRepository.createQueryBuilder('instance') .select('COUNT(instance.id)') .where(`instance.host IN (${ pubInstancesQuery.getQuery() })`) - .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT ILIKE ANY(ARRAY[:...blocked])', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) + .andWhere(meta.blockedHosts.length === 0 ? '1=1' : 'instance.host NOT IN (:...blocked)', { blocked: meta.blockedHosts.flatMap(x => [x, `%.${x}`]) }) .andWhere('instance.isSuspended = false') .andWhere('instance.isNotResponding = false') .getRawOne() diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 14a13b09c9..e364df433d 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -74,8 +74,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); - query.andWhere(':file = ANY(note.fileIds)', { file: file.id }); - + query.andWhere(':file <@ note.fileIds', { file: [file.id] }); const notes = await query.limit(ps.limit).getMany(); return await this.noteEntityService.packMany(notes, me, { diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 50aea79943..590a23a303 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -48,7 +48,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- ) { super(meta, paramDef, async (ps, me) => { const query = this.usersRepository.createQueryBuilder('user') - .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }) + .where(':tag IN (user.tags)', { tag: normalizeForSearch(ps.tag) }) .andWhere('user.isSuspended = FALSE'); const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); From 76cc50e38a26dbdecee37e6e5aa4fbdf814b71cb Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 14:26:59 +0900 Subject: [PATCH 317/501] fix --- packages/frontend/src/components/MkRemoteInfoUpdate.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkRemoteInfoUpdate.vue b/packages/frontend/src/components/MkRemoteInfoUpdate.vue index e8f8aec99a..d9fe7b9c30 100644 --- a/packages/frontend/src/components/MkRemoteInfoUpdate.vue +++ b/packages/frontend/src/components/MkRemoteInfoUpdate.vue @@ -4,20 +4,20 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<a :class="$style.root" @click="UserInfoUpdate" ><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserInfoUpdate }}</a> +<a :class="$style.root" @click="UserInfoUpdate"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i>{{ i18n.ts.remoteUserInfoUpdate }}</a> </template> <script lang="ts" setup> import { i18n } from '@/i18n.js'; -import { api } from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const props = withDefaults(defineProps<{ UserId: string; }>(), { - UserId: null, + UserId: null, }); function UserInfoUpdate() { - api('federation/update-remote-user',{userId: props.UserId}) + misskeyApi('federation/update-remote-user', { userId: props.UserId }); } </script> From ce8d9871b144b5a2462ea9e20ec90590a9fd1764 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 14:49:59 +0900 Subject: [PATCH 318/501] fix --- packages/backend/src/core/QueryService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index 7a2c325715..1a67dc77eb 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -212,8 +212,8 @@ export class QueryService { // または 自分自身 .orWhere('note.userId = :meId') // または 自分宛て - .orWhere(':meId IN (note.visibleUserIds)') - .orWhere(':meId IN (note.mentions)') + .orWhere(':meId IN (SELECT unnest(note.visibleUserIds))') + .orWhere(':meId IN (SELECT unnest(note.mentions))') .orWhere(new Brackets(qb => { qb // または フォロワー宛ての投稿であり、 From e25da383932f9ba141024a7c8292bdb62f24dc23 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 19:52:16 +0900 Subject: [PATCH 319/501] fix --- packages/frontend/src/pages/about.emojis.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 37fb7934a9..b9855db474 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span class="tag _button" v-for="tag in customEmojiTags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> </div> --> - </div> + </div> <MkFoldableSection v-if="searchEmojis"> <template #header>{{ i18n.ts.searchResult }}</template> @@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {watch, defineAsyncComponent, ref, computed} from 'vue'; +import { watch, defineAsyncComponent, ref, computed } from 'vue'; import * as Misskey from 'misskey-js'; import XEmoji from './emojis.emoji.vue'; import MkButton from '@/components/MkButton.vue'; @@ -56,6 +56,7 @@ import { i18n } from '@/i18n.js'; import * as os from '@/os'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { misskeyApiGet } from '@/scripts/misskey-api.js'; let tab = ref('emojis'); const headerActions = computed(() => []); @@ -73,7 +74,7 @@ definePageMetadata(ref({})); let q = ref(''); let searchEmojis = ref<Misskey.entities.CustomEmoji[]>(null); let selectedTags = ref(new Set()); -const requestEmojis = await os.apiGet('emoji-requests'); +const requestEmojis = await misskeyApiGet('emoji-requests'); function search() { if ((q.value === '' || q.value == null) && selectedTags.value.size === 0) { From 88965c86080d4b7707d14ffa5184b31047ba077e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 19:59:44 +0900 Subject: [PATCH 320/501] fix --- .../frontend/src/components/MkCustomEmojiEditRequest.vue | 5 +++-- packages/frontend/src/components/MkNoteSimple.vue | 3 ++- packages/frontend/src/pages/about.emojis.vue | 1 - packages/frontend/src/pages/avatar-decorations.vue | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue index e875155dec..cd970ac901 100644 --- a/packages/frontend/src/components/MkCustomEmojiEditRequest.vue +++ b/packages/frontend/src/components/MkCustomEmojiEditRequest.vue @@ -37,6 +37,7 @@ import MkPagination from '@/components/MkPagination.vue'; import * as os from '@/os'; import { i18n } from '@/i18n'; import MkButton from '@/components/MkButton.vue'; +import {misskeyApi} from "@/scripts/misskey-api.js"; const emojisRequestPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); @@ -77,7 +78,7 @@ async function unrequested(emoji) { }); if (canceled) return; - await os.api('admin/emoji/update-request', { + await misskeyApi('admin/emoji/update-request', { id: emoji.id, fileId: emoji.fileId, name: emoji.name, @@ -100,7 +101,7 @@ async function deleteRequest(emoji) { }); if (canceled) return; - os.api('admin/emoji/delete', { + misskeyApi('admin/emoji/delete', { id: emoji.id, }).then(() => { emojisRequestPaginationComponent.value.removeItem((item) => item.id === emoji.id); diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 49c681b3ee..9698c3f4a2 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -34,6 +34,7 @@ import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkCwButton from '@/components/MkCwButton.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; +import {misskeyApi} from "@/scripts/misskey-api.js"; const isDeleted = ref(false); const props = defineProps<{ note: Misskey.entities.Note & { @@ -72,7 +73,7 @@ async function editScheduleNote() { if (canceled) return; - await os.api('notes/schedule/delete', { scheduledNoteId: props.note.scheduledNoteId }) + await misskeyApi('notes/schedule/delete', { scheduledNoteId: props.note.scheduledNoteId }) .then(() => { isDeleted.value = true; }); diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index b9855db474..4d48f92cc6 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -53,7 +53,6 @@ import MkInput from '@/components/MkInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import { customEmojis, customEmojiCategories } from '@/custom-emojis.js'; import { i18n } from '@/i18n.js'; -import * as os from '@/os'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 4c0e0bfa74..e81621f265 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -97,7 +97,7 @@ async function setCategoryBulk() { if (selectItemsId.value.length > 1) { for (let i = 0; i < selectItemsId.value.length; i++) { let decorationId = selectItemsId.value[i]; - await os.api('admin/avatar-decorations/update', { + await misskeyApi('admin/avatar-decorations/update', { id: decorationId, category: result, }); From 14fb95c79849fad5673784a4cebd8e0f555d39a4 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 20:01:55 +0900 Subject: [PATCH 321/501] fix --- .../src/components/MkDrive.folder.vue | 216 +++++++++--------- 1 file changed, 104 insertions(+), 112 deletions(-) diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue index fb6a2cc31b..dcd7da7b83 100644 --- a/packages/frontend/src/components/MkDrive.folder.vue +++ b/packages/frontend/src/components/MkDrive.folder.vue @@ -146,19 +146,19 @@ function onDrop(ev: DragEvent) { if (driveFile != null && driveFile !== '') { const file = JSON.parse(driveFile); emit('removeFile', file.id); - if (props.selectedFiles.length > 0) { - props.selectedFiles.forEach((e)=>{ + if (props.selectedFiles.length > 0) { + props.selectedFiles.forEach((e) => { misskeyApi('drive/files/update', { - fileId: e.id, - folderId: props.folder.id, - }); - }) - }else{ + fileId: e.id, + folderId: props.folder.id, + }); + }); + } else { misskeyApi('drive/files/update', { - fileId: file.id, - folderId: props.folder.id, - }); - } + fileId: file.id, + folderId: props.folder.id, + }); + } } //#endregion @@ -234,119 +234,111 @@ function rename() { function deleteFolder() { misskeyApi('drive/folders/show', { - folderId: props.folder.id, - }).then(async (r) => { + folderId: props.folder.id, + }).then(async (r) => { + if (r.foldersCount > 0) { + await os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。', + }); + } - if (r.foldersCount > 0) { - await os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: 'フォルダ内にフォルダが存在するため、削除できません。 \n フォルダ内のフォルダを削除してから試してみてください。', - }); - } + if (r.filesCount > 0) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.t('driveFolderDeleteConfirm', { name: props.folder.name }), + }); - if (r.filesCount > 0) { - - const {canceled} = await os.confirm({ - type: 'warning', - text: i18n.t('driveFolderDeleteConfirm', {name: props.folder.name}), - }); - - if (canceled) return; - - let allResults = []; - let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); - allResults = allResults.concat(Result) - while (Result.length >= 31) { - const untilId = Result[Result.length - 1].id; - Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); - allResults = allResults.concat(Result); // pushをconcatに変更 - } - allResults.forEach((r,i)=>{ - os.api('drive/files/delete',{fileId: r.id}) - }) + if (canceled) return; + let allResults = []; + let Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31 }); + allResults = allResults.concat(Result); + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); // pushをconcatに変更 + } + allResults.forEach((r, i) => { + misskeyApi('drive/files/delete', { fileId: r.id }); + }); misskeyApi('drive/folders/show', { - folderId: props.folder.id, - }).then(async (r) =>{ - if (r.filesCount > 0) { - - let allResults = []; - let Result = await os.api('drive/files', {folderId: props.folder.id, limit: 31}); - allResults = allResults.concat(Result) - while (Result.length >= 31) { - const untilId = Result[Result.length - 1].id; - Result = await os.api('drive/files', { folderId: props.folder.id, limit: 31, untilId }); - allResults = allResults.concat(Result); - } - allResults.forEach((r,i)=>{ - os.api('drive/files/delete',{fileId: r.id}) - }) + folderId: props.folder.id, + }).then(async (r) => { + if (r.filesCount > 0) { + let allResults = []; + let Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31 }); + allResults = allResults.concat(Result); + while (Result.length >= 31) { + const untilId = Result[Result.length - 1].id; + Result = await misskeyApi('drive/files', { folderId: props.folder.id, limit: 31, untilId }); + allResults = allResults.concat(Result); + } + allResults.forEach((r, i) => { + misskeyApi('drive/files/delete', { fileId: r.id }); + }); misskeyApi('drive/folders/delete', { - folderId: props.folder.id, - }).then(() => { - if (defaultStore.state.uploadFolder === props.folder.id) { - defaultStore.set('uploadFolder', null); - } - }).catch(err => { - switch (err.id) { - case 'b0fc8a17-963c-405d-bfbc-859a487295e1': - os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders, - }); - break; - default: - os.alert({ - type: 'error', - text: i18n.ts.unableToDelete, - }); - } - }); + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); misskeyApi('drive/folders/delete', { - folderId: props.folder.id, - }) - }else{ + folderId: props.folder.id, + }); + } else { misskeyApi('drive/folders/delete', { - folderId: props.folder.id, - }) - } - }) - - } else { - - await misskeyApi('drive/folders/delete', { - folderId: props.folder.id, - }).then(() => { - if (defaultStore.state.uploadFolder === props.folder.id) { - defaultStore.set('uploadFolder', null); - } - }).catch(err => { - switch (err.id) { - case 'b0fc8a17-963c-405d-bfbc-859a487295e1': - os.alert({ - type: 'error', - title: i18n.ts.unableToDelete, - text: i18n.ts.hasChildFilesOrFolders, - }); - break; - default: - os.alert({ - type: 'error', - text: i18n.ts.unableToDelete, - }); - } - }); - } - }) - + folderId: props.folder.id, + }); + } + }); + } else { + await misskeyApi('drive/folders/delete', { + folderId: props.folder.id, + }).then(() => { + if (defaultStore.state.uploadFolder === props.folder.id) { + defaultStore.set('uploadFolder', null); + } + }).catch(err => { + switch (err.id) { + case 'b0fc8a17-963c-405d-bfbc-859a487295e1': + os.alert({ + type: 'error', + title: i18n.ts.unableToDelete, + text: i18n.ts.hasChildFilesOrFolders, + }); + break; + default: + os.alert({ + type: 'error', + text: i18n.ts.unableToDelete, + }); + } + }); + } + }); } - function setAsUploadFolder() { defaultStore.set('uploadFolder', props.folder.id); } From 68aab48f4e531dde144a23184ceb05a8f0f1d8cb Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 20:07:23 +0900 Subject: [PATCH 322/501] fix --- .../src/components/MkAvatarDecoEditDialog.vue | 2 +- packages/frontend/src/components/MkDrive.vue | 4 +- .../src/components/MkNotifyButton.vue | 127 +++++++++--------- .../src/pages/settings/account-stats.vue | 2 +- .../src/pages/settings/notifications.vue | 4 +- .../src/scripts/get-drive-file-menu.ts | 4 +- 6 files changed, 71 insertions(+), 72 deletions(-) diff --git a/packages/frontend/src/components/MkAvatarDecoEditDialog.vue b/packages/frontend/src/components/MkAvatarDecoEditDialog.vue index 9ef5be1df2..a39bbc000b 100644 --- a/packages/frontend/src/components/MkAvatarDecoEditDialog.vue +++ b/packages/frontend/src/components/MkAvatarDecoEditDialog.vue @@ -81,7 +81,7 @@ function del() { text: i18n.t('deleteAreYouSure', { x: props.avatarDecoration?.name }), }).then(({ canceled }) => { if (canceled) return; - os.api('admin/avatar-decorations/delete', { id: props.avatarDecoration?.id }).then(() => { + misskeyApi('admin/avatar-decorations/delete', { id: props.avatarDecoration?.id }).then(() => { }); }); diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 35feb470ab..7136503a83 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -693,7 +693,7 @@ async function isSensitive(Files, isSensitive: boolean) { if (canceled) return; Files.forEach((file) => { - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: file.id, isSensitive, }); @@ -708,7 +708,7 @@ async function fileDelete(Files) { if (canceled) return; Files.forEach((file) => { - os.api('drive/files/delete', { + misskeyApi('drive/files/delete', { fileId: file.id, }); }); diff --git a/packages/frontend/src/components/MkNotifyButton.vue b/packages/frontend/src/components/MkNotifyButton.vue index fd9767846e..d6d3ad0c83 100644 --- a/packages/frontend/src/components/MkNotifyButton.vue +++ b/packages/frontend/src/components/MkNotifyButton.vue @@ -3,63 +3,63 @@ SPDX-FileCopyrightText: syuilo and other misskey contributors SPDX-License-Identifier: AGPL-3.0-only --> <template> - <button - class="_button" - :class="[$style.root,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' -,}]" v-if="isFollowing" - @click="onClick" - > - <span v-if="props.user.notify === 'none'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] "><i class="ti ti-bell"></i></span> - <span v-else-if="props.user.notify === 'normal'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"><i class="ti ti-bell-off"></i></span> - </button> +<button + v-if="isFollowing" + class="_button" :class="[$style.root,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' + ,}]" + @click="onClick" +> + <span v-if="props.user.notify === 'none'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }] "><i class="ti ti-bell"></i></span> + <span v-else-if="props.user.notify === 'normal'" :class="[{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]"><i class="ti ti-bell-off"></i></span> +</button> </template> <script lang="ts" setup> -import {computed, onBeforeUnmount, onMounted, ref, watch} from 'vue'; +import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; -import {useStream} from '@/stream.js'; -import {defaultStore} from "@/store.js"; +import { useStream } from '@/stream.js'; +import { defaultStore } from '@/store.js'; let gaming = ref(''); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gaming.value = 'light'; } else { - gaming.value = ''; + gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); const props = withDefaults(defineProps<{ user: Misskey.entities.UserDetailed, full?: boolean, large?: boolean, }>(), { - full: false, - large: false, + full: false, + large: false, }); let isFollowing = ref(props.user.isFollowing); @@ -67,47 +67,49 @@ let notify = ref(props.user.notify); const connection = useStream().useChannel('main'); if (props.user.isFollowing == null) { - os.api('users/show', { - userId: props.user.id, - }).then(onFollowChange); + misskeyApi('users/show', { + userId: props.user.id, + }).then(onFollowChange); } if (props.user.notify == null) { - os.api('users/show', { - userId: props.user.id, - }).then(onNotifyChange); + misskeyApi('users/show', { + userId: props.user.id, + }).then(onNotifyChange); } function onFollowChange(user: Misskey.entities.UserDetailed) { - if (user.id === props.user.id) { - isFollowing.value = user.isFollowing; - } + if (user.id === props.user.id) { + isFollowing.value = user.isFollowing; + } } -function onNotifyChange(user: Misskey.entities.UserDetailed) { - if (user.id === props.user.id) { - notify.value = user.notify; - console.log(props.user.notify) - } -} -async function onClick() { - try { - await os.apiWithDialog('following/update', { - userId: props.user.id, - notify: props.user.notify === 'normal' ? 'none' : 'normal', - }).then(() => { - props.user.notify = props.user.notify === 'normal' ? 'none' : 'normal'; - }); - }finally { - } +function onNotifyChange(user: Misskey.entities.UserDetailed) { + if (user.id === props.user.id) { + notify.value = user.notify; + console.log(props.user.notify); + } +} + +async function onClick() { + try { + await os.apiWithDialog('following/update', { + userId: props.user.id, + notify: props.user.notify === 'normal' ? 'none' : 'normal', + }).then(() => { + props.user.notify = props.user.notify === 'normal' ? 'none' : 'normal'; + }); + } finally { + + } } onMounted(() => { - connection.on('follow', onFollowChange); - connection.on('unfollow', onFollowChange); + connection.on('follow', onFollowChange); + connection.on('unfollow', onFollowChange); }); onBeforeUnmount(() => { - connection.dispose(); + connection.dispose(); }); </script> @@ -278,10 +280,8 @@ onBeforeUnmount(() => { } - } - .gamingDark { color: black; @@ -292,7 +292,6 @@ onBeforeUnmount(() => { } - @-webkit-keyframes AnimationLight { 0% { background-position: 0% 50% diff --git a/packages/frontend/src/pages/settings/account-stats.vue b/packages/frontend/src/pages/settings/account-stats.vue index 4e591c648a..658c12b558 100644 --- a/packages/frontend/src/pages/settings/account-stats.vue +++ b/packages/frontend/src/pages/settings/account-stats.vue @@ -101,7 +101,7 @@ import { definePageMetadata } from '@/scripts/page-metadata'; const stats = ref<any>({}); onMounted(() => { - os.api('i/stats', { + misskeyApi('i/stats', { userId: $i!.id, }).then(response => { stats.value = response; diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue index f939e1e42b..8709821595 100644 --- a/packages/frontend/src/pages/settings/notifications.vue +++ b/packages/frontend/src/pages/settings/notifications.vue @@ -79,11 +79,11 @@ const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadM const userLists = await misskeyApi('users/lists/list'); async function readAllUnreadNotes() { - await os.api('i/read-all-unread-notes'); + await misskeyApi('i/read-all-unread-notes'); } async function readAllNotifications() { - await os.api('notifications/mark-all-as-read'); + await misskeyApi('notifications/mark-all-as-read'); } async function updateReceiveConfig(type, value) { diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts index 286e7154cf..fb0fe807e0 100644 --- a/packages/frontend/src/scripts/get-drive-file-menu.ts +++ b/packages/frontend/src/scripts/get-drive-file-menu.ts @@ -82,14 +82,14 @@ async function MultideleteFile(files: Misskey.entities.DriveFile[] | null) { if (canceled) return; files.forEach((e)=>{ - os.api('drive/files/delete', { + misskeyApi('drive/files/delete', { fileId: e.id, }); }) } function isSensitive(files: Misskey.entities.DriveFile[] | null ,sensitive:boolean) { files.forEach((e)=>{ - os.api('drive/files/update', { + misskeyApi('drive/files/update', { fileId: e.id, isSensitive: sensitive, }).catch(err => { From bc58540e6218cd3e94f6ce202cd8d1ac0b05de7f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 20:12:48 +0900 Subject: [PATCH 323/501] fix --- .../src/pages/settings/account-stats.vue | 189 +++++++++--------- 1 file changed, 94 insertions(+), 95 deletions(-) diff --git a/packages/frontend/src/pages/settings/account-stats.vue b/packages/frontend/src/pages/settings/account-stats.vue index 658c12b558..b01a7dc54b 100644 --- a/packages/frontend/src/pages/settings/account-stats.vue +++ b/packages/frontend/src/pages/settings/account-stats.vue @@ -1,111 +1,110 @@ <template> - <div class="_gaps_m"> - <FormSection v-if="stats" first> - <template #label>{{ i18n.ts.statistics }}</template> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.notesCount }}</template> - <template #value>{{ number(stats.notesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.repliesCount }}</template> - <template #value>{{ number(stats.repliesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.renotesCount }}</template> - <template #value>{{ number(stats.renotesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.repliedCount }}</template> - <template #value>{{ number(stats.repliedCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.renotedCount }}</template> - <template #value>{{ number(stats.renotedCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.pollVotesCount }}</template> - <template #value>{{ number(stats.pollVotesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.pollVotedCount }}</template> - <template #value>{{ number(stats.pollVotedCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.sentReactionsCount }}</template> - <template #value>{{ number(stats.sentReactionsCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.receivedReactionsCount }}</template> - <template #value>{{ number(stats.receivedReactionsCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.noteFavoritesCount }}</template> - <template #value>{{ number(stats.noteFavoritesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.followingCount }}</template> - <template #value>{{ number(stats.followingCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.local }})</template> - <template #value>{{ number(stats.localFollowingCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.remote }})</template> - <template #value>{{ number(stats.remoteFollowingCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.followersCount }}</template> - <template #value>{{ number(stats.followersCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.local }})</template> - <template #value>{{ number(stats.localFollowersCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.remote }})</template> - <template #value>{{ number(stats.remoteFollowersCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.pageLikesCount }}</template> - <template #value>{{ number(stats.pageLikesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.pageLikedCount }}</template> - <template #value>{{ number(stats.pageLikedCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.driveFilesCount }}</template> - <template #value>{{ number(stats.driveFilesCount) }}</template> - </MkKeyValue> - <MkKeyValue oneline style="margin: 1em 0;"> - <template #key>{{ i18n.ts.driveUsage }}</template> - <template #value>{{ bytes(stats.driveUsage) }}</template> - </MkKeyValue> - </FormSection> - - </div> +<div class="_gaps_m"> + <FormSection v-if="stats" first> + <template #label>{{ i18n.ts.statistics }}</template> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.notesCount }}</template> + <template #value>{{ number(stats.notesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.repliesCount }}</template> + <template #value>{{ number(stats.repliesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.renotesCount }}</template> + <template #value>{{ number(stats.renotesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.repliedCount }}</template> + <template #value>{{ number(stats.repliedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.renotedCount }}</template> + <template #value>{{ number(stats.renotedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pollVotesCount }}</template> + <template #value>{{ number(stats.pollVotesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pollVotedCount }}</template> + <template #value>{{ number(stats.pollVotedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.sentReactionsCount }}</template> + <template #value>{{ number(stats.sentReactionsCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.receivedReactionsCount }}</template> + <template #value>{{ number(stats.receivedReactionsCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.noteFavoritesCount }}</template> + <template #value>{{ number(stats.noteFavoritesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followingCount }}</template> + <template #value>{{ number(stats.followingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.local }})</template> + <template #value>{{ number(stats.localFollowingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followingCount }} ({{ i18n.ts.remote }})</template> + <template #value>{{ number(stats.remoteFollowingCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followersCount }}</template> + <template #value>{{ number(stats.followersCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.local }})</template> + <template #value>{{ number(stats.localFollowersCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.followersCount }} ({{ i18n.ts.remote }})</template> + <template #value>{{ number(stats.remoteFollowersCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pageLikesCount }}</template> + <template #value>{{ number(stats.pageLikesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.pageLikedCount }}</template> + <template #value>{{ number(stats.pageLikedCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.driveFilesCount }}</template> + <template #value>{{ number(stats.driveFilesCount) }}</template> + </MkKeyValue> + <MkKeyValue oneline style="margin: 1em 0;"> + <template #key>{{ i18n.ts.driveUsage }}</template> + <template #value>{{ bytes(stats.driveUsage) }}</template> + </MkKeyValue> + </FormSection> +</div> </template> <script lang="ts" setup> -import { onMounted, ref ,computed } from 'vue'; +import { onMounted, ref, computed } from 'vue'; import FormSection from '@/components/form/section.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; -import * as os from '@/os'; import number from '@/filters/number'; import bytes from '@/filters/bytes'; import { $i } from '@/account'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const stats = ref<any>({}); onMounted(() => { - misskeyApi('i/stats', { - userId: $i!.id, - }).then(response => { - stats.value = response; - }); + misskeyApi('i/stats', { + userId: $i!.id, + }).then(response => { + stats.value = response; + }); }); const headerActions = computed(() => []); @@ -113,7 +112,7 @@ const headerActions = computed(() => []); const headerTabs = computed(() => []); definePageMetadata({ - title: i18n.ts.accountInfo, - icon: 'ti ti-info-circle', + title: i18n.ts.accountInfo, + icon: 'ti ti-info-circle', }); </script> From cd8b62d7ebdf4868719feafe751cb7b8f0d5d96d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 6 Jan 2024 23:39:16 +0900 Subject: [PATCH 324/501] fix --- packages/frontend/src/pages/about.emojis.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index 4d48f92cc6..a5b520bf3e 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -56,7 +56,7 @@ import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; - +import * as os from '@/os.js'; let tab = ref('emojis'); const headerActions = computed(() => []); From 56066ae8363fe8038275454e8b70daf01ad8f038 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 8 Jan 2024 00:53:52 +0900 Subject: [PATCH 325/501] fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3599b75727..91c482a3f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.12.2-PrisMisskey.2", + "version": "2023.12.2-PrisMisskey.3", "codename": "nasubi", "repository": { "type": "git", From cb3983b741546f86f37134fa182c19f1926eedfc Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 8 Jan 2024 05:52:35 +0900 Subject: [PATCH 326/501] fix --- packages/frontend/src/components/MkButton.vue | 161 ++++++----- .../src/ui/_common_/navbar-for-mobile.vue | 270 ++++++++++-------- 2 files changed, 227 insertions(+), 204 deletions(-) diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue index 7818d011e8..772fd9a6b3 100644 --- a/packages/frontend/src/components/MkButton.vue +++ b/packages/frontend/src/components/MkButton.vue @@ -4,69 +4,69 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <button - v-if="!link" - ref="el" class="_button" - :class="[ - $style.root, - { - [$style.inline]: inline, - [$style.primary]: primary, - [$style.gradate]: gradate, - [$style.danger]: danger, - [$style.rounded]: rounded, - [$style.full]: full, - [$style.small]: small, - [$style.large]: large, - [$style.transparent]: transparent, - [$style.asLike]: asLike, - [$style.gamingDark]: gaming === 'dark', - [$style.gamingLight]: gaming === 'light', - } - ]" - :type="type" - :name="name" - :value="value" - @click="emit('click', $event)" - @mousedown="onMousedown" - > - <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> - <div :class="$style.content"> - <slot></slot> - </div> - </button> - <MkA - v-else class="_button" - :class="[ - $style.root, - { - [$style.inline]: inline, - [$style.primary]: primary, - [$style.gradate]: gradate, - [$style.danger]: danger, - [$style.rounded]: rounded, - [$style.full]: full, - [$style.small]: small, - [$style.large]: large, - [$style.transparent]: transparent, - [$style.asLike]: asLike, - [$style.gamingDark]: gaming === 'dark', - [$style.gamingLight]: gaming === 'light', - } - ]" - :to="to" - @mousedown="onMousedown" - > - <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> - <div :class="$style.content"> - <slot></slot> - </div> - </MkA> +<button + v-if="!link" + ref="el" class="_button" + :class="[ + $style.root, + { + [$style.inline]: inline, + [$style.primary]: primary, + [$style.gradate]: gradate, + [$style.danger]: danger, + [$style.rounded]: rounded, + [$style.full]: full, + [$style.small]: small, + [$style.large]: large, + [$style.transparent]: transparent, + [$style.asLike]: asLike, + [$style.gamingDark]: gaming === 'dark', + [$style.gamingLight]: gaming === 'light', + } + ]" + :type="type" + :name="name" + :value="value" + @click="emit('click', $event)" + @mousedown="onMousedown" +> + <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> + <div :class="$style.content"> + <slot></slot> + </div> +</button> +<MkA + v-else class="_button" + :class="[ + $style.root, + { + [$style.inline]: inline, + [$style.primary]: primary, + [$style.gradate]: gradate, + [$style.danger]: danger, + [$style.rounded]: rounded, + [$style.full]: full, + [$style.small]: small, + [$style.large]: large, + [$style.transparent]: transparent, + [$style.asLike]: asLike, + [$style.gamingDark]: gaming === 'dark', + [$style.gamingLight]: gaming === 'light', + } + ]" + :to="to" + @mousedown="onMousedown" +> + <div ref="ripples" :class="$style.ripples" :data-children-class="$style.ripple"></div> + <div :class="$style.content"> + <slot></slot> + </div> +</MkA> </template> <script lang="ts" setup> import { nextTick, onMounted, shallowRef, computed, ref, watch } from 'vue'; -import {defaultStore} from "@/store.js"; +import { defaultStore } from '@/store.js'; const props = defineProps<{ type?: 'button' | 'submit' | 'reset'; @@ -96,32 +96,32 @@ let gaming = ref(''); // 0-off , 1-dark , 2-light // gaming.valueに新しい値を代入する if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'light'; -}else{ - gaming.value = ''; + gaming.value = 'light'; +} else { + gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary|| darkMode.value && gamingMode.value && props.gradate) { - gaming.value = 'light'; - }else{ - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); watch(gamingMode, () => { - if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { - gaming.value = 'light'; - }else{ - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value && props.primary || darkMode.value && gamingMode.value && props.gradate ) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); const emit = defineEmits<{ (ev: 'click', payload: MouseEvent): void; }>(); @@ -257,6 +257,9 @@ function onMousedown(evt: MouseEvent): void { -moz-animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; animation: AnimationLight var(--gamingspeed) cubic-bezier(0, 0.2, 0.90, 1) infinite ; } + &:hover{ + background: var(--accent); + } } &.gamingDark { diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 10419195e7..6e23c73b42 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -4,167 +4,188 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div :class="$style.root"> - <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> - <button class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> - </button> - </div> - <div :class="$style.middle"> - <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" - :activeClass="$style.active" to="/" exact> - <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ - i18n.ts.timeline - }}</span> - </MkA> - <template v-for="item in menu"> - <div v-if="item === '-'" :class="$style.divider"></div> - <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" - v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" - :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" - :activeClass="$style.active" :to="navbarItemDef[item].to" - v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> - <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span - :class="$style.itemText">{{ navbarItemDef[item].title }}</span> - <span v-if="navbarItemDef[item].indicated" - :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> +<div :class="$style.root"> + <div :class="$style.top"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ bannerUrl })` }"></div> + <button class="_button" :class="$style.instance" @click="openInstanceMenu"> + <img :src="iconUrl" alt="" :class="$style.instanceIcon"/> + </button> + </div> + <div :class="$style.middle"> + <MkA + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/" exact + > + <i :class="$style.itemIcon" class="ti ti-home ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.timeline + }}</span> + </MkA> + <template v-for="item in menu"> + <div v-if="item === '-'" :class="$style.divider"></div> + <component + :is="navbarItemDef[item].to ? 'MkA' : 'button'" + v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" class="_button" + :class="[$style.item, { [$style.active]: gaming === '' && navbarItemDef[item].active, [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" :to="navbarItemDef[item].to" + v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}" + > + <i class="ti-fw" :class="[$style.itemIcon, navbarItemDef[item].icon]"></i><span + :class="$style.itemText" + >{{ navbarItemDef[item].title }}</span> + <span + v-if="navbarItemDef[item].indicated" + :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]" + > <span v-if="navbarItemDef[item].indicateValue && indicatorCounterToggle" class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span><i - v-else class="_indicatorCircle"></i></span> - </component> - </template> - <div :class="$style.divider"></div> - <MkA v-if="$i.isAdmin || $i.isModerator" - :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" - :activeClass="$style.active" to="/admin"> - <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span - :class="$style.itemText">{{ i18n.ts.controlPanel }}</span> - </MkA> - <button - :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" - class="_button" @click="more"> - <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ - i18n.ts.more - }}</span> - <span v-if="otherMenuItemIndicated" - :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i - class="_indicatorCircle"></i></span> - </button> - <MkA :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" - :activeClass="$style.active" to="/settings"> - <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ - i18n.ts.settings - }}</span> - </MkA> - </div> - <div :class="$style.bottom"> - <button class="_button" - :class="[$style.post ,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light',}]" - data-cy-open-post-form @click="os.post"> - <i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ - i18n.ts.note - }}</span> - </button> - <button class="_button" :class="$style.account" @click="openAccountMenu"> - <MkAvatar :user="$i" :class="$style.avatar"/> - <MkAcct :class="$style.acct" class="_nowrap" :user="$i"/> - </button> - </div> - </div> + v-else class="_indicatorCircle" + ></i></span> + </component> + </template> + <div :class="$style.divider"></div> + <MkA + v-if="$i.isAdmin || $i.isModerator" + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/admin" + > + <i :class="$style.itemIcon" class="ti ti-dashboard ti-fw"></i><span + :class="$style.itemText" + >{{ i18n.ts.controlPanel }}</span> + </MkA> + <button + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + class="_button" @click="more" + > + <i :class="$style.itemIcon" class="ti ti-grid-dots ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.more + }}</span> + <span + v-if="otherMenuItemIndicated" + :class="[$style.itemIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]" + ><i + class="_indicatorCircle" + ></i></span> + </button> + <MkA + :class="[$style.item, { [$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }]" + :activeClass="$style.active" to="/settings" + > + <i :class="$style.itemIcon" class="ti ti-settings ti-fw"></i><span :class="$style.itemText">{{ + i18n.ts.settings + }}</span> + </MkA> + </div> + <div :class="$style.bottom"> + <button + class="_button" + :class="[$style.post ,{[$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light',}]" + data-cy-open-post-form @click="os.post" + > + <i :class="$style.postIcon" class="ti ti-pencil ti-fw"></i><span style="position: relative;">{{ + i18n.ts.note + }}</span> + </button> + <button class="_button" :class="$style.account" @click="openAccountMenu"> + <MkAvatar :user="$i" :class="$style.avatar"/> + <MkAcct :class="$style.acct" class="_nowrap" :user="$i"/> + </button> + </div> +</div> </template> <script lang="ts" setup> -import {computed, defineAsyncComponent, ref, toRef, watch} from 'vue'; -import {openInstanceMenu} from './common.js'; +import { computed, defineAsyncComponent, ref, toRef, watch } from 'vue'; +import { openInstanceMenu } from './common.js'; import * as os from '@/os'; -import {navbarItemDef} from '@/navbar.js'; -import {$i, openAccountMenu as openAccountMenu_} from '@/account'; -import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from '@/store'; -import {i18n} from '@/i18n'; -import {instance} from '@/instance'; +import { navbarItemDef } from '@/navbar.js'; +import { $i, openAccountMenu as openAccountMenu_ } from '@/account'; +import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store'; +import { i18n } from '@/i18n'; +let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); + function hexToRgb(hex) { - // 16進数のカラーコードから "#" を除去 - hex = hex.replace(/^#/, ''); + // 16進数のカラーコードから "#" を除去 + hex = hex.replace(/^#/, ''); - // 16進数をRGBに変換 - const r = parseInt(hex.substring(0, 2), 16); - const g = parseInt(hex.substring(2, 4), 16); - const b = parseInt(hex.substring(4, 6), 16); + // 16進数をRGBに変換 + const r = parseInt(hex.substring(0, 2), 16); + const g = parseInt(hex.substring(2, 4), 16); + const b = parseInt(hex.substring(4, 6), 16); - return `${r},${g},${b}`; + return `${r},${g},${b}`; } + const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); document.documentElement.style.setProperty('--homeColor', hexToRgb(defaultStore.state.homeColor)); -document.documentElement.style.setProperty("--followerColor",hexToRgb(defaultStore.state.followerColor)); -document.documentElement.style.setProperty("--specifiedColor",hexToRgb(defaultStore.state.specifiedColor)) -document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed+'s'); +document.documentElement.style.setProperty('--followerColor', hexToRgb(defaultStore.state.followerColor)); +document.documentElement.style.setProperty('--specifiedColor', hexToRgb(defaultStore.state.specifiedColor)); +document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed + 's'); -let gaming = ref() +let gaming = ref(); if (darkMode.value) { - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; } else { - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; } watch(darkMode, () => { - if (darkMode.value) { - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; - } else { - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; - } -}) + if (darkMode.value) { + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; + } else { + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; + } +}); // gaming.valueに新しい値を代入する if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gaming.value = 'light'; } else { - gaming.value = ''; + gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); const menu = toRef(defaultStore.state, 'menu'); const otherMenuItemIndicated = computed(() => { - for (const def in navbarItemDef) { - if (menu.value.includes(def)) continue; - if (navbarItemDef[def].indicated) return true; - } - return false; + for (const def in navbarItemDef) { + if (menu.value.includes(def)) continue; + if (navbarItemDef[def].indicated) return true; + } + return false; }); function openAccountMenu(ev: MouseEvent) { - openAccountMenu_({ - withExtraOperation: true, - }, ev); + openAccountMenu_({ + withExtraOperation: true, + }, ev); } function more() { - os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, {}, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), {}, {}, 'closed'); } </script> @@ -306,7 +327,6 @@ function more() { } } - } .postIcon { From be2626ea0d79009e7eaeed0f25f1b2eb61f8835d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 10 Jan 2024 00:49:46 +0900 Subject: [PATCH 327/501] fix --- .../src/components/MkEmojiEditDialog.vue | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index b41b62a8ae..61f1cbd1f3 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -68,11 +68,9 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch> <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkSwitch v-model="isNotifyIsHome"> - {{ i18n.ts.isNotifyIsHome }} - </MkSwitch> - - + <MkSwitch v-model="isNotifyIsHome"> + {{ i18n.ts.isNotifyIsHome }} + </MkSwitch> </div> </MkSpacer> <div :class="$style.footer"> @@ -87,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {computed, ref, watch} from 'vue'; +import { computed, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import { DriveFile } from 'misskey-js/built/entities.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; @@ -166,11 +164,12 @@ async function done() { isSensitive: isSensitive.value, localOnly: localOnly.value, roleIdsThatCanBeUsedThisEmojiAsReaction: rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id), - }; + isNotifyIsHome: isNotifyIsHome.value, + }; - if (file.value) { - params.fileId = file.value.id; - } + if (file.value) { + params.fileId = file.value.id; + } if (props.emoji) { if (isRequest.value) { await os.apiWithDialog('admin/emoji/update-request', { From 5320e6db77d3da5b425fe68e0d02c9688d9eda4e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 11 Jan 2024 07:03:14 +0900 Subject: [PATCH 328/501] =?UTF-8?q?Feat:=20=E3=82=A2=E3=83=90=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=83=87=E3=82=B3=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E6=A4=9C=E7=B4=A2=E6=A9=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/settings/avatar-decoration.vue | 65 ++++++++++++++++++- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 02d5dac8c2..df479cae51 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -28,7 +28,16 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton danger @click="detachAllDecorations">{{ i18n.ts.detachAll }}</MkButton> </div> - + <MkInput v-model="q" :placeholder="i18n.ts.search"/> + <div v-if="searchResult.length > 0" :class="$style.decorations"> + <span> {{ i18n.ts.searchResult }}</span><br> + <XDecoration + v-for="avatarDecoration in searchResult" + :key="avatarDecoration.name" + :decoration="avatarDecoration" + @click="openDecoration(avatarDecoration)" + /> + </div> <div v-for="category in categories"> <MkFoldableSection :defaultOpen="false"> <template #header> {{ (category !== '') ? category : i18n.ts.other }}</template> @@ -51,7 +60,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, defineAsyncComponent, computed } from 'vue'; +import { ref, defineAsyncComponent, computed, watch } from 'vue'; import * as Misskey from 'misskey-js'; import XDecoration from './avatar-decoration.decoration.vue'; import MkButton from '@/components/MkButton.vue'; @@ -62,12 +71,62 @@ import { signinRequired } from '@/account.js'; import MkInfo from '@/components/MkInfo.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import MkInput from '@/components/MkInput.vue'; const $i = signinRequired(); - +const searchResult = ref([]); const loading = ref(true); const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse & { category:string }>([]); +const q = ref<string>(''); +watch(() => q.value, () => { + const searchCustom = () => { + const max = 100; + const matches = new Set(); + const decos = avatarDecorations.value; + const exactMatch = decos.find(avatarDecoration => avatarDecoration.name === q.value); + if (exactMatch) matches.add(exactMatch); + if (decos.includes(' ')) { // AND検索 + const keywords = q.value.split(' '); + + // 名前にキーワードが含まれている + for (const deco of decos) { + if (keywords.every(keyword => deco.name.includes(keyword))) { + matches.add(deco); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; + + // 名前またはエイリアスにキーワードが含まれている + for (const deco of decos) { + if (keywords.every(keyword => deco.name.includes(keyword))) { + matches.add(deco); + if (matches.size >= max) break; + } + } + } else { + for (const deco of decos) { + if (deco.name.startsWith(q.value)) { + matches.add(deco); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; + + for (const deco of decos) { + if (deco.name.includes(q.value)) { + matches.add(deco); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; + } + + return matches; + }; + searchResult.value = Array.from(searchCustom()); +}); misskeyApi('get-avatar-decorations').then(_avatarDecorations => { avatarDecorations.value = _avatarDecorations; loading.value = false; From 158ece67f5b36a8e79640c225adec2c04a5794ef Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 11 Jan 2024 21:52:04 +0900 Subject: [PATCH 329/501] 2023.12.2-PrisMisskey.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 91c482a3f0..a05afa02d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.12.2-PrisMisskey.3", + "version": "2023.12.2-PrisMisskey.4", "codename": "nasubi", "repository": { "type": "git", From 693efb99a0b60435c73ef61133338ae5fd8c1d88 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 14 Jan 2024 03:39:43 +0900 Subject: [PATCH 330/501] list multiple select --- .../src/components/MkUserSelectDialog.vue | 15 ++++++++----- packages/frontend/src/os.ts | 19 +++++++++++++--- packages/frontend/src/pages/my-lists/list.vue | 22 +++++++++++++------ 3 files changed, 41 insertions(+), 15 deletions(-) diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index f4aa06950d..90f2194a97 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkModalWindow ref="dialogEl" :withOkButton="true" - :okButtonDisabled="selected == null" + :okButtonDisabled="(!selected && multipleSelected.length < 1)" @click="cancel()" @close="cancel()" @ok="ok()" @@ -27,9 +27,10 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </FormSplit> </div> + <div v-if="username != '' || host != ''" :class="[$style.result, { [$style.hit]: users.length > 0 }]"> <div v-if="users.length > 0" :class="$style.users"> - <div v-for="user in users" :key="user.id" class="_button" :class="[$style.user, { [$style.selected]: selected && selected.id === user.id }]" @click="selected = user" @dblclick="ok()"> + <div v-for="user in users" :key="user.id" class="_button" :class="[$style.user, { [$style.selected]: selected && selected.id === user.id || multipleSelected.includes(user)}]" @click="multiple ? (multipleSelected.includes(user) ? multipleSelected.splice(multipleSelected.indexOf(user), 1) : multipleSelected.push(user)) : selected = user" @dblclick="ok()"> <MkAvatar :user="user" :class="$style.avatar" indicator/> <div :class="$style.userBody"> <MkUserName :user="user" :class="$style.userName"/> @@ -43,7 +44,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-if="username == '' && host == ''" :class="$style.recent"> <div :class="$style.users"> - <div v-for="user in recentUsers" :key="user.id" class="_button" :class="[$style.user, { [$style.selected]: selected && selected.id === user.id }]" @click="selected = user" @dblclick="ok()"> + <div v-for="user in recentUsers" :key="user.id" class="_button" :class="[$style.user, { [$style.selected]: selected && selected.id === user.id || multipleSelected.includes(user) }]" @click="multiple ? (multipleSelected.includes(user) ? multipleSelected.splice(multipleSelected.indexOf(user), 1) : multipleSelected.push(user)) : selected = user" @dblclick="ok()"> <MkAvatar :user="user" :class="$style.avatar" indicator/> <div :class="$style.userBody"> <MkUserName :user="user" :class="$style.userName"/> @@ -67,6 +68,7 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { hostname } from '@/config.js'; +import { multipleSelectUser } from '@/os.js'; const emit = defineEmits<{ (ev: 'ok', selected: Misskey.entities.UserDetailed): void; @@ -76,6 +78,7 @@ const emit = defineEmits<{ const props = defineProps<{ includeSelf?: boolean; + multiple?: boolean; }>(); const username = ref(''); @@ -83,6 +86,7 @@ const host = ref(''); const users = ref<Misskey.entities.UserDetailed[]>([]); const recentUsers = ref<Misskey.entities.UserDetailed[]>([]); const selected = ref<Misskey.entities.UserDetailed | null>(null); +const multipleSelected = ref<Misskey.entities.UserDetailed[]>([]); const dialogEl = ref(); const search = () => { @@ -101,11 +105,12 @@ const search = () => { }; const ok = () => { - if (selected.value == null) return; - emit('ok', selected.value); + if ((!selected.value && multipleSelected.value.length < 1)) return; + emit('ok', selected.value ?? multipleSelected.value); dialogEl.value.close(); // 最近使ったユーザー更新 + if (multipleSelected.value.length < 0) return; let recents = defaultStore.state.recentlyUsedUsers; recents = recents.filter(x => x !== selected.value.id); recents.unshift(selected.value.id); diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 11ab7cc0a9..a5df3a6ff0 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -24,7 +24,7 @@ import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; -import MkSwitch from "@/components/MkSwitch.vue"; +import MkSwitch from '@/components/MkSwitch.vue'; export const openingWindowsCount = ref(0); @@ -220,7 +220,7 @@ export function switch1(props: { text?: string | null; okText?: string; cancelText?: string; -}): Promise<{ canceled: boolean , result: boolean }> { +}): Promise<{ canceled: boolean, result: boolean }> { return new Promise((resolve, reject) => { popup(MkDialog, { ...props, @@ -437,7 +437,20 @@ export function form(title, form) { }); } -export async function selectUser(opts: { includeSelf?: boolean } = {}) { +export async function selectUser(opts: { includeSelf?: boolean, multiple?: boolean, } = {}) { + return new Promise((resolve, reject) => { + popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { + includeSelf: opts.includeSelf, + multiple: opts.multiple, + }, { + ok: user => { + resolve(user); + }, + }, 'closed'); + }); +} + +export async function multipleSelectUser(opts: { includeSelf?: boolean } = {}) { return new Promise((resolve, reject) => { popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { includeSelf: opts.includeSelf, diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 85775a2fdd..326d2b75e2 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -104,14 +104,22 @@ function fetchList() { } function addUser() { - os.selectUser().then(user => { + os.selectUser( { multiple: true }).then(user => { if (!list.value) return; - os.apiWithDialog('users/lists/push', { - listId: list.value.id, - userId: user.id, - }).then(() => { - paginationEl.value.reload(); - }); + if (Array.isArray(user)) { + user.forEach(u => { + misskeyApi('users/lists/push', { + listId: list.value.id, + userId: u.id, + }); + }); + } else if (typeof user === 'string') { + os.apiWithDialog('users/lists/push', { + listId: list.value.id, + userId: user.id, + }); + } + paginationEl.value.reload(); }); } From 6fa319afc65cfaeaeb1a7ad4e53ef2c544277e0c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 14 Jan 2024 13:54:37 +0900 Subject: [PATCH 331/501] Feat: emoji picker profile --- .config/example.yml | 8 +- locales/en-US.yml | 2 + locales/index.d.ts | 3 + locales/ja-JP.yml | 3 + package.json | 2 +- packages/backend/src/core/RoleService.ts | 4 +- .../frontend/src/components/MkEmojiPicker.vue | 650 +++++++++--------- .../src/components/MkUserSelectDialog.vue | 2 - packages/frontend/src/const.ts | 1 + packages/frontend/src/pages/admin/roles.vue | 7 + .../src/pages/settings/emoji-picker.vue | 45 +- packages/frontend/src/store.ts | 120 +++- 12 files changed, 502 insertions(+), 345 deletions(-) diff --git a/.config/example.yml b/.config/example.yml index df423c2c83..49f45efb7e 100644 --- a/.config/example.yml +++ b/.config/example.yml @@ -118,7 +118,7 @@ redis: # ┌───────────────────────────┐ #───┘ MeiliSearch configuration └───────────────────────────── -# You can set scope to local (default value) or global +# You can set scope to local (default value) or global # (include notes from remote). #meilisearch: @@ -180,7 +180,9 @@ id: 'aidx' #outgoingAddressFamily: ipv4 # Proxy for HTTP/HTTPS -#proxy: http://127.0.0.1:3128 +# + + proxyBypassHosts: - api.deepl.com @@ -214,7 +216,7 @@ proxyRemoteFiles: true signToActivityPubGet: true # For security reasons, uploading attachments from the intranet is prohibited, -# but exceptions can be made from the following settings. Default value is "undefined". +# but exceptions can be made from the following settings. Default value is "undefined". # Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)). #allowedPrivateNetworks: [ # '127.0.0.1/32' diff --git a/locales/en-US.yml b/locales/en-US.yml index dc988ccee6..34b17f9abc 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -9,6 +9,8 @@ notifications: "Notifications" username: "Username" password: "Password" forgotPassword: "Forgot password" +setDefaultProfileConfirm: "Do you want to make this profile the default?" +emojiPickerProfile: "Emoji picker profile" fetchingAsApObject: "Fetching from the Fediverse..." ok: "OK" gotIt: "Got it!" diff --git a/locales/index.d.ts b/locales/index.d.ts index 42c658403f..b807edcd9e 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -14,6 +14,8 @@ export interface Locale { "forgotPassword": string; "fetchingAsApObject": string; "ok": string; + "setDefaultProfileConfirm": string; + "emojiPickerProfile": string; "notificationIndicator": string; "hanntenn": string; "hanntennInfo": string; @@ -1767,6 +1769,7 @@ export interface Locale { }; "_options": { "gtlAvailable": string; + "emojiPickerProfileLimit": string; "ltlAvailable": string; "canPublicNote": string; "canEditNote": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 508d4e655a..78d90976cc 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -11,6 +11,8 @@ password: "パスワード" forgotPassword: "パスワードを忘れた" fetchingAsApObject: "連合に照会中" ok: "OK" +setDefaultProfileConfirm: "このプロファイルをデフォルトにしますか?" +emojiPickerProfile: "絵文字ピッカーのプロファイル" notificationIndicator: "通知のインジケーターの数字を表示する" hanntenn: "アイコンとバナーを反転させる" hanntennInfo: "ダークだったらライトのアイコンに、ライトだったらダークのアイコンに。" @@ -1673,6 +1675,7 @@ _role: high: "高" _options: gtlAvailable: "グローバルタイムラインの閲覧" + emojiPickerProfileLimit: "絵文字ピッカーのプロファイルの上限数(最大5)" ltlAvailable: "ローカルタイムラインの閲覧" canPublicNote: "パブリック投稿の許可" canEditNote: "ノートの編集" diff --git a/package.json b/package.json index a05afa02d7..4d669abd12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.12.2-PrisMisskey.4", + "version": "2023.12.2-PrisMisskey.5", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index 64cb12d4f5..de214debeb 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -59,6 +59,7 @@ export type RolePolicies = { userEachUserListsLimit: number; rateLimitFactor: number; avatarDecorationLimit: number; + emojiPickerProfileLimit: number; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -74,7 +75,6 @@ export const DEFAULT_POLICIES: RolePolicies = { canManageCustomEmojis: false, canRequestCustomEmojis: false, canManageAvatarDecorations: false, - canRequestCustomEmojis: false, canSearchNotes: false, canUseTranslator: true, canHideAds: false, @@ -90,6 +90,7 @@ export const DEFAULT_POLICIES: RolePolicies = { userEachUserListsLimit: 50, rateLimitFactor: 1, avatarDecorationLimit: 1, + emojiPickerProfileLimit: 2, }; @Injectable() @@ -355,6 +356,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { userEachUserListsLimit: calc('userEachUserListsLimit', vs => Math.max(...vs)), rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)), avatarDecorationLimit: calc('avatarDecorationLimit', vs => Math.max(...vs)), + emojiPickerProfileLimit: calc('emojiPickerProfileLimit', vs => Math.max(...vs)), }; } diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 43ce3ed7b9..a58c51e0b0 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -4,98 +4,103 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> - <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> - <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> - <div ref="emojisEl" class="emojis" tabindex="-1"> - <section class="result"> - <div v-if="searchResultCustom.length > 0" class="body"> - <button - v-for="emoji in searchResultCustom" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji class="emoji" :name="emoji.name"/> - </button> - </div> - <div v-if="searchResultUnicode.length > 0" class="body"> - <button - v-for="emoji in searchResultUnicode" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkEmoji class="emoji" :emoji="emoji.char"/> - </button> - </div> - </section> +<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> + <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> + <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> + <div ref="emojisEl" class="emojis" tabindex="-1"> + <section class="result"> + <div v-if="searchResultCustom.length > 0" class="body"> + <button + v-for="emoji in searchResultCustom" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji class="emoji" :name="emoji.name"/> + </button> + </div> + <div v-if="searchResultUnicode.length > 0" class="body"> + <button + v-for="emoji in searchResultUnicode" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkEmoji class="emoji" :emoji="emoji.char"/> + </button> + </div> + </section> - <div v-if="tab === 'index'" class="group index"> - <section v-if="showPinned && pinned.length > 0"> - <div class="body"> - <button - v-for="emoji in pinned" - :key="emoji" - :data-emoji="emoji" - class="_button item" - tabindex="0" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> - </section> + <div v-if="tab === 'index'" class="group index"> + <section v-if="showPinned"> + <div style="display: flex; "> + <div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)"> + {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} + </div> + </div> + <div class="body"> + <button + v-for="emoji in pinnedEmojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + tabindex="0" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> - <section> - <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> - <div class="body"> - <button - v-for="emoji in recentlyUsedEmojis" - :key="emoji" - class="_button item" - :data-emoji="emoji" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> - </section> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> - <XSection - v-for="child in customEmojiFolderRoot.children" - :key="`custom:${child.value}`" - :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" - :hasChildSection="child.children.length !== 0" - :customEmojiTree="child.children" - @chosen="chosen" - > - {{ child.value || i18n.ts.other }} - </XSection> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.emoji }}</header> - <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> - </div> - </div> - <div class="tabs"> - <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> - </div> - </div> + <section> + <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> + <div class="body"> + <button + v-for="emoji in recentlyUsedEmojis" + :key="emoji" + class="_button item" + :data-emoji="emoji" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> + <XSection + v-for="child in customEmojiFolderRoot.children" + :key="`custom:${child.value}`" + :initialShown="false" + :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" + :hasChildSection="child.children.length !== 0" + :customEmojiTree="child.children" + @chosen="chosen" + > + {{ child.value || i18n.ts.other }} + </XSection> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.emoji }}</header> + <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> + </div> + </div> + <div class="tabs"> + <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> + </div> +</div> </template> <script lang="ts" setup> @@ -103,12 +108,12 @@ import { ref, shallowRef, computed, watch, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; import XSection from '@/components/MkEmojiPicker.section.vue'; import { - emojilist, - emojiCharByCategory, - UnicodeEmojiDef, - unicodeEmojiCategories as categories, - getEmojiName, - CustomEmojiFolderTree, + emojilist, + emojiCharByCategory, + UnicodeEmojiDef, + unicodeEmojiCategories as categories, + getEmojiName, + CustomEmojiFolderTree, } from '@/scripts/emojilist.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; import * as os from '@/os.js'; @@ -117,8 +122,10 @@ import { deviceKind } from '@/scripts/device-kind.js'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js'; -import { $i } from '@/account.js'; - +import { signinRequired } from '@/account.js'; +import MkButton from '@/components/MkButton.vue'; +import { deepClone } from '@/scripts/clone.js'; +const $i = signinRequired(); const props = withDefaults(defineProps<{ showPinned?: boolean; pinnedEmojis?: string[]; @@ -127,21 +134,21 @@ const props = withDefaults(defineProps<{ asWindow?: boolean; asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう }>(), { - showPinned: true, + showPinned: true, }); const emit = defineEmits<{ (ev: 'chosen', v: string): void; }>(); - +const profileMax = $i.policies.emojiPickerProfileLimit; const searchEl = shallowRef<HTMLInputElement>(); const emojisEl = shallowRef<HTMLDivElement>(); const { - emojiPickerScale, - emojiPickerWidth, - emojiPickerHeight, - recentlyUsedEmojis, + emojiPickerScale, + emojiPickerWidth, + emojiPickerHeight, + recentlyUsedEmojis, } = defaultStore.reactiveState; const pinned = computed(() => props.pinnedEmojis); @@ -152,81 +159,80 @@ const q = ref<string>(''); const searchResultCustom = ref<Misskey.entities.EmojiSimple[]>([]); const searchResultUnicode = ref<UnicodeEmojiDef[]>([]); const tab = ref<'index' | 'custom' | 'unicode' | 'tags'>('index'); - +const pinnedEmojis = ref(pinned.value); const customEmojiFolderRoot: CustomEmojiFolderTree = { value: '', category: '', children: [] }; function parseAndMergeCategories(input: string, root: CustomEmojiFolderTree): CustomEmojiFolderTree { - const parts = input.split('/').map(p => p.trim()); // スラッシュで区切って配列にしてる - let currentNode: CustomEmojiFolderTree = root; // currentNode は root - let includesPart = customEmojis.value.some(emoji => emoji.category !== null && emoji.category.includes(parts[0]+'/')) ; - console.log(includesPart) - if (parts.length === 1 && parts[0] !== '' && includesPart) { // parts が 1 つで空じゃなかったら - parts.push(parts[0]) // parts に parts[0] を追加 (test category だったら test/test category になる) - } + const parts = input.split('/').map(p => p.trim()); // スラッシュで区切って配列にしてる + let currentNode: CustomEmojiFolderTree = root; // currentNode は root + let includesPart = customEmojis.value.some(emoji => emoji.category !== null && emoji.category.includes(parts[0] + '/')); + if (parts.length === 1 && parts[0] !== '' && includesPart) { // parts が 1 つで空じゃなかったら + parts.push(parts[0]); // parts に parts[0] を追加 (test category だったら test/test category になる) + } - for (const part of parts) { // parts を順番に見ていく - let existingNode = currentNode.children.find((node) => node.value === part); // currentNode の children から part と同じ value を持つ node を探す + for (const part of parts) { // parts を順番に見ていく + let existingNode = currentNode.children.find((node) => node.value === part); // currentNode の children から part と同じ value を持つ node を探す - if (!existingNode) { // なかったら - const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] }; // 新しい node を作る + if (!existingNode) { // なかったら + const newNode: CustomEmojiFolderTree = { value: part, category: input, children: [] }; // 新しい node を作る - currentNode.children.push(newNode); // currentNode の children に newNode を追加 - existingNode = newNode; // existingNode に newNode を代入 - } + currentNode.children.push(newNode); // currentNode の children に newNode を追加 + existingNode = newNode; // existingNode に newNode を代入 + } - currentNode = existingNode; // currentNode に existingNode を代入 - } - return currentNode; + currentNode = existingNode; // currentNode に existingNode を代入 + } + return currentNode; } customEmojiCategories.value.forEach(ec => { - if (ec !== null) { - parseAndMergeCategories(ec, customEmojiFolderRoot); - } + if (ec !== null) { + parseAndMergeCategories(ec, customEmojiFolderRoot); + } }); parseAndMergeCategories('', customEmojiFolderRoot); watch(q, () => { - if (emojisEl.value) emojisEl.value.scrollTop = 0; + if (emojisEl.value) emojisEl.value.scrollTop = 0; - if (q.value === '') { - searchResultCustom.value = []; - searchResultUnicode.value = []; - return; - } + if (q.value === '') { + searchResultCustom.value = []; + searchResultUnicode.value = []; + return; + } - const newQ = q.value.replace(/:/g, '').toLowerCase(); + const newQ = q.value.replace(/:/g, '').toLowerCase(); - const searchCustom = () => { - const max = 100; - const emojis = customEmojis.value; - const matches = new Set<Misskey.entities.EmojiSimple>(); + const searchCustom = () => { + const max = 100; + const emojis = customEmojis.value; + const matches = new Set<Misskey.entities.EmojiSimple>(); - const exactMatch = emojis.find(emoji => emoji.name === newQ); - if (exactMatch) matches.add(exactMatch); + const exactMatch = emojis.find(emoji => emoji.name === newQ); + if (exactMatch) matches.add(exactMatch); - if (newQ.includes(' ')) { // AND検索 - const keywords = newQ.split(' '); + if (newQ.includes(' ')) { // AND検索 + const keywords = newQ.split(' '); - // 名前にキーワードが含まれている - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + // 名前にキーワードが含まれている + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - // 名前またはエイリアスにキーワードが含まれている - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } else { - if (customEmojisMap.has(newQ)) { + // 名前またはエイリアスにキーワードが含まれている + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword) || emoji.aliases.some(alias => alias.includes(keyword)))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } else { + if (customEmojisMap.has(newQ)) { matches.add(customEmojisMap.get(newQ)!); } if (matches.size >= max) return matches; @@ -237,211 +243,219 @@ watch(q, () => { if (matches.size >= max) break; } } - if (matches.size >= max) return matches;for (const emoji of emojis) { - if (emoji.name.startsWith(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + if (matches.size >= max) return matches; for (const emoji of emojis) { + if (emoji.name.startsWith(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.aliases.some(alias => alias.startsWith(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias.startsWith(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.name.includes(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.name.includes(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const emoji of emojis) { - if (emoji.aliases.some(alias => alias.includes(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } + for (const emoji of emojis) { + if (emoji.aliases.some(alias => alias.includes(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } - return matches; - }; + return matches; + }; - const searchUnicode = () => { - const max = 100; - const emojis = emojilist; - const matches = new Set<UnicodeEmojiDef>(); + const searchUnicode = () => { + const max = 100; + const emojis = emojilist; + const matches = new Set<UnicodeEmojiDef>(); - const exactMatch = emojis.find(emoji => emoji.name === newQ); - if (exactMatch) matches.add(exactMatch); + const exactMatch = emojis.find(emoji => emoji.name === newQ); + if (exactMatch) matches.add(exactMatch); - if (newQ.includes(' ')) { // AND検索 - const keywords = newQ.split(' '); + if (newQ.includes(' ')) { // AND検索 + const keywords = newQ.split(' '); - for (const emoji of emojis) { - if (keywords.every(keyword => emoji.name.includes(keyword))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (keywords.every(keyword => emoji.name.includes(keyword))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (keywords.every(keyword => index[emoji.char].some(k => k.includes(keyword)))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } - } else { - for (const emoji of emojis) { - if (emoji.name.startsWith(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (keywords.every(keyword => index[emoji.char].some(k => k.includes(keyword)))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } + } else { + for (const emoji of emojis) { + if (emoji.name.startsWith(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (index[emoji.char].some(k => k.startsWith(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (index[emoji.char].some(k => k.startsWith(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } - for (const emoji of emojis) { - if (emoji.name.includes(newQ)) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - if (matches.size >= max) return matches; + for (const emoji of emojis) { + if (emoji.name.includes(newQ)) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + if (matches.size >= max) return matches; - for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { - for (const emoji of emojis) { - if (index[emoji.char].some(k => k.includes(newQ))) { - matches.add(emoji); - if (matches.size >= max) break; - } - } - } - } + for (const index of Object.values(defaultStore.state.additionalUnicodeEmojiIndexes)) { + for (const emoji of emojis) { + if (index[emoji.char].some(k => k.includes(newQ))) { + matches.add(emoji); + if (matches.size >= max) break; + } + } + } + } - return matches; - }; + return matches; + }; - searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); - searchResultUnicode.value = Array.from(searchUnicode()); + searchResultCustom.value = Array.from(searchCustom()).filter(filterAvailable); + searchResultUnicode.value = Array.from(searchUnicode()); }); function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { - return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); + return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); } function focus() { - if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { - searchEl.value?.focus({ - preventScroll: true, - }); - } + if (!['smartphone', 'tablet'].includes(deviceKind) && !isTouchUsing) { + searchEl.value?.focus({ + preventScroll: true, + }); + } } function reset() { - if (emojisEl.value) emojisEl.value.scrollTop = 0; - q.value = ''; + if (emojisEl.value) emojisEl.value.scrollTop = 0; + q.value = ''; } function getKey(emoji: string | Misskey.entities.EmojiSimple | UnicodeEmojiDef): string { - return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; + return typeof emoji === 'string' ? emoji : 'char' in emoji ? emoji.char : `:${emoji.name}:`; } /** @see MkEmojiPicker.section.vue */ function computeButtonTitle(ev: MouseEvent): void { - const elm = ev.target as HTMLElement; - const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + const elm = ev.target as HTMLElement; + const emoji = elm.dataset.emoji as string; + elm.title = getEmojiName(emoji) ?? emoji; } function chosen(emoji: any, ev?: MouseEvent) { - const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; - if (el) { - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); - } + const el = ev && (ev.currentTarget ?? ev.target) as HTMLElement | null | undefined; + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } - const key = getKey(emoji); - emit('chosen', key); + const key = getKey(emoji); + emit('chosen', key); - // 最近使った絵文字更新 - if (!pinned.value?.includes(key)) { - let recents = defaultStore.state.recentlyUsedEmojis; - recents = recents.filter((emoji: any) => emoji !== key); - recents.unshift(key); - defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); - } + // 最近使った絵文字更新 + if (!pinned.value?.includes(key)) { + let recents = defaultStore.state.recentlyUsedEmojis; + recents = recents.filter((emoji: any) => emoji !== key); + recents.unshift(key); + defaultStore.set('recentlyUsedEmojis', recents.splice(0, 32)); + } } function input(): void { - // Using custom input event instead of v-model to respond immediately on - // Android, where composition happens on all languages - // (v-model does not update during composition) - q.value = searchEl.value?.value.trim() ?? ''; + // Using custom input event instead of v-model to respond immediately on + // Android, where composition happens on all languages + // (v-model does not update during composition) + q.value = searchEl.value?.value.trim() ?? ''; } function paste(event: ClipboardEvent): void { - const pasted = event.clipboardData?.getData('text') ?? ''; - if (done(pasted)) { - event.preventDefault(); - } + const pasted = event.clipboardData?.getData('text') ?? ''; + if (done(pasted)) { + event.preventDefault(); + } } function onEnter(ev: KeyboardEvent) { - if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; - done(); + if (ev.isComposing || ev.key === 'Process' || ev.keyCode === 229) return; + done(); +} + +const activeIndex = ref(defaultStore.state.pickerProfileDefault); +pinnedEmojis.value = props.asReactionPicker ? deepClone(defaultStore.state[`reactions${activeIndex.value > 1 ? activeIndex.value - 1 : ''}`]) : deepClone(defaultStore.state[`pinnedEmojis${activeIndex.value > 1 ? activeIndex.value - 1 : ''}`]); + +function pinnedProfileSelect(index:number) { + pinnedEmojis.value = props.asReactionPicker ? deepClone(defaultStore.state[`reactions${index > 1 ? index - 1 : ''}`]) : deepClone(defaultStore.state[`pinnedEmojis${index > 1 ? index - 1 : ''}`]); + activeIndex.value = index; } function done(query?: string): boolean | void { - if (query == null) query = q.value; - if (query == null || typeof query !== 'string') return; + if (query == null) query = q.value; + if (query == null || typeof query !== 'string') return; - const q2 = query.replace(/:/g, ''); - const exactMatchCustom = customEmojisMap.get(q2); - if (exactMatchCustom) { - chosen(exactMatchCustom); - return true; - } - const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2); - if (exactMatchUnicode) { - chosen(exactMatchUnicode); - return true; - } - if (searchResultCustom.value.length > 0) { - chosen(searchResultCustom.value[0]); - return true; - } - if (searchResultUnicode.value.length > 0) { - chosen(searchResultUnicode.value[0]); - return true; - } + const q2 = query.replace(/:/g, ''); + const exactMatchCustom = customEmojisMap.get(q2); + if (exactMatchCustom) { + chosen(exactMatchCustom); + return true; + } + const exactMatchUnicode = emojilist.find(emoji => emoji.char === q2 || emoji.name === q2); + if (exactMatchUnicode) { + chosen(exactMatchUnicode); + return true; + } + if (searchResultCustom.value.length > 0) { + chosen(searchResultCustom.value[0]); + return true; + } + if (searchResultUnicode.value.length > 0) { + chosen(searchResultUnicode.value[0]); + return true; + } } onMounted(() => { - focus(); + focus(); }); defineExpose({ - focus, - reset, + focus, + reset, }); </script> @@ -685,4 +699,24 @@ left: 0;*/ } } } +.sllfktkhgl{ + display: inline-block; + padding: 0 4px; + font-size: 12px; + line-height: 32px; + text-align: center; + color: var(--fg); + cursor: pointer; + width: 100%; + transition: transform 0.3s ease; + box-shadow: 0 1.5px 0 var(--divider); + height: 32px; + overflow: hidden; + &:hover { + transform: translateY(1.5px); + } + &.active { + transform: translateY(5px); + } +} </style> diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue index 90f2194a97..76350e5716 100644 --- a/packages/frontend/src/components/MkUserSelectDialog.vue +++ b/packages/frontend/src/components/MkUserSelectDialog.vue @@ -68,8 +68,6 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { hostname } from '@/config.js'; -import { multipleSelectUser } from '@/os.js'; - const emit = defineEmits<{ (ev: 'ok', selected: Misskey.entities.UserDetailed): void; (ev: 'cancel'): void; diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index b2b6c663d5..ddb7bfa030 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -99,6 +99,7 @@ export const ROLE_POLICIES = [ 'userEachUserListsLimit', 'rateLimitFactor', 'avatarDecorationLimit', + 'emojiPickerProfileLimit' ] as const; // なんか動かない diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 25958213bd..dc0d43f7b7 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -72,6 +72,13 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'pickerProfileDefault'])"> + <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> + <template #suffix>{{ policies.emojiPickerProfileLimit }}</template> + <MkInput v-model="policies.emojiPickerProfileLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> <template #suffix>{{ policies.inviteLimit }}</template> diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue index 61f3332122..07c6e2c61d 100644 --- a/packages/frontend/src/pages/settings/emoji-picker.vue +++ b/packages/frontend/src/pages/settings/emoji-picker.vue @@ -5,6 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps_m"> + <MkSelect v-model="nowProfileId"> + <template #label>{{ i18n.ts.emojiPickerProfile }}</template> + <option v-for="a in profileMax" :key="a" :value="a">{{ a }}. {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} </option> + </MkSelect> + <MkInput v-model="profileName"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> <MkFolder :defaultOpen="true"> <template #icon><i class="ti ti-pin"></i></template> <template #label>{{ i18n.ts.pinned }} ({{ i18n.ts.reaction }})</template> @@ -84,7 +91,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> </MkFolder> - + <MkButton inline primary @click="setDefaultProfile"> {{ i18n.ts.default }}</MkButton> <FormSection> <template #label>{{ i18n.ts.emojiPickerDisplay }}</template> @@ -139,6 +146,9 @@ import { emojiPicker } from '@/scripts/emoji-picker.js'; import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue'; import MkEmoji from '@/components/global/MkEmoji.vue'; import MkFolder from '@/components/MkFolder.vue'; +import MkSelect from '@/components/MkSelect.vue'; +import { signinRequired } from '@/account.js'; +import MkInput from '@/components/MkInput.vue'; const pinnedEmojisForReaction: Ref<string[]> = ref(deepClone(defaultStore.state.reactions)); const pinnedEmojis: Ref<string[]> = ref(deepClone(defaultStore.state.pinnedEmojis)); @@ -155,6 +165,20 @@ const setDefaultReaction = () => setDefault(pinnedEmojisForReaction); const removeEmoji = (reaction: string, ev: MouseEvent) => remove(pinnedEmojis, reaction, ev); const chooseEmoji = (ev: MouseEvent) => pickEmoji(pinnedEmojis, ev); const setDefaultEmoji = () => setDefault(pinnedEmojis); +const nowProfileId = ref(defaultStore.state.pickerProfileDefault); + +const $i = signinRequired(); +const profileMax = $i.policies.emojiPickerProfileLimit; +const profileName = ref(defaultStore.state[`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); +pinnedEmojisForReaction.value = deepClone(defaultStore.state[`reactions${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); +pinnedEmojis.value = deepClone(defaultStore.state[`pinnedEmojis${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); +profileName.value = deepClone(defaultStore.state[`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); + +watch(nowProfileId, () => { + pinnedEmojisForReaction.value = deepClone(defaultStore.state[`reactions${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); + pinnedEmojis.value = deepClone(defaultStore.state[`pinnedEmojis${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); + profileName.value = deepClone(defaultStore.state[`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); +}); function previewReaction(ev: MouseEvent) { reactionPicker.show(getHTMLElement(ev)); @@ -226,17 +250,32 @@ function getHTMLElement(ev: MouseEvent): HTMLElement { } watch(pinnedEmojisForReaction, () => { - defaultStore.set('reactions', pinnedEmojisForReaction.value); + defaultStore.set(`reactions${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`, pinnedEmojisForReaction.value); }, { deep: true, }); watch(pinnedEmojis, () => { - defaultStore.set('pinnedEmojis', pinnedEmojis.value); + defaultStore.set( `pinnedEmojis${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`, pinnedEmojis.value); }, { deep: true, }); +watch(profileName, () => { + defaultStore.set(`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`, profileName.value); +}, { + deep: true, +}); + +async function setDefaultProfile() { + const { canceled } = await os.confirm({ + type: 'info', + text: i18n.ts.setDefaultProfileConfirm, + }); + if (canceled) return; + await defaultStore.set('pickerProfileDefault', nowProfileId.value); +} + definePageMetadata({ title: i18n.ts.emojiPicker, icon: 'ti ti-mood-happy', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 8ce4963581..d4181d21d8 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -58,11 +58,10 @@ export const noteActions: NoteAction[] = []; export const noteViewInterruptors: NoteViewInterruptor[] = []; export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; -export const bannerDark='https://files.prismisskey.space/misskey/e088c6d1-b07f-4312-8d41-fee2f64071e9.png' -export const bannerLight ='https://files.prismisskey.space/misskey/85500d2f-41a9-48ff-a737-65d6fdf74604.png' -export const iconDark='https://files.prismisskey.space/misskey/484efc68-de41-4786-b2b6-e5085c31c2c4.webp' -export const iconLight='https://files.prismisskey.space/misskey/c3d722fe-379f-4c85-9414-90c232d53237.webp' - +export const bannerDark = 'https://files.prismisskey.space/misskey/e088c6d1-b07f-4312-8d41-fee2f64071e9.png'; +export const bannerLight = 'https://files.prismisskey.space/misskey/85500d2f-41a9-48ff-a737-65d6fdf74604.png'; +export const iconDark = 'https://files.prismisskey.space/misskey/484efc68-de41-4786-b2b6-e5085c31c2c4.webp'; +export const iconLight = 'https://files.prismisskey.space/misskey/c3d722fe-379f-4c85-9414-90c232d53237.webp'; // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) // あと、現行の定義の仕方なら「whereが何であるかに関わらずキー名の重複不可」という制約を付けられるメリットもあるからそのメリットを引き継ぐ方法も考えないといけない @@ -129,6 +128,74 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: [], }, + reactions1: { + where: 'account', + default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], + }, + pinnedEmojis1: { + where: 'account', + default: [], + }, + reactions2: { + where: 'account', + default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], + }, + pinnedEmojis2: { + where: 'account', + default: [], + }, + reactions3: { + where: 'account', + default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], + }, + pinnedEmojis3: { + where: 'account', + default: [], + }, + reactions4: { + where: 'account', + default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], + }, + pinnedEmojis4: { + where: 'account', + default: [], + }, + reactions5: { + where: 'account', + default: ['👍', '❤️', '😆', '🤔', '😮', '🎉', '💢', '😥', '😇', '🍮'], + }, + pinnedEmojis5: { + where: 'account', + default: [], + }, + pickerProfileName: { + where: 'account', + default: 'default', + }, + pickerProfileName1: { + where: 'account', + default: '1', + }, + pickerProfileName2: { + where: 'account', + default: '2', + }, + pickerProfileName3: { + where: 'account', + default: '3', + }, + pickerProfileName4: { + where: 'account', + default: '4', + }, + pickerProfileName5: { + where: 'account', + default: '5', + }, + pickerProfileDefault: { + where: 'account', + default: 1, + }, reactionAcceptance: { where: 'account', default: 'nonSensitiveOnly' as 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null, @@ -296,19 +363,19 @@ export const defaultStore = markRaw(new Storage('base', { }, gamingType: { where: 'device', - default: 'dark', + default: 'dark', }, indicatorCounterToggle: { where: 'device', default: 'true', }, - bannerUrl:{ + bannerUrl: { where: 'device', - default: bannerDark + default: bannerDark, }, - iconUrl:{ + iconUrl: { where: 'device', - default: iconDark + default: iconDark, }, instanceTicker: { where: 'device', @@ -334,9 +401,9 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: [] as string[], }, - enablehanntenn:{ - where:'device', - default: false + enablehanntenn: { + where: 'device', + default: false, }, recentlyUsedUsers: { where: 'device', @@ -378,39 +445,39 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 3, }, - specifiedColor:{ + specifiedColor: { where: 'device', default: '#FFFF64', }, - followerColor:{ + followerColor: { where: 'device', default: '#FF00FF', }, - homeColor:{ + homeColor: { where: 'device', default: '#00FFFF', }, - localOnlyColor:{ - where:'device', - default: '#2b2c41' + localOnlyColor: { + where: 'device', + default: '#2b2c41', }, numberOfGamingSpeed: { where: 'device', default: 44, }, - onlyAndWithSave:{ + onlyAndWithSave: { where: 'device', default: false, }, - onlyFiles:{ + onlyFiles: { where: 'device', default: false, }, - withReplies:{ + withReplies: { where: 'device', default: true, }, - withRenotes:{ + withRenotes: { where: 'device', default: true, }, @@ -422,15 +489,15 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, - showMediaTimeline:{ + showMediaTimeline: { where: 'device', default: true, }, - showGlobalTimeline:{ + showGlobalTimeline: { where: 'device', default: true, }, - showVisibilityColor:{ + showVisibilityColor: { where: 'device', default: false, }, @@ -557,7 +624,6 @@ export const defaultStore = markRaw(new Storage('base', { }, })); - // TODO: 他のタブと永続化されたstateを同期 const PREFIX = 'miux:' as const; From eab0a7d0182665bdb93042141d23ce49fe0e46a7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 15 Jan 2024 23:17:02 +0900 Subject: [PATCH 332/501] Feat: emoji picker profile --- packages/frontend/src/pages/settings/emoji-picker.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/settings/emoji-picker.vue b/packages/frontend/src/pages/settings/emoji-picker.vue index 07c6e2c61d..139de97c7a 100644 --- a/packages/frontend/src/pages/settings/emoji-picker.vue +++ b/packages/frontend/src/pages/settings/emoji-picker.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_m"> <MkSelect v-model="nowProfileId"> <template #label>{{ i18n.ts.emojiPickerProfile }}</template> - <option v-for="a in profileMax" :key="a" :value="a">{{ a }}. {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} </option> + <option v-for="a in profileMax" :key="a" :value="a">{{ a }}. {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} {{ nowDefaultProfileId === a ? `(${i18n.ts.default})`: '' }} </option> </MkSelect> <MkInput v-model="profileName"> <template #label>{{ i18n.ts.name }}</template> @@ -170,6 +170,9 @@ const nowProfileId = ref(defaultStore.state.pickerProfileDefault); const $i = signinRequired(); const profileMax = $i.policies.emojiPickerProfileLimit; const profileName = ref(defaultStore.state[`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); +const nowDefaultProfileId = ref(defaultStore.state['pickerProfileDefault']); +const nowDefaultProfileName = ref(); +nowDefaultProfileName.value = deepClone(defaultStore.state[`pickerProfileName${nowDefaultProfileId.value > 1 ? nowDefaultProfileId.value - 1 : ''}`]); pinnedEmojisForReaction.value = deepClone(defaultStore.state[`reactions${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); pinnedEmojis.value = deepClone(defaultStore.state[`pinnedEmojis${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); profileName.value = deepClone(defaultStore.state[`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); @@ -273,6 +276,8 @@ async function setDefaultProfile() { text: i18n.ts.setDefaultProfileConfirm, }); if (canceled) return; + nowDefaultProfileId.value = nowProfileId.value; + nowDefaultProfileName.value = deepClone(defaultStore.state[`pickerProfileName${nowProfileId.value > 1 ? nowProfileId.value - 1 : ''}`]); await defaultStore.set('pickerProfileDefault', nowProfileId.value); } From 9ec48aa0f63dd7fb1e5a1a7e2e1cb0769cf5136f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 15 Jan 2024 23:28:11 +0900 Subject: [PATCH 333/501] Feat: emoji picker profile --- packages/frontend/src/ui/_common_/common.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index 5251ef5787..e3574cfa59 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -12,7 +12,7 @@ import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; function toolsMenuItems(): MenuItem[] { - return[{ + return [{ type: 'link', to: '/scratchpad', text: i18n.ts.scratchpad, @@ -27,6 +27,11 @@ function toolsMenuItems(): MenuItem[] { to: '/clicker', text: '●👈', icon: 'ti ti-cookie', + }, { + type: 'link', + to: '/bubble-game', + text: i18n.ts.bubbleGame, + icon: 'ti ti-apple', }, ($i && ($i.isAdmin || $i.policies.canManageCustomEmojis)) ? { type: 'link', to: '/custom-emojis-manager', From 5cfa86e85e509a2b73c79e5195b00e70f7b96ee5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 17 Jan 2024 06:57:32 +0900 Subject: [PATCH 334/501] Feat: drop-and-fusion update --- .../src/pages/drop-and-fusion.game.vue | 78 ++++- .../frontend/src/pages/drop-and-fusion.vue | 4 +- .../src/scripts/drop-and-fusion-engine.ts | 299 +++++++++++++++++- 3 files changed, 376 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue index a8fa953c38..9531147061 100644 --- a/packages/frontend/src/pages/drop-and-fusion.game.vue +++ b/packages/frontend/src/pages/drop-and-fusion.game.vue @@ -220,7 +220,7 @@ const NORAML_MONOS: FrontendMonoDefinition[] = [{ img: '/client-assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png', imgSizeX: 256, imgSizeY: 256, - spriteScale: 1.12, + spriteScale: 1.0, }, { id: 'beb30459-b064-4888-926b-f572e4e72e0c', sfxPitch: 0.75, @@ -494,9 +494,81 @@ const SWEETS_MONOS: FrontendMonoDefinition[] = [{ imgSizeY: 32, spriteScale: 1, }]; +const PRISMISSKEY_MONOS: FrontendMonoDefinition[] = [{ + id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', + sfxPitch: 0.25, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2Fc4c7e430-bd92-415a-a7d3-031ddb7f0641.apng', + imgSizeX: 3400, + imgSizeY: 3400, + spriteScale: 2.3, +}, { + id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', + sfxPitch: 0.5, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2F5ba71ab2-1673-4eb0-bd3b-b44e063365ba.apng', + imgSizeX: 2000, + imgSizeY: 2000, + spriteScale: 1.65, +}, { + id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', + sfxPitch: 0.75, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2Fd6fe438b-c550-484f-95d1-1739a2b5173d.apng', + + imgSizeX: 2000, + imgSizeY: 2000, + spriteScale: 1.8, +}, { + id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', + sfxPitch: 1, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2F8226211b-58e2-46ba-be20-4ea635d614ab.webp', + imgSizeX: 500, + imgSizeY: 501, + spriteScale: 1.0, +}, { + id: '1092e069-fe1a-450b-be97-b5d477ec398c', + sfxPitch: 1.5, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2Fde30a66c-5c98-4c2e-a425-4d1f73d96899.png', + imgSizeX: 340, + imgSizeY: 351, + spriteScale: 0.98, +}, { + id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', + sfxPitch: 2, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2Fdc8d893a-6d1f-4a86-847e-a30e56270249.png', + imgSizeX: 1023, + imgSizeY: 1000, + spriteScale: 1.0, +}, { + id: 'ea8a61af-e350-45f7-ba6a-366fcd65692a', + sfxPitch: 2.5, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2F56a303b9-4385-44bb-a4d4-2453450eef01.png', + imgSizeX: 256, + imgSizeY: 256, + spriteScale: 0.4, +}, { + id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', + sfxPitch: 3, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2F96a87c60-543d-4e83-a24d-c2a3247eb2ea.webp', + imgSizeX: 630, + imgSizeY: 620, + spriteScale: 0.6, +}, { + id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', + sfxPitch: 3.5, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2Fdff2812b-2c80-4ecf-b5f1-b2874048899e.webp', + imgSizeX: 1500, + imgSizeY: 1500, + spriteScale: 1.15, +}, { + id: '35e476ee-44bd-4711-ad42-87be245d3efd', + sfxPitch: 4, + img: '/proxy/image.webp?url=https%3A%2F%2Ffiles.prismisskey.space%2Fmisskey%2Fc4448bf6-d95f-49e5-844d-b6b9530e82cc.png', + imgSizeX: 200, + imgSizeY: 200, + spriteScale: 1.5, +}]; const props = defineProps<{ - gameMode: 'normal' | 'square' | 'yen' | 'sweets'; + gameMode: 'normal' | 'square' | 'yen' | 'sweets' | 'prismisskey'; mute: boolean; }>(); @@ -509,6 +581,7 @@ const monoDefinitions = computed(() => { props.gameMode === 'square' ? SQUARE_MONOS : props.gameMode === 'yen' ? YEN_MONOS : props.gameMode === 'sweets' ? SWEETS_MONOS : + props.gameMode === 'prismisskey' ? PRISMISSKEY_MONOS : [] as never; }); @@ -517,6 +590,7 @@ function getScoreUnit(gameMode: string) { gameMode === 'square' ? 'pt' : gameMode === 'yen' ? '円' : gameMode === 'sweets' ? 'kcal' : + gameMode === 'prismisskey' ? 'pt' : '' as never; } diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 18d3f56ca2..00a2dfe292 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="square">SQUARE</option> <option value="yen">YEN</option> <option value="sweets">SWEETS</option> + <option value="prismisskey">PRISMISSKEY</option> </MkSelect> <MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton> </div> @@ -94,7 +95,7 @@ import MkSelect from '@/components/MkSelect.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; -const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets'>('normal'); +const gameMode = ref<'normal' | 'square' | 'yen' | 'sweets' | 'prismisskey'>('normal'); const gameStarted = ref(false); const mute = ref(false); const ranking = ref(null); @@ -108,6 +109,7 @@ function getScoreUnit(gameMode: string) { gameMode === 'square' ? 'pt' : gameMode === 'yen' ? '円' : gameMode === 'sweets' ? 'kcal' : + gameMode === 'prismisskey' ? 'pt' : '' as never; } diff --git a/packages/frontend/src/scripts/drop-and-fusion-engine.ts b/packages/frontend/src/scripts/drop-and-fusion-engine.ts index 7c75822a20..4378bef5e4 100644 --- a/packages/frontend/src/scripts/drop-and-fusion-engine.ts +++ b/packages/frontend/src/scripts/drop-and-fusion-engine.ts @@ -976,7 +976,301 @@ const SWEETS_MONOS: Mono[] = [{ score: 30, dropCandidate: true, }]; +const PRISMISSKEY_BASE_SIZE = 28; +const PRISMISSKEY_MONOS: Mono[] = [{ + id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', + level: 10, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [[ + { 'x': 1680, 'y': 270 }, + { 'x': 2150, 'y': 560 }, + { 'x': 2600, 'y': 910 }, + { 'x': 3279, 'y': 650 }, + { 'x': 3130, 'y': 1100 }, + { 'x': 2750, 'y': 1250 }, + { 'x': 2800, 'y': 1860 }, + { 'x': 1760, 'y': 1980 }, + { 'x': 1500, 'y': 1840 }, + { 'x': 989, 'y': 1800 }, + { 'x': 888, 'y': 1900 }, + { 'x': 688, 'y': 1540 }, + { 'x': 783, 'y': 1250 }, + { 'x': 660, 'y': 1190 }, + { 'x': 600, 'y': 800 }, + { 'x': 722, 'y': 382 }, + { 'x': 1100, 'y': 835 }, + { 'x': 1400, 'y': 670 }, + ]], + verticesSize: 1800, + score: 512, + dropCandidate: false, +}, { + id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', + level: 9, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { 'x': 1023, 'y': 266 }, + { 'x': 1476, 'y': 474 }, + { 'x': 1595, 'y': 362 }, + { 'x': 1747, 'y': 359 }, + { 'x': 1939, 'y': 481 }, + { 'x': 1769, 'y': 700 }, + { 'x': 1666, 'y': 690 }, + { 'x': 1711, 'y': 982 }, + { 'x': 1638, 'y': 1227 }, + { 'x': 1494, 'y': 1300 }, + { 'x': 1037, 'y': 1300 }, + { 'x': 914, 'y': 1183 }, + { 'x': 447, 'y': 1200 }, + { 'x': 290, 'y': 1300 }, + { 'x': 204, 'y': 1280 }, + { 'x': 170, 'y': 835 }, + { 'x': 321, 'y': 523 }, + { 'x': 647, 'y': 323 }, + { 'x': 857, 'y': 277 }, + + ], + ], + verticesSize: 1300, + score: 256, + dropCandidate: false, +}, { + id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', + level: 8, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'custom', + vertices: [ + [ + { 'x': 1177, 'y': 85 }, + { 'x': 1498, 'y': 404 }, + { 'x': 1370, 'y': 374 }, + { 'x': 1737, 'y': 619 }, + { 'x': 1946, 'y': 721 }, + { 'x': 1764, 'y': 975 }, + { 'x': 1674, 'y': 949 }, + { 'x': 1715, 'y': 1183 }, + { 'x': 1641, 'y': 1498 }, + { 'x': 1479, 'y': 1555 }, + { 'x': 1008, 'y': 1566 }, + { 'x': 912, 'y': 1447 }, + { 'x': 456, 'y': 1453 }, + { 'x': 298, 'y': 1561 }, + { 'x': 240, 'y': 1551 }, + { 'x': 187, 'y': 1017 }, + { 'x': 283, 'y': 825 }, + { 'x': 517, 'y': 609 }, + { 'x': 187, 'y': 1017 }, + { 'x': 352, 'y': 521 }, + { 'x': 821, 'y': 333 }, + ], + ], + verticesSize: 1300, + score: 128, + dropCandidate: false, +}, { + id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', + level: 7, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 64, + dropCandidate: false, +}, { + id: '1092e069-fe1a-450b-be97-b5d477ec398c', + level: 6, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + shape: 'circle', + score: 32, + dropCandidate: false, +}, { + id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', + level: 5, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25, + + shape: 'custom', + vertices: [ + [ + { + 'x': 410, + 'y': 14, + }, + { + 'x': 741, + 'y': 160, + }, + { + 'x': 834, + 'y': 346, + }, + { + 'x': 853, + 'y': 637, + }, + { + 'x': 768, + 'y': 760, + }, + { + 'x': 422, + 'y': 813, + }, { + 'x': 66, + 'y': 748, + }, + { + 'x': 27, + 'y': 702, + }, + { + 'x': 7, + 'y': 543, + }, { + 'x': 63, + 'y': 263, + }, { + 'x': 170, + 'y': 108, + }, + { + 'x': 310, + 'y': 26, + }, + ], + ], + verticesSize: 1024, + score: 16, + dropCandidate: true, +}, { + id: 'ea8a61af-e350-45f7-ba6a-366fcd65692a', + level: 4, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, + shape: 'rectangle', + score: 8, + dropCandidate: true, +}, { + id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', + level: 3, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25, + + shape: 'custom', + vertices: [ + [ + { + 'x': 87, + 'y': 6, + }, + { + 'x': 169, + 'y': 53, + }, + { + 'x': 169, + 'y': 147, + }, + { + 'x': 87, + 'y': 195, + }, + { + 'x': 6, + 'y': 147, + }, + { + 'x': 6, + 'y': 53, + }, + ], + ], + verticesSize: 128, + score: 4, + dropCandidate: true, +}, { + id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', + level: 2, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25, + + shape: 'custom', + vertices: [ + [ + { + 'x': 749, + 'y': 12, + }, + { + 'x': 1386, + 'y': 379, + }, + { + 'x': 1387, + 'y': 1116, + }, + { + 'x': 749, + 'y': 1484, + }, + { + 'x': 111, + 'y': 1116, + }, + { + 'x': 111, + 'y': 380, + }, + ], + ], + verticesSize: 1536, + score: 2, + dropCandidate: true, +}, { + id: '35e476ee-44bd-4711-ad42-87be245d3efd', + level: 1, + sizeX: PRISMISSKEY_BASE_SIZE, + sizeY: PRISMISSKEY_BASE_SIZE, + shape: 'custom', + vertices: [ + [ + { + 'x': 87, + 'y': 6, + }, + { + 'x': 169, + 'y': 53, + }, + { + 'x': 169, + 'y': 147, + }, + { + 'x': 87, + 'y': 195, + }, + { + 'x': 6, + 'y': 147, + }, + { + 'x': 6, + 'y': 53, + }, + ], + ], + verticesSize: 128, + score: 1, + dropCandidate: true, +}]; export class DropAndFusionGame extends EventEmitter<{ changeScore: (newScore: number) => void; changeCombo: (newCombo: number) => void; @@ -1003,7 +1297,7 @@ export class DropAndFusionGame extends EventEmitter<{ private tickCallbackQueue: { frame: number; callback: () => void; }[] = []; private overflowCollider: Matter.Body; private isGameOver = false; - private gameMode: 'normal' | 'yen' | 'square' | 'sweets'; + private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'prismisskey'; private rng: () => number; private logs: Log[] = []; @@ -1031,6 +1325,7 @@ export class DropAndFusionGame extends EventEmitter<{ case 'yen': return YEN_MONOS; case 'square': return SQUARE_MONOS; case 'sweets': return SWEETS_MONOS; + case 'prismisskey': return PRISMISSKEY_MONOS; } } @@ -1268,7 +1563,7 @@ export class DropAndFusionGame extends EventEmitter<{ for (let i = 0; i < this.STOCK_MAX; i++) { this.stock.push({ id: this.rng().toString(), - mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], + mono: this.monoDefinitions[7], }); } this.emit('changeStock', this.stock); From eee41cc6ba322df2f93b9f8dca5148e12b14785c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 17 Jan 2024 15:41:55 +0900 Subject: [PATCH 335/501] Feat: remote local timeline --- .../backend/src/server/api/EndpointsModule.ts | 18 +- packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/notes/any-local-timeline.ts | 247 ++++++++++++++ .../frontend/src/components/MkTimeline.vue | 15 +- .../frontend/src/pages/settings/general.vue | 130 ++++++++ packages/frontend/src/pages/timeline.vue | 311 ++++++++++-------- packages/frontend/src/store.ts | 80 +++++ 7 files changed, 652 insertions(+), 151 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 813cdeaaf8..6ae73c9dc3 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -283,6 +283,7 @@ import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; +import * as ep___notes_anyLocalTimeline from './endpoints/notes/any-local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; @@ -600,7 +601,7 @@ const $i_importMuting: Provider = { provide: 'ep:i/import-muting', useClass: ep_ const $i_importUserLists: Provider = { provide: 'ep:i/import-user-lists', useClass: ep___i_importUserLists.default }; const $i_importAntennas: Provider = { provide: 'ep:i/import-antennas', useClass: ep___i_importAntennas.default }; const $i_notifications: Provider = { provide: 'ep:i/notifications', useClass: ep___i_notifications.default }; -const $i_userstats: Provider = { provide: 'ep:i/stats', useClass: ep___users_user_stats.default } +const $i_userstats: Provider = { provide: 'ep:i/stats', useClass: ep___users_user_stats.default }; const $i_notificationsGrouped: Provider = { provide: 'ep:i/notifications-grouped', useClass: ep___i_notificationsGrouped.default }; const $i_pageLikes: Provider = { provide: 'ep:i/page-likes', useClass: ep___i_pageLikes.default }; const $i_pages: Provider = { provide: 'ep:i/pages', useClass: ep___i_pages.default }; @@ -658,6 +659,7 @@ const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep__ const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default }; const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default }; const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default }; +const $notes_anyLocalTimeline: Provider = { provide: 'ep:notes/any-local-timeline', useClass: ep___notes_anyLocalTimeline.default }; const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default }; const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default }; const $notes_polls_vote: Provider = { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default }; @@ -783,8 +785,8 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $admin_drive_files, $admin_drive_showFile, $admin_emoji_addAliasesBulk, - $admin_emoji_setlocalOnlyBulk, - $admin_emoji_setisSensitiveBulk, + $admin_emoji_setlocalOnlyBulk, + $admin_emoji_setisSensitiveBulk, $admin_emoji_add, $admin_emoji_addRequest, $admin_emoji_copy, @@ -866,7 +868,7 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $channels_timeline, $channels_unfollow, $channels_update, - $i_userstats, + $i_userstats, $channels_favorite, $channels_unfavorite, $channels_myFavorites, @@ -1036,6 +1038,7 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $notes_globalTimeline, $notes_hybridTimeline, $notes_localTimeline, + $notes_anyLocalTimeline, $notes_mentions, $notes_polls_recommendation, $notes_polls_vote, @@ -1168,12 +1171,12 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $admin_emoji_setAliasesBulk, $admin_emoji_setCategoryBulk, $admin_emoji_setLicenseBulk, - $admin_emoji_setlocalOnlyBulk, - $admin_emoji_setisSensitiveBulk, + $admin_emoji_setlocalOnlyBulk, + $admin_emoji_setisSensitiveBulk, $admin_emoji_update, $admin_emoji_updateRequest, $admin_federation_deleteAllFiles, - $i_userstats, + $i_userstats, $admin_federation_refreshRemoteInstanceMetadata, $admin_federation_removeAllFollowing, $admin_federation_updateInstance, @@ -1408,6 +1411,7 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $notes_globalTimeline, $notes_hybridTimeline, $notes_localTimeline, + $notes_anyLocalTimeline, $notes_mentions, $notes_polls_recommendation, $notes_polls_vote, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 7e5e6d5e97..c858735870 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -284,6 +284,7 @@ import * as ep___notes_featured from './endpoints/notes/featured.js'; import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js'; import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js'; import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js'; +import * as ep___notes_anyLocalTimeline from './endpoints/notes/any-local-timeline.js'; import * as ep___notes_mentions from './endpoints/notes/mentions.js'; import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js'; import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js'; @@ -656,6 +657,7 @@ const eps = [ ['notes/global-timeline', ep___notes_globalTimeline], ['notes/hybrid-timeline', ep___notes_hybridTimeline], ['notes/local-timeline', ep___notes_localTimeline], + ['notes/any-local-timeline', ep___notes_anyLocalTimeline], ['notes/mentions', ep___notes_mentions], ['notes/polls/recommendation', ep___notes_polls_recommendation], ['notes/polls/vote', ep___notes_polls_vote], diff --git a/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts new file mode 100644 index 0000000000..3619661be8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts @@ -0,0 +1,247 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { IdService } from '@/core/IdService.js'; +import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; +import { HttpRequestService } from '@/core/HttpRequestService.js'; +import { ApDbResolverService } from '@/core/activitypub/ApDbResolverService.js'; +import { ApResolverService } from '@/core/activitypub/ApResolverService.js'; +import { getApId, isActor, isPost } from '@/core/activitypub/type.js'; +import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { DI } from '@/di-symbols.js'; +import type { NotesRepository } from '@/models/_.js'; +import { MiNote, MiUser } from '@/models/_.js'; +import { bindThis } from '@/decorators.js'; +import { MiLocalUser } from '@/models/User.js'; +import { SchemaType } from '@/misc/json-schema.js'; +import { UtilityService } from '@/core/UtilityService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { MetaService } from '@/core/MetaService.js'; +import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notes'], + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'Note', + }, + }, + + errors: { + hostIsNull: { + message: 'Host is null', + code: 'HOST_NULL', + id: 'PRSMSK-ANY-LTL-0001', + }, + + bothWithRepliesAndWithFiles: { + message: 'Specifying both withReplies and withFiles is not supported', + code: 'BOTH_WITH_REPLIES_AND_WITH_FILES', + id: 'dd9c8400-1cb5-4eef-8a31-200c5f933793', + }, + remoteTokenIsNull: { + message: 'remoteToken is null', + code: 'REMOTE_TOKEN_NULL', + id: 'PRSMSK-ANY-LTL-0002', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + withFiles: { type: 'boolean', default: false }, + withRenotes: { type: 'boolean', default: true }, + withReplies: { type: 'boolean', default: false }, + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + allowPartial: { type: 'boolean', default: false }, // true is recommended but for compatibility false by default + sinceDate: { type: 'integer' }, + untilDate: { type: 'integer' }, + host: { type: 'string' }, + remoteToken: { type: 'string' }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private idService: IdService, + private federatedInstanceService: FederatedInstanceService, + private httpRequestService: HttpRequestService, + private utilityService: UtilityService, + private userEntityService: UserEntityService, + private noteEntityService: NoteEntityService, + private metaService: MetaService, + private apResolverService: ApResolverService, + private apDbResolverService: ApDbResolverService, + private apPersonService: ApPersonService, + private apNoteService: ApNoteService, + ) { + super(meta, paramDef, async (ps, me) => { + const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); + const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); + if (ps.host === undefined) throw new ApiError(meta.errors.hostIsNull); + if (ps.remoteToken === undefined) throw new ApiError(meta.errors.remoteTokenIsNull); + const i = await this.federatedInstanceService.fetch(ps.host); + const noteIds = []; + + if (i.softwareName === 'misskey') { + const remoteTimeline: string[] = await (await this.httpRequestService.send('https://' + ps.host + '/api/notes/local-timeline', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + i: ps.remoteToken, + withFiles: ps.withFiles, + withRenotes: ps.withRenotes, + withReplies: ps.withReplies, + limit: 30, + }), + })).json() as string[]; + + if (remoteTimeline.length > 0) { + for (const note of remoteTimeline) { + const uri = `https://${ps.host}/notes/${note.id}`; + const note_ = await this.fetchAny(uri, me); + if (note_ == null) continue; + noteIds.push(note_.id); + } + } + + let notes = await this.notesRepository.findBy({ id: In(noteIds) }); + let packedNote: any[] = await this.noteEntityService.packMany(notes, me, { detail: true }); + if (untilId) { + let lastRemoteId; + const lastUri = packedNote[packedNote.length - 1].uri; + lastRemoteId = lastUri.split('/')[lastUri.split('/').length - 1]; + do { + const remoteTimeline: string[] = await (await this.httpRequestService.send('https://' + ps.host + '/api/notes/local-timeline', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + i: ps.remoteToken, + withFiles: ps.withFiles, + withRenotes: ps.withRenotes, + withReplies: ps.withReplies, + untilId: lastRemoteId, + limit: 30, + }), + })).json() as string[]; + + if (remoteTimeline.length > 0) { + for (const note of remoteTimeline) { + const uri = `https://${ps.host}/notes/${note.id}`; + const note_ = await this.fetchAny(uri, me); + if (note_ == null) continue; + //noteIds.push(note_.id); + lastRemoteId = note_.id; + if (lastRemoteId === ps.untilId) { + break; + } + } + } + } while (lastRemoteId !== ps.untilId); + const remoteTimeline: string[] = await (await this.httpRequestService.send('https://' + ps.host + '/api/notes/local-timeline', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + i: ps.remoteToken, + withFiles: ps.withFiles, + withRenotes: ps.withRenotes, + withReplies: ps.withReplies, + untilId: lastRemoteId, + limit: 30, + }), + })).json() as string[]; + + if (remoteTimeline.length > 0) { + for (const note of remoteTimeline) { + const uri = `https://${ps.host}/notes/${note.id}`; + const note_ = await this.fetchAny(uri, me); + if (note_ == null) continue; + noteIds.push(note_.id); + } + } + } + + notes = await this.notesRepository.findBy({ id: In(noteIds) }); + packedNote = await this.noteEntityService.packMany(notes, me, { detail: true }); + return packedNote.reverse(); + } + }); + } + @bindThis + private async fetchAny(uri: string, me: MiLocalUser | null | undefined) { + // ブロックしてたら中断 + const fetchedMeta = await this.metaService.fetch(); + if (this.utilityService.isBlockedHost(fetchedMeta.blockedHosts, this.utilityService.extractDbHost(uri))) return null; + + let local = await this.mergePack(me, ...await Promise.all([ + this.apDbResolverService.getUserFromApId(uri), + this.apDbResolverService.getNoteFromApId(uri), + ])); + if (local != null) return local; + + // リモートから一旦オブジェクトフェッチ + let object; + try { + const resolver = this.apResolverService.createResolver(); + object = await resolver.resolve(uri) as any; + } catch (e) { + return null; + } + if (!object) return null; + // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する + // これはDBに存在する可能性があるため再度DB検索 + if (uri !== object.id) { + local = await this.mergePack(me, ...await Promise.all([ + this.apDbResolverService.getUserFromApId(object.id), + this.apDbResolverService.getNoteFromApId(object.id), + ])); + if (local != null) return local; + } + + return await this.mergePack( + me, + isActor(object) ? await this.apPersonService.createPerson(getApId(object)) : null, + isPost(object) ? await this.apNoteService.createNote(getApId(object), undefined, true) : null, + ); + } + + @bindThis + private async mergePack(me: MiLocalUser | null | undefined, user: MiUser | null | undefined, note: MiNote | null | undefined) { + if (note != null) { + try { + const object = await this.noteEntityService.pack(note, me, { detail: true }); + + return object; + } catch (e) { + return null; + } + } + + return null; + } +} diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 3971c71e5e..4f14c62ade 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -152,6 +152,9 @@ function connectChannel() { roleId: props.role, }); } + if (props.src.startsWith('custom-timeline')) { + return; + } if (props.src !== 'directs' || props.src !== 'mentions') connection.on('note', prepend); } @@ -189,13 +192,13 @@ function updatePaginationQuery() { withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined, }; - }else if (props.src === 'global') { + } else if (props.src === 'global') { endpoint = 'notes/global-timeline'; query = { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, }; - }else if(props.src === 'media'){ + } else if (props.src === 'media') { endpoint = 'notes/hybrid-timeline'; query = { withFiles: true, @@ -227,7 +230,13 @@ function updatePaginationQuery() { query = { roleId: props.role, }; - } else { + } else if (props.src.startsWith('custom-timeline')) { + endpoint = "notes/any-local-timeline"; + query = { + host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split("-")[2]}`], + remoteToken:defaultStore.state[`remoteLocalTimelineToken${props.src.split("-")[2]}`] + }; + }else { endpoint = null; query = null; } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 8256670474..44214e925a 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -220,6 +220,88 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.other }}</template> <div class="_gaps"> + <MkFolder> + <template #label>リモートのローカルタイムラインをTLの上のバーのやつに表示する</template> + <div style="padding: 0 16px;"> + <div v-if="maxLocalTimeline >= 1" style="padding: 16px 0 ;" > + <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> + <template #label>{{i18n.ts.name}}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain1" placeholder="prismisskey.space"> + <template #label>instance Url</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken1" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Token</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable1"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + <div v-if="maxLocalTimeline >= 2" style="padding: 16px 0 ;" > + <MkInput v-model="remoteLocalTimelineName2" placeholder="prismisskey"> + <template #label>{{i18n.ts.name}}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain2" placeholder="prismisskey.space"> + <template #label>instance Url</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken2" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Token</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable2"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + <div v-if="maxLocalTimeline >= 3" style="padding: 16px 0 ;" > + <MkInput v-model="remoteLocalTimelineName3" placeholder="prismisskey"> + <template #label>{{i18n.ts.name}}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain3" placeholder="prismisskey.space"> + <template #label>instance Url</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken3" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Token</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable3"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + <div v-if="maxLocalTimeline >= 4" style="padding: 16px 0 ;" > + <MkInput v-model="remoteLocalTimelineName4" placeholder="prismisskey"> + <template #label>{{i18n.ts.name}}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain4" placeholder="prismisskey.space"/> + <MkInput v-model="remoteLocalTimelineToken4" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Token</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable4"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + <div v-if="maxLocalTimeline >= 5" style="padding: 16px 0 ;" > + <MkInput v-model="remoteLocalTimelineName5" placeholder="prismisskey"> + <template #label>{{i18n.ts.name}}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain5" placeholder="prismisskey.space"> + <template #label>instance Url</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken5" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Token</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable5"> + {{ i18n.ts.enable }} + </MkSwitch> + + </div> + <MkButton @click="remoteLocaltimelineSave"> + {{ i18n.ts.save}} + </MkButton> + </div> + </MkFolder> <MkFolder> <template #label>{{ i18n.ts.additionalEmojiDictionary }}</template> <div class="_buttons"> @@ -261,6 +343,7 @@ import { globalEvents } from '@/events.js'; import { claimAchievement } from '@/scripts/achievements.js'; import MkColorInput from '@/components/MkColorInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import MkInput from '@/components/MkInput.vue'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -326,6 +409,29 @@ const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibili const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); +const maxLocalTimeline = 3; +const remoteLocalTimelineDomain1 = ref(defaultStore.state['remoteLocalTimelineDomain1']); +const remoteLocalTimelineToken1 = ref(defaultStore.state['remoteLocalTimelineToken1']); +const remoteLocalTimelineDomain2 = ref(defaultStore.state['remoteLocalTimelineDomain2']); +const remoteLocalTimelineToken2 = ref(defaultStore.state['remoteLocalTimelineToken2']); +const remoteLocalTimelineDomain3 = ref(defaultStore.state['remoteLocalTimelineDomain3']); +const remoteLocalTimelineToken3 = ref(defaultStore.state['remoteLocalTimelineToken3']); +const remoteLocalTimelineDomain4 = ref(defaultStore.state['remoteLocalTimelineDomain4']); +const remoteLocalTimelineToken4 = ref(defaultStore.state['remoteLocalTimelineToken4']); +const remoteLocalTimelineDomain5 = ref(defaultStore.state['remoteLocalTimelineDomain5']); +const remoteLocalTimelineToken5 = ref(defaultStore.state['remoteLocalTimelineToken5']); +const remoteLocalTimelineName1 = ref(defaultStore.state['remoteLocalTimelineName1']); +const remoteLocalTimelineName2 = ref(defaultStore.state['remoteLocalTimelineName2']); +const remoteLocalTimelineName3 = ref(defaultStore.state['remoteLocalTimelineName3']); +const remoteLocalTimelineName4 = ref(defaultStore.state['remoteLocalTimelineName4']); +const remoteLocalTimelineName5 = ref(defaultStore.state['remoteLocalTimelineName5']); + +const remoteLocalTimelineEnable1 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable1')); +const remoteLocalTimelineEnable2 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable2')); +const remoteLocalTimelineEnable3 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable3')); +const remoteLocalTimelineEnable4 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable4')); +const remoteLocalTimelineEnable5 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable5')); + watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); @@ -398,6 +504,30 @@ watch([ await reloadAsk(); }); +async function remoteLocaltimelineSave() { + os.alert({ + type: 'success', + text: i18n.ts.saved, + }); + await reloadAsk(); + defaultStore.set('remoteLocalTimelineDomain1', remoteLocalTimelineDomain1.value); + defaultStore.set('remoteLocalTimelineToken1', remoteLocalTimelineToken1.value); + defaultStore.set('remoteLocalTimelineDomain2', remoteLocalTimelineDomain2.value); + defaultStore.set('remoteLocalTimelineToken2', remoteLocalTimelineToken2.value); + defaultStore.set('remoteLocalTimelineDomain3', remoteLocalTimelineDomain3.value); + defaultStore.set('remoteLocalTimelineToken3', remoteLocalTimelineToken3.value); + defaultStore.set('remoteLocalTimelineDomain4', remoteLocalTimelineDomain4.value); + defaultStore.set('remoteLocalTimelineToken4', remoteLocalTimelineToken4.value); + defaultStore.set('remoteLocalTimelineDomain5', remoteLocalTimelineDomain5.value); + defaultStore.set('remoteLocalTimelineToken5', remoteLocalTimelineToken5.value); + defaultStore.set('remoteLocalTimelineName1', remoteLocalTimelineName1.value); + defaultStore.set('remoteLocalTimelineName2', remoteLocalTimelineName2.value); + defaultStore.set('remoteLocalTimelineName3', remoteLocalTimelineName3.value); + defaultStore.set('remoteLocalTimelineName4', remoteLocalTimelineName4.value); + defaultStore.set('remoteLocalTimelineName5', remoteLocalTimelineName5.value); + +} + const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; function getEmojiIndexLangName(targetLang: typeof emojiIndexLangs[number]) { diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 9f2d02f68f..8d5ab4f990 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -4,32 +4,32 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> - <MkSpacer :contentMax="800"> - <div ref="rootEl" v-hotkey.global="keymap"> - <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> +<MkStickyContainer> + <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> + <MkSpacer :contentMax="800"> + <div ref="rootEl" v-hotkey.global="keymap"> + <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> {{ i18n.ts._timelineDescription[src] }} </MkInfo> - <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> - <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> - <div :class="$style.tl"> - <MkTimeline - ref="tlComponent" - :key="src + withRenotes + withReplies + onlyFiles" - :src="src.split(':')[0]" - :list="src.split(':')[1]" - :withRenotes="withRenotes" - :withReplies="withReplies" - :onlyFiles="onlyFiles" - :sound="true" - @queue="queueUpdated" - /> - </div> - </div> - </MkSpacer> - </MkStickyContainer> + <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> + <div :class="$style.tl"> + <MkTimeline + ref="tlComponent" + :key="src + withRenotes + withReplies + onlyFiles" + :src="src.split(':')[0]" + :list="src.split(':')[1]" + :withRenotes="withRenotes" + :withReplies="withReplies" + :onlyFiles="onlyFiles" + :sound="true" + @queue="queueUpdated" + /> + </div> + </div> + </MkSpacer> +</MkStickyContainer> </template> <script lang="ts" setup> @@ -56,7 +56,7 @@ provide('shouldOmitHeaderTitle', true); const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable && defaultStore.state.showGlobalTimeline) || ($i != null && $i.policies.gtlAvailable && defaultStore.state.showGlobalTimeline); const keymap = { - 't': focus, + 't': focus, }; const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>(); @@ -68,29 +68,33 @@ const src = computed({ get: () => ($i ? defaultStore.reactiveState.tl.value.src const withRenotes = ref(true); const withReplies = ref($i ? defaultStore.state.tlWithReplies : false); const onlyFiles = ref(false); -const isShowMediaTimeline = ref(defaultStore.state.showMediaTimeline) - +const isShowMediaTimeline = ref(defaultStore.state.showMediaTimeline); +const remoteLocalTimelineEnable1 = ref(defaultStore.state.remoteLocalTimelineEnable1); +const remoteLocalTimelineEnable2 = ref(defaultStore.state.remoteLocalTimelineEnable2); +const remoteLocalTimelineEnable3 = ref(defaultStore.state.remoteLocalTimelineEnable3); +const remoteLocalTimelineEnable4 = ref(defaultStore.state.remoteLocalTimelineEnable4); +const remoteLocalTimelineEnable5 = ref(defaultStore.state.remoteLocalTimelineEnable5); watch(src, () => queue.value = 0); watch(withReplies, (x) => { - if ($i) defaultStore.set('tlWithReplies', x); + if ($i) defaultStore.set('tlWithReplies', x); }); function queueUpdated(q: number): void { - queue.value = q; + queue.value = q; } function top(): void { - if (rootEl.value) scroll(rootEl.value, { top: 0 }); + if (rootEl.value) scroll(rootEl.value, { top: 0 }); } async function chooseList(ev: MouseEvent): Promise<void> { - const lists = await userListsCache.fetch(); - const items : MenuItem[] = [ + const lists = await userListsCache.fetch(); + const items : MenuItem[] = [ ... lists.map(list => ({ - type: 'link' as const, - text: list.name, - to: `/timeline/list/${list.id}`,})), + type: 'link' as const, + text: list.name, + to: `/timeline/list/${list.id}` })), (lists.length === 0 ? undefined : { type: 'divider' }), { type: 'link' as const, @@ -98,18 +102,18 @@ async function chooseList(ev: MouseEvent): Promise<void> { text: i18n.ts.createNew, to: '/my/lists', }, - ]; - os.popupMenu(items, ev.currentTarget ?? ev.target); + ]; + os.popupMenu(items, ev.currentTarget ?? ev.target); } async function chooseAntenna(ev: MouseEvent): Promise<void> { - const antennas = await antennasCache.fetch(); - const items : MenuItem[] = [ + const antennas = await antennasCache.fetch(); + const items : MenuItem[] = [ ... antennas.map(antenna => ({ - type: 'link' as const, - text: antenna.name, - indicate: antenna.hasUnreadNote, - to: `/timeline/antenna/${antenna.id}`,})), + type: 'link' as const, + text: antenna.name, + indicate: antenna.hasUnreadNote, + to: `/timeline/antenna/${antenna.id}` })), (antennas.length === 0 ? undefined : { type: 'divider' }), { type: 'link' as const, @@ -117,23 +121,23 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> { text: i18n.ts.createNew, to: '/my/antennas', }, - ]; - os.popupMenu(items, ev.currentTarget ?? ev.target); + ]; + os.popupMenu(items, ev.currentTarget ?? ev.target); } async function chooseChannel(ev: MouseEvent): Promise<void> { - const channels = await misskeyApi('channels/my-favorites', { - limit: 100, - }); - const items: MenuItem[] = [ + const channels = await misskeyApi('channels/my-favorites', { + limit: 100, + }); + const items: MenuItem[] = [ ...channels.map(channel => { - const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null; + const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null; const hasUnreadNote = (lastReadedAt && channel.lastNotedAt) ? Date.parse(channel.lastNotedAt) > lastReadedAt : !!(!lastReadedAt && channel.lastNotedAt); - return {type: 'link' as const, - text: channel.name, - indicate: hasUnreadNote, - to: `/channels/${channel.id}`,}; + return { type: 'link' as const, + text: channel.name, + indicate: hasUnreadNote, + to: `/channels/${channel.id}` }; }), (channels.length === 0 ? undefined : { type: 'divider' }), { @@ -142,34 +146,34 @@ async function chooseChannel(ev: MouseEvent): Promise<void> { text: i18n.ts.createNew, to: '/channels', }, - ]; - os.popupMenu(items, ev.currentTarget ?? ev.target); + ]; + os.popupMenu(items, ev.currentTarget ?? ev.target); } function saveSrc(newSrc: 'home' | 'local' | 'media' | 'social' | 'global' | `list:${string}`): void { - let userList = null; - if (newSrc.startsWith('userList:')) { - const id = newSrc.substring('userList:'.length); - userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id); - } - defaultStore.set('tl', { - src: newSrc, - userList, - }); - srcWhenNotSignin.value = newSrc; + let userList = null; + if (newSrc.startsWith('userList:')) { + const id = newSrc.substring('userList:'.length); + userList = defaultStore.reactiveState.pinnedUserLists.value.find(l => l.id === id); + } + defaultStore.set('tl', { + src: newSrc, + userList, + }); + srcWhenNotSignin.value = newSrc; } async function timetravel(): Promise<void> { - const { canceled, result: date } = await os.inputDate({ - title: i18n.ts.date, - }); - if (canceled) return; + const { canceled, result: date } = await os.inputDate({ + title: i18n.ts.date, + }); + if (canceled) return; - tlComponent.value.timetravel(date); + tlComponent.value.timetravel(date); } function focus(): void { - tlComponent.value.focus(); + tlComponent.value.focus(); } function closeTutorial(): void { @@ -180,28 +184,28 @@ function closeTutorial(): void { } const headerActions = computed(() => { - const tmp = [ - {icon: 'ti ti-dots', - text: i18n.ts.options, - handler: (ev) => { - os.popupMenu([{ - type: 'switch', - text: i18n.ts.showRenotes, + const tmp = [ + { icon: 'ti ti-dots', + text: i18n.ts.options, + handler: (ev) => { + os.popupMenu([{ + type: 'switch', + text: i18n.ts.showRenotes, - ref: withRenotes, - }, src.value === 'local' || src.value === 'social' ? { - type: 'switch', - text: i18n.ts.showRepliesToOthersInTimeline, - ref: withReplies, - disabled: onlyFiles,} : undefined, { - type: 'switch', - text: i18n.ts.fileAttachedOnly, + ref: withRenotes, + }, src.value === 'local' || src.value === 'social' ? { + type: 'switch', + text: i18n.ts.showRepliesToOthersInTimeline, + ref: withReplies, + disabled: onlyFiles } : undefined, { + type: 'switch', + text: i18n.ts.fileAttachedOnly, - ref: onlyFiles, - disabled: src.value === 'local' || src.value === 'social' ? withReplies : false, - }], ev.currentTarget ?? ev.target); - }, -}, + ref: onlyFiles, + disabled: src.value === 'local' || src.value === 'social' ? withReplies : false, + }], ev.currentTarget ?? ev.target); + }, + }, ]; if (deviceKind === 'desktop') { tmp.unshift({ @@ -217,70 +221,95 @@ const headerActions = computed(() => { }); const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({ - key: 'list:' + l.id, - title: l.name, - icon: 'ti ti-star', - iconOnly: true, + key: 'list:' + l.id, + title: l.name, + icon: 'ti ti-star', + iconOnly: true, }))), { - key: 'home', - title: i18n.ts._timelines.home, - icon: 'ti ti-home', - iconOnly: true, + key: 'home', + title: i18n.ts._timelines.home, + icon: 'ti ti-home', + iconOnly: true, }, ...(isLocalTimelineAvailable ? [{ - key: 'local', - title: i18n.ts._timelines.local, - icon: 'ti ti-planet', - iconOnly: true, + key: 'local', + title: i18n.ts._timelines.local, + icon: 'ti ti-planet', + iconOnly: true, }, ...(isShowMediaTimeline.value ? [{ - key: 'media', - title: i18n.ts._timelines.media, - icon: 'ti ti-photo', - iconOnly: true, + key: 'media', + title: i18n.ts._timelines.media, + icon: 'ti ti-photo', + iconOnly: true, }] : []), { - key: 'social', - title: i18n.ts._timelines.social, - icon: 'ti ti-universe', - iconOnly: true, + key: 'social', + title: i18n.ts._timelines.social, + icon: 'ti ti-universe', + iconOnly: true, +}] : []), ...(remoteLocalTimelineEnable1.value ? [{ + key: 'custom-timeline-1', + title: defaultStore.state.remoteLocalTimelineName1, + icon: 'ti ti-plus', + iconOnly: false, +}] : []), ...(remoteLocalTimelineEnable2.value ? [{ + key: 'custom-timeline-2', + title: defaultStore.state.remoteLocalTimelineName2, + icon: 'ti ti-plus', + iconOnly: false, +}] : []), ...(remoteLocalTimelineEnable3.value ? [{ + key: 'custom-timeline-3', + title: defaultStore.state.remoteLocalTimelineName3, + icon: 'ti ti-plus', + iconOnly: false, +}] : []), ...(remoteLocalTimelineEnable4.value ? [{ + key: 'custom-timeline-4', + title: defaultStore.state.remoteLocalTimelineName4, + icon: 'ti ti-plus', + iconOnly: false, +}] : []), ...(remoteLocalTimelineEnable5.value ? [{ + key: 'custom-timeline-5', + title: defaultStore.state.remoteLocalTimelineName5, + icon: 'ti ti-plus', + iconOnly: false, }] : []), ...(isGlobalTimelineAvailable ? [{ - key: 'global', - title: i18n.ts._timelines.global, - icon: 'ti ti-whirl', - iconOnly: true, + key: 'global', + title: i18n.ts._timelines.global, + icon: 'ti ti-whirl', + iconOnly: true, }] : []), { - icon: 'ti ti-list', - title: i18n.ts.lists, - iconOnly: true, - onClick: chooseList, + icon: 'ti ti-list', + title: i18n.ts.lists, + iconOnly: true, + onClick: chooseList, }, { - icon: 'ti ti-antenna', - title: i18n.ts.antennas, - iconOnly: true, - onClick: chooseAntenna, + icon: 'ti ti-antenna', + title: i18n.ts.antennas, + iconOnly: true, + onClick: chooseAntenna, }, { - icon: 'ti ti-device-tv', - title: i18n.ts.channel, - iconOnly: true, - onClick: chooseChannel, + icon: 'ti ti-device-tv', + title: i18n.ts.channel, + iconOnly: true, + onClick: chooseChannel, }] as Tab[]); const headerTabsWhenNotLogin = computed(() => [ - ...(isLocalTimelineAvailable ? [{ - key: 'local', - title: i18n.ts._timelines.local, - icon: 'ti ti-planet', - iconOnly: true, - }] : []), - ...(isGlobalTimelineAvailable ? [{ - key: 'global', - title: i18n.ts._timelines.global, - icon: 'ti ti-whirl', - iconOnly: true, - }] : []), + ...(isLocalTimelineAvailable ? [{ + key: 'local', + title: i18n.ts._timelines.local, + icon: 'ti ti-planet', + iconOnly: true, + }] : []), + ...(isGlobalTimelineAvailable ? [{ + key: 'global', + title: i18n.ts._timelines.global, + icon: 'ti ti-whirl', + iconOnly: true, + }] : []), ] as Tab[]); definePageMetadata(computed(() => ({ - title: i18n.ts.timeline, - icon: src.value === 'local' ? 'ti ti-planet' : src.value === 'social' ? 'ti ti-universe' : src.value === 'global' ? 'ti ti-whirl' : 'ti ti-home', + title: i18n.ts.timeline, + icon: src.value === 'local' ? 'ti ti-planet' : src.value === 'social' ? 'ti ti-universe' : src.value === 'global' ? 'ti ti-whirl' : 'ti ti-home', }))); </script> diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index d4181d21d8..aba29558db 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -465,6 +465,86 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 44, }, + remoteLocalTimelineDomain1: { + where: 'account', + default: '', + }, + remoteLocalTimelineDomain2: { + where: 'account', + default: '', + }, + remoteLocalTimelineDomain3: { + where: 'account', + default: '', + }, + remoteLocalTimelineDomain4: { + where: 'account', + default: '', + }, + remoteLocalTimelineDomain5: { + where: 'account', + default: '', + }, + remoteLocalTimelineToken1: { + where: 'account', + default: '', + }, + remoteLocalTimelineToken2: { + where: 'account', + default: '', + }, + remoteLocalTimelineToken3: { + where: 'account', + default: '', + }, + remoteLocalTimelineToken4: { + where: 'account', + default: '', + }, + remoteLocalTimelineToken5: { + where: 'account', + default: '', + }, + remoteLocalTimelineEnable1: { + where: 'account', + default: false, + }, + remoteLocalTimelineEnable2: { + where: 'account', + default: false, + }, + remoteLocalTimelineEnable3: { + where: 'account', + default: false, + }, + remoteLocalTimelineEnable4: { + where: 'account', + default: false, + }, + remoteLocalTimelineEnable5: { + where: 'account', + default: false, + }, + remoteLocalTimelineName1: { + where: 'account', + default: 'custom timeline 1', + }, + remoteLocalTimelineName2: { + where: 'account', + default: 'custom timeline 2 ', + }, + remoteLocalTimelineName3: { + where: 'account', + default: 'custom timeline 3', + }, + remoteLocalTimelineName4: { + where: 'account', + default: 'custom timeline 4', + }, + remoteLocalTimelineName5: { + where: 'account', + default: 'custom timeline 5', + }, onlyAndWithSave: { where: 'device', default: false, From bacb98a1c18bf3902e379dfd88bf6a01bc2556b5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 17 Jan 2024 20:20:41 +0900 Subject: [PATCH 336/501] Feat: drop-and-fusion update --- packages/frontend/src/pages/drop-and-fusion.vue | 11 +++++++---- .../frontend/src/scripts/drop-and-fusion-engine.ts | 2 +- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 00a2dfe292..39313bd8c6 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="square">SQUARE</option> <option value="yen">YEN</option> <option value="sweets">SWEETS</option> - <option value="prismisskey">PRISMISSKEY</option> + <option value="prismisskey">PRISMISSKEY</option> </MkSelect> <MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton> </div> @@ -72,8 +72,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_s" style="padding: 16px;"> <div><b>Credit</b></div> <div> - <div>Ai-chan illustration: @poteriri@misskey.io</div> - <div>BGM: @ys@misskey.design</div> + <div>Ai-chan illustration: <MkA href="/@poteriri@misskey.io">@poteriri@misskey.io</MkA></div> + <div>BGM: <MkA href="/@ys@misskey.design">@ys@misskey.design</MkA></div> + <div>Emoji Thanks: <MkA to="/@User2_Moo@prismisskey.space">@User2_Moo@prismisskey.space</MkA></div> + <div>Emoji Thanks: <MkA to="/@z_n_jin@prismisskey.space">@z_n_jin@prismisskey.space</MkA></div> + <div>Emoji Thanks: <MkA to="/@nekomimi@prismisskey.space">@nekomimi@prismisskey.space</MkA></div> </div> </div> </div> @@ -109,7 +112,7 @@ function getScoreUnit(gameMode: string) { gameMode === 'square' ? 'pt' : gameMode === 'yen' ? '円' : gameMode === 'sweets' ? 'kcal' : - gameMode === 'prismisskey' ? 'pt' : + gameMode === 'prismisskey' ? 'pt' : '' as never; } diff --git a/packages/frontend/src/scripts/drop-and-fusion-engine.ts b/packages/frontend/src/scripts/drop-and-fusion-engine.ts index 4378bef5e4..1d55ea62d8 100644 --- a/packages/frontend/src/scripts/drop-and-fusion-engine.ts +++ b/packages/frontend/src/scripts/drop-and-fusion-engine.ts @@ -1563,7 +1563,7 @@ export class DropAndFusionGame extends EventEmitter<{ for (let i = 0; i < this.STOCK_MAX; i++) { this.stock.push({ id: this.rng().toString(), - mono: this.monoDefinitions[7], + mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], }); } this.emit('changeStock', this.stock); From 9b2ff43cc7802f4d44d6c974ac8996e263979dce Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 18 Jan 2024 15:53:56 +0900 Subject: [PATCH 337/501] Feat: drop-and-fusion update --- .../frontend/src/pages/drop-and-fusion.vue | 1 + .../src/scripts/drop-and-fusion-engine.ts | 24 +++++++++---------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue index 09ceeecb04..78d1c9f7b8 100644 --- a/packages/frontend/src/pages/drop-and-fusion.vue +++ b/packages/frontend/src/pages/drop-and-fusion.vue @@ -28,6 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="square">SQUARE</option> <option value="yen">YEN</option> <option value="sweets">SWEETS</option> + <option value="prismisskey">PRISMISSKEY</option> <!--<option value="space">SPACE</option>--> </MkSelect> <MkButton primary gradate large rounded inline @click="start">{{ i18n.ts.start }}</MkButton> diff --git a/packages/frontend/src/scripts/drop-and-fusion-engine.ts b/packages/frontend/src/scripts/drop-and-fusion-engine.ts index 89da97851e..624f39965a 100644 --- a/packages/frontend/src/scripts/drop-and-fusion-engine.ts +++ b/packages/frontend/src/scripts/drop-and-fusion-engine.ts @@ -981,8 +981,8 @@ const PRISMISSKEY_BASE_SIZE = 28; const PRISMISSKEY_MONOS: Mono[] = [{ id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', level: 10, - sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, - sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'custom', vertices: [[ { 'x': 1680, 'y': 270 }, @@ -1005,7 +1005,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ { 'x': 1400, 'y': 670 }, ]], verticesSize: 1800, - score: 512, + score: 10000, dropCandidate: false, }, { id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', @@ -1038,13 +1038,13 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1300, - score: 256, + score: 1000, dropCandidate: false, }, { id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', level: 8, - sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, - sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'custom', vertices: [ [ @@ -1072,7 +1072,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1300, - score: 128, + score: 800, dropCandidate: false, }, { id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', @@ -1088,7 +1088,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'circle', - score: 32, + score: 500, dropCandidate: false, }, { id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', @@ -1155,7 +1155,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, shape: 'rectangle', - score: 8, + score: 200, dropCandidate: true, }, { id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', @@ -1193,7 +1193,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 128, - score: 4, + score: 100, dropCandidate: true, }, { id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', @@ -1231,7 +1231,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1536, - score: 2, + score: 50, dropCandidate: true, }, { id: '35e476ee-44bd-4711-ad42-87be245d3efd', @@ -1268,7 +1268,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 128, - score: 1, + score: 10, dropCandidate: true, }]; export class DropAndFusionGame extends EventEmitter<{ From 0c600065da9122da3401cda4874acbf1fd00859b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 18 Jan 2024 22:17:36 +0900 Subject: [PATCH 338/501] Feat: drop-and-fusion update --- packages/frontend/src/pages/settings/general.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index d496dc349f..eb7a332385 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -511,7 +511,6 @@ async function remoteLocaltimelineSave() { type: 'success', text: i18n.ts.saved, }); - await reloadAsk(); defaultStore.set('remoteLocalTimelineDomain1', remoteLocalTimelineDomain1.value); defaultStore.set('remoteLocalTimelineToken1', remoteLocalTimelineToken1.value); defaultStore.set('remoteLocalTimelineDomain2', remoteLocalTimelineDomain2.value); @@ -527,7 +526,7 @@ async function remoteLocaltimelineSave() { defaultStore.set('remoteLocalTimelineName3', remoteLocalTimelineName3.value); defaultStore.set('remoteLocalTimelineName4', remoteLocalTimelineName4.value); defaultStore.set('remoteLocalTimelineName5', remoteLocalTimelineName5.value); - + await reloadAsk(); } const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; From 9a4b52a1bbe69cf785dda93dad395e083a68c9b7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 18 Jan 2024 22:55:46 +0900 Subject: [PATCH 339/501] wakariyasuku --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + .../frontend/src/pages/settings/general.vue | 215 +++++++++--------- 3 files changed, 114 insertions(+), 103 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index c9eeeb225d..0d205ef15a 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -658,6 +658,7 @@ export interface Locale { "medium": string; "small": string; "generateAccessToken": string; + "accessToken": string; "permission": string; "adminPermission": string; "enableAll": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5eb3c4c2d3..1fcc2b3b0a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -655,6 +655,7 @@ large: "大" medium: "中" small: "小" generateAccessToken: "アクセストークンの発行" +accessToken: "アクセストークン" permission: "権限" adminPermission: "管理者権限" enableAll: "全て有効にする" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index eb7a332385..63ad544526 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -159,7 +159,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFoldableSection> <MkFoldableSection :defaultOpen="false" class="item"> - <template #header>{{ i18n.ts.behavior }}</template> + <template #header>{{ i18n.ts.behavior }}</template> <div class="_gaps_m"> <div class="_gaps_s"> @@ -216,93 +216,102 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkFoldableSection> + <MkFoldableSection> + <template #header>他のサーバーのローカルタイムラインを覗けるようにする</template> + <div class="_gaps_m"> + <MkFoldableSection> + <template #header>{{ i18n.ts.accessToken }} の発行の仕方</template> + <img width="400" href="https://files.prismisskey.space/misskey/676e4b79-7897-4ea9-b074-a98139312f76.gif"> + </MkFoldableSection> + <div v-if="maxLocalTimeline >= 1" > + <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain1" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken1" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable1"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + <div v-if="maxLocalTimeline >= 2" > + <MkInput v-model="remoteLocalTimelineName2" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain2" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken2" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable2"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + + <div v-if="maxLocalTimeline >= 3" > + <MkInput v-model="remoteLocalTimelineName3" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain3" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken3" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable3"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + + <div v-if="maxLocalTimeline >= 4" > + <MkInput v-model="remoteLocalTimelineName4" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain4" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken4" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable4"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + + <div v-if="maxLocalTimeline >= 5" > + <MkInput v-model="remoteLocalTimelineName5" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain5" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken5" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable5"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + + <MkButton @click="remoteLocaltimelineSave"> + {{ i18n.ts.save }} + </MkButton> + </div> + </MkFoldableSection> <FormSection> <template #label>{{ i18n.ts.other }}</template> <div class="_gaps"> - <MkFolder> - <template #label>リモートのローカルタイムラインをTLの上のバーのやつに表示する</template> - <div style="padding: 0 16px;"> - <div v-if="maxLocalTimeline >= 1" style="padding: 16px 0 ;" > - <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> - <template #label>{{i18n.ts.name}}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain1" placeholder="prismisskey.space"> - <template #label>instance Url</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken1" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Token</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable1"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> - <div v-if="maxLocalTimeline >= 2" style="padding: 16px 0 ;" > - <MkInput v-model="remoteLocalTimelineName2" placeholder="prismisskey"> - <template #label>{{i18n.ts.name}}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain2" placeholder="prismisskey.space"> - <template #label>instance Url</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken2" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Token</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable2"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> - <div v-if="maxLocalTimeline >= 3" style="padding: 16px 0 ;" > - <MkInput v-model="remoteLocalTimelineName3" placeholder="prismisskey"> - <template #label>{{i18n.ts.name}}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain3" placeholder="prismisskey.space"> - <template #label>instance Url</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken3" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Token</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable3"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> - <div v-if="maxLocalTimeline >= 4" style="padding: 16px 0 ;" > - <MkInput v-model="remoteLocalTimelineName4" placeholder="prismisskey"> - <template #label>{{i18n.ts.name}}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain4" placeholder="prismisskey.space"/> - <MkInput v-model="remoteLocalTimelineToken4" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Token</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable4"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> - <div v-if="maxLocalTimeline >= 5" style="padding: 16px 0 ;" > - <MkInput v-model="remoteLocalTimelineName5" placeholder="prismisskey"> - <template #label>{{i18n.ts.name}}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain5" placeholder="prismisskey.space"> - <template #label>instance Url</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken5" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Token</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable5"> - {{ i18n.ts.enable }} - </MkSwitch> - - </div> - <MkButton @click="remoteLocaltimelineSave"> - {{ i18n.ts.save}} - </MkButton> - </div> - </MkFolder> <MkFolder> <template #label>{{ i18n.ts.additionalEmojiDictionary }}</template> <div class="_buttons"> @@ -507,26 +516,26 @@ watch([ }); async function remoteLocaltimelineSave() { - os.alert({ - type: 'success', - text: i18n.ts.saved, - }); - defaultStore.set('remoteLocalTimelineDomain1', remoteLocalTimelineDomain1.value); - defaultStore.set('remoteLocalTimelineToken1', remoteLocalTimelineToken1.value); - defaultStore.set('remoteLocalTimelineDomain2', remoteLocalTimelineDomain2.value); - defaultStore.set('remoteLocalTimelineToken2', remoteLocalTimelineToken2.value); - defaultStore.set('remoteLocalTimelineDomain3', remoteLocalTimelineDomain3.value); - defaultStore.set('remoteLocalTimelineToken3', remoteLocalTimelineToken3.value); - defaultStore.set('remoteLocalTimelineDomain4', remoteLocalTimelineDomain4.value); - defaultStore.set('remoteLocalTimelineToken4', remoteLocalTimelineToken4.value); - defaultStore.set('remoteLocalTimelineDomain5', remoteLocalTimelineDomain5.value); - defaultStore.set('remoteLocalTimelineToken5', remoteLocalTimelineToken5.value); - defaultStore.set('remoteLocalTimelineName1', remoteLocalTimelineName1.value); - defaultStore.set('remoteLocalTimelineName2', remoteLocalTimelineName2.value); - defaultStore.set('remoteLocalTimelineName3', remoteLocalTimelineName3.value); - defaultStore.set('remoteLocalTimelineName4', remoteLocalTimelineName4.value); - defaultStore.set('remoteLocalTimelineName5', remoteLocalTimelineName5.value); - await reloadAsk(); + os.alert({ + type: 'success', + text: i18n.ts.saved, + }); + defaultStore.set('remoteLocalTimelineDomain1', remoteLocalTimelineDomain1.value); + defaultStore.set('remoteLocalTimelineToken1', remoteLocalTimelineToken1.value); + defaultStore.set('remoteLocalTimelineDomain2', remoteLocalTimelineDomain2.value); + defaultStore.set('remoteLocalTimelineToken2', remoteLocalTimelineToken2.value); + defaultStore.set('remoteLocalTimelineDomain3', remoteLocalTimelineDomain3.value); + defaultStore.set('remoteLocalTimelineToken3', remoteLocalTimelineToken3.value); + defaultStore.set('remoteLocalTimelineDomain4', remoteLocalTimelineDomain4.value); + defaultStore.set('remoteLocalTimelineToken4', remoteLocalTimelineToken4.value); + defaultStore.set('remoteLocalTimelineDomain5', remoteLocalTimelineDomain5.value); + defaultStore.set('remoteLocalTimelineToken5', remoteLocalTimelineToken5.value); + defaultStore.set('remoteLocalTimelineName1', remoteLocalTimelineName1.value); + defaultStore.set('remoteLocalTimelineName2', remoteLocalTimelineName2.value); + defaultStore.set('remoteLocalTimelineName3', remoteLocalTimelineName3.value); + defaultStore.set('remoteLocalTimelineName4', remoteLocalTimelineName4.value); + defaultStore.set('remoteLocalTimelineName5', remoteLocalTimelineName5.value); + await reloadAsk(); } const emojiIndexLangs = ['en-US', 'ja-JP', 'ja-JP_hira'] as const; From c94474dc0c56475f65ea7f4261f543eb3280c57c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 18 Jan 2024 23:12:54 +0900 Subject: [PATCH 340/501] wakariyasuku --- packages/frontend/src/pages/settings/general.vue | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 63ad544526..7df18e2dcb 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -216,13 +216,13 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkFoldableSection> - <MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> <template #header>他のサーバーのローカルタイムラインを覗けるようにする</template> <div class="_gaps_m"> - <MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> <template #header>{{ i18n.ts.accessToken }} の発行の仕方</template> - <img width="400" href="https://files.prismisskey.space/misskey/676e4b79-7897-4ea9-b074-a98139312f76.gif"> + <img width="400" src="https://files.prismisskey.space/misskey/676e4b79-7897-4ea9-b074-a98139312f76.gif"> </MkFoldableSection> <div v-if="maxLocalTimeline >= 1" > <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> From 4f93ddaf620421a99540087d54410820279f1936 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 20 Jan 2024 17:30:29 +0900 Subject: [PATCH 341/501] wakariyasuku --- locales/index.d.ts | 268 ++++++++++-- packages/misskey-bubble-game/src/game.ts | 5 +- packages/misskey-bubble-game/src/monos.ts | 489 +--------------------- 3 files changed, 259 insertions(+), 503 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 1a7da7ded7..54b370bd0d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -60,11 +60,29 @@ export interface Locale extends ILocale { * OK */ "ok": string; + /** + * このプロファイルをデフォルトにしますか? + */ "setDefaultProfileConfirm": string; + /** + * 絵文字ピッカーのプロファイル + */ "emojiPickerProfile": string; + /** + * 通知のインジケーターの数字を表示する + */ "notificationIndicator": string; + /** + * アイコンとバナーを反転させる + */ "hanntenn": string; + /** + * ダークだったらライトのアイコンに、ライトだったらダークのアイコンに。 + */ "hanntennInfo": string; + /** + * ルビ + */ "ruby": string; /** * わかった @@ -83,9 +101,12 @@ export interface Locale extends ILocale { */ "enterUsername": string; /** - * {user}がリノート + * グローバルタイムラインを表示する */ "showGlobalTimeline": string; + /** + * {user}がリノート + */ "renotedBy": ParameterizedString<"user">; /** * ノートはありません @@ -327,13 +348,25 @@ export interface Locale extends ILocale { * ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。 */ "driveFileDeleteConfirm": ParameterizedString<"name">; + /** + * {name}つのファイルを削除しますか?このファイルを使用した一部のコンテンツも削除されます。 + */ + "driveFilesDeleteConfirm": ParameterizedString<"name">; + /** + * {name}つのファイルをセンシティブにしますか? + */ + "driveFilesSensitiveonConfirm": ParameterizedString<"name">; + /** + * {name}つのファイルのセンシティブを解除しますか? + */ + "driveFilesSensitiveoffConfirm": ParameterizedString<"name">; + /** + * フォルダ「{name}」を削除しますか?このフォルダの中に存在するファイルを使用した一部のコンテンツも削除されます。 + */ + "driveFolderDeleteConfirm": ParameterizedString<"name">; /** * {name}のフォローを解除しますか? */ - "driveFilesDeleteConfirm": string; - "driveFilesSensitiveonConfirm": string; - "driveFilesSensitiveoffConfirm": string; - "driveFolderDeleteConfirm": string; "unfollowConfirm": ParameterizedString<"name">; /** * エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。 @@ -700,20 +733,32 @@ export interface Locale extends ILocale { */ "flagAsCat": string; /** - * にゃにゃにゃ?? + * ウホウホウホホウホウホウホウホホホ!!!!!!!!!!! */ "flagAsGorilla": string; + /** + * にゃにゃにゃ?? + */ "flagAsCatDescription": string; + /** + * ウホウホウホ?? + */ + "flagAsGorillaDescription": string; /** * タイムラインにノートへの返信を表示する */ - "flagAsGorillaDescription": string; "flagShowTimelineReplies": string; + /** + * メディアタイムラインを表示する + */ + "showMediaTimeline": string; + /** + * オンにするとメディアタイムラインを上のバーに表示します。 オフにすると表示しなくなります。 + */ + "showMediaTimelineInfo": string; /** * オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。 */ - "showMediaTimeline": string; - "showMediaTimelineInfo": string; "flagShowTimelineRepliesDescription": string; /** * フォロー中ユーザーからのフォロリクを自動承認 @@ -1083,10 +1128,10 @@ export interface Locale extends ILocale { * 削除しました */ "removed": string; - "requestApprovalAreYouSure": string; - "removeAreYouSure": string; - "deleteAreYouSure": string; - "undraftAreYouSure": string; + /** + * 「{x}」のリクエストを承認しますか? + */ + "requestApprovalAreYouSure": ParameterizedString<"x">; /** * 「{x}」を削除しますか? */ @@ -1095,6 +1140,10 @@ export interface Locale extends ILocale { * 「{x}」を削除しますか? */ "deleteAreYouSure": ParameterizedString<"x">; + /** + * 「{x}」をドラフト解除しますか? + */ + "undraftAreYouSure": ParameterizedString<"x">; /** * リセットしますか? */ @@ -1119,6 +1168,9 @@ export interface Locale extends ILocale { * オリジナル画像を保持 */ "keepOriginalUploading": string; + /** + * ホーム投稿で通知する + */ "isNotifyIsHome": string; /** * 画像をアップロードする時にオリジナル版を保持します。オフにするとアップロード時にブラウザでWeb公開用画像を生成します。 @@ -1240,7 +1292,13 @@ export interface Locale extends ILocale { * ダークモードで使うテーマ */ "themeForDarkMode": string; + /** + * ゲーミングモード + */ "gamingMode": string; + /** + * ボタンなどの装飾をいい感じのグラデーションにします。 激しい点滅などはございません。 + */ "gamingModeInfo": string; /** * ライト @@ -1254,6 +1312,9 @@ export interface Locale extends ILocale { * 明るいテーマ */ "lightThemes": string; + /** + * アイコンなどが正常に表示されない場合にここをクリックしてください。 + */ "remoteUserInfoUpdate": string; /** * 暗いテーマ @@ -1307,6 +1368,9 @@ export interface Locale extends ILocale { * フォルダーを削除 */ "deleteFolder": string; + /** + * フォルダー + */ "Folder": string; /** * フォルダー @@ -2221,10 +2285,16 @@ export interface Locale extends ILocale { */ "showFixedPostFormInChannel": string; /** - * フォローする際、デフォルトで返信をTLに含むようにする + * ユーザーのページで最新のノートを表示する */ "FeaturedOrNote": string; + /** + * ユーザーのページに行ったときにハイライトか最新のノートを表示するかを選択することができます。 オフでハイライト オンで最新のノート です + */ "FeaturedOrNoteInfo": string; + /** + * フォローする際、デフォルトで返信をTLに含むようにする + */ "withRepliesByDefaultForNewlyFollowed": string; /** * 新しいノートがあります @@ -2470,6 +2540,9 @@ export interface Locale extends ILocale { * アンケート */ "poll": string; + /** + * 予約投稿 + */ "schedulePost": string; /** * 内容を隠す @@ -2563,6 +2636,9 @@ export interface Locale extends ILocale { * アクセストークンの発行 */ "generateAccessToken": string; + /** + * アクセストークン + */ "accessToken": string; /** * 権限 @@ -2700,6 +2776,9 @@ export interface Locale extends ILocale { * ログ */ "logs": string; + /** + * mfm 装飾 + */ "mfm": string; /** * 遅延 @@ -2753,7 +2832,13 @@ export interface Locale extends ILocale { * スペースで区切って複数設定できます。 */ "setMultipleBySeparatingWithSpace": string; + /** + * 名前には英数字と_が利用できます。 + */ "emojiNameValidation": string; + /** + * センシティブ + */ "isSensitive": string; /** * ファイルIDまたはURL @@ -2815,6 +2900,9 @@ export interface Locale extends ILocale { * 送信 */ "send": string; + /** + * ファイル付きのみ + */ "fileAttachedOnly": string; /** * 対応済みにする @@ -2944,7 +3032,13 @@ export interface Locale extends ILocale { * アンケートに投票した数 */ "pollVotesCount": string; + /** + * タイムラインの絞り込みを保存する + */ "onlyAndWithSave": string; + /** + * ファイルのみ や リプライのみ などが保存されるようになります + */ "onlyAndWithSaveInfo": string; /** * アンケートに投票された数 @@ -3430,8 +3524,17 @@ export interface Locale extends ILocale { * 低 */ "low": string; + /** + * 一覧 + */ "list": string; + /** + * ゲーミングの光るスピードの調整 + */ "GamingSpeedChange": string; + /** + * 左にすれば早くなる、右にすれば遅くなる。それだけ。 + */ "GamingSpeedChangeInfo": string; /** * メールアドレスの設定がされていません。 @@ -3441,8 +3544,17 @@ export interface Locale extends ILocale { * 比率 */ "ratio": string; + /** + * ノートの公開範囲を色付けする + */ "showVisibilityColor": string; + /** + * 新しい絵文字 + */ "newEmojis": string; + /** + * 申請されている絵文字 + */ "draftEmojis": string; /** * 本文をプレビュー @@ -4053,9 +4165,12 @@ export interface Locale extends ILocale { */ "manageCustomEmojis": string; /** - * アバターデコレーションの管理 + * カスタム絵文字のリクエスト */ "requestCustomEmojis": string; + /** + * アバターデコレーションの管理 + */ "manageAvatarDecorations": string; /** * これ以上作成することはできません。 @@ -4233,10 +4348,25 @@ export interface Locale extends ILocale { * ライセンス */ "license": string; + /** + * 申請中 + */ "requestPending": string; + /** + * 承認 + */ "approval": string; + /** + * リクエストされている絵文字 + */ "requestingEmojis": string; + /** + * ドラフト + */ "draft": string; + /** + * ドラフト解除 + */ "undrafted": string; /** * お気に入り解除しますか? @@ -4306,8 +4436,17 @@ export interface Locale extends ILocale { * データセーバー */ "dataSaver": string; + /** + * モバイルデータ通信でデータセーバーをオンにする + */ "cellularWithDataSaver": string; + /** + * 究極のデータセーバー + */ "UltimateDataSaver": string; + /** + * モバイルデータ通信で究極のデータセーバーをオンにする + */ "cellularWithUltimateDataSaver": string; /** * アカウントの移行 @@ -4685,10 +4824,6 @@ export interface Locale extends ILocale { * 相互フォロー */ "mutualFollow": string; - /** - * ファイル付きのみ - */ - "fileAttachedOnly": string; /** * TLに他の人への返信を含める */ @@ -6365,6 +6500,9 @@ export interface Locale extends ILocale { * グローバルタイムラインの閲覧 */ "gtlAvailable": string; + /** + * 絵文字ピッカーのプロファイルの上限数(最大5) + */ "emojiPickerProfileLimit": string; /** * ローカルタイムラインの閲覧 @@ -6375,10 +6513,16 @@ export interface Locale extends ILocale { */ "canPublicNote": string; /** - * サーバー招待コードの発行 + * ノートの編集 */ "canEditNote": string; + /** + * 予約投稿の許可 + */ "canScheduleNote": string; + /** + * サーバー招待コードの発行 + */ "canInvite": string; /** * 招待コードの作成可能数 @@ -6396,6 +6540,9 @@ export interface Locale extends ILocale { * カスタム絵文字の管理 */ "canManageCustomEmojis": string; + /** + * カスタム絵文字のリクエスト + */ "canRequestCustomEmojis": string; /** * アバターデコレーションの管理 @@ -6838,6 +6985,9 @@ export interface Locale extends ILocale { * ソースコード */ "source": string; + /** + * 当フォークのソースコード + */ "forksource": string; /** * Misskeyを翻訳 @@ -6983,6 +7133,9 @@ export interface Locale extends ILocale { * キーワードをスラッシュで囲むと正規表現になります。 */ "muteWordsDescription2": string; + /** + * ミュートされた単語を含むノートを非表示にする + */ "hideMutedNotes": string; }; "_instanceMute": { @@ -7426,15 +7579,45 @@ export interface Locale extends ILocale { "day": string; }; "_timelineTutorial": { + /** + * Misskeyの使い方 + */ "title": string; - "step1_1": string; - "step1_2": string; - "step1_3": string; + /** + * この画面は「タイムライン」です。{name}に投稿された「ノート」が時系列で表示されます。 + */ + "step1_1": ParameterizedString<"name">; + /** + * タイムラインにはいくつか種類があり、例えば「ホームタイムライン」にはあなたがフォローしている人のノートが流れ、「ローカルタイムライン」には{name}全体のノートが流れます。 + */ + "step1_2": ParameterizedString<"name">; + /** + * この2つ以外にも、「ソーシャルタイムライン」は ホームTL + ローカルTL のようなもので、 「メディアタイムライン」 には{name}で何かしらのファイル付きで投稿されたノートが流れます。 + */ + "step1_3": ParameterizedString<"name">; + /** + * 試しに、何かノートを投稿してみましょう。画面上にある鉛筆マークのボタンを押すとフォームが開きます。 + */ "step2_1": string; - "step2_2": string; + /** + * 初めてのノートの内容は、あなたの自己紹介や「{name}始めました」などがおすすめです。 + */ + "step2_2": ParameterizedString<"name">; + /** + * 投稿できましたか? + */ "step3_1": string; + /** + * あなたのノートがタイムラインに表示されていれば成功です。 + */ "step3_2": string; + /** + * ノートには、「リアクション」を付けることができます。 + */ "step4_1": string; + /** + * リアクションを付けるには、ノートの「+」マークをクリックして、好きな絵文字を選択します。 + */ "step4_2": string; }; "_2fa": { @@ -8000,7 +8183,13 @@ export interface Locale extends ILocale { * 通知 */ "notifications": string; + /** + * ゲーミングモード + */ "gamingMode": string; + /** + * 反転モード + */ "gyakubariMode": string; /** * タイムライン @@ -8497,9 +8686,12 @@ export interface Locale extends ILocale { */ "local": string; /** - * ソーシャル + * メディア */ "media": string; + /** + * ソーシャル + */ "social": string; /** * グローバル @@ -9437,13 +9629,37 @@ export interface Locale extends ILocale { }; }; "_schedulePost": { + /** + * 予約投稿一覧 + */ "list": string; + /** + * 日付 + */ "postDate": string; + /** + * 時刻 + */ "postTime": string; + /** + * 端末に設定されているタイムゾーンの時刻で投稿されます。 + */ "localTime": string; + /** + * 予約設定 + */ "addSchedule": string; - "willBePostedAtX": string; + /** + * {date}に投稿予約しました。 + */ + "willBePostedAtX": ParameterizedString<"date">; + /** + * 予約投稿を削除しますか? + */ "deleteAreYouSure": string; + /** + * 予約投稿を削除して編集しますか? + */ "deleteAndEditConfirm": string; }; "_dataSaver": { diff --git a/packages/misskey-bubble-game/src/game.ts b/packages/misskey-bubble-game/src/game.ts index e01a011eee..7c99f83959 100644 --- a/packages/misskey-bubble-game/src/game.ts +++ b/packages/misskey-bubble-game/src/game.ts @@ -6,7 +6,7 @@ import { EventEmitter } from 'eventemitter3'; import * as Matter from 'matter-js'; import seedrandom from 'seedrandom'; -import { NORAML_MONOS, SQUARE_MONOS, SWEETS_MONOS, YEN_MONOS } from './monos.js'; +import { NORAML_MONOS, PRISMISSKEY_MONOS, SQUARE_MONOS, SWEETS_MONOS, YEN_MONOS } from './monos.js'; export type Mono = { id: string; @@ -58,7 +58,7 @@ export class DropAndFusionGame extends EventEmitter<{ private tickCallbackQueue: { frame: number; callback: () => void; }[] = []; private overflowCollider: Matter.Body; private isGameOver = false; - private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'space'; + private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'space' | 'prismisskey'; private rng: () => number; private logs: Log[] = []; @@ -87,6 +87,7 @@ export class DropAndFusionGame extends EventEmitter<{ case 'square': return SQUARE_MONOS; case 'sweets': return SWEETS_MONOS; case 'space': return NORAML_MONOS; + case 'prismisskey' : return PRISMISSKEY_MONOS; } } diff --git a/packages/misskey-bubble-game/src/monos.ts b/packages/misskey-bubble-game/src/monos.ts index d1b1b0c5da..eb122c6749 100644 --- a/packages/misskey-bubble-game/src/monos.ts +++ b/packages/misskey-bubble-game/src/monos.ts @@ -950,13 +950,14 @@ export const SWEETS_MONOS: Mono[] = [{ score: 30, dropCandidate: true, }]; + const PRISMISSKEY_BASE_SIZE = 28; -const PRISMISSKEY_MONOS: Mono[] = [{ +export const PRISMISSKEY_MONOS: Mono[] = [{ id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', level: 10, - sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, - sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'custom', vertices: [[ { 'x': 1680, 'y': 270 }, @@ -979,7 +980,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ { 'x': 1400, 'y': 670 }, ]], verticesSize: 1800, - score: 10000, + score: 512, dropCandidate: false, }, { id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', @@ -1012,13 +1013,13 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1300, - score: 1000, + score: 256, dropCandidate: false, }, { id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', level: 8, - sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, - sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'custom', vertices: [ [ @@ -1046,7 +1047,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1300, - score: 800, + score: 128, dropCandidate: false, }, { id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', @@ -1062,7 +1063,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'circle', - score: 500, + score: 32, dropCandidate: false, }, { id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', @@ -1129,7 +1130,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, shape: 'rectangle', - score: 200, + score: 8, dropCandidate: true, }, { id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', @@ -1167,7 +1168,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 128, - score: 100, + score: 4, dropCandidate: true, }, { id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', @@ -1205,7 +1206,7 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1536, - score: 50, + score: 2, dropCandidate: true, }, { id: '35e476ee-44bd-4711-ad42-87be245d3efd', @@ -1242,468 +1243,6 @@ const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 128, - score: 10, + score: 1, dropCandidate: true, }]; -export class DropAndFusionGame extends EventEmitter<{ - changeScore: (newScore: number) => void; - changeCombo: (newCombo: number) => void; - changeStock: (newStock: { id: string; mono: Mono }[]) => void; - changeHolding: (newHolding: { id: string; mono: Mono } | null) => void; - dropped: (x: number) => void; - fusioned: (x: number, y: number, nextMono: Mono | null, scoreDelta: number) => void; - collision: (energy: number, bodyA: Matter.Body, bodyB: Matter.Body) => void; - monoAdded: (mono: Mono) => void; - gameOver: () => void; -}> { - private PHYSICS_QUALITY_FACTOR = 16; // 低いほどパフォーマンスが高いがガタガタして安定しなくなる、逆に高すぎても何故か不安定になる - private COMBO_INTERVAL = 60; // frame - public readonly GAME_VERSION = 3; - public readonly GAME_WIDTH = 450; - public readonly GAME_HEIGHT = 600; - public readonly DROP_COOLTIME = 30; // frame - public readonly PLAYAREA_MARGIN = 25; - private STOCK_MAX = 4; - private TICK_DELTA = 1000 / 60; // 60fps - - public frame = 0; - public engine: Matter.Engine; - private tickCallbackQueue: { frame: number; callback: () => void; }[] = []; - private overflowCollider: Matter.Body; - private isGameOver = false; - private gameMode: 'normal' | 'yen' | 'square' | 'sweets' | 'space' | 'prismisskey'; - private rng: () => number; - private logs: Log[] = []; - - /** - * フィールドに出ていて、かつ合体の対象となるアイテム - */ - private fusionReadyBodyIds: Matter.Body['id'][] = []; - - private gameOverReadyBodyIds: Matter.Body['id'][] = []; - - /** - * fusion予約アイテムのペア - * TODO: これらのモノは光らせるなどの演出をすると視覚的に楽しそう - */ - private fusionReservedPairs: { bodyA: Matter.Body; bodyB: Matter.Body }[] = []; - - private latestDroppedAt = 0; // frame - private latestFusionedAt = 0; // frame - private stock: { id: string; mono: Mono }[] = []; - private holding: { id: string; mono: Mono } | null = null; - - public get monoDefinitions() { - switch (this.gameMode) { - case 'normal': return NORAML_MONOS; - case 'yen': return YEN_MONOS; - case 'square': return SQUARE_MONOS; - case 'sweets': return SWEETS_MONOS; - case 'prismisskey': return PRISMISSKEY_MONOS; - case 'space': return NORAML_MONOS; - } - } - - private _combo = 0; - private get combo() { - return this._combo; - } - private set combo(value: number) { - this._combo = value; - this.emit('changeCombo', value); - } - - private _score = 0; - private get score() { - return this._score; - } - private set score(value: number) { - this._score = value; - this.emit('changeScore', value); - } - - private getMonoRenderOptions: null | ((mono: Mono) => Partial<Matter.IBodyRenderOptions>) = null; - - public replayPlaybackRate = 1; - - constructor(env: { - seed: string; - gameMode: DropAndFusionGame['gameMode']; - getMonoRenderOptions?: (mono: Mono) => Partial<Matter.IBodyRenderOptions>; - }) { - super(); - - //#region BIND - this.tick = this.tick.bind(this); - //#endregion - - this.gameMode = env.gameMode; - this.getMonoRenderOptions = env.getMonoRenderOptions ?? null; - this.rng = seedrandom(env.seed); - - // sweetsモードは重いため - const physicsQualityFactor = this.gameMode === 'sweets' ? 4 : this.PHYSICS_QUALITY_FACTOR; - this.engine = Matter.Engine.create({ - constraintIterations: 2 * physicsQualityFactor, - positionIterations: 6 * physicsQualityFactor, - velocityIterations: 4 * physicsQualityFactor, - gravity: { - x: 0, - y: this.gameMode === 'space' ? 0.0125 : 1, - }, - timing: { - timeScale: 2, - }, - enableSleeping: false, - }); - - this.engine.world.bodies = []; - - //#region walls - const WALL_OPTIONS: Matter.IChamferableBodyDefinition = { - label: '_wall_', - isStatic: true, - friction: 0.7, - slop: this.gameMode === 'space' ? 0.01 : 0.7, - render: { - strokeStyle: 'transparent', - fillStyle: 'transparent', - }, - }; - - const thickness = 100; - Matter.Composite.add(this.engine.world, [ - Matter.Bodies.rectangle(this.GAME_WIDTH / 2, this.GAME_HEIGHT + (thickness / 2) - this.PLAYAREA_MARGIN, this.GAME_WIDTH, thickness, WALL_OPTIONS), - Matter.Bodies.rectangle(this.GAME_WIDTH + (thickness / 2) - this.PLAYAREA_MARGIN, this.GAME_HEIGHT / 2, thickness, this.GAME_HEIGHT, WALL_OPTIONS), - Matter.Bodies.rectangle(-((thickness / 2) - this.PLAYAREA_MARGIN), this.GAME_HEIGHT / 2, thickness, this.GAME_HEIGHT, WALL_OPTIONS), - ]); - //#endregion - - this.overflowCollider = Matter.Bodies.rectangle(this.GAME_WIDTH / 2, 0, this.GAME_WIDTH, 200, { - label: '_overflow_', - isStatic: true, - isSensor: true, - render: { - strokeStyle: 'transparent', - fillStyle: 'transparent', - }, - }); - Matter.Composite.add(this.engine.world, this.overflowCollider); - } - - public msToFrame(ms: number) { - return Math.round(ms / this.TICK_DELTA); - } - - public frameToMs(frame: number) { - return frame * this.TICK_DELTA; - } - - private createBody(mono: Mono, x: number, y: number) { - const options: Matter.IBodyDefinition = { - label: mono.id, - density: this.gameMode === 'space' ? 0.01 : ((mono.sizeX * mono.sizeY) / 10000), - restitution: this.gameMode === 'space' ? 0.5 : 0.2, - frictionAir: this.gameMode === 'space' ? 0 : 0.01, - friction: this.gameMode === 'space' ? 0.5 : 0.7, - frictionStatic: this.gameMode === 'space' ? 0 : 5, - slop: this.gameMode === 'space' ? 0.01 : 0.7, - //mass: 0, - render: this.getMonoRenderOptions ? this.getMonoRenderOptions(mono) : undefined, - }; - if (mono.shape === 'circle') { - return Matter.Bodies.circle(x, y, mono.sizeX / 2, options); - // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - } else if (mono.shape === 'rectangle') { - return Matter.Bodies.rectangle(x, y, mono.sizeX, mono.sizeY, options); - } else if (mono.shape === 'custom') { - return Matter.Bodies.fromVertices(x, y, mono.vertices!.map(i => i.map(j => ({ - x: (j.x / mono.verticesSize!) * mono.sizeX, - y: (j.y / mono.verticesSize!) * mono.sizeY, - }))), options); - } else { - throw new Error('unrecognized shape'); - } - } - - private fusion(bodyA: Matter.Body, bodyB: Matter.Body) { - if (this.latestFusionedAt > this.frame - this.COMBO_INTERVAL) { - this.combo++; - } else { - this.combo = 1; - } - this.latestFusionedAt = this.frame; - - const newX = (bodyA.position.x + bodyB.position.x) / 2; - const newY = (bodyA.position.y + bodyB.position.y) / 2; - - this.fusionReadyBodyIds = this.fusionReadyBodyIds.filter(x => x !== bodyA.id && x !== bodyB.id); - this.gameOverReadyBodyIds = this.gameOverReadyBodyIds.filter(x => x !== bodyA.id && x !== bodyB.id); - Matter.Composite.remove(this.engine.world, [bodyA, bodyB]); - - const currentMono = this.monoDefinitions.find(y => y.id === bodyA.label)!; - const nextMono = this.monoDefinitions.find(x => x.level === currentMono.level + 1) ?? null; - - if (nextMono) { - const body = this.createBody(nextMono, newX, newY); - Matter.Composite.add(this.engine.world, body); - - // 連鎖してfusionした場合の分かりやすさのため少し間を置いてからfusion対象になるようにする - this.tickCallbackQueue.push({ - frame: this.frame + this.msToFrame(100), - callback: () => { - this.fusionReadyBodyIds.push(body.id); - }, - }); - - this.emit('monoAdded', nextMono); - } - - const hasComboBonus = this.gameMode !== 'yen' && this.gameMode !== 'sweets'; - const comboBonus = hasComboBonus ? 1 + ((this.combo - 1) / 5) : 1; - const additionalScore = Math.round(currentMono.score * comboBonus); - this.score += additionalScore; - - this.emit('fusioned', newX, newY, nextMono, additionalScore); - } - - private onCollision(event: Matter.IEventCollision<Matter.Engine>) { - for (const pairs of event.pairs) { - const { bodyA, bodyB } = pairs; - - const shouldFusion = (bodyA.label === bodyB.label) && - !this.fusionReservedPairs.some(x => - x.bodyA.id === bodyA.id || - x.bodyA.id === bodyB.id || - x.bodyB.id === bodyA.id || - x.bodyB.id === bodyB.id); - - if (shouldFusion) { - if (this.fusionReadyBodyIds.includes(bodyA.id) && this.fusionReadyBodyIds.includes(bodyB.id)) { - this.fusion(bodyA, bodyB); - } else { - this.fusionReservedPairs.push({ bodyA, bodyB }); - this.tickCallbackQueue.push({ - frame: this.frame + this.msToFrame(100), - callback: () => { - this.fusionReservedPairs = this.fusionReservedPairs.filter(x => x.bodyA.id !== bodyA.id && x.bodyB.id !== bodyB.id); - this.fusion(bodyA, bodyB); - }, - }); - } - } else { - const energy = pairs.collision.depth; - - if (bodyA.label === '_overflow_' || bodyB.label === '_overflow_') continue; - - if (bodyA.label !== '_wall_' && bodyB.label !== '_wall_') { - if (!this.gameOverReadyBodyIds.includes(bodyA.id)) this.gameOverReadyBodyIds.push(bodyA.id); - if (!this.gameOverReadyBodyIds.includes(bodyB.id)) this.gameOverReadyBodyIds.push(bodyB.id); - } - - this.emit('collision', energy, bodyA, bodyB); - } - } - } - - private onCollisionActive(event: Matter.IEventCollision<Matter.Engine>) { - for (const pairs of event.pairs) { - const { bodyA, bodyB } = pairs; - - // ハコからあふれたかどうかの判定 - if (bodyA.id === this.overflowCollider.id || bodyB.id === this.overflowCollider.id) { - if (this.gameOverReadyBodyIds.includes(bodyA.id) || this.gameOverReadyBodyIds.includes(bodyB.id)) { - this.gameOver(); - break; - } - continue; - } - } - } - - public surrender() { - this.logs.push({ - frame: this.frame, - operation: 'surrender', - }); - - this.gameOver(); - } - - private gameOver() { - this.isGameOver = true; - this.emit('gameOver'); - } - - public start() { - for (let i = 0; i < this.STOCK_MAX; i++) { - this.stock.push({ - id: this.rng().toString(), - mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], - }); - } - this.emit('changeStock', this.stock); - - Matter.Events.on(this.engine, 'collisionStart', this.onCollision.bind(this)); - Matter.Events.on(this.engine, 'collisionActive', this.onCollisionActive.bind(this)); - } - - public getLogs() { - return this.logs; - } - - public tick() { - this.frame++; - - if (this.latestFusionedAt < this.frame - this.COMBO_INTERVAL) { - this.combo = 0; - } - - this.tickCallbackQueue = this.tickCallbackQueue.filter(x => { - if (x.frame === this.frame) { - x.callback(); - return false; - } else { - return true; - } - }); - - Matter.Engine.update(this.engine, this.TICK_DELTA); - - const hasNextTick = !this.isGameOver; - - return hasNextTick; - } - - public getActiveMonos() { - return this.engine.world.bodies.map(x => this.monoDefinitions.find((mono) => mono.id === x.label)!).filter(x => x !== undefined); - } - - public drop(_x: number) { - if (this.isGameOver) return; - if (this.frame - this.latestDroppedAt < this.DROP_COOLTIME) return; - - const head = this.stock.shift()!; - this.stock.push({ - id: this.rng().toString(), - mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], - }); - this.emit('changeStock', this.stock); - - const inputX = Math.round(_x); - const x = Math.min(this.GAME_WIDTH - this.PLAYAREA_MARGIN - (head.mono.sizeX / 2), Math.max(this.PLAYAREA_MARGIN + (head.mono.sizeX / 2), inputX)); - const body = this.createBody(head.mono, x, 50 + head.mono.sizeY / 2); - this.logs.push({ - frame: this.frame, - operation: 'drop', - x: inputX, - }); - - // add force - if (this.gameMode === 'space') { - Matter.Body.applyForce(body, body.position, { - x: 0, - y: (Math.PI * head.mono.sizeX * head.mono.sizeY) / 65536, - }); - } - - Matter.Composite.add(this.engine.world, body); - - this.fusionReadyBodyIds.push(body.id); - this.latestDroppedAt = this.frame; - - this.emit('dropped', x); - this.emit('monoAdded', head.mono); - } - - public hold() { - if (this.isGameOver) return; - - this.logs.push({ - frame: this.frame, - operation: 'hold', - }); - - if (this.holding) { - const head = this.stock.shift()!; - this.stock.unshift(this.holding); - this.holding = head; - this.emit('changeHolding', this.holding); - this.emit('changeStock', this.stock); - } else { - const head = this.stock.shift()!; - this.holding = head; - this.stock.push({ - id: this.rng().toString(), - mono: this.monoDefinitions.filter(x => x.dropCandidate)[Math.floor(this.rng() * this.monoDefinitions.filter(x => x.dropCandidate).length)], - }); - this.emit('changeHolding', this.holding); - this.emit('changeStock', this.stock); - } - } - - public static serializeLogs(logs: Log[]) { - const _logs: number[][] = []; - - for (let i = 0; i < logs.length; i++) { - const log = logs[i]; - const frameDelta = i === 0 ? log.frame : log.frame - logs[i - 1].frame; - - switch (log.operation) { - case 'drop': - _logs.push([frameDelta, 0, log.x]); - break; - case 'hold': - _logs.push([frameDelta, 1]); - break; - case 'surrender': - _logs.push([frameDelta, 2]); - break; - } - } - - return _logs; - } - - public static deserializeLogs(logs: number[][]) { - const _logs: Log[] = []; - - let frame = 0; - - for (const log of logs) { - const frameDelta = log[0]; - frame += frameDelta; - - const operation = log[1]; - - switch (operation) { - case 0: - _logs.push({ - frame, - operation: 'drop', - x: log[2], - }); - break; - case 1: - _logs.push({ - frame, - operation: 'hold', - }); - break; - case 2: - _logs.push({ - frame, - operation: 'surrender', - }); - break; - } - } - - return _logs; - } - - public dispose() { - Matter.World.clear(this.engine.world, false); - Matter.Engine.clear(this.engine); - } -} From f701fbfbebb551bae222aa87a98a42c28690ab7a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 20 Jan 2024 23:02:52 +0900 Subject: [PATCH 342/501] wakariyasuku --- packages/misskey-bubble-game/src/monos.ts | 24 +++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/misskey-bubble-game/src/monos.ts b/packages/misskey-bubble-game/src/monos.ts index eb122c6749..2df840a187 100644 --- a/packages/misskey-bubble-game/src/monos.ts +++ b/packages/misskey-bubble-game/src/monos.ts @@ -956,8 +956,8 @@ const PRISMISSKEY_BASE_SIZE = 28; export const PRISMISSKEY_MONOS: Mono[] = [{ id: 'f75fd0ba-d3d4-40a4-9712-b470e45b0525', level: 10, - sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, - sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'custom', vertices: [[ { 'x': 1680, 'y': 270 }, @@ -980,7 +980,7 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ { 'x': 1400, 'y': 670 }, ]], verticesSize: 1800, - score: 512, + score: 10000, dropCandidate: false, }, { id: '7b70f4af-1c01-45fd-af72-61b1f01e03d1', @@ -1013,13 +1013,13 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1300, - score: 256, + score: 1000, dropCandidate: false, }, { id: '41607ef3-b6d6-4829-95b6-3737bf8bb956', level: 8, - sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, - sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, + sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'custom', vertices: [ [ @@ -1047,7 +1047,7 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1300, - score: 128, + score: 800, dropCandidate: false, }, { id: '8a8310d2-0374-460f-bb50-ca9cd3ee3416', @@ -1063,7 +1063,7 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25 * 1.25 * 1.25, shape: 'circle', - score: 32, + score: 500, dropCandidate: false, }, { id: '2294734d-7bb8-4781-bb7b-ef3820abf3d0', @@ -1130,7 +1130,7 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ sizeX: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, sizeY: PRISMISSKEY_BASE_SIZE * 1.25 * 1.25 * 1.25, shape: 'rectangle', - score: 8, + score: 200, dropCandidate: true, }, { id: 'd0c74815-fc1c-4fbe-9953-c92e4b20f919', @@ -1168,7 +1168,7 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 128, - score: 4, + score: 100, dropCandidate: true, }, { id: 'd8fbd70e-611d-402d-87da-1a7fd8cd2c8d', @@ -1206,7 +1206,7 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 1536, - score: 2, + score: 50, dropCandidate: true, }, { id: '35e476ee-44bd-4711-ad42-87be245d3efd', @@ -1243,6 +1243,6 @@ export const PRISMISSKEY_MONOS: Mono[] = [{ ], ], verticesSize: 128, - score: 1, + score: 10, dropCandidate: true, }]; From 7c50a574f814e1075ad6633b9e52bbab29a1187a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 21 Jan 2024 13:43:42 +0900 Subject: [PATCH 343/501] 2023.12.2-PrisMisskey.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b90f51d470..d57de73833 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2023.12.2-PrisMisskey.5", + "version": "2023.12.2-PrisMisskey.6", "codename": "nasubi", "repository": { "type": "git", From 0eacd5e1f7ea6c63cd9a171ffab3c6d5991544a1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 23 Jan 2024 13:06:33 +0900 Subject: [PATCH 344/501] localOnly --- packages/frontend/src/components/MkPostForm.vue | 14 ++++++++++++++ packages/frontend/src/pages/settings/privacy.vue | 6 +++++- packages/frontend/src/store.ts | 8 ++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 2eaf88446d..529257b682 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -208,6 +208,7 @@ const showAddMfmFunction = ref(defaultStore.state.enableQuickAddMfmFunction); watch(showAddMfmFunction, () => defaultStore.set('enableQuickAddMfmFunction', showAddMfmFunction.value)); const cw = ref<string | null>(props.initialCw ?? null); const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly); + const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]); const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]); if (props.initialVisibleUsers) { @@ -290,6 +291,19 @@ watch(text, () => { }, { immediate: true }); watch(visibility, () => { + + switch (visibility.value) { + case 'public': + localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; + break; + case 'home': + localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultHomeNoteLocalOnly; + break; + case 'followers': + localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultFollowersNoteLocalOnly; + break; + } + checkMissingMention(); }, { immediate: true }); diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue index 3698590d51..732770c691 100644 --- a/packages/frontend/src/pages/settings/privacy.vue +++ b/packages/frontend/src/pages/settings/privacy.vue @@ -61,7 +61,9 @@ SPDX-License-Identifier: AGPL-3.0-only <option value="followers">{{ i18n.ts._visibility.followers }}</option> <option value="specified">{{ i18n.ts._visibility.specified }}</option> </MkSelect> - <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.disableFederation }}</MkSwitch> + <MkSwitch v-model="defaultNoteLocalOnly">{{ i18n.ts._visibility.public }}+{{ i18n.ts._visibility.disableFederation }}</MkSwitch> + <MkSwitch v-model="defaultHomeNoteLocalOnly">{{ i18n.ts._visibility.home }}+{{ i18n.ts._visibility.disableFederation }}</MkSwitch> + <MkSwitch v-model="defaultFollowersNoteLocalOnly">{{ i18n.ts._visibility.followers }}+{{ i18n.ts._visibility.disableFederation }}</MkSwitch> </div> </MkFolder> </div> @@ -97,6 +99,8 @@ const followersVisibility = ref($i.followersVisibility); const defaultNoteVisibility = computed(defaultStore.makeGetterSetter('defaultNoteVisibility')); const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly')); +const defaultHomeNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultHomeNoteLocalOnly')); +const defaultFollowersNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultFollowersNoteLocalOnly')); const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberNoteVisibility')); const keepCw = computed(defaultStore.makeGetterSetter('keepCw')); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 16f85aed0f..cb5f1b3416 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -104,6 +104,14 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: false, }, + defaultHomeNoteLocalOnly: { + where: 'account', + default: false, + }, + defaultFollowersNoteLocalOnly: { + where: 'account', + default: false, + }, uploadFolder: { where: 'account', default: null as string | null, From 7dd7c6ad4af288bd9c759331ef7dc6128f1a03be Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 23 Jan 2024 21:01:55 +0900 Subject: [PATCH 345/501] localOnly --- packages/frontend/src/components/MkTimeline.vue | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 004f53381c..c022565c24 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -204,7 +204,7 @@ function updatePaginationQuery() { query = { withFiles: true, withRenotes: props.withRenotes, - withReplies: props.withReplies, + withReplies: false, }; } else if (props.src === 'mentions') { endpoint = 'notes/mentions'; @@ -232,12 +232,12 @@ function updatePaginationQuery() { roleId: props.role, }; } else if (props.src.startsWith('custom-timeline')) { - endpoint = "notes/any-local-timeline"; - query = { - host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split("-")[2]}`], - remoteToken:defaultStore.state[`remoteLocalTimelineToken${props.src.split("-")[2]}`] - }; - }else { + endpoint = 'notes/any-local-timeline'; + query = { + host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split('-')[2]}`], + remoteToken: defaultStore.state[`remoteLocalTimelineToken${props.src.split('-')[2]}`], + }; + } else { endpoint = null; query = null; } From 6286e94f66215d8d26fbaebbe7c62ad15db3727d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Jan 2024 01:14:06 +0900 Subject: [PATCH 346/501] ui fix --- packages/frontend/src/components/MkDrive.vue | 18 +++++++++-------- .../components/global/MkPageHeader.tabs.vue | 3 ++- .../src/components/global/MkPageHeader.vue | 5 +++-- packages/frontend/src/pages/notifications.vue | 15 +++++--------- packages/frontend/src/pages/timeline.vue | 20 +++++++++---------- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 660072f478..2c80a5bafa 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <XNavFolder :class="[$style.navPathItem, { [$style.navCurrent]: folder == null }]" :parentFolder="folder" - :selectedFiles="selectedFiles" + :selectedFiles="selectedFiles" @move="move" @upload="upload" @removeFile="removeFile" @@ -35,9 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="folder != null" :class="[$style.navPathItem, $style.navCurrent]">{{ folder.name }}</span> </div> <button v-if="!multiple" class="_button" :class="$style.navMenu" @click="filesSelect">複数選択モード</button> - <span v-if="multiple && selectedFiles.length > 0" style="padding-right: 12px; margin-top: auto; margin-bottom: auto;opacity: 0.5;"> - ({{ number(selectedFiles.length) }}) - </span> + <span v-if="multiple && selectedFiles.length > 0" style="padding-right: 12px; margin-top: auto; margin-bottom: auto;opacity: 0.5;"> + ({{ number(selectedFiles.length) }}) + </span> <button v-if="multiple" class="_button" :class="$style.navMenu" @click="filesSelect">複数選択モード解除</button> <button v-if="multiple && selectedFiles.length === 0" style="padding-right: 12px;" class="_button" @click="filesAllSelect"> 全選択 @@ -66,7 +66,7 @@ SPDX-License-Identifier: AGPL-3.0-only :folder="f" :selectMode="select === 'folder'" :isSelected="selectedFolders.some(x => x.id === f.id)" - :selectedFiles="selectedFiles" + :selectedFiles="selectedFiles" @chosen="chooseFolder" @move="move" @upload="upload" @@ -90,7 +90,7 @@ SPDX-License-Identifier: AGPL-3.0-only :selectMode="select === 'file'" :SelectFiles="selectedFiles" :isSelected="selectedFiles.some(x => x.id === file.id)" - @click.shift.left.exact="filesSelect" + @click.shift.left.exact="filesSelect" @chosen="chooseFile" @dragstart="isDragSource = true" @dragend="isDragSource = false" @@ -133,7 +133,7 @@ import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; import { uploadFile, uploads } from '@/scripts/upload.js'; import { claimAchievement } from '@/scripts/achievements.js'; -import number from "@/filters/number.js"; +import number from '@/filters/number.js'; const props = withDefaults(defineProps<{ initialFolder?: Misskey.entities.DriveFolder; @@ -525,7 +525,7 @@ function removeFolder(folderToRemove: Misskey.entities.DriveFolder | string) { function removeFile(file: Misskey.entities.DriveFile | string) { const fileId = typeof file === 'object' ? file.id : file; files.value = files.value.filter(f => f.id !== fileId); - selectedFiles.value = selectedFiles.value.filter(f => f.id !== fileId); + selectedFiles.value = selectedFiles.value.filter(f => f.id !== fileId); } function appendFile(file: Misskey.entities.DriveFile) { @@ -834,6 +834,7 @@ onBeforeUnmount(() => { font-size: 0.9em; box-shadow: 0 1px 0 var(--divider); user-select: none; + height: 55px; } .navPath { @@ -841,6 +842,7 @@ onBeforeUnmount(() => { vertical-align: bottom; line-height: 42px; white-space: nowrap; + margin: auto 0; } .navPathItem { diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index adbb270b14..bda76a2c2e 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -53,7 +53,7 @@ export type Tab = { </script> <script lang="ts" setup> -import {onMounted, onUnmounted, watch, nextTick, shallowRef, ref, computed} from 'vue'; +import { onMounted, onUnmounted, watch, nextTick, shallowRef, ref, computed } from 'vue'; import { defaultStore } from '@/store.js'; const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); @@ -232,6 +232,7 @@ onUnmounted(() => { .tabIcon + .tabTitle { padding-left: 4px; + font-weight: 900; } .tabTitle { diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 8624aebdcf..16e2eb877f 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/> <template v-if="metadata"> - <div v-if="!hideTitle" :class="$style.titleContainer" @click="top"> + <div v-if="!hideTitle && !hide" :class="$style.titleContainer" @click="top"> <div v-if="metadata.avatar" :class="$style.titleAvatarContainer"> <MkAvatar :class="$style.titleAvatar" :user="metadata.avatar" indicator/> </div> @@ -56,6 +56,7 @@ const props = withDefaults(defineProps<{ actions?: PageHeaderItem[] | null; thin?: boolean; displayMyAvatar?: boolean; + hide?:boolean; }>(), { tabs: () => ([] as Tab[]), }); @@ -143,7 +144,7 @@ onUnmounted(() => { } .upper { - --height: 50px; + --height: 55px; display: flex; gap: var(--margin); height: var(--height); diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index e6098c90b3..cc32ef51d0 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> + <template #header><MkPageHeader v-model:tab="tab" :tabs="headerTabs" hide="true" :actions="headerActions"/></template> <MkSpacer :contentMax="800"> <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'all'" key="all"> @@ -31,6 +31,7 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { notificationTypes } from '@/const.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const tab = ref('all'); const includeTypes = ref<string[] | null>(null); @@ -67,19 +68,13 @@ function setFilter(ev) { os.popupMenu(items, ev.currentTarget ?? ev.target); } -const headerActions = computed(() => [tab.value === 'all' ? { +const headerActions = computed(() => [{ text: i18n.ts.filter, icon: 'ti ti-filter', highlighted: includeTypes.value != null, handler: setFilter, -} : undefined, tab.value === 'all' ? { - text: i18n.ts.markAllAsRead, - icon: 'ti ti-check', - handler: () => { - os.apiWithDialog('notifications/mark-all-as-read'); - }, -} : undefined].filter(x => x !== undefined)); - +} ].filter(x => x !== undefined)); +misskeyApi('notifications/mark-all-as-read'); const headerTabs = computed(() => [{ key: 'all', title: i18n.ts.all, diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index aea1fa9d7e..4e10304892 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -253,10 +253,10 @@ const headerActions = computed(() => { disabled: onlyFiles } : undefined, { type: 'switch', text: i18n.ts.withSensitive, - ref: withSensitive, - }, { - type: 'switch', - text: i18n.ts.fileAttachedOnly, + ref: withSensitive, + }, { + type: 'switch', + text: i18n.ts.fileAttachedOnly, ref: onlyFiles, disabled: src.value === 'local' || src.value === 'social' ? withReplies : false, @@ -280,27 +280,27 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList key: 'list:' + l.id, title: l.name, icon: 'ti ti-star', - iconOnly: true, + iconOnly: false, }))), { key: 'home', title: i18n.ts._timelines.home, icon: 'ti ti-home', - iconOnly: true, + iconOnly: false, }, ...(isLocalTimelineAvailable ? [{ key: 'local', title: i18n.ts._timelines.local, icon: 'ti ti-planet', - iconOnly: true, + iconOnly: false, }, ...(isShowMediaTimeline.value ? [{ key: 'media', title: i18n.ts._timelines.media, icon: 'ti ti-photo', - iconOnly: true, + iconOnly: false, }] : []), { key: 'social', title: i18n.ts._timelines.social, icon: 'ti ti-universe', - iconOnly: true, + iconOnly: false, }] : []), ...(remoteLocalTimelineEnable1.value ? [{ key: 'custom-timeline-1', title: defaultStore.state.remoteLocalTimelineName1, @@ -330,7 +330,7 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList key: 'global', title: i18n.ts._timelines.global, icon: 'ti ti-whirl', - iconOnly: true, + iconOnly: false, }] : []), { icon: 'ti ti-list', title: i18n.ts.lists, From 852c5d40350311251d5abd7e25bab6d8d53c4211 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 28 Jan 2024 07:56:32 +0900 Subject: [PATCH 347/501] =?UTF-8?q?=E3=81=84=E3=82=8D=E3=81=84=E3=82=8D?= =?UTF-8?q?=E3=81=8B=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 20 + locales/ja-JP.yml | 5 + packages/backend/src/core/RoleService.ts | 6 + .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/notes/user-list-timeline.ts | 2 +- .../endpoints/users/lists/list-favorite.ts | 85 ++++ .../server/api/endpoints/users/lists/list.ts | 38 +- packages/frontend/src/cache.ts | 1 + packages/frontend/src/const.ts | 6 +- packages/frontend/src/pages/admin/roles.vue | 401 ++++++++++-------- packages/frontend/src/pages/list.vue | 19 +- .../frontend/src/pages/my-lists/index.vue | 80 +++- packages/frontend/src/pages/my-lists/list.vue | 24 +- .../frontend/src/pages/settings/general.vue | 229 +++++----- packages/frontend/src/pages/timeline.vue | 6 +- .../frontend/src/pages/user-list-timeline.vue | 1 + 17 files changed, 593 insertions(+), 336 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/users/lists/list-favorite.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index 292559b429..f5be8a3fd4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -92,6 +92,10 @@ export interface Locale extends ILocale { * キャンセル */ "cancel": string; + /** + * 自分の作成したリスト + */ + "myLists": string; /** * やめておく */ @@ -128,6 +132,14 @@ export interface Locale extends ILocale { * 通知の設定 */ "notificationSettings": string; + /** + * このサーバーの公開のリスト + */ + "localListList": string; + /** + * お気に入りのリスト + */ + "favoriteLists": string; /** * 基本設定 */ @@ -6628,6 +6640,14 @@ export interface Locale extends ILocale { * アイコンデコレーションの最大取付個数 */ "avatarDecorationLimit": string; + /** + * ピン留めリストの最大数 + */ + "listPinnedLimit": string; + /** + * 他鯖のローカルTL除けるやつ(最大値5) + */ + "localTimelineAnyLimit": string; }; "_condition": { /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index baf07249cd..46f1719271 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -19,6 +19,7 @@ hanntennInfo: "ダークだったらライトのアイコンに、ライトだ ruby: "ルビ" gotIt: "わかった" cancel: "キャンセル" +myLists: "自分の作成したリスト" noThankYou: "やめておく" enterUsername: "ユーザー名を入力" showGlobalTimeline: "グローバルタイムラインを表示する" @@ -28,6 +29,8 @@ noNotifications: "通知はありません" instance: "サーバー" settings: "設定" notificationSettings: "通知の設定" +localListList: "このサーバーの公開のリスト" +favoriteLists: "お気に入りのリスト" basicSettings: "基本設定" otherSettings: "その他の設定" openInWindow: "ウィンドウで開く" @@ -1711,6 +1714,8 @@ _role: canSearchNotes: "ノート検索の利用" canUseTranslator: "翻訳機能の利用" avatarDecorationLimit: "アイコンデコレーションの最大取付個数" + listPinnedLimit: "ピン留めリストの最大数" + localTimelineAnyLimit: "他鯖のローカルTL除けるやつ(最大値5)" _condition: isLocal: "ローカルユーザー" isRemote: "リモートユーザー" diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index afa7ebe98b..0e60260a2d 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -60,6 +60,8 @@ export type RolePolicies = { rateLimitFactor: number; avatarDecorationLimit: number; emojiPickerProfileLimit: number; + listPinnedLimit: number; + localTimelineAnyLimit: number; }; export const DEFAULT_POLICIES: RolePolicies = { @@ -91,6 +93,8 @@ export const DEFAULT_POLICIES: RolePolicies = { rateLimitFactor: 1, avatarDecorationLimit: 1, emojiPickerProfileLimit: 2, + listPinnedLimit: 2, + localTimelineAnyLimit: 3, }; @Injectable() @@ -359,6 +363,8 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { rateLimitFactor: calc('rateLimitFactor', vs => Math.max(...vs)), avatarDecorationLimit: calc('avatarDecorationLimit', vs => Math.max(...vs)), emojiPickerProfileLimit: calc('emojiPickerProfileLimit', vs => Math.max(...vs)), + listPinnedLimit: calc('listPinnedLimit', vs => Math.max(...vs)), + localTimelineAnyLimit: calc('localTimelineAnyLimit', vs => Math.max(...vs)), }; } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index c7e3742a8b..a32fed9cc9 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -6,6 +6,7 @@ import { Module } from '@nestjs/common'; import { CoreModule } from '@/core/CoreModule.js'; +import * as ep___users_lists_list_favorite from '@/server/api/endpoints/users/lists/list-favorite.js'; import * as ep___admin_meta from './endpoints/admin/meta.js'; import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-reports.js'; import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; @@ -733,6 +734,7 @@ const $users_featuredNotes: Provider = { provide: 'ep:users/featured-notes', use const $users_lists_create: Provider = { provide: 'ep:users/lists/create', useClass: ep___users_lists_create.default }; const $users_lists_delete: Provider = { provide: 'ep:users/lists/delete', useClass: ep___users_lists_delete.default }; const $users_lists_list: Provider = { provide: 'ep:users/lists/list', useClass: ep___users_lists_list.default }; +const $users_lists_list_favorite: Provider = { provide: 'ep:users/lists/list-favorite', useClass: ep___users_lists_list_favorite.default }; const $users_lists_pull: Provider = { provide: 'ep:users/lists/pull', useClass: ep___users_lists_pull.default }; const $users_lists_push: Provider = { provide: 'ep:users/lists/push', useClass: ep___users_lists_push.default }; const $users_lists_show: Provider = { provide: 'ep:users/lists/show', useClass: ep___users_lists_show.default }; @@ -1119,6 +1121,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $users_lists_create, $users_lists_delete, $users_lists_list, + $users_lists_list_favorite, $users_lists_pull, $users_lists_push, $users_lists_show, @@ -1496,6 +1499,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $users_lists_create, $users_lists_delete, $users_lists_list, + $users_lists_list_favorite, $users_lists_pull, $users_lists_push, $users_lists_show, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 6d56ea1876..2fcadae307 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -351,6 +351,7 @@ import * as ep___users_featuredNotes from './endpoints/users/featured-notes.js'; import * as ep___users_lists_create from './endpoints/users/lists/create.js'; import * as ep___users_lists_delete from './endpoints/users/lists/delete.js'; import * as ep___users_lists_list from './endpoints/users/lists/list.js'; +import * as ep___users_lists_list_favorite from './endpoints/users/lists/list-favorite.js'; import * as ep___users_lists_pull from './endpoints/users/lists/pull.js'; import * as ep___users_lists_push from './endpoints/users/lists/push.js'; import * as ep___users_lists_show from './endpoints/users/lists/show.js'; @@ -731,6 +732,7 @@ const eps = [ ['users/lists/create', ep___users_lists_create], ['users/lists/delete', ep___users_lists_delete], ['users/lists/list', ep___users_lists_list], + ['users/lists/list-favorite', ep___users_lists_list_favorite], ['users/lists/pull', ep___users_lists_pull], ['users/lists/push', ep___users_lists_push], ['users/lists/show', ep___users_lists_show], diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 71c2b8054e..618df922a4 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -92,7 +92,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const list = await this.userListsRepository.findOneBy({ id: ps.listId, - userId: me.id, + isPublic: true, }); if (list == null) { diff --git a/packages/backend/src/server/api/endpoints/users/lists/list-favorite.ts b/packages/backend/src/server/api/endpoints/users/lists/list-favorite.ts new file mode 100644 index 0000000000..a7d01afc86 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/users/lists/list-favorite.ts @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { In } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ApiError } from '@/server/api/error.js'; +import { DI } from '@/di-symbols.js'; +import type { UserListsRepository, UserListFavoritesRepository } from '@/models/_.js'; +import { UserListEntityService } from '@/core/entities/UserListEntityService.js'; +import { QueryService } from '@/core/QueryService.js'; +export const meta = { + tags: ['lists', 'account'], + + requireCredential: false, + + kind: 'read:account', + + description: 'Show all lists that the authenticated user has created.', + + res: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'object', + optional: false, nullable: false, + ref: 'UserList', + }, + }, + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: 'a8af4a82-0980-4cc4-a6af-8b0ffd54465e', + }, + remoteUser: { + message: 'Not allowed to load the remote user\'s list', + code: 'REMOTE_USER_NOT_ALLOWED', + id: '53858f1b-3315-4a01-81b7-db9b48d4b79a', + }, + invalidParam: { + message: 'Invalid param.', + code: 'INVALID_PARAM', + id: 'ab36de0e-29e9-48cb-9732-d82f1281620d', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + }, + required: [], +} as const; + +@Injectable() // eslint-disable-next-line import/no-default-export +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.userListFavoritesRepository) + private userListFavoritesRepository: UserListFavoritesRepository, + @Inject(DI.userListsRepository) + private userListsRepository: UserListsRepository, + private userListEntityService: UserListEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + if (!me) { + throw new ApiError(meta.errors.noSuchUser); + } + const favorites = await this.userListFavoritesRepository.findBy({ userId: me.id }); + + if (favorites == null) { + return []; + } + const listIds = favorites.map(favorite => favorite.userListId); + const lists = await this.userListsRepository.findBy({ id: In(listIds) }); + return await Promise.all(lists.map(async list => await this.userListEntityService.pack(list))); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/users/lists/list.ts b/packages/backend/src/server/api/endpoints/users/lists/list.ts index 0e86dd3a68..6b7d05a712 100644 --- a/packages/backend/src/server/api/endpoints/users/lists/list.ts +++ b/packages/backend/src/server/api/endpoints/users/lists/list.ts @@ -51,6 +51,7 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, + publicAll: { type: 'boolean', nullable: false }, }, required: [], } as const; @@ -67,22 +68,29 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private userListEntityService: UserListEntityService, ) { super(meta, paramDef, async (ps, me) => { - if (typeof ps.userId !== 'undefined') { - const user = await this.usersRepository.findOneBy({ id: ps.userId }); - if (user === null) throw new ApiError(meta.errors.noSuchUser); - if (user.host !== null) throw new ApiError(meta.errors.remoteUser); - } else if (me === null) { - throw new ApiError(meta.errors.invalidParam); + if (!ps.publicAll ) { + if (typeof ps.userId !== 'undefined') { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); + if (user === null) throw new ApiError(meta.errors.noSuchUser); + if (user.host !== null) throw new ApiError(meta.errors.remoteUser); + } else if (me === null) { + throw new ApiError(meta.errors.invalidParam); + } + + const userLists = await this.userListsRepository.findBy(typeof ps.userId === 'undefined' && me !== null ? { + userId: me.id, + } : { + userId: ps.userId, + isPublic: true, + }); + + return await Promise.all(userLists.map(x => this.userListEntityService.pack(x))); + } else { + const userLists = await this.userListsRepository.findBy({ + isPublic: true, + }); + return await Promise.all(userLists.map(x => this.userListEntityService.pack(x))); } - - const userLists = await this.userListsRepository.findBy(typeof ps.userId === 'undefined' && me !== null ? { - userId: me.id, - } : { - userId: ps.userId, - isPublic: true, - }); - - return await Promise.all(userLists.map(x => this.userListEntityService.pack(x))); }); } } diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts index 20950add80..17d813a3f2 100644 --- a/packages/frontend/src/cache.ts +++ b/packages/frontend/src/cache.ts @@ -11,3 +11,4 @@ export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list')); export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list')); export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list')); +export const userFavoriteListsCache = new Cache(1000 * 60 * 30, () => misskeyApi('users/lists/list-favorite')); diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index f07a97933d..c98aef3189 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -99,7 +99,9 @@ export const ROLE_POLICIES = [ 'userEachUserListsLimit', 'rateLimitFactor', 'avatarDecorationLimit', - 'emojiPickerProfileLimit' + 'emojiPickerProfileLimit', + 'listPinnedLimit', + 'localTimelineAnyLimit', ] as const; // なんか動かない @@ -129,7 +131,7 @@ export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { position: ['x=', 'y='], fg: ['color='], bg: ['color='], - border: ['width=', 'style=', 'color=', 'radius=', 'noclip'], + border: ['width=', 'style=', 'color=', 'radius=', 'noclip'], font: ['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'], blur: [], rainbow: ['speed=', 'delay='], diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index dc0d43f7b7..a3c415f2e1 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -23,212 +23,255 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> </MkRange> </MkFolder> + <MkFoldableSection :defaultOpen="false"> + <template #header>タイムライン系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> + <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.gtlAvailable"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> - <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> - <template #suffix>{{ policies.gtlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.gtlAvailable"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> + <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.ltlAvailable"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>ノート系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> + <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canPublicNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.ltlAvailable, 'ltlAvailable'])"> - <template #label>{{ i18n.ts._role._options.ltlAvailable }}</template> - <template #suffix>{{ policies.ltlAvailable ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.ltlAvailable"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canEditNote }}</template> + <template #suffix>{{ policies.canEditNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canEditNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])"> - <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> - <template #suffix>{{ policies.canPublicNote ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canPublicNote"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canScheduleNote, 'canScheduleNote'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canScheduleNote }}</template> + <template #suffix>{{ policies.canScheduleNote ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canScheduleNote"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> + <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canSearchNotes"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])"> - <template #label>{{ i18n.ts._role._options.canEditNote }}</template> - <template #suffix>{{ policies.canEditNote ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canEditNote"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> + <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canUseTranslator"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.pinMax }}</template> + <template #suffix>{{ policies.pinLimit }}</template> + <MkInput v-model="policies.pinLimit" type="number"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>招待系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> + <template #label>{{ i18n.ts._role._options.canInvite }}</template> + <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canInvite"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> + <template #suffix>{{ policies.inviteLimit }}</template> + <MkInput v-model="policies.inviteLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canScheduleNote, 'canScheduleNote'])"> - <template #label>{{ i18n.ts._role._options.canScheduleNote }}</template> - <template #suffix>{{ policies.canScheduleNote ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canScheduleNote"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> + <template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template> + <MkInput v-model="policies.inviteLimitCycle" type="number"> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> - <template #label>{{ i18n.ts._role._options.canInvite }}</template> - <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canInvite"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteExpirationTime, 'inviteExpirationTime'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.inviteExpirationTime }}</template> + <template #suffix>{{ policies.inviteExpirationTime + i18n.ts._time.minute }}</template> + <MkInput v-model="policies.inviteExpirationTime" type="number"> + <template #suffix>{{ i18n.ts._time.minute }}</template> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>PrisMisskey独自機能系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'pickerProfileDefault'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> + <template #suffix>{{ policies.emojiPickerProfileLimit }}</template> + <MkInput v-model="policies.emojiPickerProfileLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'pickerProfileDefault'])"> - <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> - <template #suffix>{{ policies.emojiPickerProfileLimit }}</template> - <MkInput v-model="policies.emojiPickerProfileLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.listPinnedLimit, 'listPinnedLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> + <template #suffix>{{ policies.listPinnedLimit }}</template> + <MkInput v-model="policies.listPinnedLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.localTimelineAnyLimit, 'localTimelineAnyLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> + <template #suffix>{{ policies.localTimelineAnyLimit }}</template> + <MkInput v-model="policies.localTimelineAnyLimit" type="number"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>カスタム絵文字系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> + <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> - <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> - <template #suffix>{{ policies.inviteLimit }}</template> - <MkInput v-model="policies.inviteLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix>{{ policies.canRequestCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canRequestCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>ドライブ、ファイル系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> + <template #suffix>{{ policies.driveCapacityMb }}MB</template> + <MkInput v-model="policies.driveCapacityMb" type="number"> + <template #suffix>MB</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])"> - <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> - <template #suffix>{{ policies.inviteLimitCycle + i18n.ts._time.minute }}</template> - <MkInput v-model="policies.inviteLimitCycle" type="number"> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> + <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.alwaysMarkNsfw"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>アイコンデコレーション系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> + <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageAvatarDecorations"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template> + <template #suffix>{{ policies.avatarDecorationLimit }}</template> + <MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>クリップ系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.clipMax }}</template> + <template #suffix>{{ policies.clipLimit }}</template> + <MkInput v-model="policies.clipLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteExpirationTime, 'inviteExpirationTime'])"> - <template #label>{{ i18n.ts._role._options.inviteExpirationTime }}</template> - <template #suffix>{{ policies.inviteExpirationTime + i18n.ts._time.minute }}</template> - <MkInput v-model="policies.inviteExpirationTime" type="number"> - <template #suffix>{{ i18n.ts._time.minute }}</template> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> + <template #suffix>{{ policies.noteEachClipsLimit }}</template> + <MkInput v-model="policies.noteEachClipsLimit" type="number"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>リスト系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.userListMax }}</template> + <template #suffix>{{ policies.userListLimit }}</template> + <MkInput v-model="policies.userListLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])"> - <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> - <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageAvatarDecorations"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> + <template #suffix>{{ policies.userEachUserListsLimit }}</template> + <MkInput v-model="policies.userEachUserListsLimit" type="number"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :defaultOpen="false"> + <template #header>その他</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.antennaMax }}</template> + <template #suffix>{{ policies.antennaLimit }}</template> + <MkInput v-model="policies.antennaLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])"> - <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> - <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageCustomEmojis"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> + <template #suffix>{{ policies.wordMuteLimit }}</template> + <MkInput v-model="policies.wordMuteLimit" type="number"> + <template #suffix>chars</template> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> - <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> - <template #suffix>{{ policies.canRequestCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canRequestCustomEmojis"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.webhookMax }}</template> + <template #suffix>{{ policies.webhookLimit }}</template> + <MkInput v-model="policies.webhookLimit" type="number"> + </MkInput> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])"> - <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> - <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canSearchNotes"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])"> - <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> - <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canUseTranslator"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])"> - <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> - <template #suffix>{{ policies.driveCapacityMb }}MB</template> - <MkInput v-model="policies.driveCapacityMb" type="number"> - <template #suffix>MB</template> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])"> - <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> - <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.alwaysMarkNsfw"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])"> - <template #label>{{ i18n.ts._role._options.pinMax }}</template> - <template #suffix>{{ policies.pinLimit }}</template> - <MkInput v-model="policies.pinLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canHideAds }}</template> + <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canHideAds"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> + - <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])"> - <template #label>{{ i18n.ts._role._options.antennaMax }}</template> - <template #suffix>{{ policies.antennaLimit }}</template> - <MkInput v-model="policies.antennaLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])"> - <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> - <template #suffix>{{ policies.wordMuteLimit }}</template> - <MkInput v-model="policies.wordMuteLimit" type="number"> - <template #suffix>chars</template> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])"> - <template #label>{{ i18n.ts._role._options.webhookMax }}</template> - <template #suffix>{{ policies.webhookLimit }}</template> - <MkInput v-model="policies.webhookLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])"> - <template #label>{{ i18n.ts._role._options.clipMax }}</template> - <template #suffix>{{ policies.clipLimit }}</template> - <MkInput v-model="policies.clipLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])"> - <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> - <template #suffix>{{ policies.noteEachClipsLimit }}</template> - <MkInput v-model="policies.noteEachClipsLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])"> - <template #label>{{ i18n.ts._role._options.userListMax }}</template> - <template #suffix>{{ policies.userListLimit }}</template> - <MkInput v-model="policies.userListLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])"> - <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> - <template #suffix>{{ policies.userEachUserListsLimit }}</template> - <MkInput v-model="policies.userEachUserListsLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])"> - <template #label>{{ i18n.ts._role._options.canHideAds }}</template> - <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canHideAds"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])"> - <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template> - <template #suffix>{{ policies.avatarDecorationLimit }}</template> - <MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0"> - </MkInput> - </MkFolder> <MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> </div> diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue index 03db02a350..59e39e4bae 100644 --- a/packages/frontend/src/pages/list.vue +++ b/packages/frontend/src/pages/list.vue @@ -6,7 +6,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> - <MKSpacer v-if="!(typeof error === 'undefined')" :contentMax="1200"> + + <MkSpacer v-if="!(typeof error === 'undefined')" :contentMax="1200"> <div :class="$style.root"> <img :class="$style.img" :src="serverErrorImageUrl" class="_ghost"/> <p :class="$style.text"> @@ -14,10 +15,14 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.nothing }} </p> </div> - </MKSpacer> + </MkSpacer> <MkSpacer v-else-if="list" :contentMax="700" :class="$style.main"> - <div v-if="list" class="members _margin"> - <div :class="$style.member_text">{{ i18n.ts.members }}</div> + <MkButton v-if="list.isLiked" v-tooltip="i18n.ts.unlike" inline :class="$style.button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="list.likedCount > 0" class="count">{{ list.likedCount }}</span></MkButton> + <MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton> + <MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton> + <MkFolder v-if="list" class="members _margin"> + <template #label>{{ i18n.ts.members }}</template> + <div :class="$style.member_text"></div> <div class="_gaps_s"> <div v-for="user in users" :key="user.id" :class="$style.userItem"> <MkA :class="$style.userItemBody" :to="`${userPage(user)}`"> @@ -25,10 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> </div> </div> - </div> - <MkButton v-if="list.isLiked" v-tooltip="i18n.ts.unlike" inline :class="$style.button" asLike primary @click="unlike()"><i class="ti ti-heart-off"></i><span v-if="list.likedCount > 0" class="count">{{ list.likedCount }}</span></MkButton> - <MkButton v-if="!list.isLiked" v-tooltip="i18n.ts.like" inline :class="$style.button" asLike @click="like()"><i class="ti ti-heart"></i><span v-if="1 > 0" class="count">{{ list.likedCount }}</span></MkButton> - <MkButton inline @click="create()"><i class="ti ti-download" :class="$style.import"></i>{{ i18n.ts.import }}</MkButton> + </MkFolder> </MkSpacer> </MkStickyContainer> </template> @@ -44,6 +46,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkButton from '@/components/MkButton.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { serverErrorImageUrl } from '@/instance.js'; +import MkFolder from '@/components/MkFolder.vue'; const props = defineProps<{ listId: string; diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 295112b0ba..21ce2f960d 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -7,44 +7,87 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700"> - <div class="_gaps"> - <div v-if="items.length === 0" class="empty"> - <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> - <div>{{ i18n.ts.nothing }}</div> + <MkFoldableSection style="margin-bottom: 32px;"> + <template #header>{{ i18n.ts.favoriteLists }}</template> + + <div class="_gaps"> + <div v-if="feautureList.length === 0" class="empty"> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </div> + + <div v-if="feautureList.length > 0" class="_gaps"> + <MkA v-for="list in feautureList" :key="list.id" class="_panel" :class="$style.list" :to="`/list/${ list.id }`"> + <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds.length}` }) }})</span></div> + <MkAvatars :userIds="list.userIds" :limit="10"/> + </MkA> </div> </div> + </MkFoldableSection> + <MkFoldableSection style="margin-bottom: 32px;"> + <template #header>{{ i18n.ts.localListList }}</template> + <div class="_gaps"> + <div v-if="localList.length === 0" class="empty"> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </div> - <MkButton primary rounded style="margin: 0 auto;" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton> - - <div v-if="items.length > 0" class="_gaps"> - <MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`"> - <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div> - <MkAvatars :userIds="list.userIds" :limit="10"/> - </MkA> + <div v-if="localList.length > 0" class="_gaps"> + <MkA v-for="list in localList" :key="list.id" class="_panel" :class="$style.list" :to="`/list/${ list.id }`"> + <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds.length}` }) }})</span></div> + <MkAvatars :userIds="list.userIds" :limit="10"/> + </MkA> + </div> </div> - </div> + </MkFoldableSection> + <MkFoldableSection> + <template #header>{{ i18n.ts.myLists }}</template> + <div class="_gaps"> + <div v-if="items.length === 0" class="empty"> + <div class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> + </div> + </div> + + <div v-if="items.length > 0" class="_gaps"> + <MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`"> + <div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div> + <MkAvatars :userIds="list.userIds" :limit="10"/> + </MkA> + </div> + </div> + </MkFoldableSection> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> import { onActivated, computed } from 'vue'; -import MkButton from '@/components/MkButton.vue'; import MkAvatars from '@/components/MkAvatars.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { userListsCache } from '@/cache.js'; +import { userFavoriteListsCache, userListsCache } from '@/cache.js'; import { infoImageUrl } from '@/instance.js'; import { signinRequired } from '@/account.js'; +import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import { misskeyApi } from '@/scripts/misskey-api.js'; const $i = signinRequired(); const items = computed(() => userListsCache.value.value ?? []); +const localList = await misskeyApi('users/lists/list', { publicAll: true }); +const feautureList = computed(() => userFavoriteListsCache.value.value ?? []); function fetch() { userListsCache.fetch(); + userFavoriteListsCache.delete(); + userFavoriteListsCache.fetch(); } fetch(); @@ -67,12 +110,17 @@ const headerActions = computed(() => [{ userListsCache.delete(); fetch(); }, +}, { + asFullButton: true, + icon: 'ti ti-plus', + text: i18n.ts.createList, + handler: create, }]); const headerTabs = computed(() => []); definePageMetadata({ - title: i18n.ts.manageLists, + title: i18n.ts._exportOrImport.userLists, icon: 'ti ti-list', }); diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue index 773bb6c2df..0eaf433b0b 100644 --- a/packages/frontend/src/pages/my-lists/list.vue +++ b/packages/frontend/src/pages/my-lists/list.vue @@ -8,22 +8,20 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MkSpacer :contentMax="700" :class="$style.main"> <div v-if="list" class="_gaps"> - <MkFolder> - <template #label>{{ i18n.ts.settings }}</template> + <div>{{ i18n.ts.settings }}</div> - <div class="_gaps"> - <MkInput v-model="name"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkSwitch v-model="isPublic">{{ i18n.ts.public }}</MkSwitch> - <div class="_buttons"> - <MkButton rounded primary @click="updateSettings">{{ i18n.ts.save }}</MkButton> - <MkButton rounded danger @click="deleteList()">{{ i18n.ts.delete }}</MkButton> - </div> + <div class="_gaps" style="margin: 8px 0; "> + <MkInput v-model="name"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkSwitch v-model="isPublic">{{ i18n.ts.public }}</MkSwitch> + <div class="_buttons"> + <MkButton rounded primary @click="updateSettings">{{ i18n.ts.save }}</MkButton> + <MkButton rounded danger @click="deleteList()">{{ i18n.ts.delete }}</MkButton> </div> - </MkFolder> + </div> - <MkFolder defaultOpen> + <MkFolder> <template #label>{{ i18n.ts.members }}</template> <template #caption>{{ i18n.tsx.nUsers({ n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }}</template> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index d5ab1eff4c..4ee4e011ff 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -38,9 +38,13 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> - <!-- 複数ピン止め管理できるようにしたいけどめんどいので一旦ひとつのみ --> - <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length === 0" @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> - <MkButton v-else danger @click="removePinnedList()"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + <div class="_margin" v-for="pinnedLists in defaultStore.reactiveState.pinnedUserLists.value"> + {{ pinnedLists.name }} + <MkButton danger @click="removePinnedList(pinnedLists.id,pinnedLists.name)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + </div> + <MkButton v-if="pinnedMax > defaultStore.reactiveState.pinnedUserLists.value.length " @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> + <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length " danger @click="removePinnedList('all')"><i class="ti ti-trash"></i> {{i18n.ts.all}}{{ i18n.ts.remove }}</MkButton> + </MkFolder> <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline }}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> @@ -166,7 +170,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFoldableSection> <MkFoldableSection :defaultOpen="false" class="item"> - <template #header>{{ i18n.ts.behavior }}</template> + <template #header>{{ i18n.ts.behavior }}</template> <div class="_gaps_m"> <div class="_gaps_s"> @@ -223,98 +227,97 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :defaultOpen="false"> + <template #header>他のサーバーのローカルタイムラインを覗けるようにする</template> + <div class="_gaps_m"> + <MkFoldableSection :defaultOpen="false"> + <template #header>{{ i18n.ts.accessToken }} の発行の仕方</template> + <img width="400" src="https://files.prismisskey.space/misskey/676e4b79-7897-4ea9-b074-a98139312f76.gif"> + </MkFoldableSection> + <div v-if="maxLocalTimeline >= 1"> + <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain1" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken1" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable1"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> + <div v-if="maxLocalTimeline >= 2"> + <MkInput v-model="remoteLocalTimelineName2" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain2" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken2" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable2"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> - <template #header>他のサーバーのローカルタイムラインを覗けるようにする</template> - <div class="_gaps_m"> - <MkFoldableSection :defaultOpen="false"> - <template #header>{{ i18n.ts.accessToken }} の発行の仕方</template> - <img width="400" src="https://files.prismisskey.space/misskey/676e4b79-7897-4ea9-b074-a98139312f76.gif"> - </MkFoldableSection> - <div v-if="maxLocalTimeline >= 1" > - <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain1" placeholder="prismisskey.space"> - <template #label>サーバーURL</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken1" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.accessToken }}</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable1"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> - <div v-if="maxLocalTimeline >= 2" > - <MkInput v-model="remoteLocalTimelineName2" placeholder="prismisskey"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain2" placeholder="prismisskey.space"> - <template #label>サーバーURL</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken2" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.accessToken }}</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable2"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> + <div v-if="maxLocalTimeline >= 3"> + <MkInput v-model="remoteLocalTimelineName3" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain3" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken3" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable3"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> - <div v-if="maxLocalTimeline >= 3" > - <MkInput v-model="remoteLocalTimelineName3" placeholder="prismisskey"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain3" placeholder="prismisskey.space"> - <template #label>サーバーURL</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken3" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.accessToken }}</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable3"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> + <div v-if="maxLocalTimeline >= 4"> + <MkInput v-model="remoteLocalTimelineName4" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain4" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken4" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable4"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> - <div v-if="maxLocalTimeline >= 4" > - <MkInput v-model="remoteLocalTimelineName4" placeholder="prismisskey"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain4" placeholder="prismisskey.space"> - <template #label>サーバーURL</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken4" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.accessToken }}</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable4"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> + <div v-if="maxLocalTimeline >= 5"> + <MkInput v-model="remoteLocalTimelineName5" placeholder="prismisskey"> + <template #label>{{ i18n.ts.name }}</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineDomain5" placeholder="prismisskey.space"> + <template #label>サーバーURL</template> + </MkInput> + <MkInput v-model="remoteLocalTimelineToken5" placeholder=""> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>{{ i18n.ts.accessToken }}</template> + </MkInput> + <MkSwitch v-model="remoteLocalTimelineEnable5"> + {{ i18n.ts.enable }} + </MkSwitch> + </div> - <div v-if="maxLocalTimeline >= 5" > - <MkInput v-model="remoteLocalTimelineName5" placeholder="prismisskey"> - <template #label>{{ i18n.ts.name }}</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineDomain5" placeholder="prismisskey.space"> - <template #label>サーバーURL</template> - </MkInput> - <MkInput v-model="remoteLocalTimelineToken5" placeholder=""> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>{{ i18n.ts.accessToken }}</template> - </MkInput> - <MkSwitch v-model="remoteLocalTimelineEnable5"> - {{ i18n.ts.enable }} - </MkSwitch> - </div> - - <MkButton @click="remoteLocaltimelineSave"> - {{ i18n.ts.save }} - </MkButton> - </div> - </MkFoldableSection> + <MkButton @click="remoteLocaltimelineSave"> + {{ i18n.ts.save }} + </MkButton> + </div> + </MkFoldableSection> <FormSection> <template #label>{{ i18n.ts.other }}</template> @@ -351,7 +354,7 @@ import MkInfo from '@/components/MkInfo.vue'; import { langs } from '@/config.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; -import { misskeyApi } from '@/scripts/misskey-api.js'; +import {signinRequired} from '@/account.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -361,6 +364,7 @@ import { claimAchievement } from '@/scripts/achievements.js'; import MkColorInput from '@/components/MkColorInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkInput from '@/components/MkInput.vue'; +import { userFavoriteListsCache, userListsCache } from '@/cache.js'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -397,7 +401,6 @@ const disableDrawer = computed(defaultStore.makeGetterSetter('disableDrawer')); const disableShowingAnimatedImages = computed(defaultStore.makeGetterSetter('disableShowingAnimatedImages')); const forceShowAds = computed(defaultStore.makeGetterSetter('forceShowAds')); const loadRawImages = computed(defaultStore.makeGetterSetter('loadRawImages')); -const enableCellularWithDataSaver = computed(defaultStore.makeGetterSetter('enableCellularWithDataSaver')); const highlightSensitiveMedia = computed(defaultStore.makeGetterSetter('highlightSensitiveMedia')); const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); @@ -428,7 +431,6 @@ const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disable const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); const enableSeasonalScreenEffect = computed(defaultStore.makeGetterSetter('enableSeasonalScreenEffect')); const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHorizontalSwipe')); -const maxLocalTimeline = 3; const remoteLocalTimelineDomain1 = ref(defaultStore.state['remoteLocalTimelineDomain1']); const remoteLocalTimelineToken1 = ref(defaultStore.state['remoteLocalTimelineToken1']); const remoteLocalTimelineDomain2 = ref(defaultStore.state['remoteLocalTimelineDomain2']); @@ -450,7 +452,9 @@ const remoteLocalTimelineEnable2 = computed(defaultStore.makeGetterSetter('remot const remoteLocalTimelineEnable3 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable3')); const remoteLocalTimelineEnable4 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable4')); const remoteLocalTimelineEnable5 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable5')); - +const $i = signinRequired(); +const pinnedMax = $i.policies?.listPinnedLimit; +const maxLocalTimeline = $i.policies?.localTimelineAnyLimit; watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); @@ -592,7 +596,9 @@ function removeEmojiIndex(lang: string) { } async function setPinnedList() { - const lists = await misskeyApi('users/lists/list'); + const myLists = await userListsCache.fetch(); + const favoriteLists = await userFavoriteListsCache.fetch(); + let lists = [...new Set([...myLists, ...favoriteLists])]; const { canceled, result: list } = await os.select({ title: i18n.ts.selectList, items: lists.map(x => ({ @@ -600,12 +606,35 @@ async function setPinnedList() { })), }); if (canceled) return; + let pinnedLists = defaultStore.state.pinnedUserLists; - defaultStore.set('pinnedUserLists', [list]); + // Check if the id is already present in pinnedLists + if (!pinnedLists.some(pinnedList => pinnedList.id === list.id)) { + pinnedLists.push(list); + defaultStore.set('pinnedUserLists', pinnedLists); + } } -function removePinnedList() { - defaultStore.set('pinnedUserLists', []); +async function removePinnedList(id,name) { + + if (!id) return; + const {canceled} = await os.confirm({ + type: 'warning', + text: i18n.tsx.removeAreYouSure({x: name ?? id }), + }); + if (canceled) return; + + if (id === 'all') { + + if (canceled) return; + + defaultStore.set('pinnedUserLists', []); + return; + } + + const pinnedLists = defaultStore.state.pinnedUserLists; + const newPinnedLists = pinnedLists.filter(pinnedList => pinnedList.id !== id); + defaultStore.set('pinnedUserLists', newPinnedLists); } let smashCount = 0; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 4e10304892..32b66680a5 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -48,7 +48,7 @@ import { i18n } from '@/i18n.js'; import { instance } from '@/instance.js'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { antennasCache, userListsCache } from '@/cache.js'; +import { antennasCache, userFavoriteListsCache, userListsCache } from '@/cache.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; @@ -123,7 +123,9 @@ function top(): void { } async function chooseList(ev: MouseEvent): Promise<void> { - const lists = await userListsCache.fetch(); + const myLists = await userListsCache.fetch(); + const favoriteLists = await userFavoriteListsCache.fetch(); + let lists = [...new Set([...myLists, ...favoriteLists])]; const items : MenuItem[] = [ ... lists.map(list => ({ type: 'link' as const, diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue index 10a21ef20d..2a3b0cf88a 100644 --- a/packages/frontend/src/pages/user-list-timeline.vue +++ b/packages/frontend/src/pages/user-list-timeline.vue @@ -47,6 +47,7 @@ const rootEl = shallowRef<HTMLElement>(); watch(() => props.listId, async () => { list.value = await misskeyApi('users/lists/show', { listId: props.listId, + forPublic: true, }); }, { immediate: true }); From 9031d418127be619bee84d63aa65e401249418d1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 29 Jan 2024 01:08:54 +0900 Subject: [PATCH 348/501] =?UTF-8?q?=E3=81=84=E3=82=8D=E3=81=84=E3=82=8D?= =?UTF-8?q?=E3=81=8B=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 10 ++++- .../api/endpoints/notes/user-list-timeline.ts | 3 ++ .../frontend/src/ui/universal.widgets.vue | 43 +++++++++---------- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 9209bd1f74..41f645ff5d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2916,11 +2916,14 @@ export interface Locale extends ILocale { * 送信 */ "send": string; - "reportedNote": string; /** * ファイル付きのみ */ "fileAttachedOnly": string; + /** + * 通報されたノート + */ + "reportedNote": string; /** * 対応済みにする */ @@ -3702,9 +3705,12 @@ export interface Locale extends ILocale { */ "emailRequiredForSignup": string; /** - * 未読 + * GDPRモードを有効にする */ "enableGDPRMode": string; + /** + * 未読 + */ "unread": string; /** * フィルタ diff --git a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts index 618df922a4..656eaaf32a 100644 --- a/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/user-list-timeline.ts @@ -93,6 +93,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const list = await this.userListsRepository.findOneBy({ id: ps.listId, isPublic: true, + }) ?? await this.userListsRepository.findOneBy({ + id: ps.listId, + userId: me.id, }); if (list == null) { diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue index efa6119e4a..786848a7e1 100644 --- a/packages/frontend/src/ui/universal.widgets.vue +++ b/packages/frontend/src/ui/universal.widgets.vue @@ -7,17 +7,16 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <XWidgets :edit="editMode" :widgets="widgets" @addWidget="addWidget" @removeWidget="removeWidget" @updateWidget="updateWidget" @updateWidgets="updateWidgets" @exit="editMode = false"/> - <button v-if="editMode" class="_textButton" style="font-size: 0.9em;" :class="{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button> + <button v-if="editMode" class="_textButton" style="font-size: 0.9em;" :class="{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button> <button v-else class="_textButton" data-cy-widget-edit :class="$style.edit, {[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' }" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button> </div> </template> <script lang="ts"> -import { computed, ref } from 'vue'; +import { computed, ref, watch } from 'vue'; const editMode = ref(false); </script> <script lang="ts" setup> -import { ref , computed , watch} from 'vue'; import XWidgets from '@/components/MkWidgets.vue'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; @@ -27,32 +26,32 @@ let gaming = ref(''); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gaming.value = 'light'; } else { - gaming.value = ''; + gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); const props = withDefaults(defineProps<{ // null = 全てのウィジェットを表示 // left = place: leftだけを表示 From e886c40032f057f0a024326e001b4c39545871bb Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 31 Jan 2024 19:37:44 +0900 Subject: [PATCH 349/501] =?UTF-8?q?=E3=81=84=E3=82=8D=E3=81=84=E3=82=8D?= =?UTF-8?q?=E3=81=8B=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/pages/admin/roles.editor.vue | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 51a62b7683..5e527ed17a 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -611,6 +611,63 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRange> </div> </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'emojiPickerProfileLimit'])"> + <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> + <template #suffix> + <span v-if="role.policies.emojiPickerProfileLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.emojiPickerProfileLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.emojiPickerProfileLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.emojiPickerProfileLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.emojiPickerProfileLimit.value" type="number" :min="0" :disabled="role.policies.emojiPickerProfileLimit.useDefault" > + <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> + </MkInput> + <MkRange v-model="role.policies.emojiPickerProfileLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.listPinnedLimit, 'listPinnedLimit'])"> + <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> + <template #suffix> + <span v-if="role.policies.listPinnedLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.listPinnedLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.listPinnedLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.listPinnedLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.listPinnedLimit.value" type="number" :min="0" :disabled="role.policies.listPinnedLimit.useDefault" > + <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> + </MkInput> + <MkRange v-model="role.policies.listPinnedLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.localTimelineAnyLimit, 'localTimelineAnyLimit'])"> + <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> + <template #suffix> + <span v-if="role.policies.localTimelineAnyLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.localTimelineAnyLimit.value}}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.localTimelineAnyLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.localTimelineAnyLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.localTimelineAnyLimit.value" type="number" :min="0" :disabled="role.policies.localTimelineAnyLimit.useDefault" > + <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> + </MkInput> + <MkRange v-model="role.policies.localTimelineAnyLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> </div> </FormSlot> </div> From 121ee723d98542547c16a54f4e246fc31d786b09 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 31 Jan 2024 20:12:11 +0900 Subject: [PATCH 350/501] =?UTF-8?q?=E3=81=84=E3=82=8D=E3=81=84=E3=82=8D?= =?UTF-8?q?=E3=81=8B=E3=81=88=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkFoldableSection.vue | 149 +++++------------- .../frontend/src/components/MkTimeline.vue | 2 +- .../frontend/src/pages/settings/general.vue | 14 +- 3 files changed, 48 insertions(+), 117 deletions(-) diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index 8019477c51..5598f7ac8d 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -4,54 +4,48 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div ref="rootEl" :class="$style.root" role="group" :aria-expanded="opened"> - <header :class="[$style.header, { [$style.opened]: opened }]" class="_button" role="button" data-cy-folder-header @click="toggle"> +<div ref="rootEl" :class="$style.root"> + <header :class="$style.header" class="_button" :style="{ background: bg }" @click="showBody = !showBody"> <div :class="$style.title"><div><slot name="header"></slot></div></div> <div :class="$style.divider"></div> <button class="_button" :class="$style.button"> - <template v-if="opened"><i class="ti ti-chevron-up"></i></template> + <template v-if="showBody"><i class="ti ti-chevron-up"></i></template> <template v-else><i class="ti ti-chevron-down"></i></template> </button> </header> - <div v-if="openedAtLeastOnce" :class="[$style.body, { [$style.bgSame]: bgSame }]" :style="{ maxHeight: maxHeight ? `${maxHeight}px` : null, overflow: maxHeight ? `auto` : null }" :aria-hidden="!opened"> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_toggle_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_toggle_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_toggle_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_toggle_leaveTo : ''" - @enter="enter" - @afterEnter="afterEnter" - @leave="leave" - @afterLeave="afterLeave" - > - <div v-show="opened"> - <slot></slot> - </div> - </Transition> - </div> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style['folder-toggle-enter-active'] : ''" + :leaveActiveClass="defaultStore.state.animation ? $style['folder-toggle-leave-active'] : ''" + :enterFromClass="defaultStore.state.animation ? $style['folder-toggle-enter-from'] : ''" + :leaveToClass="defaultStore.state.animation ? $style['folder-toggle-leave-to'] : ''" + @enter="enter" + @afterEnter="afterEnter" + @leave="leave" + @afterLeave="afterLeave" + > + <div v-show="showBody"> + <slot></slot> + </div> + </Transition> </div> </template> <script lang="ts" setup> -import { nextTick, onMounted, shallowRef, ref, watch } from 'vue'; +import { onMounted, ref, shallowRef, watch } from 'vue'; +import tinycolor from 'tinycolor2'; +import { miLocalStorage } from '@/local-storage.js'; import { defaultStore } from '@/store.js'; + const miLocalStoragePrefix = 'ui:folder:' as const; const props = withDefaults(defineProps<{ + expanded?: boolean; + persistKey?: string; defaultOpen?: boolean; - maxHeight?: number | null; }>(), { - defaultOpen: true, - maxHeight: null, + expanded: true, }); -const getBgColor = (el: HTMLElement) => { - const style = window.getComputedStyle(el); - if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) { - return style.backgroundColor; - } else { - return el.parentElement ? getBgColor(el.parentElement) : 'transparent'; - } -}; + const rootEl = shallowRef<HTMLDivElement>(); const bg = ref<string>(); const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded); @@ -62,17 +56,12 @@ watch(showBody, () => { } }); -const rootEl = shallowRef<HTMLElement>(); -const bgSame = ref(false); -const opened = ref(props.defaultOpen); -const openedAtLeastOnce = ref(props.defaultOpen); - function enter(element: Element) { const el = element as HTMLElement; const elementHeight = el.getBoundingClientRect().height; el.style.height = '0'; el.offsetHeight; // reflow - el.style.height = Math.min(elementHeight, props.maxHeight ?? Infinity) + 'px'; + el.style.height = elementHeight + 'px'; } function afterEnter(element: Element) { @@ -93,16 +82,6 @@ function afterLeave(element: Element) { el.style.height = 'unset'; } -function toggle() { - if (!opened.value) { - openedAtLeastOnce.value = true; - } - - nextTick(() => { - opened.value = !opened.value; - }); -} - onMounted(() => { function getParentBg(el?: HTMLElement | null): string { if (el == null || el.tagName === 'BODY') return 'var(--bg)'; @@ -113,10 +92,7 @@ onMounted(() => { return getParentBg(el.parentElement); } } - const computedStyle = getComputedStyle(document.documentElement); - const parentBg = getBgColor(rootEl.value.parentElement); - const myBg = computedStyle.getPropertyValue('--panel'); - bgSame.value = parentBg === myBg; + const rawBg = getParentBg(rootEl.value); const _bg = tinycolor(rawBg.startsWith('var(') ? getComputedStyle(document.documentElement).getPropertyValue(rawBg.slice(4, -1)) : rawBg); _bg.setAlpha(0.85); @@ -125,18 +101,17 @@ onMounted(() => { </script> <style lang="scss" module> -.transition_toggle_enterActive, -.transition_toggle_leaveActive { +.folder-toggle-enter-active, .folder-toggle-leave-active { overflow-y: clip; - transition: opacity 0.3s, height 0.3s, transform 0.3s !important; + transition: opacity 0.5s, height 0.5s !important; } -.transition_toggle_enterFrom, -.transition_toggle_leaveTo { + +.folder-toggle-enter-from, .folder-toggle-leave-to { opacity: 0; } .root { - display: block; + position: relative; } .header { @@ -147,64 +122,14 @@ onMounted(() => { top: var(--stickyTop, 0px); -webkit-backdrop-filter: var(--blur, blur(8px)); backdrop-filter: var(--blur, blur(20px)); - - &.opened { - border-radius: 6px 6px 0 0; - } } -.headerUpper { - display: flex; - align-items: center; -} - -.headerLower { - color: var(--fgTransparentWeak); - font-size: .85em; - padding-left: 4px; -} - -.headerIcon { - margin-right: 0.75em; - flex-shrink: 0; - text-align: center; - opacity: 0.8; - - &:empty { - display: none; - - & + .headerText { - padding-left: 4px; - } - } -} .title { display: grid; place-content: center; margin: 0; padding: 12px 16px 12px 0; } -.headerText { - white-space: nowrap; - text-overflow: ellipsis; - overflow: hidden; - padding-right: 12px; -} - -.headerTextSub { - color: var(--fgTransparentWeak); - font-size: .85em; -} - -.headerRight { - margin-left: auto; - color: var(--fgTransparentWeak); - white-space: nowrap; -} - -.headerRightText:not(:empty) { - margin-right: 0.75em; -} .divider { flex: 1; @@ -212,4 +137,14 @@ onMounted(() => { height: 1px; background: var(--divider); } + +.button { + padding: 12px 0 12px 16px; +} + +@container (max-width: 500px) { + .title { + padding: 8px 10px 8px 0; + } +} </style> diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index c7341518e9..21f501afe0 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue'; +import { computed, watch, onUnmounted, provide, shallowRef } from 'vue'; import Misskey from 'misskey-js'; import { Connection } from 'misskey-js/built/streaming.js'; import MkNotes from '@/components/MkNotes.vue'; diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 4ee4e011ff..bc8a55c76f 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> </div> </FormSection> - <MkFoldableSection :defaultOpen="false" class="item"> + <MkFoldableSection :expanded="false" class="item"> <template #header>{{ i18n.ts.displayOfNote }}</template> <div class="_gaps_m"> @@ -108,7 +108,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRadios> </div> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false" class="item"> + <MkFoldableSection :expanded="false" class="item"> <template #header>{{ i18n.ts.notificationDisplay }}</template> <div class="_gaps_m"> @@ -131,7 +131,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton @click="testNotification">{{ i18n.ts._notification.checkNotificationBehavior }}</MkButton> </div> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false" class="item"> + <MkFoldableSection :expanded="false" class="item"> <template #header>{{ i18n.ts.appearance }}</template> <div class="_gaps_m"> <div class="_gaps_s"> @@ -169,7 +169,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRadios> </div> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false" class="item"> + <MkFoldableSection :expanded="false" class="item"> <template #header>{{ i18n.ts.behavior }}</template> <div class="_gaps_m"> @@ -227,13 +227,9 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> </div> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>他のサーバーのローカルタイムラインを覗けるようにする</template> <div class="_gaps_m"> - <MkFoldableSection :defaultOpen="false"> - <template #header>{{ i18n.ts.accessToken }} の発行の仕方</template> - <img width="400" src="https://files.prismisskey.space/misskey/676e4b79-7897-4ea9-b074-a98139312f76.gif"> - </MkFoldableSection> <div v-if="maxLocalTimeline >= 1"> <MkInput v-model="remoteLocalTimelineName1" placeholder="prismisskey"> <template #label>{{ i18n.ts.name }}</template> From 9d62381d038509c7ec9075a071937451ef5ca86e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 1 Feb 2024 00:06:46 +0900 Subject: [PATCH 351/501] fix: expanded --- .../src/components/MkFoldableSection.vue | 1 - packages/frontend/src/pages/admin/roles.vue | 20 +++++++++---------- .../src/pages/settings/avatar-decoration.vue | 2 +- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index 5598f7ac8d..453a33dbf5 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -49,7 +49,6 @@ const props = withDefaults(defineProps<{ const rootEl = shallowRef<HTMLDivElement>(); const bg = ref<string>(); const showBody = ref((props.persistKey && miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`)) ? (miLocalStorage.getItem(`${miLocalStoragePrefix}${props.persistKey}`) === 't') : props.expanded); - watch(showBody, () => { if (props.persistKey) { miLocalStorage.setItem(`${miLocalStoragePrefix}${props.persistKey}`, showBody.value ? 't' : 'f'); diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index d06ee74aeb..ec817ccbf0 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template> </MkRange> </MkFolder> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>タイムライン系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])" class="_margin"> <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>ノート系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.canPublicNote, 'canPublicNote'])" class="_margin"> <template #label>{{ i18n.ts._role._options.canPublicNote }}</template> @@ -88,7 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>招待系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> @@ -120,7 +120,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>PrisMisskey独自機能系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'pickerProfileDefault'])" class="_margin"> <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> @@ -142,7 +142,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>カスタム絵文字系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])" class="_margin"> <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> @@ -160,7 +160,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>ドライブ、ファイル系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])" class="_margin"> <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> @@ -178,7 +178,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkSwitch> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>アイコンデコレーション系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])" class="_margin"> <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> @@ -194,7 +194,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>クリップ系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])" class="_margin"> <template #label>{{ i18n.ts._role._options.clipMax }}</template> @@ -210,7 +210,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>リスト系</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])" class="_margin"> <template #label>{{ i18n.ts._role._options.userListMax }}</template> @@ -226,7 +226,7 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header>その他</template> <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])" class="_margin"> <template #label>{{ i18n.ts._role._options.antennaMax }}</template> diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue index 39766978fc..4d039d5d9b 100644 --- a/packages/frontend/src/pages/settings/avatar-decoration.vue +++ b/packages/frontend/src/pages/settings/avatar-decoration.vue @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only /> </div> <div v-for="category in categories"> - <MkFoldableSection :defaultOpen="false"> + <MkFoldableSection :expanded="false"> <template #header> {{ (category !== '') ? category : i18n.ts.other }}</template> <div :class="$style.decorations"> <div v-for="avatarDecoration in avatarDecorations.filter(ad => ad.category === category)"> From f3f817ae2e74630139932117d38bb9bcc868adb7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 1 Feb 2024 00:25:21 +0900 Subject: [PATCH 352/501] fix: expanded --- .../frontend/src/components/MkFoldableSection.vue | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index 453a33dbf5..a504241c7f 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -100,15 +100,20 @@ onMounted(() => { </script> <style lang="scss" module> -.folder-toggle-enter-active, .folder-toggle-leave-active { +.folder-toggle-enter-active { overflow-y: clip; transition: opacity 0.5s, height 0.5s !important; } - -.folder-toggle-enter-from, .folder-toggle-leave-to { +.folder-toggle-leave-active { + overflow-y: clip; + transition: opacity 0.5s, height 0.5s !important; +} +.folder-toggle-enter-from { opacity: 0; } - +.folder-toggle-leave-to { + opacity: 0; +} .root { position: relative; } From 8a4f29d9986a595f069466a2628859d88ab7114e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 1 Feb 2024 00:28:17 +0900 Subject: [PATCH 353/501] fix: expanded --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8f3e0be479..ba17e1ed9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.2.0-beta.8-PrisMisskey.1", + "version": "2024.2.0-beta.8-PrisMisskey.2", "codename": "nasubi", "repository": { "type": "git", From 35c66cb66e10bac7d4aa22aa4bec3640239e931b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 1 Feb 2024 00:31:10 +0900 Subject: [PATCH 354/501] fix: expanded --- packages/frontend/src/components/MkFoldableSection.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index a504241c7f..1667ef4ab2 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -14,10 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </header> <Transition - :enterActiveClass="defaultStore.state.animation ? $style['folder-toggle-enter-active'] : ''" - :leaveActiveClass="defaultStore.state.animation ? $style['folder-toggle-leave-active'] : ''" - :enterFromClass="defaultStore.state.animation ? $style['folder-toggle-enter-from'] : ''" - :leaveToClass="defaultStore.state.animation ? $style['folder-toggle-leave-to'] : ''" + :enterActiveClass="defaultStore.state.animation ? 'folder-toggle-enter-active' : ''" + :leaveActiveClass="defaultStore.state.animation ? 'folder-toggle-leave-active' : ''" + :enterFromClass="defaultStore.state.animation ? 'folder-toggle-enter-from' : ''" + :leaveToClass="defaultStore.state.animation ? 'folder-toggle-leave-to' : ''" @enter="enter" @afterEnter="afterEnter" @leave="leave" From a46d816dc5190fa8ad44f82e19ce64ca7fc84b8b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 1 Feb 2024 00:36:13 +0900 Subject: [PATCH 355/501] fix: expanded --- .../src/components/MkFoldableSection.vue | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue index 1667ef4ab2..a4d42d162e 100644 --- a/packages/frontend/src/components/MkFoldableSection.vue +++ b/packages/frontend/src/components/MkFoldableSection.vue @@ -14,10 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </header> <Transition - :enterActiveClass="defaultStore.state.animation ? 'folder-toggle-enter-active' : ''" - :leaveActiveClass="defaultStore.state.animation ? 'folder-toggle-leave-active' : ''" - :enterFromClass="defaultStore.state.animation ? 'folder-toggle-enter-from' : ''" - :leaveToClass="defaultStore.state.animation ? 'folder-toggle-leave-to' : ''" + :enterActiveClass="defaultStore.state.animation ? $style.folder_toggle_enter_active : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.folder_toggle_leave_active : ''" + :enterFromClass="defaultStore.state.animation ? $style.folder_toggle_enter_from : ''" + :leaveToClass="defaultStore.state.animation ? $style.folder_toggle_leave_to : ''" @enter="enter" @afterEnter="afterEnter" @leave="leave" @@ -100,18 +100,18 @@ onMounted(() => { </script> <style lang="scss" module> -.folder-toggle-enter-active { +.folder_toggle_enter_active { overflow-y: clip; transition: opacity 0.5s, height 0.5s !important; } -.folder-toggle-leave-active { +.folder_toggle_leave_active { overflow-y: clip; transition: opacity 0.5s, height 0.5s !important; } -.folder-toggle-enter-from { +.folder_toggle_enter_from { opacity: 0; } -.folder-toggle-leave-to { +.folder_toggle_leave_to { opacity: 0; } .root { From 6e1e883fb459bc6a57c73e91d4b9e6e5b3db8a61 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 3 Feb 2024 14:09:30 +0900 Subject: [PATCH 356/501] emoji --- .../src/components/MkEmojiPicker.section.vue | 127 ++-- .../frontend/src/components/MkEmojiPicker.vue | 610 +++++++++--------- 2 files changed, 368 insertions(+), 369 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index 64b37d3c72..709005ac74 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -4,58 +4,58 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> -<!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) --> -<section v-if="!hasChildSection" v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);"> - <header class="_acrylic" @click="shown = !shown"> - <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-icons"></i>:{{ emojis.length }}) - </header> - <div v-if="shown" class="body"> - <button - v-for="emoji in emojis" - :key="emoji" - :data-emoji="emoji" - class="_button item" - @pointerenter="computeButtonTitle" - @click="emit('chosen', emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> -</section> -<!-- フォルダの中にはカスタム絵文字やフォルダがある --> -<section v-else v-panel style="border-radius: 6px; border-bottom: 0.5px solid var(--divider);"> - <header class="_acrylic" @click="shown = !shown"> - <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (<i class="ti ti-folder ti-fw"></i>:{{ customEmojiTree?.length }} <i class="ti ti-icons ti-fw"></i>:{{ emojis.length }}) - </header> - <div v-if="shown" style="padding-left: 9px;"> - <MkEmojiPickerSection - v-for="child in customEmojiTree" - :key="`custom:${child.value}`" - :initialShown="initialShown" - :emojis="computed(() => customEmojis.filter(e => e.category === child.category).map(e => `:${e.name}:`))" - :hasChildSection="child.children.length !== 0" - :customEmojiTree="child.children" - @chosen="nestedChosen" - > - {{ child.value || i18n.ts.other }} - </MkEmojiPickerSection> - </div> - <div v-if="shown" class="body"> - <button - v-for="emoji in emojis" - :key="emoji" - :data-emoji="emoji" - class="_button item" - @pointerenter="computeButtonTitle" - @click="emit('chosen', emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> -</section> + <!-- このコンポーネントの要素のclassは親から利用されるのでむやみに弄らないこと --> + <!-- フォルダの中にはカスタム絵文字だけ(Unicode絵文字もこっち) --> + <section v-if="!hasChildSection" style="border-radius: 6px;"> + <header class="_acrylic" @click="shown = !shown" > + <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> ({{ emojis.length }}) + </header> + <div v-if="shown" class="body"> + <button + v-for="emoji in emojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + <!-- フォルダの中にはカスタム絵文字やフォルダがある --> + <section v-else style="border-radius: 6px; padding-top: 9px;"> + <header class="_acrylic" @click="shown = !shown"> + <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (フォルダー) + </header> + <div v-if="shown" style="padding-left: 9px; "> + <MkEmojiPickerSection + v-for="child in customEmojiTree" + :key="`custom:${child.value}`" + :initialShown="initialShown" + :emojis="computed(() => customEmojis.filter(e => e.category === child.category || e.category === child.category+'/'+child.category).map(e => `:${e.name}:`))" + :hasChildSection="child.children.length !== 0" + :customEmojiTree="child.children" + @chosen="nestedChosen" + > + {{ child.value || i18n.ts.other }} + </MkEmojiPickerSection> + </div> + <div v-if="shown" class="body"> + <button + v-for="emoji in emojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + @pointerenter="computeButtonTitle" + @click="emit('chosen', emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> </template> <script lang="ts" setup> @@ -66,15 +66,14 @@ import { customEmojis } from '@/custom-emojis.js'; import MkEmojiPickerSection from '@/components/MkEmojiPicker.section.vue'; const props = defineProps<{ - category?: string[]; - emojis: string[] | Ref<string[]>; - initialShown?: boolean; - hasChildSection?: boolean; - customEmojiTree?: CustomEmojiFolderTree[]; + category?: string[]; + emojis: string[] | Ref<string[]>; + initialShown?: boolean; + hasChildSection?: boolean; + customEmojiTree?: CustomEmojiFolderTree[]; }>(); -console.log(props.customEmojiTree); const emit = defineEmits<{ - (ev: 'chosen', v: string, event: MouseEvent): void; + (ev: 'chosen', v: string, event: MouseEvent): void; }>(); const emojis = computed(() => Array.isArray(props.emojis) ? props.emojis : props.emojis.value); @@ -82,12 +81,12 @@ const shown = ref(!!props.initialShown); /** @see MkEmojiPicker.vue */ function computeButtonTitle(ev: MouseEvent): void { - const elm = ev.target as HTMLElement; - const emoji = elm.dataset.emoji as string; - elm.title = getEmojiName(emoji) ?? emoji; + const elm = ev.target as HTMLElement; + const emoji = elm.dataset.emoji as string; + elm.title = getEmojiName(emoji) ?? emoji; } -function nestedChosen(emoji: any, ev: MouseEvent) { - emit('chosen', emoji, ev); +function nestedChosen(emoji: any, ev?: MouseEvent) { + emit('chosen', emoji, ev); } </script> diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 80159e3067..39698c8790 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -4,103 +4,103 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> - <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> - <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> - <div ref="emojisEl" class="emojis" tabindex="-1"> - <section class="result"> - <div v-if="searchResultCustom.length > 0" class="body"> - <button - v-for="emoji in searchResultCustom" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji class="emoji" :name="emoji.name"/> - </button> - </div> - <div v-if="searchResultUnicode.length > 0" class="body"> - <button - v-for="emoji in searchResultUnicode" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkEmoji class="emoji" :emoji="emoji.char"/> - </button> - </div> - </section> - - <div v-if="tab === 'index'" class="group index"> - <section v-if="showPinned && (pinned && pinned.length > 0)"> - <div style="display: flex; "> - <div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)"> - {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} - </div> - </div> - <div class="body"> + <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> + <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> + <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> + <div ref="emojisEl" class="emojis" tabindex="-1"> + <section class="result"> + <div v-if="searchResultCustom.length > 0" class="body"> <button - v-for="emoji in pinnedEmojis" - :key="emoji" - :data-emoji="emoji" + v-for="emoji in searchResultCustom" + :key="emoji.name" class="_button item" + :title="emoji.name" tabindex="0" - @pointerenter="computeButtonTitle" @click="chosen(emoji, $event)" > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + <MkCustomEmoji class="emoji" :name="emoji.name"/> + </button> + </div> + <div v-if="searchResultUnicode.length > 0" class="body"> + <button + v-for="emoji in searchResultUnicode" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkEmoji class="emoji" :emoji="emoji.char"/> </button> </div> </section> - <section> - <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> - <div class="body"> - <button - v-for="emoji in recentlyUsedEmojis" - :key="emoji" - class="_button item" - :data-emoji="emoji" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> - </button> - </div> - </section> + <div v-if="tab === 'index'" class="group index"> + <section v-if="showPinned"> + <div style="display: flex; "> + <div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)"> + {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} + </div> + </div> + <div class="body"> + <button + v-for="emoji in pinnedEmojis" + :key="emoji" + :data-emoji="emoji" + class="_button item" + tabindex="0" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + + <section> + <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> + <div class="body"> + <button + v-for="emoji in recentlyUsedEmojis" + :key="emoji" + class="_button item" + :data-emoji="emoji" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="emoji[0] === ':'" class="emoji" :name="emoji" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="emoji" :normal="true"/> + </button> + </div> + </section> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> + <XSection + v-for="child in customEmojiFolderRoot.children" + :key="`custom:${child.value}`" + :initialShown="false" + :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" + :hasChildSection="child.children.length !== 0" + :customEmojiTree="child.children" + @chosen="chosen" + > + {{ child.value || i18n.ts.other }} + </XSection> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.emoji }}</header> + <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> + </div> </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> - <XSection - v-for="child in customEmojiFolderRoot.children" - :key="`custom:${child.value}`" - :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => child.value === '' ? (e.category === 'null' || !e.category) : e.category === child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category).filter(filterAvailable).map(e => `:${e.name}:`))" - :hasChildSection="child.children.length !== 0" - :customEmojiTree="child.children" - @chosen="chosen" - > - {{ child.value || i18n.ts.other }} - </XSection> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.emoji }}</header> - <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> + <div class="tabs"> + <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> </div> </div> - <div class="tabs"> - <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> - </div> -</div> </template> <script lang="ts" setup> @@ -127,18 +127,18 @@ import MkButton from '@/components/MkButton.vue'; import { deepClone } from '@/scripts/clone.js'; const $i = signinRequired(); const props = withDefaults(defineProps<{ - showPinned?: boolean; - pinnedEmojis?: string[]; - maxHeight?: number; - asDrawer?: boolean; - asWindow?: boolean; - asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう + showPinned?: boolean; + pinnedEmojis?: string[]; + maxHeight?: number; + asDrawer?: boolean; + asWindow?: boolean; + asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう }>(), { showPinned: true, }); const emit = defineEmits<{ - (ev: 'chosen', v: string): void; + (ev: 'chosen', v: string): void; }>(); const profileMax = $i.policies.emojiPickerProfileLimit; const searchEl = shallowRef<HTMLInputElement>(); @@ -349,7 +349,7 @@ watch(q, () => { }); function filterAvailable(emoji: Misskey.entities.EmojiSimple): boolean { - return ((emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction?.includes(r.id)))) ?? false; + return (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction == null || emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0) || ($i && $i.roles.some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))); } function focus() { @@ -461,262 +461,262 @@ defineExpose({ <style lang="scss" scoped> .omfetrab { - $pad: 8px; + $pad: 8px; - display: flex; - flex-direction: column; + display: flex; + flex-direction: column; - &.s1 { - --eachSize: 40px; - } + &.s1 { + --eachSize: 40px; + } - &.s2 { - --eachSize: 45px; - } + &.s2 { + --eachSize: 45px; + } - &.s3 { - --eachSize: 50px; - } + &.s3 { + --eachSize: 50px; + } - &.w1 { - width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr; - } + &.w1 { + width: calc((var(--eachSize) * 5) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr; + } - &.w2 { - width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w2 { + width: calc((var(--eachSize) * 6) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w3 { - width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w3 { + width: calc((var(--eachSize) * 7) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w4 { - width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w4 { + width: calc((var(--eachSize) * 8) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.w5 { - width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); - --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; - } + &.w5 { + width: calc((var(--eachSize) * 9) + (#{$pad} * 2)); + --columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr; + } - &.h1 { - height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); - } + &.h1 { + height: calc((var(--eachSize) * 4) + (#{$pad} * 2)); + } - &.h2 { - height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); - } + &.h2 { + height: calc((var(--eachSize) * 6) + (#{$pad} * 2)); + } - &.h3 { - height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); - } + &.h3 { + height: calc((var(--eachSize) * 8) + (#{$pad} * 2)); + } - &.h4 { - height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); - } + &.h4 { + height: calc((var(--eachSize) * 10) + (#{$pad} * 2)); + } - &.asDrawer { - width: 100% !important; + &.asDrawer { + width: 100% !important; - > .emojis { - ::v-deep(section) { - > header { - height: 32px; - line-height: 32px; - padding: 0 12px; - font-size: 15px; - } + > .emojis { + ::v-deep(section) { + > header { + height: 32px; + line-height: 32px; + padding: 0 12px; + font-size: 15px; + } - > .body { - display: grid; - grid-template-columns: var(--columns); - font-size: 30px; + > .body { + display: grid; + grid-template-columns: var(--columns); + font-size: 30px; - > .item { - aspect-ratio: 1 / 1; - width: auto; - height: auto; - min-width: 0; - } - } - } - } - } + > .item { + aspect-ratio: 1 / 1; + width: auto; + height: auto; + min-width: 0; + } + } + } + } + } - &.asWindow { - width: 100% !important; - height: 100% !important; + &.asWindow { + width: 100% !important; + height: 100% !important; - > .emojis { - ::v-deep(section) { - > .body { - display: grid; - grid-template-columns: var(--columns); - font-size: 30px; + > .emojis { + ::v-deep(section) { + > .body { + display: grid; + grid-template-columns: var(--columns); + font-size: 30px; - > .item { - aspect-ratio: 1 / 1; - width: auto; - height: auto; - min-width: 0; - } - } - } - } - } + > .item { + aspect-ratio: 1 / 1; + width: auto; + height: auto; + min-width: 0; + } + } + } + } + } - > .search { - width: 100%; - padding: 12px; - box-sizing: border-box; - font-size: 1em; - outline: none; - border: none; - background: transparent; - color: var(--fg); + > .search { + width: 100%; + padding: 12px; + box-sizing: border-box; + font-size: 1em; + outline: none; + border: none; + background: transparent; + color: var(--fg); - &:not(:focus):not(.filled) { - margin-bottom: env(safe-area-inset-bottom, 0px); - } + &:not(:focus):not(.filled) { + margin-bottom: env(safe-area-inset-bottom, 0px); + } - &:not(.filled) { - order: 1; - z-index: 2; - box-shadow: 0px -1px 0 0px var(--divider); - } - } + &:not(.filled) { + order: 1; + z-index: 2; + box-shadow: 0px -1px 0 0px var(--divider); + } + } - > .tabs { - display: flex; - display: none; + > .tabs { + display: flex; + display: none; - > .tab { - flex: 1; - height: 38px; - border-top: solid 0.5px var(--divider); + > .tab { + flex: 1; + height: 38px; + border-top: solid 0.5px var(--divider); - &.active { - border-top: solid 1px var(--accent); - color: var(--accent); - } - } - } + &.active { + border-top: solid 1px var(--accent); + color: var(--accent); + } + } + } - > .emojis { - height: 100%; - overflow-y: auto; - overflow-x: hidden; + > .emojis { + height: 100%; + overflow-y: auto; + overflow-x: hidden; - scrollbar-width: none; + scrollbar-width: none; - &::-webkit-scrollbar { - display: none; - } + &::-webkit-scrollbar { + display: none; + } - > .group { - &:not(.index) { - padding: 4px 0 8px 0; - border-top: solid 0.5px var(--divider); - } + > .group { + &:not(.index) { + padding: 4px 0 8px 0; + border-top: solid 0.5px var(--divider); + } - > header { - /*position: sticky; + > header { + /*position: sticky; top: 0; left: 0;*/ - height: 32px; - line-height: 32px; - z-index: 2; - padding: 0 8px; - font-size: 12px; - } - } + height: 32px; + line-height: 32px; + z-index: 2; + padding: 0 8px; + font-size: 12px; + } + } - ::v-deep(section) { - > header { - position: sticky; - top: 0; - left: 0; - height: 32px; - line-height: 32px; - z-index: 1; - padding: 0 8px; - font-size: 12px; - cursor: pointer; + ::v-deep(section) { + > header { + position: sticky; + top: 0; + left: 0; + height: 32px; + line-height: 32px; + z-index: 1; + padding: 0 8px; + font-size: 12px; + cursor: pointer; - &:hover { - color: var(--accent); - } - } + &:hover { + color: var(--accent); + } + } - > .body { - position: relative; - padding: $pad; + > .body { + position: relative; + padding: $pad; - > .item { - position: relative; - padding: 0; - width: var(--eachSize); - height: var(--eachSize); - contain: strict; - border-radius: 4px; - font-size: 24px; + > .item { + position: relative; + padding: 0; + width: var(--eachSize); + height: var(--eachSize); + contain: strict; + border-radius: 4px; + font-size: 24px; - &:focus-visible { - outline: solid 2px var(--focus); - z-index: 1; - } + &:focus-visible { + outline: solid 2px var(--focus); + z-index: 1; + } - &:hover { - background: rgba(0, 0, 0, 0.05); - } + &:hover { + background: rgba(0, 0, 0, 0.05); + } - &:active { - background: var(--accent); - box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); - } + &:active { + background: var(--accent); + box-shadow: inset 0 0.15em 0.3em rgba(27, 31, 35, 0.15); + } - > .emoji { - height: 1.25em; - vertical-align: -.25em; - pointer-events: none; - width: 100%; - object-fit: contain; - } - } - } + > .emoji { + height: 1.25em; + vertical-align: -.25em; + pointer-events: none; + width: 100%; + object-fit: contain; + } + } + } - &.result { - border-bottom: solid 0.5px var(--divider); + &.result { + border-bottom: solid 0.5px var(--divider); - &:empty { - display: none; - } - } - } - } + &:empty { + display: none; + } + } + } + } } .sllfktkhgl{ - display: inline-block; - padding: 0 4px; - font-size: 12px; - line-height: 32px; - text-align: center; - color: var(--fg); - cursor: pointer; - width: 100%; - transition: transform 0.3s ease; - box-shadow: 0 1.5px 0 var(--divider); - height: 32px; - overflow: hidden; - &:hover { - transform: translateY(1.5px); - } - &.active { - transform: translateY(5px); - } + display: inline-block; + padding: 0 4px; + font-size: 12px; + line-height: 32px; + text-align: center; + color: var(--fg); + cursor: pointer; + width: 100%; + transition: transform 0.3s ease; + box-shadow: 0 1.5px 0 var(--divider); + height: 32px; + overflow: hidden; + &:hover { + transform: translateY(1.5px); + } + &.active { + transform: translateY(5px); + } } </style> From fd54c299a5f70ce5c5e8c4e38ee058f59c2e885d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 4 Feb 2024 17:34:26 +0900 Subject: [PATCH 357/501] ui:fix --- packages/frontend/src/components/global/MkPageHeader.vue | 6 ++++-- packages/frontend/src/pages/notifications.vue | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index 16e2eb877f..dd34b14f9f 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="!thin_ && narrow && props.displayMyAvatar && $i" class="_button" :class="$style.buttonsLeft" @click="openAccountMenu"> <MkAvatar :class="$style.avatar" :user="$i"/> </div> - <div v-else-if="!thin_ && narrow && !hideTitle" :class="$style.buttonsLeft"/> + <div v-else-if="!thin_ && narrow && !hideTitle"/> <template v-if="metadata"> <div v-if="!hideTitle && !hide" :class="$style.titleContainer" @click="top"> @@ -170,7 +170,9 @@ onUnmounted(() => { &.slim { text-align: center; gap: 0; - + .buttonsRight { + margin-left: auto; + } .tabs:first-child { margin-left: 0; } diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index cc32ef51d0..b879ea0e75 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkStickyContainer> - <template #header><MkPageHeader v-model:tab="tab" :tabs="headerTabs" hide="true" :actions="headerActions"/></template> + <template #header><MkPageHeader v-model:tab="tab" :tabs="headerTabs" :hide="true" :actions="headerActions"/></template> <MkSpacer :contentMax="800"> <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> <div v-if="tab === 'all'" key="all"> @@ -73,7 +73,7 @@ const headerActions = computed(() => [{ icon: 'ti ti-filter', highlighted: includeTypes.value != null, handler: setFilter, -} ].filter(x => x !== undefined)); +}].filter(x => x !== undefined)); misskeyApi('notifications/mark-all-as-read'); const headerTabs = computed(() => [{ key: 'all', From 2ab6bb52c2dfb2c896cc7ccbd87eee35f4c6b11a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 01:28:12 +0900 Subject: [PATCH 358/501] ui:fix --- locales/index.d.ts | 16 +++++ locales/ja-JP.yml | 4 ++ .../src/server/api/endpoints/notes/update.ts | 58 +++++++++++-------- .../frontend/src/pages/settings/general.vue | 57 ++++++++++-------- packages/frontend/src/pages/timeline.vue | 34 ++++++----- packages/frontend/src/store.ts | 20 ++++++- 6 files changed, 122 insertions(+), 67 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index f4a1c20b34..44bef43ceb 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -108,6 +108,22 @@ export interface Locale extends ILocale { * グローバルタイムラインを表示する */ "showGlobalTimeline": string; + /** + * ホームタイムラインを表示する + */ + "showHomeTimeline": string; + /** + * ローカルタイムラインを表示する + */ + "showLocalTimeline": string; + /** + * ソーシャルタイムラインを表示する + */ + "showSocialTimeline": string; + /** + * 上のバーのTLの名前を表示する + */ + "topBarNameShown": string; /** * {user}がリノート */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fe86f66dc7..a1a66cecce 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -23,6 +23,10 @@ myLists: "自分の作成したリスト" noThankYou: "やめておく" enterUsername: "ユーザー名を入力" showGlobalTimeline: "グローバルタイムラインを表示する" +showHomeTimeline: "ホームタイムラインを表示する" +showLocalTimeline: "ローカルタイムラインを表示する" +showSocialTimeline: "ソーシャルタイムラインを表示する" +topBarNameShown: "上のバーのTLの名前を表示する" renotedBy: "{user}がリノート" noNotes: "ノートはありません" noNotifications: "通知はありません" diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index 4f01cc2217..fe6d9637e8 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -5,7 +5,7 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; -import type { UsersRepository, NotesRepository , DriveFilesRepository, MiDriveFile} from '@/models/_.js'; +import type { UsersRepository, NotesRepository, DriveFilesRepository, MiDriveFile } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { DI } from '@/di-symbols.js'; @@ -14,7 +14,6 @@ import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { ApiError } from '../../error.js'; - export const meta = { tags: ['notes'], @@ -35,11 +34,11 @@ export const meta = { code: 'NO_SUCH_NOTE', id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474', }, - noSuchFile: { - message: 'Some files are not found.', - code: 'NO_SUCH_FILE', - id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', - }, + noSuchFile: { + message: 'Some files are not found.', + code: 'NO_SUCH_FILE', + id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', + }, }, } as const; @@ -49,8 +48,8 @@ export const paramDef = { noteId: { type: 'string', format: 'misskey:id' }, visibility: { type: 'string', enum: ['public', 'home', 'followers', 'specified'], default: 'public' }, visibleUserIds: { type: 'array', uniqueItems: true, items: { - type: 'string', format: 'misskey:id', - } }, + type: 'string', format: 'misskey:id', + } }, cw: { type: 'string', nullable: true, maxLength: 100 }, localOnly: { type: 'boolean', default: false }, reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, @@ -132,34 +131,43 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - let files: MiDriveFile[] = []; - const fileIds = ps.fileIds ?? null; + let files: MiDriveFile[] = []; + const fileIds = ps.fileIds ?? null; - if (fileIds != null) { - files = await this.driveFilesRepository.createQueryBuilder('file') - .where('file.userId = :userId AND file.id IN (:...fileIds)', { - userId: me.id, - fileIds, - }) - .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') - .setParameters({ fileIds }) - .getMany(); + if (fileIds != null) { + files = await this.driveFilesRepository.createQueryBuilder('file') + .where('file.userId = :userId AND file.id IN (:...fileIds)', { + userId: me.id, + fileIds, + }) + .orderBy('array_position(ARRAY[:...fileIds], "id"::text)') + .setParameters({ fileIds }) + .getMany(); - if (files.length !== fileIds.length) { - throw new ApiError(meta.errors.noSuchFile); - } - } + if (files.length !== fileIds.length) { + throw new ApiError(meta.errors.noSuchFile); + } + } if (note.userId !== me.id) { throw new ApiError(meta.errors.noSuchNote); } - await this.notesRepository.update({ id: note.id }, { updatedAt: new Date(), cw: ps.cw, text: ps.text, fileIds: files.length > 0 ? files.map(f => f.id) : undefined, + poll: ps.poll ? { + choices: ps.poll.choices, + multiple: ps.poll.multiple ?? false, + expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, + } : undefined, + localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, + apMentions: ps.noExtractMentions ? [] : undefined, + apHashtags: ps.noExtractHashtags ? [] : undefined, + apEmojis: ps.noExtractEmojis ? [] : undefined, }); this.globalEventService.publishNoteStream(note.id, 'updated', { diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index bc8a55c76f..c217a45a41 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -38,16 +38,19 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> - <div class="_margin" v-for="pinnedLists in defaultStore.reactiveState.pinnedUserLists.value"> + <div v-for="pinnedLists in defaultStore.reactiveState.pinnedUserLists.value" class="_margin"> {{ pinnedLists.name }} - <MkButton danger @click="removePinnedList(pinnedLists.id,pinnedLists.name)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + <MkButton danger @click="removePinnedList(pinnedLists.id,pinnedLists.name)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> </div> <MkButton v-if="pinnedMax > defaultStore.reactiveState.pinnedUserLists.value.length " @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> - <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length " danger @click="removePinnedList('all')"><i class="ti ti-trash"></i> {{i18n.ts.all}}{{ i18n.ts.remove }}</MkButton> - + <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length " danger @click="removePinnedList('all')"><i class="ti ti-trash"></i> {{ i18n.ts.all }}{{ i18n.ts.remove }}</MkButton> </MkFolder> <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline }}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> + <MkSwitch v-model="showHomeTimeline">{{ i18n.ts.showHomeTimeline }}</MkSwitch> + <MkSwitch v-model="showSocialTimeline">{{ i18n.ts.showSocialTimeline }}</MkSwitch> + <MkSwitch v-model="showLocalTimeline">{{ i18n.ts.showLocalTimeline }}</MkSwitch> + <MkSwitch v-model="topBarNameShown">{{ i18n.ts.topBarNameShown }}</MkSwitch> </div> </FormSection> <MkFoldableSection :expanded="false" class="item"> @@ -350,7 +353,7 @@ import MkInfo from '@/components/MkInfo.vue'; import { langs } from '@/config.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; -import {signinRequired} from '@/account.js'; +import { signinRequired } from '@/account.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -422,6 +425,10 @@ const enableonlyAndWithSave = computed(defaultStore.makeGetterSetter('onlyAndWit const enablehanntenn = computed(defaultStore.makeGetterSetter('enablehanntenn')); const showMediaTimeline = computed(defaultStore.makeGetterSetter('showMediaTimeline')); const showGlobalTimeline = computed(defaultStore.makeGetterSetter('showGlobalTimeline')); +const showLocalTimeline = computed(defaultStore.makeGetterSetter('showLocalTimeline')); +const showHomeTimeline = computed(defaultStore.makeGetterSetter('showHomeTimeline')); +const showSocialTimeline = computed(defaultStore.makeGetterSetter('showSocialTimeline')); +const topBarNameShown = computed(defaultStore.makeGetterSetter('topBarNameShown')); const showVisibilityColor = computed(defaultStore.makeGetterSetter('showVisibilityColor')); const disableStreamingTimeline = computed(defaultStore.makeGetterSetter('disableStreamingTimeline')); const useGroupedNotifications = computed(defaultStore.makeGetterSetter('useGroupedNotifications')); @@ -449,8 +456,8 @@ const remoteLocalTimelineEnable3 = computed(defaultStore.makeGetterSetter('remot const remoteLocalTimelineEnable4 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable4')); const remoteLocalTimelineEnable5 = computed(defaultStore.makeGetterSetter('remoteLocalTimelineEnable5')); const $i = signinRequired(); -const pinnedMax = $i.policies?.listPinnedLimit; -const maxLocalTimeline = $i.policies?.localTimelineAnyLimit; +const pinnedMax = $i.policies.listPinnedLimit; +const maxLocalTimeline = $i.policies.localTimelineAnyLimit; watch(lang, () => { miLocalStorage.setItem('lang', lang.value as string); miLocalStorage.removeItem('locale'); @@ -518,6 +525,10 @@ watch([ showVisibilityColor, enableonlyAndWithSave, showGlobalTimeline, + showSocialTimeline, + showLocalTimeline, + showHomeTimeline, + topBarNameShown, disableStreamingTimeline, enableSeasonalScreenEffect, ], async () => { @@ -611,26 +622,24 @@ async function setPinnedList() { } } -async function removePinnedList(id,name) { +async function removePinnedList(id, name) { + if (!id) return; + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.tsx.removeAreYouSure({ x: name ?? id }), + }); + if (canceled) return; - if (!id) return; - const {canceled} = await os.confirm({ - type: 'warning', - text: i18n.tsx.removeAreYouSure({x: name ?? id }), - }); - if (canceled) return; + if (id === 'all') { + if (canceled) return; - if (id === 'all') { + defaultStore.set('pinnedUserLists', []); + return; + } - if (canceled) return; - - defaultStore.set('pinnedUserLists', []); - return; - } - - const pinnedLists = defaultStore.state.pinnedUserLists; - const newPinnedLists = pinnedLists.filter(pinnedList => pinnedList.id !== id); - defaultStore.set('pinnedUserLists', newPinnedLists); + const pinnedLists = defaultStore.state.pinnedUserLists; + const newPinnedLists = pinnedLists.filter(pinnedList => pinnedList.id !== id); + defaultStore.set('pinnedUserLists', newPinnedLists); } let smashCount = 0; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 32b66680a5..6a936f3ebd 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -55,7 +55,7 @@ import { miLocalStorage } from '@/local-storage.js'; provide('shouldOmitHeaderTitle', true); -const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); +const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable && defaultStore.state.showLocalTimeline) || ($i != null && $i.policies.ltlAvailable && defaultStore.state.showLocalTimeline); const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable && defaultStore.state.showGlobalTimeline) || ($i != null && $i.policies.gtlAvailable && defaultStore.state.showGlobalTimeline); const keymap = { 't': focus, @@ -110,6 +110,8 @@ const remoteLocalTimelineEnable2 = ref(defaultStore.state.remoteLocalTimelineEna const remoteLocalTimelineEnable3 = ref(defaultStore.state.remoteLocalTimelineEnable3); const remoteLocalTimelineEnable4 = ref(defaultStore.state.remoteLocalTimelineEnable4); const remoteLocalTimelineEnable5 = ref(defaultStore.state.remoteLocalTimelineEnable5); +const showHomeTimeline = ref(defaultStore.state.showHomeTimeline); +const showSocialTimeline = ref(defaultStore.state.showSocialTimeline); watch(src, () => { queue.value = 0; }); @@ -282,57 +284,57 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList key: 'list:' + l.id, title: l.name, icon: 'ti ti-star', - iconOnly: false, -}))), { + iconOnly: defaultStore.state.topBarNameShown ?? false, +}))), ...(showHomeTimeline.value ? [{ key: 'home', title: i18n.ts._timelines.home, icon: 'ti ti-home', - iconOnly: false, -}, ...(isLocalTimelineAvailable ? [{ + iconOnly: defaultStore.state.topBarNameShown ?? false, +}] : []), ...(isLocalTimelineAvailable ? [{ key: 'local', title: i18n.ts._timelines.local, icon: 'ti ti-planet', - iconOnly: false, -}, ...(isShowMediaTimeline.value ? [{ + iconOnly: defaultStore.state.topBarNameShown ?? false, +}] : []), ...(isShowMediaTimeline.value ? [{ key: 'media', title: i18n.ts._timelines.media, icon: 'ti ti-photo', - iconOnly: false, -}] : []), { + iconOnly: defaultStore.state.topBarNameShown ?? false, +}] : []), ...(showSocialTimeline.value ? [{ key: 'social', title: i18n.ts._timelines.social, icon: 'ti ti-universe', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable1.value ? [{ key: 'custom-timeline-1', title: defaultStore.state.remoteLocalTimelineName1, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable2.value ? [{ key: 'custom-timeline-2', title: defaultStore.state.remoteLocalTimelineName2, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable3.value ? [{ key: 'custom-timeline-3', title: defaultStore.state.remoteLocalTimelineName3, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable4.value ? [{ key: 'custom-timeline-4', title: defaultStore.state.remoteLocalTimelineName4, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(remoteLocalTimelineEnable5.value ? [{ key: 'custom-timeline-5', title: defaultStore.state.remoteLocalTimelineName5, icon: 'ti ti-plus', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), ...(isGlobalTimelineAvailable ? [{ key: 'global', title: i18n.ts._timelines.global, icon: 'ti ti-whirl', - iconOnly: false, + iconOnly: defaultStore.state.topBarNameShown ?? false, }] : []), { icon: 'ti ti-list', title: i18n.ts.lists, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index cb5f1b3416..009dfaa2aa 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -364,6 +364,22 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + topBarNameShown: { + where: 'device', + default: false, + }, + showHomeTimeline: { + where: 'device', + default: true, + }, + showLocalTimeline: { + where: 'device', + default: true, + }, + showSocialTimeline: { + where: 'device', + default: true, + }, showGapBetweenNotesInTimeline: { where: 'device', default: false, @@ -676,10 +692,10 @@ export const defaultStore = markRaw(new Storage('base', { sfxVolume: 1, }, }, - hemisphere: { + hemisphere: { where: 'device', default: hemisphere as 'N' | 'S', - }, + }, enableHorizontalSwipe: { where: 'device', default: true, From 3fd393ddb76dc870b7cc7a526625fe09eb32603b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 13:57:13 +0900 Subject: [PATCH 359/501] ui:fix --- packages/frontend/src/pages/about-misskey.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index acaae9f1d7..b6b76f708d 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -77,6 +77,10 @@ SPDX-License-Identifier: AGPL-3.0-only <img src="https://avatars.githubusercontent.com/u/22656849?v=4" :class="$style.contributorAvatar"> <span :class="$style.contributorUsername">@anatawa12</span> </a> + <a href="https://github.com/mattyatea" target="_blank" :class="$style.contributor"> + <img src="https://avatars.githubusercontent.com/u/56515516?v=4" :class="$style.contributorAvatar"> + <span :class="$style.contributorUsername">@mattyatea</span> + </a> </div> </FormSection> <FormSection> From ed7cf7828f7891ec44204ec6bef611a490d7e1d7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 13:57:34 +0900 Subject: [PATCH 360/501] 2024.2.0-beta.9-PrisMisskey.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e6da9cff89..a24503d604 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.2.0-beta.9-PrisMisskey.1", + "version": "2024.2.0-beta.9-PrisMisskey.2", "codename": "nasubi", "repository": { "type": "git", From dad9dfef9e7094961f57e5b6561976646f046267 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 14:10:40 +0900 Subject: [PATCH 361/501] ui:fix --- locales/index.d.ts | 6 +++- locales/ja-JP.yml | 3 +- .../frontend/src/pages/settings/general.vue | 28 +++++++++++++++---- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 44bef43ceb..0b5e5c118e 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -116,12 +116,16 @@ export interface Locale extends ILocale { * ローカルタイムラインを表示する */ "showLocalTimeline": string; + /** + * トップバーのカスタムをする + */ + "topbarCustom": string; /** * ソーシャルタイムラインを表示する */ "showSocialTimeline": string; /** - * 上のバーのTLの名前を表示する + * 上のバーにTLの名前を常時表示する */ "topBarNameShown": string; /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index a1a66cecce..4d2030077f 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -25,8 +25,9 @@ enterUsername: "ユーザー名を入力" showGlobalTimeline: "グローバルタイムラインを表示する" showHomeTimeline: "ホームタイムラインを表示する" showLocalTimeline: "ローカルタイムラインを表示する" +topbarCustom: "トップバーのカスタムをする" showSocialTimeline: "ソーシャルタイムラインを表示する" -topBarNameShown: "上のバーのTLの名前を表示する" +topBarNameShown: "上のバーにTLの名前を常時表示する" renotedBy: "{user}がリノート" noNotes: "ノートはありません" noNotifications: "通知はありません" diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index c217a45a41..c2b6d0a68c 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -45,12 +45,28 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="pinnedMax > defaultStore.reactiveState.pinnedUserLists.value.length " @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length " danger @click="removePinnedList('all')"><i class="ti ti-trash"></i> {{ i18n.ts.all }}{{ i18n.ts.remove }}</MkButton> </MkFolder> - <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.showMediaTimeline }}<template #caption>{{ i18n.ts.showMediaTimelineInfo }} </template></MkSwitch> - <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.showGlobalTimeline }}</MkSwitch> - <MkSwitch v-model="showHomeTimeline">{{ i18n.ts.showHomeTimeline }}</MkSwitch> - <MkSwitch v-model="showSocialTimeline">{{ i18n.ts.showSocialTimeline }}</MkSwitch> - <MkSwitch v-model="showLocalTimeline">{{ i18n.ts.showLocalTimeline }}</MkSwitch> - <MkSwitch v-model="topBarNameShown">{{ i18n.ts.topBarNameShown }}</MkSwitch> + <MkFoldableSection :expanded="false" class="item"> + <template #header>{{ i18n.ts.topbarCustom }}</template> + + + {{ i18n.ts._timelines.home }} + <MkSwitch v-model="showHomeTimeline">{{ i18n.ts.enable }}</MkSwitch> + <br> + {{ i18n.ts._timelines.local }} + <MkSwitch v-model="showLocalTimeline">{{ i18n.ts.enable }}</MkSwitch> + <br> + {{ i18n.ts._timelines.social }} + <MkSwitch v-model="showSocialTimeline">{{ i18n.ts.enable }}</MkSwitch> + <br> + {{ i18n.ts._timelines.media }} + <MkSwitch v-model="showMediaTimeline">{{ i18n.ts.enable }}</MkSwitch> + <br> + {{ i18n.ts._timelines.global }} + <MkSwitch v-model="showGlobalTimeline">{{ i18n.ts.enable }}</MkSwitch> + <br> + {{ i18n.ts.topBarNameShown }} + <MkSwitch v-model="topBarNameShown">{{ i18n.ts.enable }}</MkSwitch> + </MkFoldableSection> </div> </FormSection> <MkFoldableSection :expanded="false" class="item"> From a34e972301fa8c1e217dcc281f688834549819a1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 14:31:30 +0900 Subject: [PATCH 362/501] ui:fix --- locales/ja-JP.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 4d2030077f..a61951f489 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -27,7 +27,7 @@ showHomeTimeline: "ホームタイムラインを表示する" showLocalTimeline: "ローカルタイムラインを表示する" topbarCustom: "トップバーのカスタムをする" showSocialTimeline: "ソーシャルタイムラインを表示する" -topBarNameShown: "上のバーにTLの名前を常時表示する" +topBarNameShown: "上のバーにTLの名前を省略して表示する" renotedBy: "{user}がリノート" noNotes: "ノートはありません" noNotifications: "通知はありません" From a23e9a71bf313da700a6e9b7b417c0ba9eaa4909 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 14:31:48 +0900 Subject: [PATCH 363/501] ui:fix --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index a24503d604..e1887ad8ef 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.2.0-beta.9-PrisMisskey.2", + "version": "2024.2.0-beta.9-PrisMisskey.3", "codename": "nasubi", "repository": { "type": "git", From 557b5a7beb19615676bf16c53b7f6b184851391b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 5 Feb 2024 20:39:55 +0900 Subject: [PATCH 364/501] ui:fix --- locales/index.d.ts | 2 +- packages/frontend/src/components/MkAbuseReportWindow.vue | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 0b5e5c118e..a754d55d9d 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -125,7 +125,7 @@ export interface Locale extends ILocale { */ "showSocialTimeline": string; /** - * 上のバーにTLの名前を常時表示する + * 上のバーにTLの名前を省略して表示する */ "topBarNameShown": string; /** diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue index f027b5f452..9f00210524 100644 --- a/packages/frontend/src/components/MkAbuseReportWindow.vue +++ b/packages/frontend/src/components/MkAbuseReportWindow.vue @@ -23,9 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template v-if="page === 0"> <MkSpacer :marginMin="20" :marginMax="28"> <div class="_gaps_m" :class="$style.root"> - <MkPagination v-slot="{items}" :key="user.id" :pagination="Pagination"> + <MkPagination v-slot="{items}" :key="user.id" :pagination="Pagination" :disableAutoLoad="true"> <div v-for="item in items" :key="item.id" :class="$style.note"> - <MkSwitch :modelValue="item.id === initialNoteId" @update:modelValue="pushAbuseReportNote($event,item.id)"></MkSwitch> + <MkSwitch :modelValue="abuseNotesId.includes(item.id)" @update:modelValue="pushAbuseReportNote($event,item.id)"></MkSwitch> <MkAvatar :user="item.user" preview/> <MkNoteSimple :note="item"/> </div> @@ -80,7 +80,6 @@ const Pagination = { userId: props.user.id, }, }; - const emit = defineEmits<{ (ev: 'closed'): void; }>(); @@ -90,8 +89,8 @@ const page = ref(0); const uiWindow = shallowRef<InstanceType<typeof MkWindow>>(); const comment = ref(props.initialComment ?? ''); -function pushAbuseReportNote(v, id) { - if (v) { +function pushAbuseReportNote(ev, id) { + if (ev) { abuseNotesId.value.push(id); } else { abuseNotesId.value = abuseNotesId.value.filter(noteId => noteId !== id); From 399f7c451b657a1eea40e7337eceab002cdf8679 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 11 Feb 2024 20:23:39 +0900 Subject: [PATCH 365/501] =?UTF-8?q?=E8=89=B2=E3=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 + locales/ja-JP.yml | 1 + packages/frontend/src/cache.ts | 2 + .../frontend/src/components/MkPostForm.vue | 1207 +++++++++-------- .../frontend/src/components/MkTimeline.vue | 1 + .../src/components/global/MkPageHeader.vue | 1 - packages/frontend/src/pages/channel.vue | 1 - packages/frontend/src/pages/notifications.vue | 7 +- .../frontend/src/pages/settings/general.vue | 54 +- packages/frontend/src/pages/timeline.vue | 21 +- packages/frontend/src/store.ts | 5 +- 11 files changed, 691 insertions(+), 613 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index e77fd8a615..5a2272f4d8 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -84,6 +84,10 @@ export interface Locale extends ILocale { * ルビ */ "ruby": string; + /** + * ピン留めされたチャンネル + */ + "pinnedChannel": string; /** * わかった */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 77dba50625..4b5d12fe36 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -17,6 +17,7 @@ notificationIndicator: "通知のインジケーターの数字を表示する" hanntenn: "アイコンとバナーを反転させる" hanntennInfo: "ダークだったらライトのアイコンに、ライトだったらダークのアイコンに。" ruby: "ルビ" +pinnedChannel: "ピン留めされたチャンネル" gotIt: "わかった" cancel: "キャンセル" myLists: "自分の作成したリスト" diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts index 17d813a3f2..ab19837571 100644 --- a/packages/frontend/src/cache.ts +++ b/packages/frontend/src/cache.ts @@ -12,3 +12,5 @@ export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/role export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list')); export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list')); export const userFavoriteListsCache = new Cache(1000 * 60 * 30, () => misskeyApi('users/lists/list-favorite')); +export const userChannelsCache = new Cache<Misskey.entities.UserChannel[]>(1000 * 60 * 30, () => misskeyApi('channels/owned')); +export const userChannelFollowingsCache = new Cache<Misskey.entities.UserChannelFollowing[]>(1000 * 60 * 30, () => misskeyApi('channels/followed')); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 3effffe494..58f11a8b5d 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -4,103 +4,103 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div - :class="[$style.root, { [$style.modal]: modal, _popup: modal }]" - @dragover.stop="onDragover" - @dragenter="onDragenter" - @dragleave="onDragleave" - @drop.stop="onDrop" - > - <header :class="$style.header"> - <div :class="$style.headerLeft"> - <button v-if="!fixed" :class="$style.cancel" class="_button" @click="cancel"><i class="ti ti-x"></i></button> - <button v-click-anime v-tooltip="i18n.ts.switchAccount" :class="$style.account" class="_button" @click="openAccountMenu"> - <MkAvatar :user="postAccount ?? $i" :class="$style.avatar"/> - </button> - </div> - <div :class="$style.headerRight"> - <template v-if="!(channel != null && fixed)"> - <button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility"> - <span v-if="visibility === 'public'"><i class="ti ti-world"></i></span> - <span v-if="visibility === 'home'"><i class="ti ti-home"></i></span> - <span v-if="visibility === 'followers'"><i class="ti ti-lock"></i></span> - <span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span> - <span :class="$style.headerRightButtonText">{{ i18n.ts._visibility[visibility] }}</span> - </button> - <button v-else class="_button" :class="[$style.headerRightItem, $style.visibility]" disabled> - <span><i class="ti ti-device-tv"></i></span> - <span :class="$style.headerRightButtonText">{{ channel.name }}</span> - </button> - </template> - <button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly"> - <span v-if="!localOnly"><i class="ti ti-rocket"></i></span> - <span v-else><i class="ti ti-rocket-off"></i></span> - </button> - <button v-tooltip="i18n.ts.otherSettings" class="_button" :class="[$style.headerRightItem]" @click="openOtherSettingsMenu"><i class="ti ti-dots"></i></button> - <button v-click-anime class="_button" :class="$style.submit" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> - <div :class="[$style.submitInner ,{ [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"> - <template v-if="posted"></template> - <template v-else-if="posting"><MkEllipsis/></template> - <template v-else>{{ submitText }}</template> - <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : schedule ? 'ti ti-clock-hour-4' : 'ti ti-send'"></i> - </div> - </button> - </div> - </header> - <MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/> - <MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/> - <div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="ti ti-x"></i></button></div> - <div v-if="visibility === 'specified'" :class="$style.toSpecified"> - <span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span> - <div :class="$style.visibleUsers"> +<div + :class="[$style.root, { [$style.modal]: modal, _popup: modal }]" + @dragover.stop="onDragover" + @dragenter="onDragenter" + @dragleave="onDragleave" + @drop.stop="onDrop" +> + <header :class="$style.header"> + <div :class="$style.headerLeft"> + <button v-if="!fixed" :class="$style.cancel" class="_button" @click="cancel"><i class="ti ti-x"></i></button> + <button v-click-anime v-tooltip="i18n.ts.switchAccount" :class="$style.account" class="_button" @click="openAccountMenu"> + <MkAvatar :user="postAccount ?? $i" :class="$style.avatar"/> + </button> + </div> + <div :class="$style.headerRight"> + <template v-if="!(channel != null && fixed)"> + <button v-if="channel == null" ref="visibilityButton" v-click-anime v-tooltip="i18n.ts.visibility" :class="['_button', $style.headerRightItem, $style.visibility]" @click="setVisibility"> + <span v-if="visibility === 'public'"><i class="ti ti-world"></i></span> + <span v-if="visibility === 'home'"><i class="ti ti-home"></i></span> + <span v-if="visibility === 'followers'"><i class="ti ti-lock"></i></span> + <span v-if="visibility === 'specified'"><i class="ti ti-mail"></i></span> + <span :class="$style.headerRightButtonText">{{ i18n.ts._visibility[visibility] }}</span> + </button> + <button v-else class="_button" :class="[$style.headerRightItem, $style.visibility]" disabled> + <span><i class="ti ti-device-tv"></i></span> + <span :class="$style.headerRightButtonText">{{ channel.name }}</span> + </button> + </template> + <button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly"> + <span v-if="!localOnly"><i class="ti ti-rocket"></i></span> + <span v-else><i class="ti ti-rocket-off"></i></span> + </button> + <button v-tooltip="i18n.ts.otherSettings" class="_button" :class="[$style.headerRightItem]" @click="openOtherSettingsMenu"><i class="ti ti-dots"></i></button> + <button v-click-anime class="_button" :class="$style.submit" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> + <div :class="[$style.submitInner ,{ [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"> + <template v-if="posted"></template> + <template v-else-if="posting"><MkEllipsis/></template> + <template v-else>{{ submitText }}</template> + <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : schedule ? 'ti ti-clock-hour-4' : 'ti ti-send'"></i> + </div> + </button> + </div> + </header> + <MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/> + <MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/> + <div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="ti ti-x"></i></button></div> + <div v-if="visibility === 'specified'" :class="$style.toSpecified"> + <span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span> + <div :class="$style.visibleUsers"> <span v-for="u in visibleUsers" :key="u.id" :class="$style.visibleUser"> <MkAcct :user="u"/> <button class="_button" style="padding: 4px 8px;" @click="removeVisibleUser(u)"><i class="ti ti-x"></i></button> </span> - <button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button> - </div> - </div> - <MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> - <input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> - <div :class="[$style.textOuter, { [$style.withCw]: useCw }]"> - <div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div> + <button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button> + </div> + </div> + <MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> + <input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> + <div :class="[$style.textOuter, { [$style.withCw]: useCw }]"> + <div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div> <textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> - <div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div> - </div> - <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> - <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/> - <div :class="$style.postOptionsRoot"> + <div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div> + </div> + <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> + <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/> + <div :class="$style.postOptionsRoot"> <MkPollEditor v-if="poll" v-model="poll" @destroyed="poll = null"/> <MkScheduleEditor v-if="schedule" v-model="schedule" @destroyed="schedule = null"/> </div> - <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/> - <div v-if="showingOptions" style="padding: 8px 16px;"> - </div> - <footer :class="$style.footer"> - <div :class="$style.footerLeft"> - <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> - <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> - <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> - <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> - <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> - <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> - <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> - <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button> - <button v-tooltip="i18n.ts.ruby" :class="['_button', $style.footerButton]" @click="insertRuby"><i class="ti ti-abc"></i></button> + <MkNotePreview v-if="showPreview" :class="$style.preview" :text="text" :files="files" :poll="poll ?? undefined" :useCw="useCw" :cw="cw" :user="postAccount ?? $i"/> + <div v-if="showingOptions" style="padding: 8px 16px;"> + </div> + <footer :class="$style.footer"> + <div :class="$style.footerLeft"> + <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> + <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> + <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> + <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> + <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> + <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> + <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> + <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button> + <button v-tooltip="i18n.ts.ruby" :class="['_button', $style.footerButton]" @click="insertRuby"><i class="ti ti-abc"></i></button> </div> - <div :class="$style.footerRight"> - <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> - <!--<button v-tooltip="i18n.ts.more" class="_button" :class="$style.footerButton" @click="showingOptions = !showingOptions"><i class="ti ti-dots"></i></button>--> - </div> - </footer> - <datalist id="hashtags"> - <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> - </datalist> - </div> + <div :class="$style.footerRight"> + <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> + <!--<button v-tooltip="i18n.ts.more" class="_button" :class="$style.footerButton" @click="showingOptions = !showingOptions"><i class="ti ti-dots"></i></button>--> + </div> + </footer> + <datalist id="hashtags"> + <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> + </datalist> +</div> </template> <script lang="ts" setup> -import { inject, watch, nextTick, onMounted, defineAsyncComponent , provide, shallowRef, ref, computed } from 'vue'; +import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import insertTextAtCursor from 'insert-text-at-cursor'; @@ -119,13 +119,13 @@ import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFiles } from '@/scripts/select-file.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; import { - bannerDark, - bannerLight, - defaultStore, - iconDark, - iconLight, - notePostInterruptors, - postFormActions + bannerDark, + bannerLight, + defaultStore, + iconDark, + iconLight, + notePostInterruptors, + postFormActions, } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n.js'; @@ -146,7 +146,6 @@ const $i = signinRequired(); const modal = inject('modal'); let gamingType = computed(defaultStore.makeGetterSetter('gamingType')); - const props = withDefaults(defineProps<{ reply?: Misskey.entities.Note; renote?: Misskey.entities.Note; @@ -167,8 +166,8 @@ const props = withDefaults(defineProps<{ mock?: boolean; updateMode?: boolean; }>(), { - initialVisibleUsers: () => [], - autofocus: true, + initialVisibleUsers: () => [], + autofocus: true, mock: false, }); @@ -207,7 +206,7 @@ const localOnly = ref<boolean>(props.initialLocalOnly ?? defaultStore.state.reme const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility) as typeof Misskey.noteVisibilities[number]); const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]); if (props.initialVisibleUsers) { - props.initialVisibleUsers.forEach(pushVisibleUser); + props.initialVisibleUsers.forEach(pushVisibleUser); } const reactionAcceptance = ref(defaultStore.state.reactionAcceptance); const autocomplete = ref(null); @@ -220,59 +219,59 @@ const showingOptions = ref(false); const textAreaReadOnly = ref(false); const draftKey = computed((): string => { - let key = props.channel ? `channel:${props.channel.id}` : ''; + let key = props.channel ? `channel:${props.channel.id}` : ''; - if (props.renote) { - key += `renote:${props.renote.id}`; - } else if (props.reply) { - key += `reply:${props.reply.id}`; - } else { - key += `note:${$i.id}`; - } + if (props.renote) { + key += `renote:${props.renote.id}`; + } else if (props.reply) { + key += `reply:${props.reply.id}`; + } else { + key += `note:${$i.id}`; + } - return key; + return key; }); const placeholder = computed((): string => { - if (props.renote) { - return i18n.ts._postForm.quotePlaceholder; - } else if (props.reply) { - return i18n.ts._postForm.replyPlaceholder; - } else if (props.channel) { - return i18n.ts._postForm.channelPlaceholder; - } else { - const xs = [ - i18n.ts._postForm._placeholders.a, - i18n.ts._postForm._placeholders.b, - i18n.ts._postForm._placeholders.c, - i18n.ts._postForm._placeholders.d, - i18n.ts._postForm._placeholders.e, - i18n.ts._postForm._placeholders.f, - ]; - return xs[Math.floor(Math.random() * xs.length)]; - } + if (props.renote) { + return i18n.ts._postForm.quotePlaceholder; + } else if (props.reply) { + return i18n.ts._postForm.replyPlaceholder; + } else if (props.channel) { + return i18n.ts._postForm.channelPlaceholder; + } else { + const xs = [ + i18n.ts._postForm._placeholders.a, + i18n.ts._postForm._placeholders.b, + i18n.ts._postForm._placeholders.c, + i18n.ts._postForm._placeholders.d, + i18n.ts._postForm._placeholders.e, + i18n.ts._postForm._placeholders.f, + ]; + return xs[Math.floor(Math.random() * xs.length)]; + } }); const submitText = computed((): string => { - return props.renote - ? i18n.ts.quote - : props.reply - ? i18n.ts.reply - : schedule.value + return props.renote + ? i18n.ts.quote + : props.reply + ? i18n.ts.reply + : schedule.value ? i18n.ts._schedulePost.addSchedule : i18n.ts.note; }); const textLength = computed((): number => { - return (text.value + imeText.value).trim().length; + return (text.value + imeText.value).trim().length; }); const maxTextLength = computed((): number => { - return instance ? instance.maxNoteTextLength : 1000; + return instance ? instance.maxNoteTextLength : 1000; }); const canPost = computed((): boolean => { - return !props.mock && !posting.value && !posted.value && + return !props.mock && !posting.value && !posted.value && (1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!props.renote) && (textLength.value <= maxTextLength.value) && (!poll.value || poll.value.choices.length >= 2); @@ -282,60 +281,59 @@ const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtag const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags')); watch(text, () => { - checkMissingMention(); + checkMissingMention(); }, { immediate: true }); watch(visibility, () => { + switch (visibility.value) { + case 'public': + localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; + break; + case 'home': + localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultHomeNoteLocalOnly; + break; + case 'followers': + localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultFollowersNoteLocalOnly; + break; + } - switch (visibility.value) { - case 'public': - localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly; - break; - case 'home': - localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultHomeNoteLocalOnly; - break; - case 'followers': - localOnly.value = props.initialLocalOnly ?? defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultFollowersNoteLocalOnly; - break; - } - - checkMissingMention(); + checkMissingMention(); }, { immediate: true }); watch(visibleUsers, () => { - checkMissingMention(); + checkMissingMention(); }, { - deep: true, + deep: true, }); if (props.mention) { - text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; - text.value += ' '; + 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.value = `@${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) { - const ast = mfm.parse(props.reply.text); - const otherHost = props.reply.user.host; + const ast = mfm.parse(props.reply.text); + const otherHost = props.reply.user.host; - for (const x of extractMentions(ast)) { - const mention = x.host ? - `@${x.username}@${toASCII(x.host)}` : - (otherHost == null || otherHost === host) ? - `@${x.username}` : - `@${x.username}@${toASCII(otherHost)}`; + for (const x of extractMentions(ast)) { + const mention = x.host ? + `@${x.username}@${toASCII(x.host)}` : + (otherHost == null || otherHost === host) ? + `@${x.username}` : + `@${x.username}@${toASCII(otherHost)}`; - // 自分は除外 - if ($i.username === x.username && (x.host == null || x.host === host)) continue; + // 自分は除外 + if ($i.username === x.username && (x.host == null || x.host === host)) continue; - // 重複は除外 - if (text.value.includes(`${mention} `)) continue; + // 重複は除外 + if (text.value.includes(`${mention} `)) continue; - text.value += `${mention} `; - } + text.value += `${mention} `; + } } if ($i.isSilenced && visibility.value === 'public') { @@ -343,97 +341,99 @@ if ($i.isSilenced && visibility.value === 'public') { } if (props.channel) { - visibility.value = 'public'; - localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す } // 公開以外へのリプライ時は元の公開範囲を引き継ぐ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { - if (props.reply.visibility === 'home' && visibility.value === 'followers') { - visibility.value = 'followers'; - } else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') { - visibility.value = 'specified'; - } else { - visibility.value = props.reply.visibility; - } + if (props.reply.visibility === 'home' && visibility.value === 'followers') { + visibility.value = 'followers'; + } else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') { + visibility.value = 'specified'; + } else { + visibility.value = props.reply.visibility; + } - if (visibility.value === 'specified') { - if (props.reply.visibleUserIds) { - misskeyApi('users/show', { - userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId), - }).then(users => { - users.forEach(pushVisibleUser); - }); - } + if (visibility.value === 'specified') { + if (props.reply.visibleUserIds) { + misskeyApi('users/show', { + userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId), + }).then(users => { + users.forEach(pushVisibleUser); + }); + } - if (props.reply.userId !== $i.id) { - misskeyApi('users/show', { userId: props.reply.userId }).then(user => { - pushVisibleUser(user); - }); - } - } + if (props.reply.userId !== $i.id) { + misskeyApi('users/show', { userId: props.reply.userId }).then(user => { + pushVisibleUser(user); + }); + } + } } if (props.specified) { - visibility.value = 'specified'; - pushVisibleUser(props.specified); + visibility.value = 'specified'; + pushVisibleUser(props.specified); } // keep cw when reply if (defaultStore.state.keepCw && props.reply && props.reply.cw) { - useCw.value = true; - cw.value = 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.value === 'specified') { - const ast = mfm.parse(text.value); - - for (const x of extractMentions(ast)) { - if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { - hasNotSpecifiedMentions.value = true; - return;} - } - } - hasNotSpecifiedMentions.value = false; + if (visibility.value === 'specified') { + const ast = mfm.parse(text.value); + for (const x of extractMentions(ast)) { + if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { + hasNotSpecifiedMentions.value = true; + return; + } + } + } + hasNotSpecifiedMentions.value = false; } function addMissingMention() { - const ast = mfm.parse(text.value); + const ast = mfm.parse(text.value); - for (const x of extractMentions(ast)) { - if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { - misskeyApi('users/show', { username: x.username, host: x.host }).then(user => { - visibleUsers.value.push(user); - }); - } - } + for (const x of extractMentions(ast)) { + if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { + misskeyApi('users/show', { username: x.username, host: x.host }).then(user => { + visibleUsers.value.push(user); + }); + } + } } + function insertRuby() { insertTextAtCursor(textareaEl.value, '$[ruby 本文 上につくやつ]'); } + function togglePoll() { - if (poll.value) { - poll.value = null; - } else { - poll.value = { - choices: ['', ''], - multiple: false, - expiresAt: null, - expiredAfter: null, - }; - } + if (poll.value) { + poll.value = null; + } else { + poll.value = { + choices: ['', ''], + multiple: false, + expiresAt: null, + expiredAfter: null, + }; + } } function toggleSchedule() { @@ -447,346 +447,347 @@ function toggleSchedule() { } function addTag(tag: string) { - insertTextAtCursor(textareaEl.value, ` #${tag} `); + insertTextAtCursor(textareaEl.value, ` #${tag} `); } function focus() { - if (textareaEl.value) { - textareaEl.value.focus(); - textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length); - } + if (textareaEl.value) { + textareaEl.value.focus(); + textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length); + } } function chooseFileFrom(ev) { - if (props.mock) return;selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { - for (const file of files_) { - files.value.push(file); - } - }); + if (props.mock) return; selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { + for (const file of files_) { + files.value.push(file); + } + }); } function detachFile(id) { - files.value = files.value.filter(x => x.id !== id); + files.value = files.value.filter(x => x.id !== id); } function updateFileSensitive(file, sensitive) { - if (props.mock) { + if (props.mock) { emit('fileChangeSensitive', file.id, sensitive); } files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive; } function updateFileName(file, name) { - files.value[files.value.findIndex(x => x.id === file.id)].name = name; + files.value[files.value.findIndex(x => x.id === file.id)].name = name; } function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void { - files.value[files.value.findIndex(x => x.id === file.id)] = newFile; + files.value[files.value.findIndex(x => x.id === file.id)] = newFile; } function upload(file: File, name?: string): void { - if (props.mock) return;uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { - files.value.push(res); - }); + if (props.mock) return; uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { + files.value.push(res); + }); } function setVisibility() { - if (props.channel) { - visibility.value = 'public'; - localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す - return; - } + if (props.channel) { + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + return; + } - os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { - currentVisibility: visibility.value, - isSilenced: $i.isSilenced,localOnly: localOnly.value, - src: visibilityButton.value, - }, { - changeVisibility: v => { - visibility.value = v; - if (defaultStore.state.rememberNoteVisibility) { - defaultStore.set('visibility', visibility.value); - } - }, - }, 'closed'); + os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { + currentVisibility: visibility.value, + isSilenced: $i.isSilenced, localOnly: localOnly.value, + src: visibilityButton.value, + }, { + changeVisibility: v => { + visibility.value = v; + if (defaultStore.state.rememberNoteVisibility) { + defaultStore.set('visibility', visibility.value); + } + }, + }, 'closed'); } async function toggleLocalOnly() { - if (props.channel) { - visibility.value = 'public'; - localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す - return; - } + if (props.channel) { + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + return; + } - const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); + const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); - if (!localOnly.value && neverShowInfo !== 'true') { - const confirm = await os.actions({ - type: 'question', - title: i18n.ts.disableFederationConfirm, - text: i18n.ts.disableFederationConfirmWarn, - actions: [ - { - value: 'yes' as const, - text: i18n.ts.disableFederationOk, - primary: true, - }, - { - value: 'neverShow' as const, - text: `${i18n.ts.disableFederationOk} (${i18n.ts.neverShow})`, - danger: true, - }, - { - value: 'no' as const, - text: i18n.ts.cancel, - }, - ], - }); - if (confirm.canceled) return; - if (confirm.result === 'no') return; + if (!localOnly.value && neverShowInfo !== 'true') { + const confirm = await os.actions({ + type: 'question', + title: i18n.ts.disableFederationConfirm, + text: i18n.ts.disableFederationConfirmWarn, + actions: [ + { + value: 'yes' as const, + text: i18n.ts.disableFederationOk, + primary: true, + }, + { + value: 'neverShow' as const, + text: `${i18n.ts.disableFederationOk} (${i18n.ts.neverShow})`, + danger: true, + }, + { + value: 'no' as const, + text: i18n.ts.cancel, + }, + ], + }); + if (confirm.canceled) return; + if (confirm.result === 'no') return; - if (confirm.result === 'neverShow') { - miLocalStorage.setItem('neverShowLocalOnlyInfo', 'true'); - } - } + if (confirm.result === 'neverShow') { + miLocalStorage.setItem('neverShowLocalOnlyInfo', 'true'); + } + } - localOnly.value = !localOnly.value; + localOnly.value = !localOnly.value; } async function toggleReactionAcceptance() { - const select = await os.select({ - title: i18n.ts.reactionAcceptance, - items: [ - { value: null, text: i18n.ts.all }, - { value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote }, - { value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly }, - { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, - { value: 'likeOnly' as const, text: i18n.ts.likeOnly }, - ], - default: reactionAcceptance.value, - }); - if (select.canceled) return; - reactionAcceptance.value = select.result; + const select = await os.select({ + title: i18n.ts.reactionAcceptance, + items: [ + { value: null, text: i18n.ts.all }, + { value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote }, + { value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly }, + { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, + { value: 'likeOnly' as const, text: i18n.ts.likeOnly }, + ], + default: reactionAcceptance.value, + }); + if (select.canceled) return; + reactionAcceptance.value = select.result; } function pushVisibleUser(user: Misskey.entities.UserDetailed) { - if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) { - visibleUsers.value.push(user); - } + if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) { + visibleUsers.value.push(user); + } } function addVisibleUser() { - os.selectUser().then(user => { - pushVisibleUser(user); + os.selectUser().then(user => { + pushVisibleUser(user); - if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { - text.value = `@${Misskey.acct.toString(user)} ${text.value}`; - } - }); + if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { + text.value = `@${Misskey.acct.toString(user)} ${text.value}`; + } + }); } function removeVisibleUser(user) { - visibleUsers.value = erase(user, visibleUsers.value); + visibleUsers.value = erase(user, visibleUsers.value); } function clear() { - text.value = ''; - schedule.value = null;files.value = []; - poll.value = null; - quoteId.value = null; + text.value = ''; + schedule.value = null; files.value = []; + poll.value = null; + quoteId.value = null; } function onKeydown(ev: KeyboardEvent) { - if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post(); - if (ev.key === 'Escape') emit('esc'); + if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post(); + if (ev.key === 'Escape') emit('esc'); } function onCompositionUpdate(ev: CompositionEvent) { - imeText.value = ev.data; + imeText.value = ev.data; } function onCompositionEnd(ev: CompositionEvent) { - imeText.value = ''; + imeText.value = ''; } async function onPaste(ev: ClipboardEvent) { - if (props.mock) return;if (!ev.clipboardData) return; + if (props.mock) return; if (!ev.clipboardData) return; for (const { item, i } of Array.from(ev.clipboardData.items, (data, x) => ({ item: data, i: x }))) { - if (item.kind === 'file') { - const file = item.getAsFile();if (!file) continue; - const lio = file.name.lastIndexOf('.'); - const ext = lio >= 0 ? file.name.slice(lio) : ''; - const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; - upload(file, formatted); - } - } + if (item.kind === 'file') { + const file = item.getAsFile(); if (!file) continue; + const lio = file.name.lastIndexOf('.'); + const ext = lio >= 0 ? file.name.slice(lio) : ''; + const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; + upload(file, formatted); + } + } - const paste = ev.clipboardData.getData('text'); + const paste = ev.clipboardData.getData('text'); - if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) { - ev.preventDefault(); + if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) { + ev.preventDefault(); - os.confirm({ - type: 'info', - text: i18n.ts.quoteQuestion, - }).then(({ canceled }) => { - if (canceled) { - insertTextAtCursor(textareaEl.value, paste); - return; - } + os.confirm({ + type: 'info', + text: i18n.ts.quoteQuestion, + }).then(({ canceled }) => { + if (canceled) { + insertTextAtCursor(textareaEl.value, paste); + return; + } - quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null; - }); - } + quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null; + }); + } } function onDragover(ev) { - if (!ev.dataTransfer.items[0]) return; - const isFile = ev.dataTransfer.items[0].kind === 'file'; - const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; - if (isFile || isDriveFile) { - ev.preventDefault(); - draghover.value = true; - switch (ev.dataTransfer.effectAllowed) { - case 'all': - case 'uninitialized': - case 'copy': - case 'copyLink': - case 'copyMove': - ev.dataTransfer.dropEffect = 'copy'; - break; - case 'linkMove': - case 'move': - ev.dataTransfer.dropEffect = 'move'; - break; - default: - ev.dataTransfer.dropEffect = 'none'; - break; - } - } + if (!ev.dataTransfer.items[0]) return; + const isFile = ev.dataTransfer.items[0].kind === 'file'; + const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; + if (isFile || isDriveFile) { + ev.preventDefault(); + draghover.value = true; + switch (ev.dataTransfer.effectAllowed) { + case 'all': + case 'uninitialized': + case 'copy': + case 'copyLink': + case 'copyMove': + ev.dataTransfer.dropEffect = 'copy'; + break; + case 'linkMove': + case 'move': + ev.dataTransfer.dropEffect = 'move'; + break; + default: + ev.dataTransfer.dropEffect = 'none'; + break; + } + } } function onDragenter() { - draghover.value = true; + draghover.value = true; } function onDragleave() { - draghover.value = false; + draghover.value = false; } function onDrop(ev: DragEvent): void { - draghover.value = false; + draghover.value = false; - // ファイルだったら - if (ev.dataTransfer && ev.dataTransfer.files.length > 0) { - ev.preventDefault(); - for (const x of Array.from(ev.dataTransfer.files)) upload(x); - return; - } + // ファイルだったら + if (ev.dataTransfer && ev.dataTransfer.files.length > 0) { + ev.preventDefault(); + for (const x of Array.from(ev.dataTransfer.files)) upload(x); + return; + } - //#region ドライブのファイル - const driveFile = ev.dataTransfer?.getData(_DATA_TRANSFER_DRIVE_FILE_); - if (driveFile != null && driveFile !== '') { - const file = JSON.parse(driveFile); - files.value.push(file); - ev.preventDefault(); - } - //#endregion + //#region ドライブのファイル + const driveFile = ev.dataTransfer?.getData(_DATA_TRANSFER_DRIVE_FILE_); + if (driveFile != null && driveFile !== '') { + const file = JSON.parse(driveFile); + files.value.push(file); + ev.preventDefault(); + } + //#endregion } function saveDraft() { - if (props.instant || props.mock) return; + if (props.instant || props.mock) return; - const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); - draftData[draftKey.value] = { - updatedAt: new Date(), - data: { - text: text.value, - useCw: useCw.value, - cw: cw.value, - visibility: visibility.value, - localOnly: localOnly.value, - files: files.value, - poll: poll.value, - }, - }; + draftData[draftKey.value] = { + updatedAt: new Date(), + data: { + text: text.value, + useCw: useCw.value, + cw: cw.value, + visibility: visibility.value, + localOnly: localOnly.value, + files: files.value, + poll: poll.value, + }, + }; - miLocalStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } function deleteDraft() { - const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); - delete draftData[draftKey.value]; + delete draftData[draftKey.value]; - miLocalStorage.setItem('drafts', JSON.stringify(draftData)); + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); } async function post(ev?: MouseEvent) { - if (useCw.value && (cw.value == null || cw.value.trim() === '')) { + if (useCw.value && (cw.value == null || cw.value.trim() === '')) { os.alert({ type: 'error', text: i18n.ts.cwNotationRequired, }); return; - }if (ev) { - const el = (ev.currentTarget ?? ev.target) as HTMLElement | null; + } if (ev) { + const el = (ev.currentTarget ?? ev.target) as HTMLElement | null; if (el) { - const rect = el.getBoundingClientRect(); - const x = rect.left + (el.offsetWidth / 2); - const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, { x, y }, {}, 'end'); - }} + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } + } - if (props.mock) return;const annoying = + if (props.mock) return; const annoying = text.value.includes('$[x2') || text.value.includes('$[x3') || text.value.includes('$[x4') || text.value.includes('$[scale') || text.value.includes('$[position'); - if (annoying && visibility.value === 'public') { - const { canceled, result } = await os.actions({ - type: 'warning', - text: i18n.ts.thisPostMayBeAnnoying, - actions: [{ - value: 'home', - text: i18n.ts.thisPostMayBeAnnoyingHome, - primary: true, - }, { - value: 'cancel', - text: i18n.ts.thisPostMayBeAnnoyingCancel, - }, { - value: 'ignore', - text: i18n.ts.thisPostMayBeAnnoyingIgnore, - }], - }); + if (annoying && visibility.value === 'public') { + const { canceled, result } = await os.actions({ + type: 'warning', + text: i18n.ts.thisPostMayBeAnnoying, + actions: [{ + value: 'home', + text: i18n.ts.thisPostMayBeAnnoyingHome, + primary: true, + }, { + value: 'cancel', + text: i18n.ts.thisPostMayBeAnnoyingCancel, + }, { + value: 'ignore', + text: i18n.ts.thisPostMayBeAnnoyingIgnore, + }], + }); - if (canceled) return; - if (result === 'cancel') return; - if (result === 'home') { - visibility.value = 'home'; - } - } + if (canceled) return; + if (result === 'cancel') return; + if (result === 'home') { + visibility.value = 'home'; + } + } - let postData = { - text: text.value === '' ? null : 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.value ? quoteId.value : undefined, - channelId: props.channel ? props.channel.id : undefined, - schedule: schedule.value, - poll: poll.value, - cw: useCw.value ? cw.value ?? '' : null, - localOnly: localOnly.value, - visibility: visibility.value, - visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined, - reactionAcceptance: reactionAcceptance.value, + let postData = { + text: text.value === '' ? null : 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.value ? quoteId.value : undefined, + channelId: props.channel ? props.channel.id : undefined, + schedule: schedule.value, + poll: poll.value, + cw: useCw.value ? cw.value ?? '' : null, + localOnly: localOnly.value, + visibility: visibility.value, + visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined, + reactionAcceptance: reactionAcceptance.value, - noteId: props.updateMode ? props.initialNote?.id : undefined, + noteId: props.updateMode ? props.initialNote?.id : undefined, }; if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { @@ -804,46 +805,46 @@ async function post(ev?: MouseEvent) { } } - // plugin - if (notePostInterruptors.length > 0) { - for (const interruptor of notePostInterruptors) { - try { + // plugin + if (notePostInterruptors.length > 0) { + for (const interruptor of notePostInterruptors) { + try { postData = await interruptor.handler(deepClone(postData)) as typeof postData; } catch (err) { console.error(err); } - } - } + } + } - let token: string | undefined = undefined; + let token: string | undefined = undefined; - if (postAccount.value) { - const storedAccounts = await getAccounts(); - token = storedAccounts.find(x => x.id === postAccount?.value?.id)?.token; - } + if (postAccount.value) { + const storedAccounts = await getAccounts(); + token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token; + } - posting.value = true; - misskeyApi(props.updateMode ? 'notes/update' : 'notes/create', postData, token).then(() => { - if (props.freezeAfterPosted) { - posted.value = true; - } else { - clear(); - } - nextTick(() => { - deleteDraft(); - emit('posted'); - if (postData.text && postData.text !== '') { - const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[]; - const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; - miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); - } - posting.value = false; - postAccount.value = null; + posting.value = true; + misskeyApi(props.updateMode ? 'notes/update' : 'notes/create', postData, token).then(() => { + if (props.freezeAfterPosted) { + posted.value = true; + } else { + clear(); + } + nextTick(() => { + deleteDraft(); + emit('posted'); + if (postData.text && postData.text !== '') { + const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[]; + const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; + miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); + } + posting.value = false; + postAccount.value = null; - incNotesCount(); - if (notesCount === 1) { - claimAchievement('notes1'); - } + incNotesCount(); + if (notesCount === 1) { + claimAchievement('notes1'); + } poll.value = null; if (postData.schedule?.scheduledAt) { @@ -852,64 +853,64 @@ async function post(ev?: MouseEvent) { os.toast(i18n.t('_schedulePost.willBePostedAtX', { date: str })); } - const text = postData.text ?? ''; - const lowerCase = text.toLowerCase(); - if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { - claimAchievement('iLoveMisskey'); - } - if ([ - 'https://youtu.be/Efrlqw8ytg4', - 'https://www.youtube.com/watch?v=Efrlqw8ytg4', - 'https://m.youtube.com/watch?v=Efrlqw8ytg4', + const text = postData.text ?? ''; + const lowerCase = text.toLowerCase(); + if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { + claimAchievement('iLoveMisskey'); + } + if ([ + 'https://youtu.be/Efrlqw8ytg4', + 'https://www.youtube.com/watch?v=Efrlqw8ytg4', + 'https://m.youtube.com/watch?v=Efrlqw8ytg4', - 'https://youtu.be/XVCwzwxdHuA', - 'https://www.youtube.com/watch?v=XVCwzwxdHuA', - 'https://m.youtube.com/watch?v=XVCwzwxdHuA', + 'https://youtu.be/XVCwzwxdHuA', + 'https://www.youtube.com/watch?v=XVCwzwxdHuA', + 'https://m.youtube.com/watch?v=XVCwzwxdHuA', - 'https://open.spotify.com/track/3Cuj0mZrlLoXx9nydNi7RB', - 'https://open.spotify.com/track/7anfcaNPQWlWCwyCHmZqNy', - 'https://open.spotify.com/track/5Odr16TvEN4my22K9nbH7l', - 'https://open.spotify.com/album/5bOlxyl4igOrp2DwVQxBco', - ].some(url => text.includes(url))) { - claimAchievement('brainDiver'); - } + 'https://open.spotify.com/track/3Cuj0mZrlLoXx9nydNi7RB', + 'https://open.spotify.com/track/7anfcaNPQWlWCwyCHmZqNy', + 'https://open.spotify.com/track/5Odr16TvEN4my22K9nbH7l', + 'https://open.spotify.com/album/5bOlxyl4igOrp2DwVQxBco', + ].some(url => text.includes(url))) { + claimAchievement('brainDiver'); + } - if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { - claimAchievement('selfQuote'); - } + if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { + claimAchievement('selfQuote'); + } - const date = new Date(); - const h = date.getHours(); - const m = date.getMinutes(); - const s = date.getSeconds(); - if (h >= 0 && h <= 3) { - claimAchievement('postedAtLateNight'); - } - if (m === 0 && s === 0) { - claimAchievement('postedAt0min0sec'); - } - }); - }).catch(err => { - posting.value = false; - os.alert({ - type: 'error', - text: err.message + '\n' + (err as any).id, - }); - }); + const date = new Date(); + const h = date.getHours(); + const m = date.getMinutes(); + const s = date.getSeconds(); + if (h >= 0 && h <= 3) { + claimAchievement('postedAtLateNight'); + } + if (m === 0 && s === 0) { + claimAchievement('postedAt0min0sec'); + } + }); + }).catch(err => { + posting.value = false; + os.alert({ + type: 'error', + text: err.message + '\n' + (err as any).id, + }); + }); } function cancel() { - emit('cancel'); + emit('cancel'); } function insertMention() { - os.selectUser({ localOnly: localOnly.value, includeSelf: true }).then(user => { - insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' '); - }); + os.selectUser({ localOnly: localOnly.value, includeSelf: true }).then(user => { + insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' '); + }); } async function insertEmoji(ev: MouseEvent) { - textAreaReadOnly.value = true; + textAreaReadOnly.value = true; const target = ev.currentTarget ?? ev.target; if (target == null) return; emojiPicker.show( @@ -934,41 +935,41 @@ async function insertMfmFunction(ev: MouseEvent) { } function showActions(ev: MouseEvent) { - os.popupMenu(postFormActions.map(action => ({ - text: action.title, - action: () => { - action.handler({ - text: text.value, - cw: cw.value, - }, (key, value: any) => { + os.popupMenu(postFormActions.map(action => ({ + text: action.title, + action: () => { + action.handler({ + text: text.value, + cw: cw.value, + }, (key, value: any) => { if (typeof key !== 'string') return; - if (key === 'text') { text.value = value; } - if (key === 'cw') { useCw.value = value !== null; cw.value = value; } - }); - }, - })), ev.currentTarget ?? ev.target); + if (key === 'text') { text.value = value; } + if (key === 'cw') { useCw.value = value !== null; cw.value = value; } + }); + }, + })), ev.currentTarget ?? ev.target); } const postAccount = ref<Misskey.entities.UserDetailed | null>(null); function openAccountMenu(ev: MouseEvent) { - if (props.mock) return;openAccountMenu_({ - withExtraOperation: false, - includeCurrentAccount: true, - active: postAccount.value != null ? postAccount.value.id : $i.id, - onChoose: (account) => { - if (account.id === $i.id) { - postAccount.value = null; - } else { - postAccount.value = account; - } - }, - }, ev); + if (props.mock) return; openAccountMenu_({ + withExtraOperation: false, + includeCurrentAccount: true, + active: postAccount.value != null ? postAccount.value.id : $i.id, + onChoose: (account) => { + if (account.id === $i.id) { + postAccount.value = null; + } else { + postAccount.value = account; + } + }, + }, ev); } function openOtherSettingsMenu(ev: MouseEvent) { let reactionAcceptanceIcon: string; - switch (reactionAcceptance) { + switch (reactionAcceptance.value) { case 'likeOnly': reactionAcceptanceIcon = 'ti ti-heart'; break; @@ -985,13 +986,13 @@ function openOtherSettingsMenu(ev: MouseEvent) { text: i18n.ts.reactionAcceptance, icon: reactionAcceptanceIcon, action: toggleReactionAcceptance, - }, ($i.policies?.canScheduleNote) ? { + }, ($i.policies.canScheduleNote) ? { type: 'button', text: i18n.ts.schedulePost, icon: 'ti ti-calendar-time', - indicate: (schedule != null), + indicate: (schedule.value != null), action: toggleSchedule, - } : undefined, ...(($i.policies?.canScheduleNote) ? [{ type: 'divider' }, { + } : undefined, ...(($i.policies.canScheduleNote) ? [{ type: 'divider' }, { type: 'button', text: i18n.ts._schedulePost.list, icon: 'ti ti-calendar-event', @@ -1006,67 +1007,67 @@ function openOtherSettingsMenu(ev: MouseEvent) { } onMounted(() => { - if (props.autofocus) { - focus(); + if (props.autofocus) { + focus(); - nextTick(() => { - focus(); - }); - } + nextTick(() => { + focus(); + }); + } - // TODO: detach when unmount - if (textareaEl.value)new Autocomplete(textareaEl.value, text); - if (cwInputEl.value)new Autocomplete(cwInputEl.value, cw); - if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags); + // TODO: detach when unmount + if (textareaEl.value) new Autocomplete(textareaEl.value, text); + if (cwInputEl.value) new Autocomplete(cwInputEl.value, cw); + if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags); - nextTick(() => { - // 書きかけの投稿を復元 - if (!props.instant && !props.mention && !props.specified && !props.mock) { - const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value]; - if (draft) { - 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.value = draft.data.poll; - } - } - } + nextTick(() => { + // 書きかけの投稿を復元 + if (!props.instant && !props.mention && !props.specified && !props.mock) { + const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value]; + if (draft) { + 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.value = draft.data.poll; + } + } + } - // 削除して編集 - if (props.initialNote) { - const init = props.initialNote; - text.value = init.text ? init.text : ''; - files.value = init.files ?? []; - cw.value = init.cw ?? null; - useCw.value = init.cw != null; - if (init.isSchedule) { + // 削除して編集 + if (props.initialNote) { + const init = props.initialNote; + text.value = init.text ? init.text : ''; + files.value = init.files ?? []; + cw.value = init.cw ?? null; + useCw.value = init.cw != null; + if (init.isSchedule) { schedule.value = { scheduledAt: init.createdAt, }; } if (init.poll) { - poll.value = { - choices: init.poll.choices.map(x => x.text), - multiple: init.poll.multiple, - expiresAt: init.poll.expiresAt? (new Date(init.poll.expiresAt)).getTime() : null, + poll.value = { + choices: init.poll.choices.map(x => x.text), + multiple: init.poll.multiple, + expiresAt: init.poll.expiresAt ? (new Date(init.poll.expiresAt)).getTime() : null, expiredAfter: null, - }; - } - visibility.value = init.visibility; - localOnly.value = init.localOnly?? false; - quoteId.value = init.renote ? init.renote.id : null; - } + }; + } + visibility.value = init.visibility; + localOnly.value = init.localOnly ?? false; + quoteId.value = init.renote ? init.renote.id : null; + } - nextTick(() => watchForDraft()); - }); + nextTick(() => watchForDraft()); + }); }); defineExpose({ - clear, + clear, }); </script> diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 91429d7529..0206bd32be 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -38,6 +38,7 @@ const props = withDefaults(defineProps<{ withRenotes?: boolean; withReplies?: boolean; onlyFiles?: boolean; + }>(), { withRenotes: true, withReplies: false, diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index dd34b14f9f..f2496981ff 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -177,7 +177,6 @@ onUnmounted(() => { margin-left: 0; } > .titleContainer { - margin: 0 auto; max-width: 100%; } } diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue index eb9bbf918b..cf6f608c8c 100644 --- a/packages/frontend/src/pages/channel.vue +++ b/packages/frontend/src/pages/channel.vue @@ -38,7 +38,6 @@ SPDX-License-Identifier: AGPL-3.0-only <!-- スマホ・タブレットの場合、キーボードが表示されると投稿が見づらくなるので、デスクトップ場合のみ自動でフォーカスを当てる --> <MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostFormInChannel.value" :channel="channel" class="post-form _panel" fixed :autofocus="deviceKind === 'desktop'"/> - <MkTimeline :key="channelId" src="channel" :channel="channelId" @before="before" @after="after" @note="miLocalStorage.setItemAsJson(`channelLastReadedAt:${channel.id}`, Date.now())"/> </div> <div v-else-if="tab === 'featured'" key="featured"> diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue index b879ea0e75..35b036a967 100644 --- a/packages/frontend/src/pages/notifications.vue +++ b/packages/frontend/src/pages/notifications.vue @@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref } from 'vue'; +import { computed, onMounted, ref } from 'vue'; import XNotifications from '@/components/MkNotifications.vue'; import MkNotes from '@/components/MkNotes.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; @@ -74,7 +74,7 @@ const headerActions = computed(() => [{ highlighted: includeTypes.value != null, handler: setFilter, }].filter(x => x !== undefined)); -misskeyApi('notifications/mark-all-as-read'); + const headerTabs = computed(() => [{ key: 'all', title: i18n.ts.all, @@ -93,6 +93,9 @@ definePageMetadata(computed(() => ({ title: i18n.ts.notifications, icon: 'ti ti-bell', }))); +onMounted(() => { + misskeyApi('notifications/mark-all-as-read'); +}); </script> <style module lang="scss"> diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index c2b6d0a68c..0609eb6226 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -45,10 +45,18 @@ SPDX-License-Identifier: AGPL-3.0-only <MkButton v-if="pinnedMax > defaultStore.reactiveState.pinnedUserLists.value.length " @click="setPinnedList()">{{ i18n.ts.add }}</MkButton> <MkButton v-if="defaultStore.reactiveState.pinnedUserLists.value.length " danger @click="removePinnedList('all')"><i class="ti ti-trash"></i> {{ i18n.ts.all }}{{ i18n.ts.remove }}</MkButton> </MkFolder> + <MkFolder> + <template #label>{{ i18n.ts.pinnedChannel }}</template> + <div v-for="pinnedLists in defaultStore.reactiveState.pinnedChannels.value" class="_margin"> + {{ pinnedLists.name }} + <MkButton danger @click="removePinnedChannel(pinnedLists.id,pinnedLists.name)"><i class="ti ti-trash"></i> {{ i18n.ts.remove }}</MkButton> + </div> + <MkButton v-if="pinnedMax > defaultStore.reactiveState.pinnedChannels.value.length " @click="setPinnedChannel()">{{ i18n.ts.add }}</MkButton> + <MkButton v-if="defaultStore.reactiveState.pinnedChannels.value.length " danger @click="removePinnedChannel('all')"><i class="ti ti-trash"></i> {{ i18n.ts.all }}{{ i18n.ts.remove }}</MkButton> + </MkFolder> <MkFoldableSection :expanded="false" class="item"> <template #header>{{ i18n.ts.topbarCustom }}</template> - {{ i18n.ts._timelines.home }} <MkSwitch v-model="showHomeTimeline">{{ i18n.ts.enable }}</MkSwitch> <br> @@ -379,7 +387,7 @@ import { claimAchievement } from '@/scripts/achievements.js'; import MkColorInput from '@/components/MkColorInput.vue'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import MkInput from '@/components/MkInput.vue'; -import { userFavoriteListsCache, userListsCache } from '@/cache.js'; +import { userChannelFollowingsCache, userChannelsCache, userFavoriteListsCache, userListsCache } from '@/cache.js'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -638,7 +646,7 @@ async function setPinnedList() { } } -async function removePinnedList(id, name) { +async function removePinnedList(id, name?:string) { if (!id) return; const { canceled } = await os.confirm({ type: 'warning', @@ -658,6 +666,46 @@ async function removePinnedList(id, name) { defaultStore.set('pinnedUserLists', newPinnedLists); } +async function setPinnedChannel() { + const myChannels = await userChannelsCache.fetch(); + const favoriteChannels = await userChannelFollowingsCache.fetch(); + let channels = [...new Set([...myChannels, ...favoriteChannels])]; + const { canceled, result: channel } = await os.select({ + title: i18n.ts.selectList, + items: channels.map(x => ({ + value: x, text: x.name, + })), + }); + if (canceled) return; + let pinnedChannels = defaultStore.state.pinnedChannels; + + // Check if the id is already present in pinnedLists + if (!pinnedChannels.some(pinnedChannel => pinnedChannel.id === channel.id)) { + pinnedChannels.push(channel); + defaultStore.set('pinnedChannels', pinnedChannels); + } +} + +async function removePinnedChannel(id, name?:string) { + if (!id) return; + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.tsx.removeAreYouSure({ x: name ?? id }), + }); + if (canceled) return; + + if (id === 'all') { + if (canceled) return; + + defaultStore.set('pinnedChannels', []); + return; + } + + const pinnedChannels = defaultStore.state.pinnedChannels; + const newPinnedChannels = pinnedChannels.filter(pinnedchannel => pinnedchannel.id !== id); + defaultStore.set('pinnedChannels', newPinnedChannels); +} + let smashCount = 0; let smashTimer: number | null = null; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 6a936f3ebd..f694722166 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> {{ i18n.ts._timelineDescription[src] }} </MkInfo> - <MkPostForm v-if="defaultStore.reactiveState.showFixedPostForm.value" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostForm.value" :channel="channelInfo" :autofocus="deviceKind === 'desktop'" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div :class="$style.tl"> <MkTimeline @@ -20,6 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="src + withRenotes + withReplies + onlyFiles" :src="src.split(':')[0]" :list="src.split(':')[1]" + :channel="src.split(':')[1]" :withRenotes="withRenotes" :withReplies="withReplies" :onlyFiles="onlyFiles" @@ -112,8 +113,19 @@ const remoteLocalTimelineEnable4 = ref(defaultStore.state.remoteLocalTimelineEna const remoteLocalTimelineEnable5 = ref(defaultStore.state.remoteLocalTimelineEnable5); const showHomeTimeline = ref(defaultStore.state.showHomeTimeline); const showSocialTimeline = ref(defaultStore.state.showSocialTimeline); -watch(src, () => { +const channelInfo = ref(); +if (src.value.split(':')[0] === 'channel') { + const channelId = src.value.split(':')[1]; + channelInfo.value = misskeyApi('channels/show', { channelId }); +} +watch(src, async () => { queue.value = 0; + if (src.value.split(':')[0] === 'channel') { + const channelId = src.value.split(':')[1]; + channelInfo.value = misskeyApi('channels/show', { channelId }); + } else { + channelInfo.value = null; + } }); function queueUpdated(q: number): void { @@ -285,6 +297,11 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList title: l.name, icon: 'ti ti-star', iconOnly: defaultStore.state.topBarNameShown ?? false, +}))), ...(defaultStore.reactiveState.pinnedChannels.value.map(l => ({ + key: 'channel:' + l.id, + title: l.name, + icon: 'ti ti-star', + iconOnly: defaultStore.state.topBarNameShown ?? false, }))), ...(showHomeTimeline.value ? [{ key: 'home', title: i18n.ts._timelines.home, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 493e926298..f96d7eba49 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -280,7 +280,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'deviceAccount', default: [] as Misskey.entities.UserList[], }, - + pinnedChannels: { + where: 'deviceAccount', + default: [] as Misskey.entities.Channel[], + }, overridedDeviceKind: { where: 'device', default: null as null | 'smartphone' | 'tablet' | 'desktop', From c2f24a932186d54085b6a343df0a06d5dbad0475 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 11 Feb 2024 23:19:51 +0900 Subject: [PATCH 366/501] =?UTF-8?q?=E8=89=B2=E3=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/about.emojis.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue index a5b520bf3e..59316b5a32 100644 --- a/packages/frontend/src/pages/about.emojis.vue +++ b/packages/frontend/src/pages/about.emojis.vue @@ -22,14 +22,14 @@ SPDX-License-Identifier: AGPL-3.0-only --> </div> - <MkFoldableSection v-if="searchEmojis"> + <MkFoldableSection v-if="searchEmojis" :expanded="false"> <template #header>{{ i18n.ts.searchResult }}</template> <div :class="$style.emojis"> <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" :emoji="emoji" :request="emoji.request"/> </div> </MkFoldableSection> - <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category"> + <MkFoldableSection v-for="category in customEmojiCategories" v-once :key="category" :expanded="false"> <template #header>{{ category || i18n.ts.other }}</template> <div :class="$style.emojis"> <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" :emoji="emoji"/> From c1814001dfe3adadc26bd7d0f131709fed3cd8d5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 12 Feb 2024 04:08:23 +0900 Subject: [PATCH 367/501] =?UTF-8?q?=E8=89=B2=E3=80=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/about.vue | 33 ++++++++++++++------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 5f5219f692..091c847d7a 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> </div> <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> + ソースコード含め問い合わせは下記のメールアドレスへよろしくお願いします。 </div> </FormSection> @@ -102,7 +103,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {computed, ref, watch } from 'vue'; +import { computed, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; import XEmojis from './about.emojis.vue'; import XFederation from './about.federation.vue'; @@ -121,7 +122,7 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { instance } from '@/instance.js'; -import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store"; +import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store'; const props = withDefaults(defineProps<{ initialTab?: string; @@ -140,22 +141,22 @@ watch(tab, () => { let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); -if (darkMode.value){ - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; -}else{ - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; +if (darkMode.value) { + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; +} else { + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; } watch(darkMode, () => { - if (darkMode.value){ - bannerUrl.value = bannerDark; - iconUrl.value = iconDark; - }else{ - bannerUrl.value = bannerLight; - iconUrl.value = iconLight; - } -}) + if (darkMode.value) { + bannerUrl.value = bannerDark; + iconUrl.value = iconDark; + } else { + bannerUrl.value = bannerLight; + iconUrl.value = iconLight; + } +}); const initStats = () => misskeyApi('stats', { }).then((res) => { stats.value = res; From e9fda7dd1a69dc35816b69cdfda575125b10b4e1 Mon Sep 17 00:00:00 2001 From: Caipira <caipira@libnare.net> Date: Sat, 21 Oct 2023 00:29:12 +0900 Subject: [PATCH 368/501] feat(backend): Federated note update (#1) (cherry picked from commit 6af23d4e28893b0ab253182153973bcad1210ac0) --- .../migration/1696604572677-poll_vote_poll.js | 12 + packages/backend/src/core/CoreModule.ts | 6 + .../backend/src/core/GlobalEventService.ts | 2 +- .../backend/src/core/NoteCreateService.ts | 32 ++ .../backend/src/core/NoteUpdateService.ts | 297 ++++++++++++++++++ .../src/core/activitypub/ApInboxService.ts | 45 ++- .../src/core/activitypub/ApRendererService.ts | 2 + .../core/activitypub/models/ApNoteService.ts | 105 ++++++- packages/backend/src/core/activitypub/type.ts | 1 + .../src/server/api/endpoints/notes/update.ts | 75 ++--- .../src/ui/_common_/navbar-for-mobile.vue | 1 + 11 files changed, 534 insertions(+), 44 deletions(-) create mode 100644 packages/backend/migration/1696604572677-poll_vote_poll.js create mode 100644 packages/backend/src/core/NoteUpdateService.ts diff --git a/packages/backend/migration/1696604572677-poll_vote_poll.js b/packages/backend/migration/1696604572677-poll_vote_poll.js new file mode 100644 index 0000000000..da52904565 --- /dev/null +++ b/packages/backend/migration/1696604572677-poll_vote_poll.js @@ -0,0 +1,12 @@ +export class PollVotePoll1696604572677 { + name = 'PollVotePoll1696604572677'; + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "poll_vote" ADD CONSTRAINT "FK_poll_vote_poll" FOREIGN KEY ("noteId") REFERENCES "poll"("noteId") ON DELETE CASCADE`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "poll_vote" DROP CONSTRAINT "FK_poll_vote_poll"`); + } + +} diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index fb07ecd7b3..da27da8c8d 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -33,6 +33,7 @@ import { MetaService } from './MetaService.js'; import { MfmService } from './MfmService.js'; import { ModerationLogService } from './ModerationLogService.js'; import { NoteCreateService } from './NoteCreateService.js'; +import { NoteUpdateService } from './NoteUpdateService.js'; import { NoteDeleteService } from './NoteDeleteService.js'; import { NotePiningService } from './NotePiningService.js'; import { NoteReadService } from './NoteReadService.js'; @@ -171,6 +172,7 @@ const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaServic const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService }; const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService }; const $NoteCreateService: Provider = { provide: 'NoteCreateService', useExisting: NoteCreateService }; +const $NoteUpdateService: Provider = { provide: 'NoteUpdateService', useExisting: NoteUpdateService }; const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useExisting: NoteDeleteService }; const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting: NotePiningService }; const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService }; @@ -311,6 +313,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting MfmService, ModerationLogService, NoteCreateService, + NoteUpdateService, NoteDeleteService, NotePiningService, NoteReadService, @@ -447,6 +450,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $MfmService, $ModerationLogService, $NoteCreateService, + $NoteUpdateService, $NoteDeleteService, $NotePiningService, $NoteReadService, @@ -584,6 +588,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting MfmService, ModerationLogService, NoteCreateService, + NoteUpdateService, NoteDeleteService, NotePiningService, NoteReadService, @@ -719,6 +724,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $MfmService, $ModerationLogService, $NoteCreateService, + $NoteUpdateService, $NoteDeleteService, $NotePiningService, $NoteReadService, diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 069b6e261a..0ef901c20b 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -117,7 +117,7 @@ export interface NoteEventTypes { }; updated: { cw: string | null; - text: string; + text: string | null; }; reacted: { reaction: string; diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 5fc12ae8d1..7ef2658e51 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -115,6 +115,38 @@ class NotificationManager { } } +type MinimumUser = { + id: MiUser['id']; + host: MiUser['host']; + username: MiUser['username']; + uri: MiUser['uri']; +}; + +type Option = { + createdAt?: Date | null; + updatedAt?: Date | null; + name?: string | null; + text?: string | null; + reply?: MiNote | null; + renote?: MiNote | null; + files?: MiDriveFile[] | null; + poll?: IPoll | null; + event?: IEvent | null; + localOnly?: boolean | null; + reactionAcceptance?: MiNote['reactionAcceptance']; + disableRightClick?: boolean | null; + cw?: string | null; + visibility?: string; + visibleUsers?: MinimumUser[] | null; + channel?: MiChannel | null; + apMentions?: MinimumUser[] | null; + apHashtags?: string[] | null; + apEmojis?: string[] | null; + uri?: string | null; + url?: string | null; + app?: MiApp | null; +}; + @Injectable() export class NoteCreateService implements OnApplicationShutdown { #shutdownController = new AbortController(); diff --git a/packages/backend/src/core/NoteUpdateService.ts b/packages/backend/src/core/NoteUpdateService.ts new file mode 100644 index 0000000000..d52c744b25 --- /dev/null +++ b/packages/backend/src/core/NoteUpdateService.ts @@ -0,0 +1,297 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { setImmediate } from 'node:timers/promises'; +import { In, DataSource } from 'typeorm'; +import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; +import type { IMentionedRemoteUsers } from '@/models/Note.js'; +import { MiNote } from '@/models/Note.js'; +import type { NotesRepository, UsersRepository } from '@/models/_.js'; +import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/User.js'; +import { RelayService } from '@/core/RelayService.js'; +import { DI } from '@/di-symbols.js'; +import ActiveUsersChart from '@/core/chart/charts/active-users.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; +import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; +import { bindThis } from '@/decorators.js'; +import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { SearchService } from '@/core/SearchService.js'; +import { normalizeForSearch } from "@/misc/normalize-for-search.js"; +import { MiDriveFile } from '@/models/_.js'; +import { MiPoll, IPoll } from '@/models/Poll.js'; +import * as mfm from "cherrypick-mfm-js"; +import { concat } from "@/misc/prelude/array.js"; +import { extractHashtags } from "@/misc/extract-hashtags.js"; +import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js"; +import util from 'util'; + +type MinimumUser = { + id: MiUser['id']; + host: MiUser['host']; + username: MiUser['username']; + uri: MiUser['uri']; +}; + +type Option = { + updatedAt?: Date | null; + files?: MiDriveFile[] | null; + name?: string | null; + text?: string | null; + cw?: string | null; + apHashtags?: string[] | null; + apEmojis?: string[] | null; + poll?: IPoll | null; +}; + +@Injectable() +export class NoteUpdateService implements OnApplicationShutdown { + #shutdownController = new AbortController(); + + constructor( + @Inject(DI.db) + private db: DataSource, + + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + private relayService: RelayService, + private apDeliverManagerService: ApDeliverManagerService, + private apRendererService: ApRendererService, + private searchService: SearchService, + private activeUsersChart: ActiveUsersChart, + ) { } + + @bindThis + public async update(user: { + id: MiUser['id']; + username: MiUser['username']; + host: MiUser['host']; + isBot: MiUser['isBot']; + }, data: Option, note: MiNote, silent = false): Promise<MiNote | null> { + if (data.updatedAt == null) data.updatedAt = new Date(); + + if (data.text) { + if (data.text.length > DB_MAX_NOTE_TEXT_LENGTH) { + data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); + } + data.text = data.text.trim(); + } else { + data.text = null; + } + + let tags = data.apHashtags; + let emojis = data.apEmojis; + + // Parse MFM if needed + if (!tags || !emojis) { + const tokens = data.text ? mfm.parse(data.text)! : []; + const cwTokens = data.cw ? mfm.parse(data.cw)! : []; + const choiceTokens = data.poll && data.poll.choices + ? concat(data.poll.choices.map(choice => mfm.parse(choice)!)) + : []; + + const combinedTokens = tokens.concat(cwTokens).concat(choiceTokens); + + tags = data.apHashtags ?? extractHashtags(combinedTokens); + + emojis = data.apEmojis ?? extractCustomEmojisFromMfm(combinedTokens); + } + + tags = tags.filter(tag => Array.from(tag ?? '').length <= 128).splice(0, 32); + + const updatedNote = await this.updateNote(user, note, data, tags, emojis); + + if (updatedNote) { + setImmediate('post updated', { signal: this.#shutdownController.signal }).then( + () => this.postNoteUpdated(updatedNote, user, silent), + () => { /* aborted, ignore this */ }, + ); + } + + return updatedNote; + } + + @bindThis + private async updateNote(user: { + id: MiUser['id']; host: MiUser['host']; + }, note: MiNote, data: Option, tags: string[], emojis: string[]): Promise<MiNote | null> { + const updatedAtHistory = note.updatedAtHistory ? note.updatedAtHistory : []; + + const values = new MiNote({ + updatedAt: data.updatedAt!, + fileIds: data.files ? data.files.map(file => file.id) : [], + text: data.text, + hasPoll: data.poll != null, + cw: data.cw ?? null, + tags: tags.map(tag => normalizeForSearch(tag)), + emojis, + attachedFileTypes: data.files ? data.files.map(file => file.type) : [], + updatedAtHistory: [...updatedAtHistory, new Date()], + noteEditHistory: [...note.noteEditHistory, (note.cw ? note.cw + '\n' : '') + note.text!], + }); + + // 投稿を更新 + try { + if (note.hasPoll && values.hasPoll) { + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + await transactionalEntityManager.update(MiNote, { id: note.id }, values); + + if (values.hasPoll) { + const old_poll = await transactionalEntityManager.findOneBy(MiPoll, { noteId: note.id }); + if (old_poll!.choices.toString() !== data.poll!.choices.toString() || old_poll!.multiple !== data.poll!.multiple) { + await transactionalEntityManager.delete(MiPoll, { noteId: note.id }); + const poll = new MiPoll({ + noteId: note.id, + choices: data.poll!.choices, + expiresAt: data.poll!.expiresAt, + multiple: data.poll!.multiple, + votes: new Array(data.poll!.choices.length).fill(0), + noteVisibility: note.visibility, + userId: user.id, + userHost: user.host, + }); + await transactionalEntityManager.insert(MiPoll, poll); + } + } + }); + } else if (!note.hasPoll && values.hasPoll) { + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + await transactionalEntityManager.update(MiNote, { id: note.id }, values); + + if (values.hasPoll) { + const poll = new MiPoll({ + noteId: note.id, + choices: data.poll!.choices, + expiresAt: data.poll!.expiresAt, + multiple: data.poll!.multiple, + votes: new Array(data.poll!.choices.length).fill(0), + noteVisibility: note.visibility, + userId: user.id, + userHost: user.host, + }); + + await transactionalEntityManager.insert(MiPoll, poll); + } + }); + } else if (note.hasPoll && !values.hasPoll) { + // Start transaction + await this.db.transaction(async transactionalEntityManager => { + await transactionalEntityManager.update(MiNote, {id: note.id}, values); + + if (!values.hasPoll) { + await transactionalEntityManager.delete(MiPoll, {noteId: note.id}); + } + }); + } else { + await this.notesRepository.update({ id: note.id }, values); + } + + return await this.notesRepository.findOneBy({ id: note.id }); + } catch (e) { + console.error(e); + + throw e; + } + } + + @bindThis + private async postNoteUpdated(note: MiNote, user: { + id: MiUser['id']; + username: MiUser['username']; + host: MiUser['host']; + isBot: MiUser['isBot']; + }, silent: boolean) { + if (!silent) { + if (this.userEntityService.isLocalUser(user)) this.activeUsersChart.write(user); + + this.globalEventService.publishNoteStream(note.id, 'updated', { cw: note.cw, text: note.text }); + + //#region AP deliver + if (this.userEntityService.isLocalUser(user)) { + await (async () => { + // @ts-ignore + const noteActivity = await this.renderNoteActivity(note, user); + + await this.deliverToConcerned(user, note, noteActivity); + })(); + } + //#endregion + } + + // Register to search database + this.reIndex(note); + } + + @bindThis + private async renderNoteActivity(note: MiNote, user: MiUser) { + const content = this.apRendererService.renderUpdate(await this.apRendererService.renderNote(note, false), user); + + return this.apRendererService.addContext(content); + } + + @bindThis + private async getMentionedRemoteUsers(note: MiNote) { + const where = [] as any[]; + + // mention / reply / dm + const uris = (JSON.parse(note.mentionedRemoteUsers) as IMentionedRemoteUsers).map(x => x.uri); + if (uris.length > 0) { + where.push( + { uri: In(uris) }, + ); + } + + // renote / quote + if (note.renoteUserId) { + where.push({ + id: note.renoteUserId, + }); + } + + if (where.length === 0) return []; + + return await this.usersRepository.find({ + where, + }) as MiRemoteUser[]; + } + + @bindThis + private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) { + console.log('deliverToConcerned', util.inspect(content, { depth: null })); + await this.apDeliverManagerService.deliverToFollowers(user, content); + await this.relayService.deliverToRelays(user, content); + const remoteUsers = await this.getMentionedRemoteUsers(note); + for (const remoteUser of remoteUsers) { + await this.apDeliverManagerService.deliverToUser(user, content, remoteUser); + } + } + + @bindThis + private reIndex(note: MiNote) { + if (note.text == null && note.cw == null) return; + + this.searchService.unindexNote(note); + this.searchService.indexNote(note); + } + + @bindThis + public dispose(): void { + this.#shutdownController.abort(); + } + + @bindThis + public onApplicationShutdown(signal?: string | undefined): void { + this.dispose(); + } +} diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 526b04b292..ee97f6936f 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -14,6 +14,7 @@ import { NotePiningService } from '@/core/NotePiningService.js'; import { UserBlockingService } from '@/core/UserBlockingService.js'; import { NoteDeleteService } from '@/core/NoteDeleteService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js'; +import { NoteUpdateService } from '@/core/NoteUpdateService.js'; import { concat, toArray, toSingle, unique } from '@/misc/prelude/array.js'; import { AppLockService } from '@/core/AppLockService.js'; import type Logger from '@/logger.js'; @@ -73,6 +74,7 @@ export class ApInboxService { private notePiningService: NotePiningService, private userBlockingService: UserBlockingService, private noteCreateService: NoteCreateService, + private noteUpdateService: NoteUpdateService, private noteDeleteService: NoteDeleteService, private appLockService: AppLockService, private apResolverService: ApResolverService, @@ -730,11 +732,13 @@ export class ApInboxService { @bindThis private async update(actor: MiRemoteUser, activity: IUpdate): Promise<string> { + const uri = getApId(activity); + if (actor.uri !== activity.actor) { return 'skip: invalid actor'; } - this.logger.debug('Update'); + this.logger.debug(`Update: ${uri}`); const resolver = this.apResolverService.createResolver(); @@ -746,14 +750,51 @@ export class ApInboxService { if (isActor(object)) { await this.apPersonService.updatePerson(actor.uri, resolver, object); return 'ok: Person updated'; - } else if (getApType(object) === 'Question') { + } /*else if (getApType(object) === 'Question') { await this.apQuestionService.updateQuestion(object, resolver).catch(err => console.error(err)); return 'ok: Question updated'; + }*/ else if (getApType(object) === 'Note' || getApType(object) === 'Question') { + await this.updateNote(resolver, actor, object, false, activity); + return 'ok: Note updated'; } else { return `skip: Unknown type: ${getApType(object)}`; } } + @bindThis + private async updateNote(resolver: Resolver, actor: MiRemoteUser, note: IObject, silent = false, activity?: IUpdate): Promise<string> { + const uri = getApId(note); + + if (typeof note === 'object') { + if (actor.uri !== note.attributedTo) { + return 'skip: actor.uri !== note.attributedTo'; + } + + if (typeof note.id === 'string') { + if (this.utilityService.extractDbHost(actor.uri) !== this.utilityService.extractDbHost(note.id)) { + return 'skip: host in actor.uri !== note.id'; + } + } + } + + const unlock = await this.appLockService.getApLock(uri); + + try { + //const exist = await this.apNoteService.fetchNote(note); + //if (exist) return 'skip: note exists'; + await this.apNoteService.updateNote(note, resolver, silent); + return 'ok'; + } catch (err) { + if (err instanceof StatusError && err.isClientError) { + return `skip ${err.statusCode}`; + } else { + throw err; + } + } finally { + unlock(); + } + } + @bindThis private async move(actor: MiRemoteUser, activity: IMove): Promise<string> { // fetch the new and old accounts diff --git a/packages/backend/src/core/activitypub/ApRendererService.ts b/packages/backend/src/core/activitypub/ApRendererService.ts index 494622909a..6c6a3615da 100644 --- a/packages/backend/src/core/activitypub/ApRendererService.ts +++ b/packages/backend/src/core/activitypub/ApRendererService.ts @@ -108,6 +108,7 @@ export class ApRendererService { actor: this.userEntityService.genLocalUserUri(note.userId), type: 'Announce', published: this.idService.parse(note.id).date.toISOString(), + updated: note.updatedAt?.toISOString() ?? undefined, to, cc, object, @@ -437,6 +438,7 @@ export class ApRendererService { _misskey_quote: quote, quoteUrl: quote, published: this.idService.parse(note.id).date.toISOString(), + updated: note.updatedAt?.toISOString() ?? undefined, to, cc, inReplyTo, diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 8da9407216..9ada6129aa 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-FileCopyrightText: syuilo and misskey-project, cherrypick contributors * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import promiseLimit from 'promise-limit'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { PollsRepository, EmojisRepository } from '@/models/_.js'; +import type { PollsRepository, EmojisRepository, NotesRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; import type { MiRemoteUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; @@ -24,10 +24,12 @@ import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { checkHttps } from '@/misc/check-https.js'; -import { getOneApId, getApId, getOneApHrefNullable, validPost, isEmoji, getApType } from '../type.js'; +import type { IObject, IPost } from '../type.js'; +import { getApId, getApType, getOneApHrefNullable, getOneApId, isEmoji, validPost } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApMfmService } from '../ApMfmService.js'; import { ApDbResolverService } from '../ApDbResolverService.js'; +import type { Resolver } from '../ApResolverService.js'; import { ApResolverService } from '../ApResolverService.js'; import { ApAudienceService } from '../ApAudienceService.js'; import { ApPersonService } from './ApPersonService.js'; @@ -35,8 +37,7 @@ import { extractApHashtags } from './tag.js'; import { ApMentionService } from './ApMentionService.js'; import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; -import type { Resolver } from '../ApResolverService.js'; -import type { IObject, IPost } from '../type.js'; +import { NoteUpdateService } from '@/core/NoteUpdateService.js'; @Injectable() export class ApNoteService { @@ -52,6 +53,12 @@ export class ApNoteService { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, + @Inject(DI.messagingMessagesRepository) + private messagingMessagesRepository: MessagingMessagesRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + private idService: IdService, private apMfmService: ApMfmService, private apResolverService: ApResolverService, @@ -69,6 +76,7 @@ export class ApNoteService { private appLockService: AppLockService, private pollService: PollService, private noteCreateService: NoteCreateService, + private noteUpdateService: NoteUpdateService, private apDbResolverService: ApDbResolverService, private apLoggerService: ApLoggerService, ) { @@ -278,6 +286,7 @@ export class ApNoteService { try { return await this.noteCreateService.create(actor, { createdAt: note.published ? new Date(note.published) : null, + updatedAt: note.updated ? new Date(note.updated) : null, files, reply, renote: quote, @@ -307,6 +316,92 @@ export class ApNoteService { } } + @bindThis + public async updateNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<MiNote | null> { + if (resolver == null) resolver = this.apResolverService.createResolver(); + + const object = await resolver.resolve(value); + const entryUri = getApId(value); + + const err = this.validateNote(object, entryUri); + if (err) { + this.logger.error(err.message, { + resolver: { history: resolver.getHistory() }, + value, + object, + }); + throw new Error('invalid note'); + } + + const note = object as IPost; + + // 投稿者をフェッチ + if (note.attributedTo == null) { + throw new Error('invalid note.attributedTo: ' + note.attributedTo); + } + + const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser; + + // 投稿者が凍結されていたらスキップ + if (actor.isSuspended) { + throw new Error('actor has been suspended'); + } + + const b_note = await this.notesRepository.findOneBy({ + uri: entryUri + }).then(x => { + if (x == null) throw new Error('note not found'); + return x; + }); + + const limit = promiseLimit<MiDriveFile>(2); + const files = (await Promise.all(toArray(note.attachment).map(attach => ( + limit(() => this.apImageService.resolveImage(actor, { + ...attach, + sensitive: note.sensitive, // Noteがsensitiveなら添付もsensitiveにする + })) + )))); + + const cw = note.summary === '' ? null : note.summary; + + // テキストのパース + let text: string | null = null; + if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') { + text = note.source.content; + } else if (typeof note._misskey_content !== 'undefined') { + text = note._misskey_content; + } else if (typeof note.content === 'string') { + text = this.apMfmService.htmlToMfm(note.content, note.tag); + } + + const apHashtags = extractApHashtags(note.tag); + + const emojis = await this.extractEmojis(note.tag ?? [], actor.host).catch(e => { + this.logger.info(`extractEmojis: ${e}`); + return []; + }); + + const apEmojis = emojis.map(emoji => emoji.name); + + const poll = await this.apQuestionService.extractPollFromQuestion(note, resolver).catch(() => undefined); + + try { + return await this.noteUpdateService.update(actor, { + updatedAt: note.updated ? new Date(note.updated) : null, + files, + name: note.name, + cw, + text, + apHashtags, + apEmojis, + poll, + }, b_note, silent); + } catch (err: any) { + this.logger.warn(`note update failed: ${err}`); + return err; + } + } + /** * Noteを解決します。 * diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts index b43dddad61..012fd44c19 100644 --- a/packages/backend/src/core/activitypub/type.ts +++ b/packages/backend/src/core/activitypub/type.ts @@ -14,6 +14,7 @@ export interface IObject { summary?: string; _misskey_summary?: string; published?: string; + updated?: string; cc?: ApObject; to?: ApObject; attributedTo?: ApObject; diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index fe6d9637e8..14553fbee7 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-FileCopyrightText: syuilo and other misskey contributors, cherrypick contributors * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,12 +7,13 @@ import ms from 'ms'; import { Inject, Injectable } from '@nestjs/common'; import type { UsersRepository, NotesRepository, DriveFilesRepository, MiDriveFile } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; -import { NoteDeleteService } from '@/core/NoteDeleteService.js'; +import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; +import { NoteUpdateService } from '@/core/NoteUpdateService.js'; import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { ApiError } from '../../error.js'; +import type { DriveFilesRepository, MiDriveFile } from "@/models/_.js"; export const meta = { tags: ['notes'], @@ -39,6 +40,11 @@ export const meta = { code: 'NO_SUCH_FILE', id: 'b6992544-63e7-67f0-fa7f-32444b1b5306', }, + cannotCreateAlreadyExpiredPoll: { + message: 'Poll is already expired.', + code: 'CANNOT_CREATE_ALREADY_EXPIRED_POLL', + id: '04da457d-b083-4055-9082-955525eda5a5', + }, }, } as const; @@ -66,7 +72,7 @@ export const paramDef = { type: 'string', minLength: 1, maxLength: MAX_NOTE_TEXT_LENGTH, - nullable: true, + nullable: false, }, fileIds: { type: 'array', @@ -99,31 +105,23 @@ export const paramDef = { }, required: ['choices'], }, + disableRightClick: { type: 'boolean', default: false }, }, - // (re)note with text, files and poll are optional - anyOf: [ - { required: ['text'] }, - { required: ['renoteId'] }, - { required: ['fileIds'] }, - { required: ['mediaIds'] }, - { required: ['poll'] }, - ], + required: ['noteId', 'text', 'cw'], } as const; @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - - @Inject(DI.notesRepository) - private notesRepository: NotesRepository, + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, private getterService: GetterService, - private globalEventService: GlobalEventService, + private noteEntityService: NoteEntityService, + private noteUpdateService: NoteUpdateService, ) { super(meta, paramDef, async (ps, me) => { const note = await this.getterService.getNote(ps.noteId).catch(err => { @@ -131,9 +129,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw err; }); - let files: MiDriveFile[] = []; - const fileIds = ps.fileIds ?? null; + if (note.userId !== me.id) { + throw new ApiError(meta.errors.noSuchNote); + } + + let files: MiDriveFile[] = []; + const fileIds = ps.fileIds ?? ps.mediaIds ?? null; if (fileIds != null) { files = await this.driveFilesRepository.createQueryBuilder('file') .where('file.userId = :userId AND file.id IN (:...fileIds)', { @@ -149,31 +151,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } - if (note.userId !== me.id) { - throw new ApiError(meta.errors.noSuchNote); + if (ps.poll) { + if (typeof ps.poll.expiresAt === 'number') { + if (ps.poll.expiresAt < Date.now()) { + throw new ApiError(meta.errors.cannotCreateAlreadyExpiredPoll); + } + } else if (typeof ps.poll.expiredAfter === 'number') { + ps.poll.expiresAt = Date.now() + ps.poll.expiredAfter; + } } - await this.notesRepository.update({ id: note.id }, { - updatedAt: new Date(), - cw: ps.cw, + const data = { text: ps.text, - fileIds: files.length > 0 ? files.map(f => f.id) : undefined, + files: files, + cw: ps.cw, poll: ps.poll ? { choices: ps.poll.choices, multiple: ps.poll.multiple ?? false, expiresAt: ps.poll.expiresAt ? new Date(ps.poll.expiresAt) : null, } : undefined, - localOnly: ps.localOnly, - reactionAcceptance: ps.reactionAcceptance, - apMentions: ps.noExtractMentions ? [] : undefined, - apHashtags: ps.noExtractHashtags ? [] : undefined, - apEmojis: ps.noExtractEmojis ? [] : undefined, - }); + }; - this.globalEventService.publishNoteStream(note.id, 'updated', { - cw: ps.cw, - text: ps.text, - }); + const updatedNote = await this.noteUpdateService.update(me, data, note, false); + + return { + updatedNote: await this.noteEntityService.pack(updatedNote!, me), + }; }); } } diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index 4453c186db..4197707ee5 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -122,6 +122,7 @@ const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); document.documentElement.style.setProperty('--homeColor', hexToRgb(defaultStore.state.homeColor)); document.documentElement.style.setProperty('--followerColor', hexToRgb(defaultStore.state.followerColor)); document.documentElement.style.setProperty('--specifiedColor', hexToRgb(defaultStore.state.specifiedColor)); +document.documentElement.style.setProperty('--localOnlyColor', hexToRgb(defaultStore.state.localOnlyColor)); document.documentElement.style.setProperty('--gamingspeed', defaultStore.state.numberOfGamingSpeed + 's'); let gaming = ref(); From 588d6acfb713acb319b62198c59840e693de6390 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 13 Feb 2024 13:00:48 +0900 Subject: [PATCH 369/501] fix --- packages/backend/src/core/NoteUpdateService.ts | 16 ++++++++-------- .../src/core/activitypub/models/ApNoteService.ts | 11 ++++------- .../src/server/api/endpoints/notes/update.ts | 5 ----- 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/core/NoteUpdateService.ts b/packages/backend/src/core/NoteUpdateService.ts index d52c744b25..db80036526 100644 --- a/packages/backend/src/core/NoteUpdateService.ts +++ b/packages/backend/src/core/NoteUpdateService.ts @@ -4,8 +4,10 @@ */ import { setImmediate } from 'node:timers/promises'; +import util from 'util'; import { In, DataSource } from 'typeorm'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; +import * as mfm from 'mfm-js'; import type { IMentionedRemoteUsers } from '@/models/Note.js'; import { MiNote } from '@/models/Note.js'; import type { NotesRepository, UsersRepository } from '@/models/_.js'; @@ -20,14 +22,12 @@ import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerServ import { bindThis } from '@/decorators.js'; import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { SearchService } from '@/core/SearchService.js'; -import { normalizeForSearch } from "@/misc/normalize-for-search.js"; +import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { MiDriveFile } from '@/models/_.js'; import { MiPoll, IPoll } from '@/models/Poll.js'; -import * as mfm from "cherrypick-mfm-js"; -import { concat } from "@/misc/prelude/array.js"; -import { extractHashtags } from "@/misc/extract-hashtags.js"; -import { extractCustomEmojisFromMfm } from "@/misc/extract-custom-emojis-from-mfm.js"; -import util from 'util'; +import { concat } from '@/misc/prelude/array.js'; +import { extractHashtags } from '@/misc/extract-hashtags.js'; +import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; type MinimumUser = { id: MiUser['id']; @@ -187,10 +187,10 @@ export class NoteUpdateService implements OnApplicationShutdown { } else if (note.hasPoll && !values.hasPoll) { // Start transaction await this.db.transaction(async transactionalEntityManager => { - await transactionalEntityManager.update(MiNote, {id: note.id}, values); + await transactionalEntityManager.update(MiNote, { id: note.id }, values); if (!values.hasPoll) { - await transactionalEntityManager.delete(MiPoll, {noteId: note.id}); + await transactionalEntityManager.delete(MiPoll, { noteId: note.id }); } }); } else { diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index 9ada6129aa..dd695527c4 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -24,12 +24,11 @@ import { StatusError } from '@/misc/status-error.js'; import { UtilityService } from '@/core/UtilityService.js'; import { bindThis } from '@/decorators.js'; import { checkHttps } from '@/misc/check-https.js'; -import type { IObject, IPost } from '../type.js'; +import { NoteUpdateService } from '@/core/NoteUpdateService.js'; import { getApId, getApType, getOneApHrefNullable, getOneApId, isEmoji, validPost } from '../type.js'; import { ApLoggerService } from '../ApLoggerService.js'; import { ApMfmService } from '../ApMfmService.js'; import { ApDbResolverService } from '../ApDbResolverService.js'; -import type { Resolver } from '../ApResolverService.js'; import { ApResolverService } from '../ApResolverService.js'; import { ApAudienceService } from '../ApAudienceService.js'; import { ApPersonService } from './ApPersonService.js'; @@ -37,7 +36,8 @@ import { extractApHashtags } from './tag.js'; import { ApMentionService } from './ApMentionService.js'; import { ApQuestionService } from './ApQuestionService.js'; import { ApImageService } from './ApImageService.js'; -import { NoteUpdateService } from '@/core/NoteUpdateService.js'; +import type { Resolver } from '../ApResolverService.js'; +import type { IObject, IPost } from '../type.js'; @Injectable() export class ApNoteService { @@ -53,9 +53,6 @@ export class ApNoteService { @Inject(DI.emojisRepository) private emojisRepository: EmojisRepository, - @Inject(DI.messagingMessagesRepository) - private messagingMessagesRepository: MessagingMessagesRepository, - @Inject(DI.notesRepository) private notesRepository: NotesRepository, @@ -348,7 +345,7 @@ export class ApNoteService { } const b_note = await this.notesRepository.findOneBy({ - uri: entryUri + uri: entryUri, }).then(x => { if (x == null) throw new Error('note not found'); return x; diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index 14553fbee7..e05a70c0e3 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -13,7 +13,6 @@ import { DI } from '@/di-symbols.js'; import { GetterService } from '@/server/api/GetterService.js'; import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; import { ApiError } from '../../error.js'; -import type { DriveFilesRepository, MiDriveFile } from "@/models/_.js"; export const meta = { tags: ['notes'], @@ -116,9 +115,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- @Inject(DI.driveFilesRepository) private driveFilesRepository: DriveFilesRepository, - @Inject(DI.driveFilesRepository) - private driveFilesRepository: DriveFilesRepository, - private getterService: GetterService, private noteEntityService: NoteEntityService, private noteUpdateService: NoteUpdateService, @@ -133,7 +129,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.noSuchNote); } - let files: MiDriveFile[] = []; const fileIds = ps.fileIds ?? ps.mediaIds ?? null; if (fileIds != null) { From e1eee2872d81ed18534adc745111dc451d69f252 Mon Sep 17 00:00:00 2001 From: GrapeApple0 <84321396+grapeapple0@users.noreply.github.com> Date: Sat, 30 Sep 2023 14:53:12 +0900 Subject: [PATCH 370/501] =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AE?= =?UTF-8?q?=E7=B7=A8=E9=9B=86=E5=B1=A5=E6=AD=B4=E3=82=92=E8=A6=8B=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB(=E6=96=B0=E8=A6=8F?= =?UTF-8?q?=E3=83=8E=E3=83=BC=E3=83=88=E3=81=AE=E3=81=BF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit (cherry picked from commit 25763ee679dabc2a6e0b862e98357b79495e31bc) --- .../1696044626209-noteEditHistory.js | 16 ++++++++++ .../src/core/entities/NoteEntityService.ts | 1 + packages/backend/src/models/Note.ts | 7 ++++ .../backend/src/models/json-schema/note.ts | 4 +++ .../src/components/MkNoteDetailed.vue | 32 +++++++++++++++++-- 5 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 packages/backend/migration/1696044626209-noteEditHistory.js diff --git a/packages/backend/migration/1696044626209-noteEditHistory.js b/packages/backend/migration/1696044626209-noteEditHistory.js new file mode 100644 index 0000000000..37a542aa1e --- /dev/null +++ b/packages/backend/migration/1696044626209-noteEditHistory.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class NoteEditHistory1696044626209 { + name = 'NoteEditHistory1696044626209' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "noteEditHistory" varchar(3000) array DEFAULT '{}'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP "noteEditHistory"`); + } +} diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 92ee6231eb..6438486ebd 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -324,6 +324,7 @@ export class NoteEntityService implements OnModuleInit { id: note.id, createdAt: this.idService.parse(note.id).date.toISOString(), updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined, + noteEditHistory: note.noteEditHistory.length ? note.noteEditHistory : undefined, userId: note.userId, user: this.userEntityService.pack(note.user ?? note.userId, me), text: text, diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index e7bd33f4c7..0cc12e38d1 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -19,6 +19,13 @@ export class MiNote { }) public updatedAt: Date | null; + @Column('varchar', { + length: 3000, + array: true, + default: '{}', + }) + public noteEditHistory: string[]; + @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 73e5debcd7..0e36662f80 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -22,6 +22,10 @@ export const packedNoteSchema = { optional: true, nullable: true, format: 'date-time', }, + noteEditHistory: { + type: 'array', + optional: true, nullable: false, + }, deletedAt: { type: 'string', optional: true, nullable: true, diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 8842a34423..626021395d 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -143,10 +143,12 @@ SPDX-License-Identifier: AGPL-3.0-only </footer> </article> <div :class="$style.tabs"> + <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'replies'}]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'renotes'}]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'reactions'}]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button> - </div> + <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'history' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light']" @click="tab = 'history'"><i class="ti ti-pencil"></i> {{ i18n.ts.edited }}</button> + </div> <div> <div v-if="tab === 'replies'"> <div v-if="!repliesLoaded" style="padding: 16px"> @@ -182,8 +184,24 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </MkPagination> </div> + <div v-else-if="tab === 'history'" :class="$style.tab_history"> + <div style="display: grid;"> + <div v-for="text in appearNote.noteEditHistory.reverse()" :key="text"> + <MkNotePreview :class="$style.historyNote" :text="text"/> + </div> + </div> + </div> </div> -</div><div v-else /> +</div> +<div v-else class="_panel" :class="$style.muted" @click="muted = false"> + <I18n :src="i18n.ts.userSaysSomething" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> +</div> </template> <script lang="ts" setup> @@ -192,6 +210,7 @@ import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; import MkNoteSimple from '@/components/MkNoteSimple.vue'; +import MkNotePreview from '@/components/MkNotePreview.vue'; import MkReactionsViewer from '@/components/MkReactionsViewer.vue'; import MkMediaList from '@/components/MkMediaList.vue'; import MkCwButton from '@/components/MkCwButton.vue'; @@ -748,6 +767,9 @@ function loadConversation() { padding: 16px; } +.tab_history { + padding: 16px; +} .reactionTabs { display: flex; gap: 8px; @@ -817,6 +839,12 @@ function loadConversation() { } } +.historyNote { + padding-top: 10px; + min-height: 75px; + overflow: auto; +} + .muted { padding: 8px; text-align: center; From 23508dc56e1aacb93f7c3dd2c420f409e827e830 Mon Sep 17 00:00:00 2001 From: NoriDev <m1nthing2322@gmail.com> Date: Thu, 19 Oct 2023 18:48:59 +0900 Subject: [PATCH 371/501] fix --- CHANGELOG_CHERRYPICK.md | 0 .../1696318192428-noteUpdatedAtHistory.js | 17 +++ .../src/core/entities/NoteEntityService.ts | 1 + packages/backend/src/models/Note.ts | 6 + .../backend/src/models/json-schema/note.ts | 9 ++ packages/cherrypick-js/src/entities.ts | 0 .../src/components/MkNoteDetailed.vue | 127 ++++++++++++------ 7 files changed, 118 insertions(+), 42 deletions(-) create mode 100644 CHANGELOG_CHERRYPICK.md create mode 100644 packages/backend/migration/1696318192428-noteUpdatedAtHistory.js create mode 100644 packages/cherrypick-js/src/entities.ts diff --git a/CHANGELOG_CHERRYPICK.md b/CHANGELOG_CHERRYPICK.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/backend/migration/1696318192428-noteUpdatedAtHistory.js b/packages/backend/migration/1696318192428-noteUpdatedAtHistory.js new file mode 100644 index 0000000000..70220f3058 --- /dev/null +++ b/packages/backend/migration/1696318192428-noteUpdatedAtHistory.js @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey, cherrypick contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class NoteUpdateAtHistory1696318192428 { + name = 'NoteUpdateAtHistory1696318192428' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "updatedAtHistory" TIMESTAMP WITH TIME ZONE array`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP "updatedAtHistory"`); + } + +} diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 6438486ebd..a016c0a101 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -324,6 +324,7 @@ export class NoteEntityService implements OnModuleInit { id: note.id, createdAt: this.idService.parse(note.id).date.toISOString(), updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined, + updatedAtHistory: note.updatedAtHistory ? note.updatedAtHistory.map(x => x.toISOString()) : undefined, noteEditHistory: note.noteEditHistory.length ? note.noteEditHistory : undefined, userId: note.userId, user: this.userEntityService.pack(note.user ?? note.userId, me), diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 0cc12e38d1..03f410dab0 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -19,6 +19,12 @@ export class MiNote { }) public updatedAt: Date | null; + @Column('timestamp with time zone', { + array: true, + default: null, + }) + public updatedAtHistory: Date[] | null; + @Column('varchar', { length: 3000, array: true, diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index 0e36662f80..3e1d4500f5 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -22,6 +22,15 @@ export const packedNoteSchema = { optional: true, nullable: true, format: 'date-time', }, + updatedAtHistory: { + type: 'array', + optional: true, nullable: true, + items: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + }, noteEditHistory: { type: 'array', optional: true, nullable: false, diff --git a/packages/cherrypick-js/src/entities.ts b/packages/cherrypick-js/src/entities.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 626021395d..2cbc8ffcaa 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -77,7 +77,6 @@ SPDX-License-Identifier: AGPL-3.0-only v-if="appearNote.text" :parsedNodes="parsed" :text="appearNote.text" - :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis" :enableEmojiMenu="true" @@ -88,7 +87,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLoading v-if="translating" mini/> <div v-else-if="translation"> <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> - <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + <Mfm :text="translation.text" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> </div> </div> <div v-if="appearNote.files && appearNote.files.length > 0"> @@ -143,12 +142,11 @@ SPDX-License-Identifier: AGPL-3.0-only </footer> </article> <div :class="$style.tabs"> - <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'replies' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'replies'}]" @click="tab = 'replies'"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.replies }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'renotes'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'renotes'}]" @click="tab = 'renotes'"><i class="ti ti-repeat"></i> {{ i18n.ts.renotes }}</button> <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'reactions'},{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light' && tab === 'reactions'}]" @click="tab = 'reactions'"><i class="ti ti-icons"></i> {{ i18n.ts.reactions }}</button> - <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'history' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light']" @click="tab = 'history'"><i class="ti ti-pencil"></i> {{ i18n.ts.edited }}</button> - </div> + <button class="_button" :class="[$style.tab, { [$style.tabActive]: tab === 'history' },{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]" @click="tab = 'history'"><i class="ti ti-pencil"></i> {{ i18n.ts.edited }}</button> + </div> <div> <div v-if="tab === 'replies'"> <div v-if="!repliesLoaded" style="padding: 16px"> @@ -186,26 +184,39 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <div v-else-if="tab === 'history'" :class="$style.tab_history"> <div style="display: grid;"> - <div v-for="text in appearNote.noteEditHistory.reverse()" :key="text"> - <MkNotePreview :class="$style.historyNote" :text="text"/> + <div v-for="(text, index) in appearNote.noteEditHistory" :key="text" :class="$style.historyRoot"> + <MkAvatar :class="$style.avatar" :user="appearNote.user" link preview/> + <div :class="$style.historyMain"> + <div :class="$style.historyHeader"> + <MkUserName :user="appearNote.user" :nowrap="true"/> + <MkTime :class="$style.updatedAt" :time="appearNote.updatedAtHistory![index]"/> + </div> + <div> + <div> + <Mfm :text="text.trim()" :author="appearNote.user" :i="$i"/> + </div> + <CodeDiff + :oldString="appearNote.noteEditHistory[index - 1] || ''" + :newString="text" + :trim="true" + :hideHeader="true" + diffStyle="char" + /> + </div> + </div> + </div> + <div v-if="appearNote.noteEditHistory == null" class="_fullinfo"> + <img :src="infoImageUrl" class="_ghost"/> + <div>{{ i18n.ts.nothing }}</div> </div> </div> </div> </div> </div> -<div v-else class="_panel" :class="$style.muted" @click="muted = false"> - <I18n :src="i18n.ts.userSaysSomething" tag="small"> - <template #name> - <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> - <MkUserName :user="appearNote.user"/> - </MkA> - </template> - </I18n> -</div> </template> <script lang="ts" setup> -import {computed, inject, onMounted, provide, ref, shallowRef, watch} from 'vue'; +import { computed, inject, onMounted, provide, ref, shallowRef, watch } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; @@ -241,6 +252,13 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; +import { miLocalStorage } from '@/local-storage.js'; +import { infoImageUrl, instance } from '@/instance.js'; +import MkPostForm from '@/components/MkPostFormSimple.vue'; +import { deviceKind } from '@/scripts/device-kind.js'; + +const MOBILE_THRESHOLD = 500; +const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); const props = defineProps<{ note: Misskey.entities.Note; @@ -250,38 +268,37 @@ const inChannel = inject('inChannel', null); const note = ref(deepClone(props.note)); - let gaming = ref(''); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gaming.value = 'light'; } else { - gaming.value = ''; + gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); // plugin if (noteViewInterruptors.length > 0) { onMounted(async () => { @@ -831,12 +848,32 @@ function loadConversation() { width: 50px; height: 50px; } + .noteFooterButton { + &:not(:last-child) { + margin-right: 12px; + } + } +} +.historyRoot { + display: flex; + margin: 0; + padding: 10px; + overflow: clip; + font-size: 0.95em; +} - .noteFooterButton { - &:not(:last-child) { - margin-right: 12px; - } - } +.historyMain { + flex: 1; + min-width: 0; +} + +.historyHeader { + display: flex; + margin-bottom: 2px; + font-weight: bold; + width: 100%; + overflow: clip; + text-overflow: ellipsis; } .historyNote { @@ -845,6 +882,12 @@ function loadConversation() { overflow: auto; } +.updatedAt { + flex-shrink: 0; + margin-left: auto; + font-size: 0.9em; +} + .muted { padding: 8px; text-align: center; From 372a9190dda7b6ef789f332313796fcf1a4595a7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 13 Feb 2024 13:32:57 +0900 Subject: [PATCH 372/501] update README.md --- README.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7cca50910..45751bf98e 100644 --- a/README.md +++ b/README.md @@ -12,4 +12,14 @@ # 当フォークについて 当フォークは PrisMisskey.space で使用しているフォークになります。 -このコードを一部でも使用する場合はクレジット表示をお願いします。 +このコードを一部でも使用する場合はAbout内にクレジット表示をお願いします。 + +# Special Thanks + +[mkkey source](https://github.com/emtkmkk/mkkey) + +[mkkey Server](https://mkkey.net) + +[MisskeyIO source](https://github.com/MisskeyIO/misskey) + +[MisskeyIO Server](https://Misskey.io) From 3e9b3e5a3a1d6b9b795a679c681b559a2ce39dc5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 13 Feb 2024 13:37:49 +0900 Subject: [PATCH 373/501] update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 45751bf98e..79cf046d88 100644 --- a/README.md +++ b/README.md @@ -23,3 +23,5 @@ [MisskeyIO source](https://github.com/MisskeyIO/misskey) [MisskeyIO Server](https://Misskey.io) + +[CherryPick source](https://github.com/kokonect-link/cherrypick) From e074d975e11e5009c96df2ad7231571c83d37769 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 19:07:52 +0900 Subject: [PATCH 374/501] update README.md --- packages/backend/package.json | 1 + packages/backend/src/models/Meta.ts | 11 +++ .../src/server/api/SignupApiService.ts | 9 ++ .../src/server/api/endpoints/admin/meta.ts | 10 ++ .../server/api/endpoints/admin/update-meta.ts | 34 +++++-- .../frontend/src/pages/admin/security.vue | 24 ++++- pnpm-lock.yaml | 91 +++++++++++-------- 7 files changed, 130 insertions(+), 50 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index ee1bf676cb..b7a2e1ea12 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -150,6 +150,7 @@ "pkce-challenge": "4.1.0", "probe-image-size": "7.2.3", "promise-limit": "2.7.0", + "proxycheck-ts": "^0.0.9", "pug": "3.0.2", "punycode": "2.3.1", "pureimage": "0.3.17", diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 50371560cf..7796de6ef7 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -611,4 +611,15 @@ export class MiMeta { default: false, }) public enableGDPRMode: boolean; + + @Column('boolean', { + default: false, + }) + public enableProxyCheckio: boolean; + + @Column('varchar', { + length: 32, + nullable: true, + }) + public proxyCheckioApiKey: string; } diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 546de48e6b..01ca4ff612 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { IsNull } from 'typeorm'; +import ProxyCheck from 'proxycheck-ts'; import { DI } from '@/di-symbols.js'; import type { RegistrationTicketsRepository, UsedUsernamesRepository, UserPendingsRepository, UserProfilesRepository, UsersRepository, MiRegistrationTicket } from '@/models/_.js'; import type { Config } from '@/config.js'; @@ -74,6 +75,14 @@ export class SignupApiService { const instance = await this.metaService.fetch(true); + if (instance.enableProxyCheckio) { + if (instance.proxyCheckioApiKey == null) throw new FastifyReplyError(400, 'PROXY_CHECKIO_API_KEY_NOT_SET'); + const proxyCheck = new ProxyCheck({ api_key: instance.proxyCheckioApiKey }); + const result = await proxyCheck.check(request.headers['x-real-ip'] ?? request.ip, { + vpn: 1, + }); + if (result[request.headers['x-real-ip'] ?? request.ip].proxy === 'yes') throw new FastifyReplyError(400, 'PROXY_DETECTED'); + } // Verify *Captcha // ただしテスト時はこの機構は障害となるため無効にする if (process.env.NODE_ENV !== 'test') { diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index e8389a22d8..ef00a987bc 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -459,6 +459,14 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + enableProxyCheckio: { + type: 'boolean', + optional: false, nullable: false, + }, + proxyCheckioApiKey: { + type: 'string', + optional: false, nullable: true, + }, }, }, } as const; @@ -590,6 +598,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- EmojiBotToken: instance.EmojiBotToken, ApiBase: instance.ApiBase, enableGDPRMode: instance.enableGDPRMode, + enableProxyCheckio: instance.enableProxyCheckio, + proxyCheckioApiKey: instance.proxyCheckioApiKey, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 4b27f45d90..4acd51c0db 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -91,7 +91,7 @@ export const paramDef = { }, }, summalyProxy: { type: 'string', nullable: true }, - DiscordWebhookUrl:{ type: 'string', nullable: true}, + DiscordWebhookUrl: { type: 'string', nullable: true }, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, enableEmail: { type: 'boolean' }, @@ -152,9 +152,15 @@ export const paramDef = { type: 'string', }, }, - EmojiBotToken:{ type: 'string', nullable: true}, - ApiBase:{ type: 'string',nullable:true}, + EmojiBotToken: { type: 'string', nullable: true }, + ApiBase: { type: 'string', nullable: true }, enableGDPRMode: { type: 'boolean' }, + enableProxyCheckio: { + type: 'boolean', nullable: true, + }, + proxyCheckioApiKey: { + type: 'string', nullable: true, + }, }, required: [], } as const; @@ -201,14 +207,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.themeColor !== undefined) { set.themeColor = ps.themeColor; } - if (ps.DiscordWebhookUrl !== undefined){ - set.DiscordWebhookUrl = ps.DiscordWebhookUrl + if (ps.DiscordWebhookUrl !== undefined) { + set.DiscordWebhookUrl = ps.DiscordWebhookUrl; } - if (ps.EmojiBotToken !== undefined){ - set.EmojiBotToken = ps.EmojiBotToken + if (ps.EmojiBotToken !== undefined) { + set.EmojiBotToken = ps.EmojiBotToken; } - if (ps.ApiBase !== undefined){ - set.ApiBase = ps.ApiBase + if (ps.ApiBase !== undefined) { + set.ApiBase = ps.ApiBase; } if (ps.mascotImageUrl !== undefined) { set.mascotImageUrl = ps.mascotImageUrl; @@ -234,10 +240,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- set.serverErrorImageUrl = ps.serverErrorImageUrl; } + if (ps.enableProxyCheckio !== undefined) { + set.enableProxyCheckio = ps.enableProxyCheckio; + } + + if (ps.proxyCheckioApiKey !== undefined) { + set.proxyCheckioApiKey = ps.proxyCheckioApiKey; + } + if (ps.infoImageUrl !== undefined) { set.infoImageUrl = ps.infoImageUrl; } - console.log(ps.enableGDPRMode); + if (ps.enableGDPRMode !== undefined) { set.enableGDPRMode = ps.enableGDPRMode; } diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 6a0b3fe793..562ab545df 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -103,6 +103,22 @@ SPDX-License-Identifier: AGPL-3.0-only <MkTextarea v-model="bannedEmailDomains"> <template #label>Banned Email Domains List</template> </MkTextarea> + + <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + </div> + </MkFolder> + + <MkFolder> + <template #label> Signup Protection</template> + + <div class="_gaps_m"> + <MkSwitch v-model="enableProxyCheckio"> + <template #label>Use ProxyCheck.io API</template> + </MkSwitch> + <MkInput v-model="proxyCheckioApiKey"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>ProxyCheck.io API Key</template> + </MkInput> <MkButton primary @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> </div> </MkFolder> @@ -167,11 +183,13 @@ const enableSensitiveMediaDetectionForVideos = ref<boolean>(false); const enableIpLogging = ref<boolean>(false); const enableActiveEmailValidation = ref<boolean>(false); const enableVerifymailApi = ref<boolean>(false); +const enableProxyCheckio = ref<boolean>(false); const verifymailAuthKey = ref<string | null>(null); const enableTruemailApi = ref<boolean>(false); const truemailInstance = ref<string | null>(null); const truemailAuthKey = ref<string | null>(null); const bannedEmailDomains = ref<string>(''); +const proxyCheckioApiKey = ref<string | null>(null); async function init() { const meta = await misskeyApi('admin/meta'); @@ -196,7 +214,9 @@ async function init() { enableTruemailApi.value = meta.enableTruemailApi; truemailInstance.value = meta.truemailInstance; truemailAuthKey.value = meta.truemailAuthKey; - bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ""; + bannedEmailDomains.value = meta.bannedEmailDomains?.join('\n') || ''; + enableProxyCheckio.value = meta.enableProxyCheckio; + proxyCheckioApiKey.value = meta.proxyCheckioApiKey; } function save() { @@ -220,6 +240,8 @@ function save() { truemailInstance: truemailInstance.value, truemailAuthKey: truemailAuthKey.value, bannedEmailDomains: bannedEmailDomains.value.split('\n'), + enableProxyCheckio: enableProxyCheckio.value, + proxyCheckioApiKey: proxyCheckioApiKey.value, }).then(() => { fetchInstance(); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index eaefa9fe33..745f6b7ebe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -323,6 +323,9 @@ importers: promise-limit: specifier: 2.7.0 version: 2.7.0 + proxycheck-ts: + specifier: ^0.0.9 + version: 0.0.9 pug: specifier: 3.0.2 version: 3.0.2 @@ -1918,7 +1921,7 @@ packages: '@babel/traverse': 7.22.11 '@babel/types': 7.22.17 convert-source-map: 1.9.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -1941,7 +1944,7 @@ packages: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2043,7 +2046,7 @@ packages: '@babel/core': 7.23.5 '@babel/helper-compilation-targets': 7.22.15 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -3442,7 +3445,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.9 '@babel/types': 7.23.5 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -3460,7 +3463,7 @@ packages: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.9 '@babel/types': 7.23.5 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -4161,7 +4164,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 globals: 13.19.0 ignore: 5.2.4 @@ -4178,7 +4181,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 globals: 13.19.0 ignore: 5.2.4 @@ -4413,7 +4416,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -8595,7 +8598,7 @@ packages: '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -8624,7 +8627,7 @@ packages: '@typescript-eslint/type-utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.18.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -8650,7 +8653,7 @@ packages: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 typescript: 5.3.3 transitivePeerDependencies: @@ -8671,7 +8674,7 @@ packages: '@typescript-eslint/types': 6.18.1 '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.18.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 typescript: 5.3.3 transitivePeerDependencies: @@ -8706,7 +8709,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 @@ -8726,7 +8729,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 6.18.1(typescript@5.3.3) '@typescript-eslint/utils': 6.18.1(eslint@8.56.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 ts-api-utils: 1.0.1(typescript@5.3.3) typescript: 5.3.3 @@ -8755,7 +8758,7 @@ packages: dependencies: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -8776,7 +8779,7 @@ packages: dependencies: '@typescript-eslint/types': 6.18.1 '@typescript-eslint/visitor-keys': 6.18.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -9252,7 +9255,7 @@ packages: engines: {node: '>= 6.0.0'} requiresBuild: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -9260,7 +9263,7 @@ packages: resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==} engines: {node: '>= 14'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -9642,7 +9645,7 @@ packages: resolution: {integrity: sha512-TAlMYvOuwGyLK3PfBb5WKBXZmXz2fVCgv23d6zZFdle/q3gPjmxBaeuC0pY0Dzs5PWMSgfqqEZkrye19GlDTgw==} dependencies: archy: 1.0.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) fastq: 1.15.0 transitivePeerDependencies: - supports-color @@ -11144,6 +11147,7 @@ packages: dependencies: ms: 2.1.2 supports-color: 5.5.0 + dev: true /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} @@ -11156,7 +11160,6 @@ packages: dependencies: ms: 2.1.2 supports-color: 8.1.1 - dev: true /decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} @@ -11373,7 +11376,7 @@ packages: hasBin: true dependencies: address: 1.2.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -11697,7 +11700,7 @@ packages: peerDependencies: esbuild: '>=0.12 <1' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) esbuild: 0.18.20 transitivePeerDependencies: - supports-color @@ -12006,7 +12009,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -12053,7 +12056,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -12684,7 +12687,7 @@ packages: debug: optional: true dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) /for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -13240,6 +13243,7 @@ packages: /has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} + dev: true /has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} @@ -13377,7 +13381,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -13437,7 +13441,7 @@ packages: engines: {node: '>= 6.0.0'} dependencies: agent-base: 5.1.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: true @@ -13447,7 +13451,7 @@ packages: engines: {node: '>= 6'} dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -13456,7 +13460,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color dev: false @@ -13623,7 +13627,7 @@ packages: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -14069,7 +14073,7 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -17253,6 +17257,14 @@ packages: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} dev: true + /proxycheck-ts@0.0.9: + resolution: {integrity: sha512-qxdMgLB01kPksBAeAHI2Zt5q/fW0glZKZK9d9eNm0/KyowKuKzVmvirF2ztsGcIzU7r6s6YvPwXJuPTeQ+LBTg==} + dependencies: + cross-fetch: 4.0.0 + transitivePeerDependencies: + - encoding + dev: false + /ps-list@8.1.1: resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -17386,7 +17398,7 @@ packages: engines: {node: '>=8.16.0'} dependencies: '@types/mime-types': 2.1.4 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) extract-zip: 1.7.0 https-proxy-agent: 4.0.0 mime: 2.6.0 @@ -18387,7 +18399,7 @@ packages: dependencies: '@hapi/hoek': 10.0.1 '@hapi/wreck': 18.0.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) joi: 17.7.0 transitivePeerDependencies: - supports-color @@ -18587,7 +18599,7 @@ packages: engines: {node: '>= 14'} dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -18740,7 +18752,7 @@ packages: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -18998,6 +19010,7 @@ packages: engines: {node: '>=4'} dependencies: has-flag: 3.0.0 + dev: true /supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} @@ -19630,7 +19643,7 @@ packages: chalk: 4.1.2 cli-highlight: 2.1.11 dayjs: 1.11.10 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) dotenv: 16.0.3 glob: 10.3.10 ioredis: 5.3.2 @@ -19990,7 +20003,7 @@ packages: hasBin: true dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) mlly: 1.5.0 pathe: 1.1.2 picocolors: 1.0.0 @@ -20102,7 +20115,7 @@ packages: acorn-walk: 8.3.2 cac: 6.7.14 chai: 4.3.10 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) happy-dom: 10.0.3 local-pkg: 0.4.3 magic-string: 0.30.5 @@ -20212,7 +20225,7 @@ packages: peerDependencies: eslint: '>=6.0.0' dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.56.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 From 81d0709249a7666d0d23a05f1f86ad6c299d15be Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 19:35:57 +0900 Subject: [PATCH 375/501] a --- .../backend/src/core/NoteCreateService.ts | 18 +++++++++++++ .../src/server/api/SignupApiService.ts | 26 +++++++++++++------ 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 7ef2658e51..378c215b6c 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -265,6 +265,24 @@ export class NoteCreateService implements OnApplicationShutdown { } if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) { + const { DiscordWebhookUrl } = (await this.metaService.fetch()); + + if (DiscordWebhookUrl) { + const data_disc = { 'username': 'ノートブロックお知らせ', + 'content': + 'ユーザー名 :' + user.username + '\n' + + 'url : ' + user.host + '\n' + + 'contents : ' + data.text, + }; + + await fetch(DiscordWebhookUrl, { + 'method': 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data_disc), + }); + } throw new NoteCreateService.ContainsProhibitedWordsError(); } diff --git a/packages/backend/src/server/api/SignupApiService.ts b/packages/backend/src/server/api/SignupApiService.ts index 01ca4ff612..38585b3888 100644 --- a/packages/backend/src/server/api/SignupApiService.ts +++ b/packages/backend/src/server/api/SignupApiService.ts @@ -75,14 +75,6 @@ export class SignupApiService { const instance = await this.metaService.fetch(true); - if (instance.enableProxyCheckio) { - if (instance.proxyCheckioApiKey == null) throw new FastifyReplyError(400, 'PROXY_CHECKIO_API_KEY_NOT_SET'); - const proxyCheck = new ProxyCheck({ api_key: instance.proxyCheckioApiKey }); - const result = await proxyCheck.check(request.headers['x-real-ip'] ?? request.ip, { - vpn: 1, - }); - if (result[request.headers['x-real-ip'] ?? request.ip].proxy === 'yes') throw new FastifyReplyError(400, 'PROXY_DETECTED'); - } // Verify *Captcha // ただしテスト時はこの機構は障害となるため無効にする if (process.env.NODE_ENV !== 'test') { @@ -117,6 +109,24 @@ export class SignupApiService { const invitationCode = body['invitationCode']; const emailAddress = body['emailAddress']; + const { DiscordWebhookUrl } = (await this.metaService.fetch()); + if (DiscordWebhookUrl) { + const data_disc = { 'username': 'ユーザー登録お知らせ', + 'content': + 'ユーザー名 :' + username + '\n' + + 'メールアドレス : ' + emailAddress + '\n' + + 'IPアドレス : ' + request.headers['x-real-ip'] ?? request.ip, + }; + + await fetch(DiscordWebhookUrl, { + 'method': 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data_disc), + }); + } + if (instance.emailRequiredForSignup) { if (emailAddress == null || typeof emailAddress !== 'string') { reply.code(400); From a89d495300d2ac29b5154312a7f68a7e6f131c58 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 19:45:08 +0900 Subject: [PATCH 376/501] a --- packages/frontend/src/pages/admin/index.vue | 22 ++++++++++----------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 34e8332607..355f8fe498 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -36,11 +36,9 @@ import { instance } from '@/instance.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js'; -import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { useRouter } from '@/router/supplier.js'; - -import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js'; -import {bannerDark, bannerLight, defaultStore, iconDark, iconLight} from "@/store.js"; +import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; +import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store.js'; const isEmpty = (x: string | null) => x == null || x === ''; @@ -68,17 +66,17 @@ const currentPage = computed(() => router.currentRef.value.child); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); let iconUrl = ref(); if (darkMode.value) { - iconUrl.value = iconDark; + iconUrl.value = iconDark; } else { - iconUrl.value = iconLight; + iconUrl.value = iconLight; } watch(darkMode, () => { - if (darkMode.value) { - iconUrl.value = iconDark; - } else { - iconUrl.value = iconLight; - } -}) + if (darkMode.value) { + iconUrl.value = iconDark; + } else { + iconUrl.value = iconLight; + } +}); misskeyApi('admin/abuse-user-reports', { state: 'unresolved', limit: 1, From 332a3f2737a44d31d8bd3eeafef9d1d08248f11e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 19:46:53 +0900 Subject: [PATCH 377/501] a --- .../backend/migration/1707888646527-proxycheckio.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 packages/backend/migration/1707888646527-proxycheckio.js diff --git a/packages/backend/migration/1707888646527-proxycheckio.js b/packages/backend/migration/1707888646527-proxycheckio.js new file mode 100644 index 0000000000..62ecc0ddc7 --- /dev/null +++ b/packages/backend/migration/1707888646527-proxycheckio.js @@ -0,0 +1,13 @@ +export class Proxycheckio1707888646527 { + name = 'Proxycheckio1707888646527' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "enableProxyCheckio" boolean NOT NULL DEFAULT false`); + await queryRunner.query(`ALTER TABLE "meta" ADD "proxyCheckioApiKey" character varying(32)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "proxyCheckioApiKey"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableProxyCheckio"`); + } +} From 9e1d276bbd1a84876a8db3f7b6b3bb5f786935c0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 19:49:16 +0900 Subject: [PATCH 378/501] a --- packages/frontend/src/ui/universal.vue | 449 +++++++++++++------------ 1 file changed, 239 insertions(+), 210 deletions(-) diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue index 1e91716110..08f4c79c2c 100644 --- a/packages/frontend/src/ui/universal.vue +++ b/packages/frontend/src/ui/universal.vue @@ -4,158 +4,188 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div :class="$style.root"> - <XSidebar v-if="!isMobile" :class="$style.sidebar"/> +<div :class="$style.root"> + <XSidebar v-if="!isMobile" :class="$style.sidebar"/> - <MkStickyContainer ref="contents" :class="$style.contents" style="container-type: inline-size;" - @contextmenu.stop="onContextmenu"> - <template #header> - <div> - <XAnnouncements v-if="$i"/> - <XStatusBars :class="$style.statusbars"/> - </div> - </template> - <RouterView/> - <div :class="$style.spacer"></div> - </MkStickyContainer> + <MkStickyContainer + ref="contents" :class="$style.contents" style="container-type: inline-size;" + @contextmenu.stop="onContextmenu" + > + <template #header> + <div> + <XAnnouncements v-if="$i"/> + <XStatusBars :class="$style.statusbars"/> + </div> + </template> + <RouterView/> + <div :class="$style.spacer"></div> + </MkStickyContainer> - <div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets"> - <XWidgets/> - </div> + <div v-if="isDesktop && !pageMetadata?.needWideArea" :class="$style.widgets"> + <XWidgets/> + </div> - <button v-if="(!isDesktop || pageMetadata?.needWideArea) && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"> - <i class="ti ti-apps"></i></button> + <button v-if="(!isDesktop || pageMetadata?.needWideArea) && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"> + <i class="ti ti-apps"></i> + </button> - <div v-if="isMobile" ref="navFooter" :class="$style.nav"> - <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i - :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" - :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"><i - class="_indicatorCircle"></i></span></button> - <button :class="$style.navButton" class="_button" - @click="isRoot ? top() : mainRouter.push('/')"><i - :class="$style.navButtonIcon" class="ti ti-home"></i></button> - <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"><i - :class="$style.navButtonIcon" class="ti ti-bell"></i><span v-if="$i?.hasUnreadNotification" - :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]"> + <div v-if="isMobile" ref="navFooter" :class="$style.nav"> + <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"> + <i + :class="$style.navButtonIcon" class="ti ti-menu-2" + ></i><span + v-if="menuIndicated" + :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]" + ><i + class="_indicatorCircle" + ></i></span> + </button> + <button + :class="$style.navButton" class="_button" + @click="isRoot ? top() : mainRouter.push('/')" + > + <i + :class="$style.navButtonIcon" class="ti ti-home" + ></i> + </button> + <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"> + <i + :class="$style.navButtonIcon" class="ti ti-bell" + ></i><span + v-if="$i?.hasUnreadNotification" + :class="[$style.navButtonIndicator,{[$style.gamingDark]: gaming === 'dark',[$style.gamingLight]: gaming === 'light'}]" + > <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 && indicatorCounterToggle ? '99+' : $i.unreadNotificationsCount }}</span> - </span></button> - <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" - class="ti ti-apps"></i> - </button> - <button - :class="[{[$style.postButton_gamingDark]: gaming === 'dark',[$style.postButton_gamingLight]: gaming === 'light',[$style.postButton]: gaming === ''}]" - class="_button" @click="os.post()"><i :class="$style.navButtonIcon" - class="ti ti-pencil"></i></button> - </div> + </span> + </button> + <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"> + <i + :class="$style.navButtonIcon" + class="ti ti-apps" + ></i> + </button> + <button + :class="[{[$style.postButton_gamingDark]: gaming === 'dark',[$style.postButton_gamingLight]: gaming === 'light',[$style.postButton]: gaming === ''}]" + class="_button" @click="os.post()" + > + <i + :class="$style.navButtonIcon" + class="ti ti-pencil" + ></i> + </button> + </div> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''" - > - <div - v-if="drawerMenuShowing" - :class="$style.menuDrawerBg" - class="_modalBg" - @click="drawerMenuShowing = false" - @touchstart.passive="drawerMenuShowing = false" - ></div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''" + > + <div + v-if="drawerMenuShowing" + :class="$style.menuDrawerBg" + class="_modalBg" + @click="drawerMenuShowing = false" + @touchstart.passive="drawerMenuShowing = false" + ></div> + </Transition> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''" - > - <div v-if="drawerMenuShowing" :class="$style.menuDrawer"> - <XDrawerMenu/> - </div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''" + > + <div v-if="drawerMenuShowing" :class="$style.menuDrawer"> + <XDrawerMenu/> + </div> + </Transition> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''" - > - <div - v-if="widgetsShowing" - :class="$style.widgetsDrawerBg" - class="_modalBg" - @click="widgetsShowing = false" - @touchstart.passive="widgetsShowing = false" - ></div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''" + > + <div + v-if="widgetsShowing" + :class="$style.widgetsDrawerBg" + class="_modalBg" + @click="widgetsShowing = false" + @touchstart.passive="widgetsShowing = false" + ></div> + </Transition> - <Transition - :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''" - :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''" - :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''" - :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''" - > - <div v-if="widgetsShowing" :class="$style.widgetsDrawer"> - <button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i - class="ti ti-x"></i></button> - <XWidgets/> - </div> - </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''" + > + <div v-if="widgetsShowing" :class="$style.widgetsDrawer"> + <button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"> + <i + class="ti ti-x" + ></i> + </button> + <XWidgets/> + </div> + </Transition> - <XCommon/> - </div> + <XCommon/> +</div> </template> <script lang="ts" setup> -import {defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef, Ref} from 'vue'; +import { defineAsyncComponent, provide, onMounted, computed, ref, watch, shallowRef, Ref } from 'vue'; import XCommon from './_common_/common.vue'; import type MkStickyContainer from '@/components/global/MkStickyContainer.vue'; -import {instanceName} from '@/config'; +import { instanceName } from '@/config'; import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue'; import * as os from '@/os'; -import {defaultStore} from '@/store'; -import {navbarItemDef} from '@/navbar'; -import {i18n} from '@/i18n'; -import {$i} from '@/account'; +import { defaultStore } from '@/store'; +import { navbarItemDef } from '@/navbar'; +import { i18n } from '@/i18n'; +import { $i } from '@/account'; import { mainRouter } from '@/router/main.js'; -import {PageMetadata, provideMetadataReceiver} from '@/scripts/page-metadata'; -import {deviceKind} from '@/scripts/device-kind'; -import {miLocalStorage} from '@/local-storage'; -import {CURRENT_STICKY_BOTTOM} from '@/const'; -import {useScrollPositionManager} from '@/nirax'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata'; +import { deviceKind } from '@/scripts/device-kind'; +import { miLocalStorage } from '@/local-storage'; +import { CURRENT_STICKY_BOTTOM } from '@/const'; +import { useScrollPositionManager } from '@/nirax'; const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const gamingMode = computed(defaultStore.makeGetterSetter('gamingMode')); const indicatorCounterToggle = computed(defaultStore.makeGetterSetter('indicatorCounterToggle')); -let gaming = ref() +let gaming = ref(); // gaming.valueに新しい値を代入する if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; + gaming.value = 'dark'; } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; + gaming.value = 'light'; } else { - gaming.value = ''; + gaming.value = ''; } watch(darkMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); watch(gamingMode, () => { - if (darkMode.value && gamingMode.value == true) { - gaming.value = 'dark'; - } else if (!darkMode.value && gamingMode.value == true) { - gaming.value = 'light'; - } else { - gaming.value = ''; - } -}) + if (darkMode.value && gamingMode.value == true) { + gaming.value = 'dark'; + } else if (!darkMode.value && gamingMode.value == true) { + gaming.value = 'light'; + } else { + gaming.value = ''; + } +}); const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue')); const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue')); @@ -167,37 +197,36 @@ const DESKTOP_THRESHOLD = 1100; const MOBILE_THRESHOLD = 500; onMounted(() => { - if ( - window.navigator.connection.type === "cellular" && + if ( + window.navigator.connection.type === 'cellular' && !defaultStore.state.enableUltimateDataSaverMode && defaultStore.state.enableCellularWithUltimateDataSaver - ) { - defaultStore.state.enableDataSaverMode = true; - defaultStore.state.enableUltimateDataSaverMode = true; - } else if (window.navigator.connection.type !== "cellular" && window.navigator.connection.type !== "undefined" && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { - defaultStore.state.enableDataSaverMode = false; - defaultStore.state.enableUltimateDataSaverMode = true; - } + ) { + defaultStore.state.enableDataSaverMode = true; + defaultStore.state.enableUltimateDataSaverMode = true; + } else if (window.navigator.connection.type !== 'cellular' && window.navigator.connection.type !== 'undefined' && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { + defaultStore.state.enableDataSaverMode = false; + defaultStore.state.enableUltimateDataSaverMode = true; + } - if ( - window.navigator.connection.type === "cellular" && + if ( + window.navigator.connection.type === 'cellular' && !defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver - ) { - defaultStore.state.enableDataSaverMode = true; - - } else if (window.navigator.connection.type !== "cellular" && window.navigator.connection.type !== "undefined" && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { - defaultStore.state.enableDataSaverMode = false; - } - if (defaultStore.state.enableUltimateDataSaverMode) { - defaultStore.state.enableDataSaverMode = true; - } + ) { + defaultStore.state.enableDataSaverMode = true; + } else if (window.navigator.connection.type !== 'cellular' && window.navigator.connection.type !== 'undefined' && defaultStore.state.enableDataSaverMode && defaultStore.state.enableCellularWithDataSaver) { + defaultStore.state.enableDataSaverMode = false; + } + if (defaultStore.state.enableUltimateDataSaverMode) { + defaultStore.state.enableDataSaverMode = true; + } }); // デスクトップでウィンドウを狭くしたときモバイルUIが表示されて欲しいことはあるので deviceKind === 'desktop' の判定は行わない const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); window.addEventListener('resize', () => { - isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD; + isMobile.value = deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD; }); const pageMetadata = ref<null | PageMetadata>(null); @@ -208,107 +237,108 @@ const contents = shallowRef<InstanceType<typeof MkStickyContainer>>(); provide('router', mainRouter); provideMetadataReceiver((metadataGetter) => { const info = metadataGetter(); - pageMetadata.value = info; + pageMetadata.value = info; if (pageMetadata.value) { - if (isRoot.value && pageMetadata.value.title === instanceName) { + if (isRoot.value && pageMetadata.value.title === instanceName) { document.title = pageMetadata.value.title; } else { - document.title = `${pageMetadata.value.title} | ${instanceName}`; - }} + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } + } }); provideReactiveMetadata(pageMetadata); const menuIndicated = computed(() => { - for (const def in navbarItemDef) { - if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから - if (navbarItemDef[def].indicated) return true; - } - return false; + for (const def in navbarItemDef) { + if (def === 'notifications') continue; // 通知は下にボタンとして表示されてるから + if (navbarItemDef[def].indicated) return true; + } + return false; }); const drawerMenuShowing = ref(false); mainRouter.on('change', () => { - drawerMenuShowing.value = false; + drawerMenuShowing.value = false; }); if (window.innerWidth > 1024) { - const tempUI = miLocalStorage.getItem('ui_temp'); - if (tempUI) { - miLocalStorage.setItem('ui', tempUI); - miLocalStorage.removeItem('ui_temp'); - location.reload(); - } + const tempUI = miLocalStorage.getItem('ui_temp'); + if (tempUI) { + miLocalStorage.setItem('ui', tempUI); + miLocalStorage.removeItem('ui_temp'); + location.reload(); + } } defaultStore.loaded.then(() => { - if (defaultStore.state.widgets.length === 0) { - defaultStore.set('widgets', [{ - name: 'calendar', - id: 'a', place: 'right', data: {}, - }, { - name: 'notifications', - id: 'b', place: 'right', data: {}, - }, { - name: 'trends', - id: 'c', place: 'right', data: {}, - }]); - } + if (defaultStore.state.widgets.length === 0) { + defaultStore.set('widgets', [{ + name: 'calendar', + id: 'a', place: 'right', data: {}, + }, { + name: 'notifications', + id: 'b', place: 'right', data: {}, + }, { + name: 'trends', + id: 'c', place: 'right', data: {}, + }]); + } }); onMounted(() => { - if (!isDesktop.value) { - window.addEventListener('resize', () => { - if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true; - }, {passive: true}); - } + if (!isDesktop.value) { + window.addEventListener('resize', () => { + if (window.innerWidth >= DESKTOP_THRESHOLD) isDesktop.value = true; + }, { passive: true }); + } }); const onContextmenu = (ev) => { - const isLink = (el: HTMLElement) => { - if (el.tagName === 'A') return true; - if (el.parentElement) { - return isLink(el.parentElement); - } - }; - if (isLink(ev.target)) return; - if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; - if (window.getSelection()?.toString() !== '') return; - const path = mainRouter.getCurrentPath(); - os.contextMenu([{ - type: 'label', - text: path, - }, { - icon: 'ti ti-window-maximize', - text: i18n.ts.openInWindow, - action: () => { - os.pageWindow(path); - }, - }], ev); + const isLink = (el: HTMLElement) => { + if (el.tagName === 'A') return true; + if (el.parentElement) { + return isLink(el.parentElement); + } + }; + if (isLink(ev.target)) return; + if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; + if (window.getSelection()?.toString() !== '') return; + const path = mainRouter.getCurrentPath(); + os.contextMenu([{ + type: 'label', + text: path, + }, { + icon: 'ti ti-window-maximize', + text: i18n.ts.openInWindow, + action: () => { + os.pageWindow(path); + }, + }], ev); }; function top() { - contents.value.rootEl.scrollTo({ - top: 0, - behavior: 'smooth', - }); + contents.value.rootEl.scrollTo({ + top: 0, + behavior: 'smooth', + }); } const navFooterHeight = ref(0); provide<Ref<number>>(CURRENT_STICKY_BOTTOM, navFooterHeight); watch(navFooter, () => { - if (navFooter.value) { - navFooterHeight.value = navFooter.value.offsetHeight; - document.body.style.setProperty('--stickyBottom', `${navFooterHeight.value}px`); - document.body.style.setProperty('--minBottomSpacing', 'var(--minBottomSpacingMobile)'); - } else { - navFooterHeight.value = 0; - document.body.style.setProperty('--stickyBottom', '0px'); - document.body.style.setProperty('--minBottomSpacing', '0px'); - } + if (navFooter.value) { + navFooterHeight.value = navFooter.value.offsetHeight; + document.body.style.setProperty('--stickyBottom', `${navFooterHeight.value}px`); + document.body.style.setProperty('--minBottomSpacing', 'var(--minBottomSpacingMobile)'); + } else { + navFooterHeight.value = 0; + document.body.style.setProperty('--stickyBottom', '0px'); + document.body.style.setProperty('--minBottomSpacing', '0px'); + } }, { - immediate: true, + immediate: true, }); useScrollPositionManager(() => contents.value.rootEl, mainRouter); @@ -569,7 +599,6 @@ $widgets-hide-threshold: 1090px; background: linear-gradient(90deg, var(--X8), var(--X8)); } - } .navButtonIcon { From 9c905f84e8bee551ad6abcc2d51788b1731099a5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 20:36:23 +0900 Subject: [PATCH 379/501] a --- .../backend/src/core/NoteCreateService.ts | 34 ++++++++-- packages/backend/src/models/Meta.ts | 7 +++ .../src/server/api/endpoints/admin/meta.ts | 4 ++ .../server/api/endpoints/admin/update-meta.ts | 4 ++ .../src/server/web/ClientServerService.ts | 13 ---- packages/frontend/src/pages/about.vue | 1 - .../src/pages/admin/other-settings.vue | 62 +++++++++++-------- 7 files changed, 80 insertions(+), 45 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 378c215b6c..5951d2d6fa 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -265,17 +265,43 @@ export class NoteCreateService implements OnApplicationShutdown { } if (this.utilityService.isKeyWordIncluded(data.cw ?? data.text ?? '', meta.prohibitedWords)) { - const { DiscordWebhookUrl } = (await this.metaService.fetch()); + const { DiscordWebhookUrlWordBlock } = (await this.metaService.fetch()); + const regexpregexp = /^\/(.+)\/(.*)$/; + let matchedString = ''; + for (const filter of meta.prohibitedWords) { + // represents RegExp + const regexp = filter.match(regexpregexp); + // This should never happen due to input sanitisation. + if (!regexp) { + const words = filter.split(' '); + const foundWord = words.find(keyword => (data.cw ?? data.text ?? '').includes(keyword)); + if (foundWord) { + matchedString = foundWord; + break; + } + } else { + const match = new RE2(regexp[1], regexp[2]).exec(data.cw ?? data.text ?? ''); + if (match) { + matchedString = match[0]; + break; + } + } + } - if (DiscordWebhookUrl) { + console.log('matched', matchedString); + if (DiscordWebhookUrlWordBlock) { const data_disc = { 'username': 'ノートブロックお知らせ', 'content': 'ユーザー名 :' + user.username + '\n' + 'url : ' + user.host + '\n' + - 'contents : ' + data.text, + 'contents : ' + data.text + '\n' + + '引っかかったワード :' + matchedString, + 'allowed_mentions': { + 'parse': [], + }, }; - await fetch(DiscordWebhookUrl, { + await fetch(DiscordWebhookUrlWordBlock, { 'method': 'post', headers: { 'Content-Type': 'application/json', diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 7796de6ef7..cdb2955f01 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -229,6 +229,13 @@ export class MiMeta { nullable: true, }) public DiscordWebhookUrl: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public DiscordWebhookUrlWordBlock: string | null; + @Column('varchar', { length: 1024, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index ef00a987bc..08f2b95df6 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -458,6 +458,9 @@ export const meta = { DiscordWebhookUrl: { type: 'string', optional: false, nullable: true, + }, DiscordWebhookUrlWordBlock: { + type: 'string', + optional: false, nullable: true, }, enableProxyCheckio: { type: 'boolean', @@ -595,6 +598,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- perUserListTimelineCacheMax: instance.perUserListTimelineCacheMax, notesPerOneAd: instance.notesPerOneAd, DiscordWebhookUrl: instance.DiscordWebhookUrl, + DiscordWebhookUrlWordBlock: instance.DiscordWebhookUrlWordBlock, EmojiBotToken: instance.EmojiBotToken, ApiBase: instance.ApiBase, enableGDPRMode: instance.enableGDPRMode, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 4acd51c0db..f7ca38f81f 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -92,6 +92,7 @@ export const paramDef = { }, summalyProxy: { type: 'string', nullable: true }, DiscordWebhookUrl: { type: 'string', nullable: true }, + DiscordWebhookUrlWordBlock: { type: 'string', nullable: true }, deeplAuthKey: { type: 'string', nullable: true }, deeplIsPro: { type: 'boolean' }, enableEmail: { type: 'boolean' }, @@ -210,6 +211,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.DiscordWebhookUrl !== undefined) { set.DiscordWebhookUrl = ps.DiscordWebhookUrl; } + if (ps.DiscordWebhookUrlWordBlock !== undefined) { + set.DiscordWebhookUrlWordBlock = ps.DiscordWebhookUrlWordBlock; + } if (ps.EmojiBotToken !== undefined) { set.EmojiBotToken = ps.EmojiBotToken; } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 28acc47a40..e788b292d9 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -61,7 +61,6 @@ const clientAssets = `${_dirname}/../../../../frontend/assets/`; const assets = `${_dirname}/../../../../../built/_frontend_dist_/`; const swAssets = `${_dirname}/../../../../../built/_sw_dist_/`; const viteOut = `${_dirname}/../../../../../built/_vite_/`; -const tarball = `${_dirname}/../../../../../built/tarball/`; @Injectable() export class ClientServerService { @@ -309,18 +308,6 @@ export class ClientServerService { decorateReply: false, }); - fastify.register((fastify, options, done) => { - fastify.register(fastifyStatic, { - root: tarball, - prefix: '/tarball/', - maxAge: ms('30 days'), - immutable: true, - decorateReply: false, - }); - fastify.addHook('onRequest', handleRequestRedirectToOmitSearch); - done(); - }); - fastify.get('/favicon.ico', async (request, reply) => { return reply.sendFile('/favicon.ico', staticAssets); }); diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index c500ce49ce..71d813b696 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -87,7 +87,6 @@ SPDX-License-Identifier: AGPL-3.0-only <FormLink :to="`/.well-known/nodeinfo`" external>nodeinfo</FormLink> <FormLink :to="`/robots.txt`" external>robots.txt</FormLink> <FormLink :to="`/manifest.json`" external>manifest.json</FormLink> - <FormLink :to="`/tarball/misskey-${version}.tar.gz`" external>source code</FormLink> </div> </FormSection> </div> diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index c542686c3c..9b8f1a61b4 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -36,21 +36,25 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.turnOffToImprovePerformance }}</template> </MkSwitch> </div> - <MkInput v-model="DiscordWebhookUrl" type="password"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>Discord Webhook URL</template> - </MkInput> - <MkInput v-model="EmojiBotToken" type="password"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>EmojiBotToken</template> - </MkInput> - <MkInput v-model="ApiBase"> - <template #prefix><i class="ti ti-key"></i></template> - <template #label>ApiBase</template> - </MkInput> - <MkSwitch v-model="requestEmojiAllOk"> -絵文字の申請全部許可 - </MkSwitch> + <MkInput v-model="DiscordWebhookUrl" type="password"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Discord Webhook URL</template> + </MkInput> + <MkInput v-model="DiscordWebhookUrlWordBlock" type="password"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>Discord Webhook Url WordBlock</template> + </MkInput> + <MkInput v-model="EmojiBotToken" type="password"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>EmojiBotToken</template> + </MkInput> + <MkInput v-model="ApiBase"> + <template #prefix><i class="ti ti-key"></i></template> + <template #label>ApiBase</template> + </MkInput> + <MkSwitch v-model="requestEmojiAllOk"> + 絵文字の申請全部許可 + </MkSwitch> </div> </FormSuspense> </MkSpacer> @@ -67,26 +71,29 @@ import { fetchInstance } from '@/instance.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkSwitch from '@/components/MkSwitch.vue'; -import MkInput from "@/components/MkInput.vue"; +import MkInput from '@/components/MkInput.vue'; const enableServerMachineStats = ref<boolean>(false); const enableIdenticonGeneration = ref<boolean>(false); const enableChartsForRemoteUser = ref<boolean>(false); const enableChartsForFederatedInstances = ref<boolean>(false); -const requestEmojiAllOk = ref(false) +const requestEmojiAllOk = ref(false); let DiscordWebhookUrl = ref(null); -let EmojiBotToken= ref(null); -let ApiBase= ref(null) +let DiscordWebhookUrlWordBlock = ref(null); +let EmojiBotToken = ref(null); +let ApiBase = ref(null); + async function init() { const meta = await misskeyApi('admin/meta'); enableServerMachineStats.value = meta.enableServerMachineStats; enableIdenticonGeneration.value = meta.enableIdenticonGeneration; enableChartsForRemoteUser.value = meta.enableChartsForRemoteUser; enableChartsForFederatedInstances.value = meta.enableChartsForFederatedInstances; - requestEmojiAllOk.value = meta.requestEmojiAllOk; - DiscordWebhookUrl.value = meta.DiscordWebhookUrl; - EmojiBotToken.value = meta.EmojiBotToken; - ApiBase.value = meta.ApiBase; + requestEmojiAllOk.value = meta.requestEmojiAllOk; + DiscordWebhookUrl.value = meta.DiscordWebhookUrl; + DiscordWebhookUrlWordBlock.value = meta.DiscordWebhookUrlWordBlock; + EmojiBotToken.value = meta.EmojiBotToken; + ApiBase.value = meta.ApiBase; } function save() { @@ -94,11 +101,12 @@ function save() { enableServerMachineStats: enableServerMachineStats.value, enableIdenticonGeneration: enableIdenticonGeneration.value, enableChartsForRemoteUser: enableChartsForRemoteUser.value, - requestEmojiAllOk: requestEmojiAllOk.value, + requestEmojiAllOk: requestEmojiAllOk.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, - DiscordWebhookUrl:DiscordWebhookUrl.value, - EmojiBotToken:EmojiBotToken.value, - ApiBase:ApiBase.value + DiscordWebhookUrl: DiscordWebhookUrl.value, + EmojiBotToken: EmojiBotToken.value, + ApiBase: ApiBase.value, + DiscordWebhookUrlWordBlock: DiscordWebhookUrlWordBlock.value, }).then(() => { fetchInstance(); }); From 408acfdb5ac2e8ed00ef871d4db125ef634a2e7a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 16 Feb 2024 20:36:47 +0900 Subject: [PATCH 380/501] a --- .../1708081353629-discordwebohookwordbrock.js | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 packages/backend/migration/1708081353629-discordwebohookwordbrock.js diff --git a/packages/backend/migration/1708081353629-discordwebohookwordbrock.js b/packages/backend/migration/1708081353629-discordwebohookwordbrock.js new file mode 100644 index 0000000000..aee2bfacc4 --- /dev/null +++ b/packages/backend/migration/1708081353629-discordwebohookwordbrock.js @@ -0,0 +1,11 @@ +export class Discordwebohookwordbrock1708081353629 { + name = 'Discordwebohookwordbrock1708081353629' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "DiscordWebhookUrlWordBlock" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "DiscordWebhookUrlWordBlock"`); +} +} From 49f334df83bbd2548af29a0c1ebc5be2be1592c9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 17 Feb 2024 13:46:03 +0900 Subject: [PATCH 381/501] a --- packages/frontend/src/pages/about.vue | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 9fcaba962a..fc5b902d07 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -32,6 +32,13 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> </div> <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> + <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> + <template #icon><i class="ti ti-code"></i></template> + {{ i18n.ts.sourceCode }} + </FormLink> + <MkInfo v-else warn> + {{ i18n.ts.sourceCodeIsNotYetProvided }} + </MkInfo> ソースコード含め問い合わせは下記のメールアドレスへよろしくお願いします。 </div> </FormSection> From 2b749cb067a75c80e5764d45d479d317872c05f6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 17 Feb 2024 13:47:51 +0900 Subject: [PATCH 382/501] a --- packages/frontend/src/pages/about.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index fc5b902d07..1d860817bf 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -32,11 +32,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> </div> <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> - <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> + <FormLink v-if="$i && instance.repositoryUrl || $i && instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> <template #icon><i class="ti ti-code"></i></template> {{ i18n.ts.sourceCode }} </FormLink> - <MkInfo v-else warn> + <MkInfo v-else-if="$i" warn> {{ i18n.ts.sourceCodeIsNotYetProvided }} </MkInfo> ソースコード含め問い合わせは下記のメールアドレスへよろしくお願いします。 @@ -147,7 +147,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { instance } from '@/instance.js'; import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store'; - +import { signinRequired } from '@/account.js'; +const $i = signinRequired(); const props = withDefaults(defineProps<{ initialTab?: string; }>(), { From fb1406c2d3130c97cd18ffa4e8057eaa2d4f2d51 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 17 Feb 2024 13:52:37 +0900 Subject: [PATCH 383/501] a --- packages/frontend/src/pages/about.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 1d860817bf..945a54b7f7 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -148,7 +148,7 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { instance } from '@/instance.js'; import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store'; import { signinRequired } from '@/account.js'; -const $i = signinRequired(); +import { $i } from '@/account.js'; const props = withDefaults(defineProps<{ initialTab?: string; }>(), { From dfa62088a573b7799e70040e0d7b0ce57ca81038 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 17 Feb 2024 13:55:41 +0900 Subject: [PATCH 384/501] a --- packages/frontend/src/pages/about-misskey.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 469132a99b..0e4c93ed45 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormLink> </div> </FormSection> - <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey'"> + <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey' && $i"> <div class="_gaps_s"> <MkInfo> {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }} From 6734c38d24c9c5d0099e5de1a8f4c3eb5341c750 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 17 Feb 2024 19:01:04 +0900 Subject: [PATCH 385/501] a --- .../backend/src/core/NoteCreateService.ts | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 5951d2d6fa..b2c8fd2bd3 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -288,7 +288,6 @@ export class NoteCreateService implements OnApplicationShutdown { } } - console.log('matched', matchedString); if (DiscordWebhookUrlWordBlock) { const data_disc = { 'username': 'ノートブロックお知らせ', 'content': @@ -386,7 +385,40 @@ export class NoteCreateService implements OnApplicationShutdown { let tags = data.apHashtags; let emojis = data.apEmojis; let mentionedUsers = data.apMentions; + let tmp:string | null = null; + if (mentionedUsers !== null && mentionedUsers !== undefined && mentionedUsers.length > 0) { + await Promise.all(mentionedUsers.map(async (u) => { + if (data.text != null) { + const regex = new RegExp(`@${u.username}@${u.host ?? this.config.host}`, 'g'); + tmp = data.text.replace(regex, ''); + } + })); + if ( tmp !== null && tmp.trim() === '') { + const { DiscordWebhookUrlWordBlock } = (await this.metaService.fetch()); + if (DiscordWebhookUrlWordBlock) { + await fetch(DiscordWebhookUrlWordBlock, { + 'method': 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ 'username': 'ノートブロックお知らせ', + 'content': + 'ユーザー名 :' + user.username + '\n' + + 'url : ' + user.host + '\n' + + 'contents : ' + data.text + '\n' + + '引っかかった原因 メンションしかない', + + 'allowed_mentions': { + 'parse': [], + }, + }), + }); + } + console.log('メンションしかない'); + throw new NoteCreateService.ContainsProhibitedWordsError(); + } + } // Parse MFM if needed if (!tags || !emojis || !mentionedUsers) { const tokens = (data.text ? mfm.parse(data.text)! : []); From 4683ead75f036aba23d4ae2d13d0ba4a7c3d1721 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A8=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=85?= <17376330+u1-liquid@users.noreply.github.com> Date: Sun, 18 Feb 2024 03:47:17 +0900 Subject: [PATCH 386/501] =?UTF-8?q?spec(backend/NoteCreateService):=20?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=82=AB=E3=83=AB=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E3=83=BC=E3=81=8C=E3=81=BE=E3=81=A0=E8=AA=B0=E3=82=82=E3=83=95?= =?UTF-8?q?=E3=82=A9=E3=83=AD=E3=83=BC=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=83=AA=E3=83=A2=E3=83=BC=E3=83=88=E3=83=A6=E3=83=BC?= =?UTF-8?q?=E3=82=B6=E3=83=BC=E3=81=AB=E3=82=88=E3=82=8B=E9=80=9A=E7=9F=A5?= =?UTF-8?q?=E3=82=92=E5=BC=95=E3=81=8D=E8=B5=B7=E3=81=93=E3=81=99=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E6=80=A7=E3=81=AE=E3=81=82=E3=82=8B=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=82=92=E6=8B=92=E5=90=A6=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=20(MisskeyIO#462)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cherry-picked from 738b4d69701a9d4b232f6a44b340782d096b182b, 1b3adcc2bbc695a0f28f5865a6705e0e59830962, 33cb50761ec12fc0df0e6f99ba97e0d5d4e580fc, a27af00e23a5283e357de1e6bf2a47ebefaa77c2, 5c6236bb0f1fde9140e331c9e1390bb5fccd4f9a Co-authored-by: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com> --- packages/backend/src/core/NoteCreateService.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index b2c8fd2bd3..497328eb38 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -436,6 +436,16 @@ export class NoteCreateService implements OnApplicationShutdown { mentionedUsers = data.apMentions ?? await this.extractMentionedUsers(user, combinedTokens); } + const willCauseNotification = mentionedUsers.filter(u => u.host === null).length > 0 || data.reply?.userHost === null || data.renote?.userHost === null; + + if (process.env.MISSKEY_BLOCK_MENTIONS_FROM_UNFAMILIAR_REMOTE_USERS === 'true' && user.host !== null && willCauseNotification) { + const userEntity = await this.usersRepository.findOneBy({ id: user.id }); + if ((userEntity?.followersCount ?? 0) === 0) { + this.logger.error('Request rejected because user has no local followers', { user: user.id, note: data }); + throw new IdentifiableError('e11b3a16-f543-4885-8eb1-66cad131dbfd', 'Notes including mentions, replies, or renotes from remote users are not allowed until user has at least one local follower.'); + } + } + tags = tags.filter(tag => Array.from(tag).length <= 128).splice(0, 32); if (data.reply && (user.id !== data.reply.userId) && !mentionedUsers.some(u => u.id === data.reply!.userId)) { From a7bc276e0167ab9fdcff350111e57d3dc335442e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 18 Feb 2024 10:55:16 +0900 Subject: [PATCH 387/501] a --- packages/backend/src/core/NoteCreateService.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 497328eb38..ab33d42a59 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -438,11 +438,10 @@ export class NoteCreateService implements OnApplicationShutdown { const willCauseNotification = mentionedUsers.filter(u => u.host === null).length > 0 || data.reply?.userHost === null || data.renote?.userHost === null; - if (process.env.MISSKEY_BLOCK_MENTIONS_FROM_UNFAMILIAR_REMOTE_USERS === 'true' && user.host !== null && willCauseNotification) { + if (user.host !== null && willCauseNotification) { const userEntity = await this.usersRepository.findOneBy({ id: user.id }); if ((userEntity?.followersCount ?? 0) === 0) { - this.logger.error('Request rejected because user has no local followers', { user: user.id, note: data }); - throw new IdentifiableError('e11b3a16-f543-4885-8eb1-66cad131dbfd', 'Notes including mentions, replies, or renotes from remote users are not allowed until user has at least one local follower.'); + throw new NoteCreateService.ContainsProhibitedWordsError(); } } From fa8d6b43b3c744dfd71a99d46b572906174001a6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 18 Feb 2024 12:22:51 +0900 Subject: [PATCH 388/501] a --- packages/frontend/src/pages/about-misskey.vue | 2 +- packages/frontend/src/pages/about.vue | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index 0e4c93ed45..469132a99b 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only </FormLink> </div> </FormSection> - <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey' && $i"> + <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey'"> <div class="_gaps_s"> <MkInfo> {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }} diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 945a54b7f7..13e9b75639 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -32,11 +32,11 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> </div> <FormLink to="/about-misskey">{{ i18n.ts.aboutMisskey }}</FormLink> - <FormLink v-if="$i && instance.repositoryUrl || $i && instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> + <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> <template #icon><i class="ti ti-code"></i></template> {{ i18n.ts.sourceCode }} </FormLink> - <MkInfo v-else-if="$i" warn> + <MkInfo v-else warn> {{ i18n.ts.sourceCodeIsNotYetProvided }} </MkInfo> ソースコード含め問い合わせは下記のメールアドレスへよろしくお願いします。 From b40ecc61596e8fef9c85ef24de66cd9258f0f8a3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 6 Mar 2024 11:46:54 +0900 Subject: [PATCH 389/501] a --- locales/index.d.ts | 4 - .../api/endpoints/users/report-abuse.ts | 17 + .../frontend/src/components/MkClickerGame.vue | 40 ++- .../frontend/src/pages/admin/roles.editor.vue | 193 ++++------- packages/frontend/src/pages/admin/roles.vue | 311 +++++++++--------- .../frontend/src/widgets/WidgetClicker.vue | 13 +- 6 files changed, 252 insertions(+), 326 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 775053160e..142787ef04 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4892,10 +4892,6 @@ export interface Locale extends ILocale { * フォロー中またはフォロワー */ "followingOrFollower": string; - /** - * ファイル付きのみ - */ - "fileAttachedOnly": string; /** * TLに他の人への返信を含める */ diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 4f3d48770d..b40733e1a7 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -120,6 +120,23 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- }); } const meta = await this.metaService.fetch(); + if (meta.DiscordWebhookUrl) { + const data_disc = { 'username': '絵文字追加通知ちゃん', + 'content': + + '通報' + '\n' + + '通報' + report.comment + '\n' + + '通報したユーザー : ' + '@' + me.username + '\n' + + '通報されたユーザー : ' + report.targetUserId + '\n', + }; + await fetch(meta.DiscordWebhookUrl, { + 'method': 'post', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data_disc), + }); + } if (meta.email) { this.emailService.sendEmail(meta.email, 'New abuse report', sanitizeHtml(ps.comment), diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 1f9d871a8d..175fc5da6b 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -4,33 +4,38 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div> - <div v-if="game.ready" :class="$style.game"> - <div :class="$style.cps" class="">{{ number(cps) }}cps</div> - <div :class="$style.count" class=""><img :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" - src="https://media.discordapp.net/attachments/1153099592863334431/1162139796647448576/AfovawbDhjHYAAAAAElFTkSuQmCC.png"/> - {{ number(cookies) }} - </div> - <button v-click-anime class="_button" @click="onClick"> - <img src="https://cdn.discordapp.com/attachments/1153099592863334431/1160169965568143391/dihk_cossack.gif" - :class="$style.img"> - </button> - </div> - <div v-else> - <MkLoading/> +<div> + <div v-if="game.ready" :class="$style.game"> + <div :class="$style.cps" class="">{{ number(cps) }}cps</div> + <div :class="$style.count" class=""> + <img + :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" + src="https://files.prismisskey.space/misskey/59731116-9616-4161-82b3-7eff6c48ea72.apng" + /> + {{ number(cookies) }} </div> + <button v-click-anime class="_button" @click="onClick"> + <img + src="https://files.prismisskey.space/misskey/59731116-9616-4161-82b3-7eff6c48ea72.apng" + :class="$style.img" + > + </button> </div> + <div v-else> + <MkLoading/> + </div> +</div> </template> <script lang="ts" setup> -import {computed, onMounted, onUnmounted, ref } from 'vue'; +import { computed, onMounted, onUnmounted, ref } from 'vue'; import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue'; import * as os from '@/os.js'; import { useInterval } from '@/scripts/use-interval.js'; import * as game from '@/scripts/clicker-game.js'; import number from '@/filters/number.js'; -import {claimAchievement} from '@/scripts/achievements.js'; -import {defaultStore} from "@/store.js"; +import { claimAchievement } from '@/scripts/achievements.js'; +import { defaultStore } from '@/store.js'; const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const saveData = game.saveData; @@ -101,7 +106,6 @@ onUnmounted(() => { $color-scheme: var(--color-scheme); - .icon { width: 1.3em; vertical-align: -24%; diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 5ba019a044..cee32f54e8 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -160,65 +160,6 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])"> - <template #label>{{ i18n.ts._role._options.canEditNote }}</template> - <template #suffix> - <span v-if="role.policies.canEditNote.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.canEditNote.value ? i18n.ts.yes : i18n.ts.no }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canEditNote)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.canEditNote.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkSwitch v-model="role.policies.canEditNote.value" :disabled="role.policies.canEditNote.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - <MkRange v-model="role.policies.canEditNote.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.canScheduleNote, 'canScheduleNote'])"> - <template #label>{{ i18n.ts._role._options.canScheduleNote }}</template> - <template #suffix> - <span v-if="role.policies.canScheduleNote.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.canScheduleNote.value ? i18n.ts.yes : i18n.ts.no }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canScheduleNote)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.canScheduleNote.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkSwitch v-model="role.policies.canScheduleNote.value" :disabled="role.policies.canScheduleNote.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - <MkRange v-model="role.policies.canScheduleNote.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> - <template #label>{{ i18n.ts._role._options.mentionMax }}</template> - <template #suffix> - <span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.mentionLimit.value }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly"> - </MkInput> - <MkRange v-model="role.policies.mentionLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> <template #label>{{ i18n.ts._role._options.canInvite }}</template> <template #suffix> @@ -318,26 +259,6 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])"> - <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> - <template #suffix> - <span v-if="role.policies.canRequestCustomEmojis.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.canRequestCustomEmojis.value ? i18n.ts.yes : i18n.ts.no }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.canRequestCustomEmojis)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkSwitch v-model="role.policies.canRequestCustomEmojis.value" :disabled="role.policies.canRequestCustomEmojis.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - <MkRange v-model="role.policies.canRequestCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])"> <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> <template #suffix> @@ -630,63 +551,63 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRange> </div> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'emojiPickerProfileLimit'])"> - <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> - <template #suffix> - <span v-if="role.policies.emojiPickerProfileLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.emojiPickerProfileLimit.value }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.emojiPickerProfileLimit)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.emojiPickerProfileLimit.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkInput v-model="role.policies.emojiPickerProfileLimit.value" type="number" :min="0" :disabled="role.policies.emojiPickerProfileLimit.useDefault" > - <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> - </MkInput> - <MkRange v-model="role.policies.emojiPickerProfileLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.listPinnedLimit, 'listPinnedLimit'])"> - <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> - <template #suffix> - <span v-if="role.policies.listPinnedLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.listPinnedLimit.value }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.listPinnedLimit)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.listPinnedLimit.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkInput v-model="role.policies.listPinnedLimit.value" type="number" :min="0" :disabled="role.policies.listPinnedLimit.useDefault" > - <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> - </MkInput> - <MkRange v-model="role.policies.listPinnedLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.localTimelineAnyLimit, 'localTimelineAnyLimit'])"> - <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> - <template #suffix> - <span v-if="role.policies.localTimelineAnyLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.localTimelineAnyLimit.value}}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.localTimelineAnyLimit)"></i></span> - </template> - <div class="_gaps"> - <MkSwitch v-model="role.policies.localTimelineAnyLimit.useDefault" :readonly="readonly"> - <template #label>{{ i18n.ts._role.useBaseValue }}</template> - </MkSwitch> - <MkInput v-model="role.policies.localTimelineAnyLimit.value" type="number" :min="0" :disabled="role.policies.localTimelineAnyLimit.useDefault" > - <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> - </MkInput> - <MkRange v-model="role.policies.localTimelineAnyLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> - <template #label>{{ i18n.ts._role.priority }}</template> - </MkRange> - </div> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.emojiPickerProfileLimit, 'emojiPickerProfileLimit'])"> + <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> + <template #suffix> + <span v-if="role.policies.emojiPickerProfileLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.emojiPickerProfileLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.emojiPickerProfileLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.emojiPickerProfileLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.emojiPickerProfileLimit.value" type="number" :min="0" :disabled="role.policies.emojiPickerProfileLimit.useDefault" > + <template #label>{{ i18n.ts._role._options.emojiPickerProfileLimit }}</template> + </MkInput> + <MkRange v-model="role.policies.emojiPickerProfileLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.listPinnedLimit, 'listPinnedLimit'])"> + <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> + <template #suffix> + <span v-if="role.policies.listPinnedLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.listPinnedLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.listPinnedLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.listPinnedLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.listPinnedLimit.value" type="number" :min="0" :disabled="role.policies.listPinnedLimit.useDefault" > + <template #label>{{ i18n.ts._role._options.listPinnedLimit }}</template> + </MkInput> + <MkRange v-model="role.policies.listPinnedLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.localTimelineAnyLimit, 'localTimelineAnyLimit'])"> + <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> + <template #suffix> + <span v-if="role.policies.localTimelineAnyLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.localTimelineAnyLimit.value}}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.localTimelineAnyLimit)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.localTimelineAnyLimit.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.localTimelineAnyLimit.value" type="number" :min="0" :disabled="role.policies.localTimelineAnyLimit.useDefault" > + <template #label>{{ i18n.ts._role._options.localTimelineAnyLimit }}</template> + </MkInput> + <MkRange v-model="role.policies.localTimelineAnyLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> + </MkFolder> </div> </FormSlot> </div> diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 16f9f8b401..0116939922 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -50,7 +50,12 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> - + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix>{{ policies.mentionLimit }}</template> + <MkInput v-model="policies.mentionLimit" type="number"> + </MkInput> + </MkFolder> <MkFolder v-if="matchQuery([i18n.ts._role._options.canEditNote, 'canEditNote'])" class="_margin"> <template #label>{{ i18n.ts._role._options.canEditNote }}</template> <template #suffix>{{ policies.canEditNote ? i18n.ts.yes : i18n.ts.no }}</template> @@ -66,27 +71,27 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ i18n.ts.enable }}</template> </MkSwitch> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> - <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canSearchNotes"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canSearchNotes, 'canSearchNotes'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canSearchNotes }}</template> + <template #suffix>{{ policies.canSearchNotes ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canSearchNotes"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> - <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canUseTranslator"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.pinMax }}</template> - <template #suffix>{{ policies.pinLimit }}</template> - <MkInput v-model="policies.pinLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canUseTranslator, 'canSearchNotes'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canUseTranslator }}</template> + <template #suffix>{{ policies.canUseTranslator ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canUseTranslator"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.pinMax, 'pinLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.pinMax }}</template> + <template #suffix>{{ policies.pinLimit }}</template> + <MkInput v-model="policies.pinLimit" type="number"> + </MkInput> + </MkFolder> </MkFoldableSection> <MkFoldableSection :expanded="false"> <template #header>招待系</template> @@ -103,27 +108,21 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="policies.inviteLimit" type="number"> </MkInput> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> - <template #label>{{ i18n.ts._role._options.mentionMax }}</template> - <template #suffix>{{ policies.mentionLimit }}</template> - <MkInput v-model="policies.mentionLimit" type="number"> - </MkInput> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> - <template #label>{{ i18n.ts._role._options.canInvite }}</template> - <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canInvite"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canInvite, 'canInvite'])"> + <template #label>{{ i18n.ts._role._options.canInvite }}</template> + <template #suffix>{{ policies.canInvite ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canInvite"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> - <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> - <template #suffix>{{ policies.inviteLimit }}</template> - <MkInput v-model="policies.inviteLimit" type="number"> - </MkInput> - </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimit, 'inviteLimit'])"> + <template #label>{{ i18n.ts._role._options.inviteLimit }}</template> + <template #suffix>{{ policies.inviteLimit }}</template> + <MkInput v-model="policies.inviteLimit" type="number"> + </MkInput> + </MkFolder> <MkFolder v-if="matchQuery([i18n.ts._role._options.inviteLimitCycle, 'inviteLimitCycle'])" class="_margin"> <template #label>{{ i18n.ts._role._options.inviteLimitCycle }}</template> @@ -163,136 +162,122 @@ SPDX-License-Identifier: AGPL-3.0-only </MkInput> </MkFolder> </MkFoldableSection> - <MkFoldableSection :expanded="false"> - <template #header>カスタム絵文字系</template> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> - <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageCustomEmojis"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> - <template #suffix>{{ policies.canRequestCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canRequestCustomEmojis"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - </MkFoldableSection> - <MkFoldableSection :expanded="false"> - <template #header>ドライブ、ファイル系</template> - <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> - <template #suffix>{{ policies.driveCapacityMb }}MB</template> - <MkInput v-model="policies.driveCapacityMb" type="number"> - <template #suffix>MB</template> - </MkInput> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> - <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.alwaysMarkNsfw"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - </MkFoldableSection> - <MkFoldableSection :expanded="false"> - <template #header>アイコンデコレーション系</template> - <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> - <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canManageAvatarDecorations"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template> - <template #suffix>{{ policies.avatarDecorationLimit }}</template> - <MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0"> - </MkInput> - </MkFolder> - </MkFoldableSection> - <MkFoldableSection :expanded="false"> - <template #header>クリップ系</template> - <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.clipMax }}</template> - <template #suffix>{{ policies.clipLimit }}</template> - <MkInput v-model="policies.clipLimit" type="number"> - </MkInput> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> - <template #suffix>{{ policies.noteEachClipsLimit }}</template> - <MkInput v-model="policies.noteEachClipsLimit" type="number"> - </MkInput> - </MkFolder> - </MkFoldableSection> - <MkFoldableSection :expanded="false"> - <template #header>リスト系</template> - <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.userListMax }}</template> - <template #suffix>{{ policies.userListLimit }}</template> - <MkInput v-model="policies.userListLimit" type="number"> - </MkInput> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> - <template #suffix>{{ policies.userEachUserListsLimit }}</template> - <MkInput v-model="policies.userEachUserListsLimit" type="number"> - </MkInput> - </MkFolder> - </MkFoldableSection> - <MkFoldableSection :expanded="false"> - <template #header>その他</template> - <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.antennaMax }}</template> - <template #suffix>{{ policies.antennaLimit }}</template> - <MkInput v-model="policies.antennaLimit" type="number"> - </MkInput> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> - <template #suffix>{{ policies.wordMuteLimit }}</template> - <MkInput v-model="policies.wordMuteLimit" type="number"> - <template #suffix>chars</template> - </MkInput> - </MkFolder> - - <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.webhookMax }}</template> - <template #suffix>{{ policies.webhookLimit }}</template> - <MkInput v-model="policies.webhookLimit" type="number"> - </MkInput> - </MkFolder> - - - - - - <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])" class="_margin"> - <template #label>{{ i18n.ts._role._options.canHideAds }}</template> - <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> - <MkSwitch v-model="policies.canHideAds"> - <template #label>{{ i18n.ts.enable }}</template> - </MkSwitch> - </MkFolder> - </MkFoldableSection> - - - + <MkFoldableSection :expanded="false"> + <template #header>カスタム絵文字系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageCustomEmojis, 'canManageCustomEmojis'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canManageCustomEmojis }}</template> + <template #suffix>{{ policies.canManageCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canRequestCustomEmojis, 'canRequestCustomEmojis'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canRequestCustomEmojis }}</template> + <template #suffix>{{ policies.canRequestCustomEmojis ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canRequestCustomEmojis"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :expanded="false"> + <template #header>ドライブ、ファイル系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.driveCapacity, 'driveCapacityMb'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.driveCapacity }}</template> + <template #suffix>{{ policies.driveCapacityMb }}MB</template> + <MkInput v-model="policies.driveCapacityMb" type="number"> + <template #suffix>MB</template> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.alwaysMarkNsfw, 'alwaysMarkNsfw'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.alwaysMarkNsfw }}</template> + <template #suffix>{{ policies.alwaysMarkNsfw ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.alwaysMarkNsfw"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :expanded="false"> + <template #header>アイコンデコレーション系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canManageAvatarDecorations, 'canManageAvatarDecorations'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canManageAvatarDecorations }}</template> + <template #suffix>{{ policies.canManageAvatarDecorations ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canManageAvatarDecorations"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.avatarDecorationLimit, 'avatarDecorationLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.avatarDecorationLimit }}</template> + <template #suffix>{{ policies.avatarDecorationLimit }}</template> + <MkInput v-model="policies.avatarDecorationLimit" type="number" :min="0"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :expanded="false"> + <template #header>クリップ系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.clipMax, 'clipLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.clipMax }}</template> + <template #suffix>{{ policies.clipLimit }}</template> + <MkInput v-model="policies.clipLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.noteEachClipsMax, 'noteEachClipsLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.noteEachClipsMax }}</template> + <template #suffix>{{ policies.noteEachClipsLimit }}</template> + <MkInput v-model="policies.noteEachClipsLimit" type="number"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :expanded="false"> + <template #header>リスト系</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userListMax, 'userListLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.userListMax }}</template> + <template #suffix>{{ policies.userListLimit }}</template> + <MkInput v-model="policies.userListLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.userEachUserListsMax, 'userEachUserListsLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.userEachUserListsMax }}</template> + <template #suffix>{{ policies.userEachUserListsLimit }}</template> + <MkInput v-model="policies.userEachUserListsLimit" type="number"> + </MkInput> + </MkFolder> + </MkFoldableSection> + <MkFoldableSection :expanded="false"> + <template #header>その他</template> + <MkFolder v-if="matchQuery([i18n.ts._role._options.antennaMax, 'antennaLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.antennaMax }}</template> + <template #suffix>{{ policies.antennaLimit }}</template> + <MkInput v-model="policies.antennaLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.wordMuteMax, 'wordMuteLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.wordMuteMax }}</template> + <template #suffix>{{ policies.wordMuteLimit }}</template> + <MkInput v-model="policies.wordMuteLimit" type="number"> + <template #suffix>chars</template> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.webhookMax, 'webhookLimit'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.webhookMax }}</template> + <template #suffix>{{ policies.webhookLimit }}</template> + <MkInput v-model="policies.webhookLimit" type="number"> + </MkInput> + </MkFolder> + <MkFolder v-if="matchQuery([i18n.ts._role._options.canHideAds, 'canHideAds'])" class="_margin"> + <template #label>{{ i18n.ts._role._options.canHideAds }}</template> + <template #suffix>{{ policies.canHideAds ? i18n.ts.yes : i18n.ts.no }}</template> + <MkSwitch v-model="policies.canHideAds"> + <template #label>{{ i18n.ts.enable }}</template> + </MkSwitch> + </MkFolder> + </MkFoldableSection> <MkButton primary rounded @click="updateBaseRole">{{ i18n.ts.save }}</MkButton> </div> diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index cf8cca2c23..79011ab55c 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -5,20 +5,24 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkContainer :showHeader="widgetProps.showHeader" class="mkw-clicker"> - <template #icon><img :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" - src="https://media.discordapp.net/attachments/1153099592863334431/1162139796647448576/AfovawbDhjHYAAAAAElFTkSuQmCC.png"/></template> + <template #icon> + <img + :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" + src="https://files.prismisskey.space/misskey/59731116-9616-4161-82b3-7eff6c48ea72.apng" + /> + </template> <template #header>Clicker</template> <MkClickerGame/> </MkContainer> </template> <script lang="ts" setup> +import { computed } from 'vue'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkClickerGame from '@/components/MkClickerGame.vue'; -import {computed} from "vue"; -import {defaultStore} from "@/store.js"; +import { defaultStore } from '@/store.js'; const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); const name = 'clicker'; @@ -47,7 +51,6 @@ defineExpose<WidgetComponentExpose>({ }); </script> - <style lang="scss" module> .icon { width: 1.3em; From e9ffc0192966f529d8c6d511ebb293a3fe517140 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 6 Mar 2024 13:46:27 +0900 Subject: [PATCH 390/501] a --- .../frontend/src/components/MkEmojiPicker.vue | 180 +++++++++--------- 1 file changed, 90 insertions(+), 90 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index 7d1aed4949..cbfb3294ac 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -4,107 +4,107 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> - <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> - <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> - <div ref="emojisEl" class="emojis" tabindex="-1"> - <section class="result"> - <div v-if="searchResultCustom.length > 0" class="body"> +<div class="omfetrab" :class="['s' + size, 'w' + width, 'h' + height, { asDrawer, asWindow }]" :style="{ maxHeight: maxHeight ? maxHeight + 'px' : undefined }"> + <input ref="searchEl" :value="q" class="search" data-prevent-emoji-insert :class="{ filled: q != null && q != '' }" :placeholder="i18n.ts.search" type="search" autocapitalize="off" @input="input()" @paste.stop="paste" @keydown.stop.prevent.enter="onEnter"> + <!-- FirefoxのTabフォーカスが想定外の挙動となるためtabindex="-1"を追加 https://github.com/misskey-dev/misskey/issues/10744 --> + <div ref="emojisEl" class="emojis" tabindex="-1"> + <section class="result"> + <div v-if="searchResultCustom.length > 0" class="body"> + <button + v-for="emoji in searchResultCustom" + :key="emoji.name" + class="_button item" + :disabled="!canReact(emoji)" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji class="emoji" :name="emoji.name" :fallbackToImage="true"/> + </button> + </div> + <div v-if="searchResultUnicode.length > 0" class="body"> + <button + v-for="emoji in searchResultUnicode" + :key="emoji.name" + class="_button item" + :title="emoji.name" + tabindex="0" + @click="chosen(emoji, $event)" + > + <MkEmoji class="emoji" :emoji="emoji.char"/> + </button> + </div> + </section> + + <div v-if="tab === 'index'" class="group index"> + <section v-if="showPinned"> + <div style="display: flex; "> + <div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)"> + {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} + </div> + </div> + <div class="body"> <button - v-for="emoji in searchResultCustom" - :key="emoji.name" + v-for="emoji in pinnedEmojisDef" + :key="getKey(emoji)" + :data-emoji="getKey(emoji)" class="_button item" :disabled="!canReact(emoji)" - :title="emoji.name" tabindex="0" + @pointerenter="computeButtonTitle" @click="chosen(emoji, $event)" > - <MkCustomEmoji class="emoji" :name="emoji.name" :fallbackToImage="true"/> - </button> - </div> - <div v-if="searchResultUnicode.length > 0" class="body"> - <button - v-for="emoji in searchResultUnicode" - :key="emoji.name" - class="_button item" - :title="emoji.name" - tabindex="0" - @click="chosen(emoji, $event)" - > - <MkEmoji class="emoji" :emoji="emoji.char"/> + <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> </button> </div> </section> - <div v-if="tab === 'index'" class="group index"> - <section v-if="showPinned && (pinned && pinned.length > 0)"> - <div style="display: flex; "> - <div v-for="a in profileMax" :key="a" :title="defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`]" class="sllfktkhgl" :class="{ active: activeIndex === a || isDefaultProfile === a }" @click="pinnedProfileSelect(a)"> - {{ defaultStore.state[`pickerProfileName${a > 1 ? a - 1 : ''}`] }} - </div> - </div> - <div class="body"> - <button - v-for="emoji in pinnedEmojisDef" - :key="getKey(emoji)" - :data-emoji="getKey(emoji)" - class="_button item" - :disabled="!canReact(emoji)" - tabindex="0" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> - </button> - </div> - </section> - - <section> - <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> - <div class="body"> - <button - v-for="emoji in recentlyUsedEmojisDef" - :key="getKey(emoji)" - class="_button item" + <section> + <header class="_acrylic"><i class="ti ti-clock ti-fw"></i> {{ i18n.ts.recentUsed }}</header> + <div class="body"> + <button + v-for="emoji in recentlyUsedEmojisDef" + :key="getKey(emoji)" + class="_button item" :disabled="!canReact(emoji)" - :data-emoji="getKey(emoji)" - @pointerenter="computeButtonTitle" - @click="chosen(emoji, $event)" - > - <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> - <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> - </button> - </div> - </section> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> - <XSection - v-for="child in customEmojiFolderRoot.children" - :key="`custom:${child.value}`" - :initialShown="false" - :emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))" - :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" - :hasChildSection="child.children.length !== 0" - :customEmojiTree="child.children" - @chosen="chosen" - > - {{ child.value || i18n.ts.other }} - </XSection> - </div> - <div v-once class="group"> - <header class="_acrylic">{{ i18n.ts.emoji }}</header> - <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> - </div> + :data-emoji="getKey(emoji)" + @pointerenter="computeButtonTitle" + @click="chosen(emoji, $event)" + > + <MkCustomEmoji v-if="!emoji.hasOwnProperty('char')" class="emoji" :name="getKey(emoji)" :normal="true"/> + <MkEmoji v-else class="emoji" :emoji="getKey(emoji)" :normal="true"/> + </button> + </div> + </section> </div> - <div class="tabs"> - <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> - <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.customEmojis }}</header> + <XSection + v-for="child in customEmojiFolderRoot.children" + :key="`custom:${child.value}`" + :initialShown="false" + :emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))" + :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" + :hasChildSection="child.children.length !== 0" + :customEmojiTree="child.children" + @chosen="chosen" + > + {{ child.value || i18n.ts.other }} + </XSection> + </div> + <div v-once class="group"> + <header class="_acrylic">{{ i18n.ts.emoji }}</header> + <XSection v-for="category in categories" :key="category" :emojis="emojiCharByCategory.get(category) ?? []" :hasChildSection="false" @chosen="chosen">{{ category }}</XSection> </div> </div> + <div class="tabs"> + <button class="_button tab" :class="{ active: tab === 'index' }" @click="tab = 'index'"><i class="ti ti-asterisk ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'custom' }" @click="tab = 'custom'"><i class="ti ti-mood-happy ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'unicode' }" @click="tab = 'unicode'"><i class="ti ti-leaf ti-fw"></i></button> + <button class="_button tab" :class="{ active: tab === 'tags' }" @click="tab = 'tags'"><i class="ti ti-hash ti-fw"></i></button> + </div> +</div> </template> <script lang="ts" setup> @@ -130,8 +130,8 @@ import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-e import { signinRequired } from '@/account.js'; import { checkReactionPermissions } from '@/scripts/check-reaction-permissions.js'; import { deepClone } from '@/scripts/clone.js'; -import MkCustomEmoji from "@/components/global/MkCustomEmoji.vue"; -import MkEmoji from "@/components/global/MkEmoji.vue"; +import MkCustomEmoji from '@/components/global/MkCustomEmoji.vue'; +import MkEmoji from '@/components/global/MkEmoji.vue'; const $i = signinRequired(); const props = withDefaults(defineProps<{ showPinned?: boolean; @@ -163,7 +163,7 @@ const recentlyUsedEmojisDef = computed(() => { return recentlyUsedEmojis.value.map(getDef); }); const pinnedEmojisDef = computed(() => { - return pinned.value?.map(getDef); + return pinnedEmojis.value?.map(getDef); }); const pinned = computed(() => props.pinnedEmojis); From 51af59dfc549ea926abe41263880c029a68f1868 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 12 Mar 2024 23:18:11 +0900 Subject: [PATCH 391/501] fix: channel timeline post form --- packages/frontend/src/components/MkTimeline.vue | 2 +- packages/frontend/src/pages/timeline.vue | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index d52d34ce2d..9ef20732d8 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -29,7 +29,7 @@ import { defaultStore } from '@/store.js'; import { Paging } from '@/components/MkPagination.vue'; const props = withDefaults(defineProps<{ - src: 'home' | 'local' | 'social' | 'global' | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role'; + src: 'home' | 'local' | 'social' | 'global' | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role' | 'media'; list?: string; antenna?: string; channel?: string; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 445bb18d73..44283f0eb2 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -7,6 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> <MkSpacer :contentMax="800"> + {{ channelInfo ? channelInfo.name : '' }} <MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"> <div :key="src" ref="rootEl" v-hotkey.global="keymap"> <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> @@ -68,7 +69,7 @@ const rootEl = shallowRef<HTMLElement>(); const queue = ref(0); const srcWhenNotSignin = ref<'local' | 'global'>(isLocalTimelineAvailable ? 'local' : 'global'); -const src = computed<'home' | 'local' | 'social' | 'global' | `list:${string}`>({ +const src = computed<'home' | 'local' | 'social' | 'global' | `list:${string}`| `channel:${string}`>({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin.value), set: (x) => saveSrc(x), }); @@ -127,13 +128,14 @@ const showSocialTimeline = ref(defaultStore.state.showSocialTimeline); const channelInfo = ref(); if (src.value.split(':')[0] === 'channel') { const channelId = src.value.split(':')[1]; - channelInfo.value = misskeyApi('channels/show', { channelId }); + channelInfo.value = await misskeyApi('channels/show', { channelId }); } watch(src, async () => { queue.value = 0; + if (src.value.split(':')[0] === 'channel') { const channelId = src.value.split(':')[1]; - channelInfo.value = misskeyApi('channels/show', { channelId }); + channelInfo.value = await misskeyApi('channels/show', { channelId }); } else { channelInfo.value = null; } From 12e617123089734981cda2306a65ea0360a805b3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 12 Mar 2024 23:20:59 +0900 Subject: [PATCH 392/501] fix: channel timeline post form --- packages/frontend/src/pages/timeline.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 44283f0eb2..26dbfe9040 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -7,7 +7,6 @@ SPDX-License-Identifier: AGPL-3.0-only <MkStickyContainer> <template #header><MkPageHeader v-model:tab="src" :actions="headerActions" :tabs="$i ? headerTabs : headerTabsWhenNotLogin" :displayMyAvatar="true"/></template> <MkSpacer :contentMax="800"> - {{ channelInfo ? channelInfo.name : '' }} <MkHorizontalSwipe v-model:tab="src" :tabs="$i ? headerTabs : headerTabsWhenNotLogin"> <div :key="src" ref="rootEl" v-hotkey.global="keymap"> <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> From d3016a4d6a5408ae4013a5e22145e622ed49b29e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 18 Mar 2024 19:02:54 +0900 Subject: [PATCH 393/501] are --- locales/index.d.ts | 4 ++++ locales/ja-JP.yml | 1 + packages/frontend/src/components/MkNotes.vue | 3 ++- packages/frontend/src/components/MkTimeline.vue | 4 +++- packages/frontend/src/pages/about.vue | 8 +++++--- packages/frontend/src/pages/timeline.vue | 12 ++++++++++-- packages/frontend/src/store.ts | 1 + 7 files changed, 26 insertions(+), 7 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 5a6e9735c7..01e35cc8cd 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -4880,6 +4880,10 @@ export interface Locale extends ILocale { * リノートを表示 */ "showRenotes": string; + /** + * CWを非表示 + */ + "showCw": string; /** * 編集済み */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5c801f43a5..171129e921 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1216,6 +1216,7 @@ authentication: "認証" authenticationRequiredToContinue: "続けるには認証を行ってください" dateAndTime: "日時" showRenotes: "リノートを表示" +showCw: "CWを非表示" edited: "編集済み" notificationRecieveConfig: "通知の受信設定" mutualFollow: "相互フォロー" diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index 0856c146ba..ef5ad766b1 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only :ad="true" :class="$style.notes" > - <MkNote :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/> + <MkNote v-if="props.withCw && !note.cw || !props.withCw" :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true" /> </MkDateSeparatedList> </div> </template> @@ -43,6 +43,7 @@ const props = defineProps<{ pagination: Paging; noGap?: boolean; disableAutoLoad?: boolean; + withCw?: boolean; }>(); const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 9ef20732d8..3a65406b1e 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -10,6 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only ref="tlComponent" :pagination="paginationQuery" :noGap="!defaultStore.state.showGapBetweenNotesInTimeline" + :withCw="props.withCw" @queue="emit('queue', $event)" @status="prComponent?.setDisabled($event)" /> @@ -38,11 +39,12 @@ const props = withDefaults(defineProps<{ withRenotes?: boolean; withReplies?: boolean; onlyFiles?: boolean; - + withCw?: boolean; }>(), { withRenotes: true, withReplies: false, onlyFiles: false, + withCw: false, }); const emit = defineEmits<{ diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue index 13e9b75639..efce980034 100644 --- a/packages/frontend/src/pages/about.vue +++ b/packages/frontend/src/pages/about.vue @@ -146,9 +146,8 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { instance } from '@/instance.js'; -import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store'; -import { signinRequired } from '@/account.js'; -import { $i } from '@/account.js'; +import { bannerDark, bannerLight, defaultStore, iconDark, iconLight } from '@/store.js'; + const props = withDefaults(defineProps<{ initialTab?: string; }>(), { @@ -166,6 +165,7 @@ watch(tab, () => { let bannerUrl = ref(defaultStore.state.bannerUrl); let iconUrl = ref(defaultStore.state.iconUrl); const darkMode = computed(defaultStore.makeGetterSetter('darkMode')); + if (darkMode.value) { bannerUrl.value = bannerDark; iconUrl.value = iconDark; @@ -173,6 +173,7 @@ if (darkMode.value) { bannerUrl.value = bannerLight; iconUrl.value = iconLight; } + watch(darkMode, () => { if (darkMode.value) { bannerUrl.value = bannerDark; @@ -182,6 +183,7 @@ watch(darkMode, () => { iconUrl.value = iconLight; } }); + const initStats = () => misskeyApi('stats', { }).then((res) => { stats.value = res; diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 26dbfe9040..24f95ee35b 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -24,6 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only :withRenotes="withRenotes" :withReplies="withReplies" :onlyFiles="onlyFiles" + :withCw="withCw" :sound="true" @queue="queueUpdated" /> @@ -76,6 +77,10 @@ const withRenotes = computed<boolean>({ get: () => defaultStore.reactiveState.tl.value.filter.withRenotes, set: (x) => saveTlFilter('withRenotes', x), }); +const withCw = computed<boolean>({ + get: () => defaultStore.reactiveState.tl.value.filter.withCw, + set: (x) => saveTlFilter('withCw', x), +}); // computed内での無限ループを防ぐためのフラグ const localSocialTLFilterSwitchStore = ref<'withReplies' | 'onlyFiles' | false>('withReplies'); @@ -254,14 +259,17 @@ function closeTutorial(): void { const headerActions = computed(() => { const tmp = [ - { icon: 'ti ti-dots', + { icon: 'ti ti-dots', text: i18n.ts.options, handler: (ev) => { os.popupMenu([{ type: 'switch', text: i18n.ts.showRenotes, - ref: withRenotes, + }, { + type: 'switch', + text: i18n.ts.showCw, + ref: withCw, }, src.value === 'local' || src.value === 'social' ? { type: 'switch', text: i18n.ts.showRepliesToOthersInTimeline, diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 62d210f89b..f81e54cfd8 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -273,6 +273,7 @@ export const defaultStore = markRaw(new Storage('base', { withRenotes: true, withSensitive: true, onlyFiles: false, + withCw: true, }, }, }, From 7265431a166c4a64ff036995253ff2c6e1df9cbc Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 1 Apr 2024 22:34:20 +0900 Subject: [PATCH 394/501] are --- package.json | 2 +- packages/frontend/src/store.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3a781db83c..fa520ddfc5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.3.1-PrisMisskey.1", + "version": "2024.3.1-PrisMisskey.2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 02efc12cff..04abb762c4 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -272,7 +272,7 @@ export const defaultStore = markRaw(new Storage('base', { withRenotes: true, withSensitive: true, onlyFiles: false, - withCw: true, + withCw: false, }, }, }, From b1c751fa3a8aa9068b0016413026f934dd283abe Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 2 Apr 2024 22:39:50 +0900 Subject: [PATCH 395/501] are --- packages/backend/src/server/api/endpoints/i/update.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index db4b5cd133..924ffb16f7 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -289,6 +289,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } + console.time('update'); if (ps.mutedWords !== undefined) { checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit); validateMuteWordRegex(ps.mutedWords); @@ -432,7 +433,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const newName = updates.name === undefined ? user.name : updates.name; const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description; - const newFields = profileUpdates.fields === undefined ? profile.fields : profileUpdates.fields; + const newFields = profileUpdates.fields ?? profile.fields; if (newName != null) { const tokens = mfm.parseSimple(newName); @@ -478,7 +479,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const updatedProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - this.cacheService.userProfileCache.set(user.id, updatedProfile); + await this.cacheService.userProfileCache.set(user.id, updatedProfile); // Publish meUpdated event this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); @@ -495,7 +496,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- for (const url of urls) { this.verifyLink(url.value, user); } - + console.log('updateupdateupdateupdateupdateupdateupdateupdateupdateupdateupdateupdateupdate'); + console.timeEnd('update'); return iObj; }); } From 103c4190ebe372ee745417720426fc2679325a8a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 2 Apr 2024 22:44:12 +0900 Subject: [PATCH 396/501] are --- packages/backend/src/core/AccountUpdateService.ts | 2 ++ packages/backend/src/core/GlobalEventService.ts | 2 ++ packages/backend/src/server/api/endpoints/i/update.ts | 3 --- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 69a57b4854..a6a411873d 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -28,6 +28,7 @@ export class AccountUpdateService { @bindThis public async publishToFollowers(userId: MiUser['id']) { + console.time('AccountUpdateService.publishToFollowers'); const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); @@ -37,5 +38,6 @@ export class AccountUpdateService { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } + console.timeEnd('AccountUpdateService.publishToFollowers'); } } diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index bfd0cc8a4f..ebbefedf94 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -344,7 +344,9 @@ export class GlobalEventService { @bindThis public publishMainStream<K extends keyof MainEventTypes>(userId: MiUser['id'], type: K, value?: MainEventTypes[K]): void { + console.time('GlobalEventService.publishMainStream'); this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); + console.time('GlobalEventService.publishMainStream'); } @bindThis diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 924ffb16f7..022e9645da 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -289,7 +289,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } } - console.time('update'); if (ps.mutedWords !== undefined) { checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit); validateMuteWordRegex(ps.mutedWords); @@ -496,8 +495,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- for (const url of urls) { this.verifyLink(url.value, user); } - console.log('updateupdateupdateupdateupdateupdateupdateupdateupdateupdateupdateupdateupdate'); - console.timeEnd('update'); return iObj; }); } From 95310f07acb0dbd0b326e9568595159afe52b0e2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 2 Apr 2024 22:44:39 +0900 Subject: [PATCH 397/501] are --- packages/backend/src/core/AccountUpdateService.ts | 4 ++-- packages/backend/src/core/GlobalEventService.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index a6a411873d..3178730b45 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -28,7 +28,7 @@ export class AccountUpdateService { @bindThis public async publishToFollowers(userId: MiUser['id']) { - console.time('AccountUpdateService.publishToFollowers'); + console.time('time AccountUpdateService.publishToFollowers'); const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); @@ -38,6 +38,6 @@ export class AccountUpdateService { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } - console.timeEnd('AccountUpdateService.publishToFollowers'); + console.timeEnd('time AccountUpdateService.publishToFollowers'); } } diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index ebbefedf94..d7964bec99 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -344,9 +344,9 @@ export class GlobalEventService { @bindThis public publishMainStream<K extends keyof MainEventTypes>(userId: MiUser['id'], type: K, value?: MainEventTypes[K]): void { - console.time('GlobalEventService.publishMainStream'); + console.time('time GlobalEventService.publishMainStream'); this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); - console.time('GlobalEventService.publishMainStream'); + console.time('time GlobalEventService.publishMainStream'); } @bindThis From e56c7782db59d9c007657e7a155cf256ae439a33 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 2 Apr 2024 22:46:48 +0900 Subject: [PATCH 398/501] are --- packages/backend/src/core/GlobalEventService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index d7964bec99..f5a7b6e94d 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -346,7 +346,7 @@ export class GlobalEventService { public publishMainStream<K extends keyof MainEventTypes>(userId: MiUser['id'], type: K, value?: MainEventTypes[K]): void { console.time('time GlobalEventService.publishMainStream'); this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); - console.time('time GlobalEventService.publishMainStream'); + console.timeEnd('time GlobalEventService.publishMainStream'); } @bindThis From f6498ebbea8ce33c76776ae340ec4ef983cb6f07 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 2 Apr 2024 22:51:43 +0900 Subject: [PATCH 399/501] are --- packages/backend/src/core/GlobalEventService.ts | 2 ++ packages/backend/src/misc/cache.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index f5a7b6e94d..0f2e20fecc 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -334,7 +334,9 @@ export class GlobalEventService { @bindThis public publishInternalEvent<K extends keyof InternalEventTypes>(type: K, value?: InternalEventTypes[K]): void { + console.time('time GlobalEventService.publishInternalEvent'); this.publish('internal', type, typeof value === 'undefined' ? null : value); + console.timeEnd('time GlobalEventService.publishInternalEvent'); } @bindThis diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index bba64a06ef..d4154ca16a 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -33,6 +33,7 @@ export class RedisKVCache<T> { @bindThis public async set(key: string, value: T): Promise<void> { + console.time('time RedisKVCache.set'); this.memoryCache.set(key, value); if (this.lifetime === Infinity) { await this.redisClient.set( @@ -46,6 +47,7 @@ export class RedisKVCache<T> { 'EX', Math.round(this.lifetime / 1000), ); } + console.timeEnd('time RedisKVCache.set'); } @bindThis From e2c09e654a230413ab3397a88b26b42a62284f97 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 14 Apr 2024 21:53:57 +0900 Subject: [PATCH 400/501] are --- packages/frontend/src/components/MkEmojiPicker.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue index cbfb3294ac..58a246c938 100644 --- a/packages/frontend/src/components/MkEmojiPicker.vue +++ b/packages/frontend/src/components/MkEmojiPicker.vue @@ -85,7 +85,7 @@ SPDX-License-Identifier: AGPL-3.0-only :key="`custom:${child.value}`" :initialShown="false" :emojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).map(e => `:${e.name}:`))" - :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value && !customEmojis.some(emoji => emoji.category !== null && emoji.category.includes(e.category+'/')) || e.category === child.category+'/'+child.category && !e.category)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" + :disabledEmojis="computed(() => customEmojis.filter(e => filterCategory(e, child.value)).filter(e => !canReact(e)).map(e => `:${e.name}:`))" :hasChildSection="child.children.length !== 0" :customEmojiTree="child.children" @chosen="chosen" @@ -368,7 +368,7 @@ function canReact(emoji: Misskey.entities.EmojiSimple | UnicodeEmojiDef | string } function filterCategory(emoji: Misskey.entities.EmojiSimple, category: string): boolean { - return category === '' ? (emoji.category === 'null' || !emoji.category) : emoji.category === category; + return category === '' ? (emoji.category === 'null' || !emoji.category) : emoji.category === category && !customEmojis.value.some(e => e.category !== null && e.category.includes(emoji.category + '/')) || emoji.category === category + '/' + category && !emoji.category; } function focus() { From fa4983f8c7fddddbce2ae1b0300fed604d563aa9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 14 Apr 2024 21:58:15 +0900 Subject: [PATCH 401/501] are --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fa520ddfc5..8e4391b095 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.3.1-PrisMisskey.2", + "version": "2024.3.1-PrisMisskey.3", "codename": "nasubi", "repository": { "type": "git", From 25c0267f2e5c8d3819d553896f9e2a71248dc5ad Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 14 Apr 2024 22:00:12 +0900 Subject: [PATCH 402/501] are --- packages/frontend/src/components/MkEmojiPicker.section.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue index bc04b8d957..067a3a40fc 100644 --- a/packages/frontend/src/components/MkEmojiPicker.section.vue +++ b/packages/frontend/src/components/MkEmojiPicker.section.vue @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </section> <!-- フォルダの中にはカスタム絵文字やフォルダがある --> - <section v-else style="border-radius: 6px; padding-top: 9px;"> + <section v-else style="border-radius: 6px;"> <header class="_acrylic" @click="shown = !shown"> <i class="toggle ti-fw" :class="shown ? 'ti ti-chevron-down' : 'ti ti-chevron-up'"></i> <slot></slot> (フォルダー) </header> From 950e869f618f9e3cf4d06b333eb1cd4de2187b13 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 16 Apr 2024 06:51:24 +0900 Subject: [PATCH 403/501] are --- packages/backend/src/postgres.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 7bb7552324..cba6e07e9e 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -99,7 +99,7 @@ class MyCustomLogger implements Logger { @bindThis public logQuery(query: string, parameters?: any[]) { - sqlLogger.info(this.highlight(query).substring(0, 100)); + sqlLogger.info(this.highlight(query)); } @bindThis From d64feb78d37c7d11c8d50d14f5ee93fb521fed77 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 16 Apr 2024 07:29:51 +0900 Subject: [PATCH 404/501] =?UTF-8?q?Revert=20"fix:=20=E5=8F=A4=E3=81=84?= =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E3=81=86=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#13453)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 2c6f25b710b4f8095458fe88ddd56e6c6a41d006. --- .../backend/src/core/AccountMoveService.ts | 4 ++- packages/backend/src/core/CacheService.ts | 4 +-- .../backend/src/core/GlobalEventService.ts | 1 - .../backend/src/core/UserFollowingService.ts | 27 ++++++++++++------- packages/backend/src/misc/cache.ts | 8 ------ .../RelationshipProcessorService.ts | 2 +- .../server/api/endpoints/following/create.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 6 ++--- 8 files changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index b6b591d240..07ae576750 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -20,6 +20,7 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { CacheService } from '@/core/CacheService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -59,6 +60,7 @@ export class AccountMoveService { private instanceChart: InstanceChart, private metaService: MetaService, private relayService: RelayService, + private cacheService: CacheService, private queueService: QueueService, ) { } @@ -82,7 +84,7 @@ export class AccountMoveService { Object.assign(src, update); // Update cache - this.globalEventService.publishInternalEvent('localUserUpdated', src); + this.cacheService.uriPersonCache.set(srcUri, src); const srcPerson = await this.apRendererService.renderPerson(src); const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src)); diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index d008e7ec52..0fc47bf8ec 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -129,12 +129,10 @@ export class CacheService implements OnApplicationShutdown { switch (type) { case 'userChangeSuspendedState': case 'userChangeDeletedState': - case 'remoteUserUpdated': - case 'localUserUpdated': { + case 'remoteUserUpdated': { const user = await this.usersRepository.findOneBy({ id: body.id }); if (user == null) { this.userByIdCache.delete(body.id); - this.localUserByIdCache.delete(body.id); for (const [k, v] of this.uriPersonCache.cache.entries()) { if (v.value?.id === body.id) { this.uriPersonCache.delete(k); diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 0f2e20fecc..4044caaa5e 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -215,7 +215,6 @@ export interface InternalEventTypes { userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; }; userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; remoteUserUpdated: { id: MiUser['id']; }; - localUserUpdated: { id: MiUser['id']; }; follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index deeecdeb1f..309aa5daad 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -101,24 +101,33 @@ export class UserFollowingService implements OnModuleInit { this.queueService.deliver(followee, content, follower.inbox, false); } + /** + * ThinUserでなくともユーザーの情報が最新でない場合はこちらを使うべき + */ @bindThis - public async follow( + public async followByThinUser( _follower: ThinUser, _followee: ThinUser, + options: Parameters<typeof this.follow>[2] = {}, + ) { + const [follower, followee] = await Promise.all([ + this.usersRepository.findOneByOrFail({ id: _follower.id }), + this.usersRepository.findOneByOrFail({ id: _followee.id }), + ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; + + await this.follow(follower, followee, options); + } + + @bindThis + public async follow( + follower: MiLocalUser | MiRemoteUser, + followee: MiLocalUser | MiRemoteUser, { requestId, silent = false, withReplies }: { requestId?: string, silent?: boolean, withReplies?: boolean, } = {}, ): Promise<void> { - /** - * 必ず最新のユーザー情報を取得する - */ - const [follower, followee] = await Promise.all([ - this.usersRepository.findOneByOrFail({ id: _follower.id }), - this.usersRepository.findOneByOrFail({ id: _followee.id }), - ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; - if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) { // What? throw new Error('Remote user cannot follow remote user.'); diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index d4154ca16a..c6bb231d08 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -189,10 +189,6 @@ export class RedisSingleCache<T> { // TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする? export class MemoryKVCache<T> { - /** - * データを持つマップ - * @deprecated これを直接操作するべきではない - */ public cache: Map<string, { date: number; value: T; }>; private lifetime: number; private gcIntervalHandle: NodeJS.Timeout; @@ -207,10 +203,6 @@ export class MemoryKVCache<T> { } @bindThis - /** - * Mapにキャッシュをセットします - * @deprecated これを直接呼び出すべきではない。InternalEventなどで変更を全てのプロセス/マシンに通知するべき - */ public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts index 408b02fb38..53dbb42169 100644 --- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts +++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts @@ -35,7 +35,7 @@ export class RelationshipProcessorService { @bindThis public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> { this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`); - await this.userFollowingService.follow(job.data.from, job.data.to, { + await this.userFollowingService.followByThinUser(job.data.from, job.data.to, { requestId: job.data.requestId, silent: job.data.silent, withReplies: job.data.withReplies, diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index db320e7129..042d7f119d 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -71,7 +71,7 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - withReplies: { type: 'boolean' }, + withReplies: { type: 'boolean' } }, required: ['userId'], } as const; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 022e9645da..ca95b7c9d5 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -461,9 +461,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.hashtagService.updateUsertags(user, tags); //#endregion - if (Object.keys(updates).length > 0) { - await this.usersRepository.update(user.id, updates); - this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); + if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); + if (Object.keys(updates).includes('alsoKnownAs')) { + this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates }); } await this.userProfilesRepository.update(user.id, { From fe116be5403c3adb9de11b5a660813f5ef335664 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 16 Apr 2024 07:32:42 +0900 Subject: [PATCH 405/501] =?UTF-8?q?Revert=20"Revert=20"fix:=20=E5=8F=A4?= =?UTF-8?q?=E3=81=84=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E3=81=86=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3=20(#1345?= =?UTF-8?q?3)""?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d64feb78d37c7d11c8d50d14f5ee93fb521fed77. --- .../backend/src/core/AccountMoveService.ts | 4 +-- packages/backend/src/core/CacheService.ts | 4 ++- .../backend/src/core/GlobalEventService.ts | 1 + .../backend/src/core/UserFollowingService.ts | 29 +++++++------------ packages/backend/src/misc/cache.ts | 8 +++++ .../RelationshipProcessorService.ts | 2 +- .../server/api/endpoints/following/create.ts | 2 +- .../src/server/api/endpoints/i/update.ts | 6 ++-- 8 files changed, 28 insertions(+), 28 deletions(-) diff --git a/packages/backend/src/core/AccountMoveService.ts b/packages/backend/src/core/AccountMoveService.ts index 07ae576750..b6b591d240 100644 --- a/packages/backend/src/core/AccountMoveService.ts +++ b/packages/backend/src/core/AccountMoveService.ts @@ -20,7 +20,6 @@ import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { CacheService } from '@/core/CacheService.js'; import { ProxyAccountService } from '@/core/ProxyAccountService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { MetaService } from '@/core/MetaService.js'; @@ -60,7 +59,6 @@ export class AccountMoveService { private instanceChart: InstanceChart, private metaService: MetaService, private relayService: RelayService, - private cacheService: CacheService, private queueService: QueueService, ) { } @@ -84,7 +82,7 @@ export class AccountMoveService { Object.assign(src, update); // Update cache - this.cacheService.uriPersonCache.set(srcUri, src); + this.globalEventService.publishInternalEvent('localUserUpdated', src); const srcPerson = await this.apRendererService.renderPerson(src); const updateAct = this.apRendererService.addContext(this.apRendererService.renderUpdate(srcPerson, src)); diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index 0fc47bf8ec..d008e7ec52 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -129,10 +129,12 @@ export class CacheService implements OnApplicationShutdown { switch (type) { case 'userChangeSuspendedState': case 'userChangeDeletedState': - case 'remoteUserUpdated': { + case 'remoteUserUpdated': + case 'localUserUpdated': { const user = await this.usersRepository.findOneBy({ id: body.id }); if (user == null) { this.userByIdCache.delete(body.id); + this.localUserByIdCache.delete(body.id); for (const [k, v] of this.uriPersonCache.cache.entries()) { if (v.value?.id === body.id) { this.uriPersonCache.delete(k); diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 4044caaa5e..0f2e20fecc 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -215,6 +215,7 @@ export interface InternalEventTypes { userChangeDeletedState: { id: MiUser['id']; isDeleted: MiUser['isDeleted']; }; userTokenRegenerated: { id: MiUser['id']; oldToken: string; newToken: string; }; remoteUserUpdated: { id: MiUser['id']; }; + localUserUpdated: { id: MiUser['id']; }; follow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; unfollow: { followerId: MiUser['id']; followeeId: MiUser['id']; }; blockingCreated: { blockerId: MiUser['id']; blockeeId: MiUser['id']; }; diff --git a/packages/backend/src/core/UserFollowingService.ts b/packages/backend/src/core/UserFollowingService.ts index 309aa5daad..deeecdeb1f 100644 --- a/packages/backend/src/core/UserFollowingService.ts +++ b/packages/backend/src/core/UserFollowingService.ts @@ -101,33 +101,24 @@ export class UserFollowingService implements OnModuleInit { this.queueService.deliver(followee, content, follower.inbox, false); } - /** - * ThinUserでなくともユーザーの情報が最新でない場合はこちらを使うべき - */ - @bindThis - public async followByThinUser( - _follower: ThinUser, - _followee: ThinUser, - options: Parameters<typeof this.follow>[2] = {}, - ) { - const [follower, followee] = await Promise.all([ - this.usersRepository.findOneByOrFail({ id: _follower.id }), - this.usersRepository.findOneByOrFail({ id: _followee.id }), - ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; - - await this.follow(follower, followee, options); - } - @bindThis public async follow( - follower: MiLocalUser | MiRemoteUser, - followee: MiLocalUser | MiRemoteUser, + _follower: ThinUser, + _followee: ThinUser, { requestId, silent = false, withReplies }: { requestId?: string, silent?: boolean, withReplies?: boolean, } = {}, ): Promise<void> { + /** + * 必ず最新のユーザー情報を取得する + */ + const [follower, followee] = await Promise.all([ + this.usersRepository.findOneByOrFail({ id: _follower.id }), + this.usersRepository.findOneByOrFail({ id: _followee.id }), + ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser]; + if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isRemoteUser(followee)) { // What? throw new Error('Remote user cannot follow remote user.'); diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index c6bb231d08..d4154ca16a 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -189,6 +189,10 @@ export class RedisSingleCache<T> { // TODO: メモリ節約のためあまり参照されないキーを定期的に削除できるようにする? export class MemoryKVCache<T> { + /** + * データを持つマップ + * @deprecated これを直接操作するべきではない + */ public cache: Map<string, { date: number; value: T; }>; private lifetime: number; private gcIntervalHandle: NodeJS.Timeout; @@ -203,6 +207,10 @@ export class MemoryKVCache<T> { } @bindThis + /** + * Mapにキャッシュをセットします + * @deprecated これを直接呼び出すべきではない。InternalEventなどで変更を全てのプロセス/マシンに通知するべき + */ public set(key: string, value: T): void { this.cache.set(key, { date: Date.now(), diff --git a/packages/backend/src/queue/processors/RelationshipProcessorService.ts b/packages/backend/src/queue/processors/RelationshipProcessorService.ts index 53dbb42169..408b02fb38 100644 --- a/packages/backend/src/queue/processors/RelationshipProcessorService.ts +++ b/packages/backend/src/queue/processors/RelationshipProcessorService.ts @@ -35,7 +35,7 @@ export class RelationshipProcessorService { @bindThis public async processFollow(job: Bull.Job<RelationshipJobData>): Promise<string> { this.logger.info(`${job.data.from.id} is trying to follow ${job.data.to.id} ${job.data.withReplies ? "with replies" : "without replies"}`); - await this.userFollowingService.followByThinUser(job.data.from, job.data.to, { + await this.userFollowingService.follow(job.data.from, job.data.to, { requestId: job.data.requestId, silent: job.data.silent, withReplies: job.data.withReplies, diff --git a/packages/backend/src/server/api/endpoints/following/create.ts b/packages/backend/src/server/api/endpoints/following/create.ts index 042d7f119d..db320e7129 100644 --- a/packages/backend/src/server/api/endpoints/following/create.ts +++ b/packages/backend/src/server/api/endpoints/following/create.ts @@ -71,7 +71,7 @@ export const paramDef = { type: 'object', properties: { userId: { type: 'string', format: 'misskey:id' }, - withReplies: { type: 'boolean' } + withReplies: { type: 'boolean' }, }, required: ['userId'], } as const; diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index ca95b7c9d5..022e9645da 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -461,9 +461,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.hashtagService.updateUsertags(user, tags); //#endregion - if (Object.keys(updates).length > 0) await this.usersRepository.update(user.id, updates); - if (Object.keys(updates).includes('alsoKnownAs')) { - this.cacheService.uriPersonCache.set(this.userEntityService.genLocalUserUri(user.id), { ...user, ...updates }); + if (Object.keys(updates).length > 0) { + await this.usersRepository.update(user.id, updates); + this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } await this.userProfilesRepository.update(user.id, { From 958f94dbc7b4534db50446ab183f6169ec800901 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 16 Apr 2024 07:38:55 +0900 Subject: [PATCH 406/501] =?UTF-8?q?Revert=20"refactor(backend):=20UserEnti?= =?UTF-8?q?tyService.packMany()=E3=81=AE=E9=AB=98=E9=80=9F=E5=8C=96=20(#13?= =?UTF-8?q?550)"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 5c1d86b796d6ab878bc4f9bd2faf4207998e71cf. --- .../src/core/entities/UserEntityService.ts | 229 +------- .../server/api/endpoints/users/relation.ts | 8 +- .../test/unit/entities/UserEntityService.ts | 528 ------------------ 3 files changed, 36 insertions(+), 729 deletions(-) delete mode 100644 packages/backend/test/unit/entities/UserEntityService.ts diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index d1131cb166..df01e9835a 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -7,7 +7,6 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import _Ajv from 'ajv'; import { ModuleRef } from '@nestjs/core'; -import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { Packed } from '@/misc/json-schema.js'; @@ -15,30 +14,9 @@ import type { Promiseable } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import { USER_ACTIVE_THRESHOLD, USER_ONLINE_THRESHOLD } from '@/const.js'; import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js'; -import { - birthdaySchema, - descriptionSchema, - localUsernameSchema, - locationSchema, - nameSchema, - passwordSchema, -} from '@/models/User.js'; -import type { - BlockingsRepository, - FollowingsRepository, - FollowRequestsRepository, - MiFollowing, - MiUserNotePining, - MiUserProfile, - MutingsRepository, - NoteUnreadsRepository, - RenoteMutingsRepository, - UserMemoRepository, - UserNotePiningsRepository, - UserProfilesRepository, - UserSecurityKeysRepository, - UsersRepository, -} from '@/models/_.js'; +import { birthdaySchema, descriptionSchema, localUsernameSchema, locationSchema, nameSchema, passwordSchema } from '@/models/User.js'; +import { MiNotification } from '@/models/Notification.js'; +import type { UsersRepository, UserSecurityKeysRepository, FollowingsRepository, FollowRequestsRepository, BlockingsRepository, MutingsRepository, DriveFilesRepository, NoteUnreadsRepository, UserNotePiningsRepository, UserProfilesRepository, AnnouncementReadsRepository, AnnouncementsRepository, MiUserProfile, RenoteMutingsRepository, UserMemoRepository } from '@/models/_.js'; import { bindThis } from '@/decorators.js'; import { RoleService } from '@/core/RoleService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; @@ -68,23 +46,11 @@ function isRemoteUser(user: MiUser | { host: MiUser['host'] }): boolean { return !isLocalUser(user); } -export type UserRelation = { - id: MiUser['id'] - following: MiFollowing | null, - isFollowing: boolean - isFollowed: boolean - hasPendingFollowRequestFromYou: boolean - hasPendingFollowRequestToYou: boolean - isBlocking: boolean - isBlocked: boolean - isMuted: boolean - isRenoteMuted: boolean -} - @Injectable() export class UserEntityService implements OnModuleInit { private apPersonService: ApPersonService; private noteEntityService: NoteEntityService; + private driveFileEntityService: DriveFileEntityService; private pageEntityService: PageEntityService; private customEmojiService: CustomEmojiService; private announcementService: AnnouncementService; @@ -123,6 +89,9 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.renoteMutingsRepository) private renoteMutingsRepository: RenoteMutingsRepository, + @Inject(DI.driveFilesRepository) + private driveFilesRepository: DriveFilesRepository, + @Inject(DI.noteUnreadsRepository) private noteUnreadsRepository: NoteUnreadsRepository, @@ -132,6 +101,12 @@ export class UserEntityService implements OnModuleInit { @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + @Inject(DI.announcementReadsRepository) + private announcementReadsRepository: AnnouncementReadsRepository, + + @Inject(DI.announcementsRepository) + private announcementsRepository: AnnouncementsRepository, + @Inject(DI.userMemosRepository) private userMemosRepository: UserMemoRepository, ) { @@ -140,6 +115,7 @@ export class UserEntityService implements OnModuleInit { onModuleInit() { this.apPersonService = this.moduleRef.get('ApPersonService'); this.noteEntityService = this.moduleRef.get('NoteEntityService'); + this.driveFileEntityService = this.moduleRef.get('DriveFileEntityService'); this.pageEntityService = this.moduleRef.get('PageEntityService'); this.customEmojiService = this.moduleRef.get('CustomEmojiService'); this.announcementService = this.moduleRef.get('AnnouncementService'); @@ -162,7 +138,7 @@ export class UserEntityService implements OnModuleInit { public isRemoteUser = isRemoteUser; @bindThis - public async getRelation(me: MiUser['id'], target: MiUser['id']): Promise<UserRelation> { + public async getRelation(me: MiUser['id'], target: MiUser['id']) { const [ following, isFollowed, @@ -235,59 +211,6 @@ export class UserEntityService implements OnModuleInit { }; } - @bindThis - public async getRelations(me: MiUser['id'], targets: MiUser['id'][]): Promise<Map<MiUser['id'], UserRelation>> { - const [ - followers, - followees, - followersRequests, - followeesRequests, - blockers, - blockees, - muters, - renoteMuters, - ] = await Promise.all([ - this.followingsRepository.findBy({ followerId: me }) - .then(f => new Map(f.map(it => [it.followeeId, it]))), - this.followingsRepository.findBy({ followeeId: me }) - .then(it => it.map(it => it.followerId)), - this.followRequestsRepository.findBy({ followerId: me }) - .then(it => it.map(it => it.followeeId)), - this.followRequestsRepository.findBy({ followeeId: me }) - .then(it => it.map(it => it.followerId)), - this.blockingsRepository.findBy({ blockerId: me }) - .then(it => it.map(it => it.blockeeId)), - this.blockingsRepository.findBy({ blockeeId: me }) - .then(it => it.map(it => it.blockerId)), - this.mutingsRepository.findBy({ muterId: me }) - .then(it => it.map(it => it.muteeId)), - this.renoteMutingsRepository.findBy({ muterId: me }) - .then(it => it.map(it => it.muteeId)), - ]); - - return new Map( - targets.map(target => { - const following = followers.get(target) ?? null; - - return [ - target, - { - id: target, - following: following, - isFollowing: following != null, - isFollowed: followees.includes(target), - hasPendingFollowRequestFromYou: followersRequests.includes(target), - hasPendingFollowRequestToYou: followeesRequests.includes(target), - isBlocking: blockers.includes(target), - isBlocked: blockees.includes(target), - isMuted: muters.includes(target), - isRenoteMuted: renoteMuters.includes(target), - }, - ]; - }), - ); - } - @bindThis public async getHasUnreadAntenna(userId: MiUser['id']): Promise<boolean> { /* @@ -380,9 +303,6 @@ export class UserEntityService implements OnModuleInit { schema?: S, includeSecrets?: boolean, userProfile?: MiUserProfile, - userRelations?: Map<MiUser['id'], UserRelation>, - userMemos?: Map<MiUser['id'], string | null>, - pinNotes?: Map<MiUser['id'], MiUserNotePining[]>, }, ): Promise<Packed<S>> { const opts = Object.assign({ @@ -397,41 +317,13 @@ export class UserEntityService implements OnModuleInit { const isMe = meId === user.id; const iAmModerator = me ? await this.roleService.isModerator(me as MiUser) : false; - const profile = isDetailed - ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) - : null; - - let relation: UserRelation | null = null; - if (meId && !isMe && isDetailed) { - if (opts.userRelations) { - relation = opts.userRelations.get(user.id) ?? null; - } else { - relation = await this.getRelation(meId, user.id); - } - } - - let memo: string | null = null; - if (isDetailed && meId) { - if (opts.userMemos) { - memo = opts.userMemos.get(user.id) ?? null; - } else { - memo = await this.userMemosRepository.findOneBy({ userId: meId, targetUserId: user.id }) - .then(row => row?.memo ?? null); - } - } - - let pins: MiUserNotePining[] = []; - if (isDetailed) { - if (opts.pinNotes) { - pins = opts.pinNotes.get(user.id) ?? []; - } else { - pins = await this.userNotePiningsRepository.createQueryBuilder('pin') - .where('pin.userId = :userId', { userId: user.id }) - .innerJoinAndSelect('pin.note', 'note') - .orderBy('pin.id', 'DESC') - .getMany(); - } - } + const relation = meId && !isMe && isDetailed ? await this.getRelation(meId, user.id) : null; + const pins = isDetailed ? await this.userNotePiningsRepository.createQueryBuilder('pin') + .where('pin.userId = :userId', { userId: user.id }) + .innerJoinAndSelect('pin.note', 'note') + .orderBy('pin.id', 'DESC') + .getMany() : []; + const profile = isDetailed ? (opts.userProfile ?? await this.userProfilesRepository.findOneByOrFail({ userId: user.id })) : null; const followingCount = profile == null ? null : (profile.followingVisibility === 'public') || isMe ? user.followingCount : @@ -525,7 +417,9 @@ export class UserEntityService implements OnModuleInit { twoFactorEnabled: profile!.twoFactorEnabled, usePasswordLessLogin: profile!.usePasswordLessLogin, securityKeys: profile!.twoFactorEnabled - ? this.userSecurityKeysRepository.countBy({ userId: user.id }).then(result => result >= 1) + ? this.userSecurityKeysRepository.countBy({ + userId: user.id, + }).then(result => result >= 1) : false, roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({ id: role.id, @@ -537,7 +431,10 @@ export class UserEntityService implements OnModuleInit { isAdministrator: role.isAdministrator, displayOrder: role.displayOrder, }))), - memo: memo, + memo: meId == null ? null : await this.userMemosRepository.findOneBy({ + userId: meId, + targetUserId: user.id, + }).then(row => row?.memo ?? null), moderationNote: iAmModerator ? (profile!.moderationNote ?? '') : undefined, } : {}), @@ -618,7 +515,7 @@ export class UserEntityService implements OnModuleInit { return await awaitAll(packed); } - public async packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>( + public packMany<S extends 'MeDetailed' | 'UserDetailedNotMe' | 'UserDetailed' | 'UserLite' = 'UserLite'>( users: (MiUser['id'] | MiUser)[], me?: { id: MiUser['id'] } | null | undefined, options?: { @@ -626,70 +523,6 @@ export class UserEntityService implements OnModuleInit { includeSecrets?: boolean, }, ): Promise<Packed<S>[]> { - // -- IDのみの要素を補完して完全なエンティティ一覧を作る - - const _users = users.filter((user): user is MiUser => typeof user !== 'string'); - if (_users.length !== users.length) { - _users.push( - ...await this.usersRepository.findBy({ - id: In(users.filter((user): user is string => typeof user === 'string')), - }), - ); - } - const _userIds = _users.map(u => u.id); - - // -- 特に前提条件のない値群を取得 - - const profilesMap = await this.userProfilesRepository.findBy({ userId: In(_userIds) }) - .then(profiles => new Map(profiles.map(p => [p.userId, p]))); - - // -- 実行者の有無や指定スキーマの種別によって要否が異なる値群を取得 - - let userRelations: Map<MiUser['id'], UserRelation> = new Map(); - let userMemos: Map<MiUser['id'], string | null> = new Map(); - let pinNotes: Map<MiUser['id'], MiUserNotePining[]> = new Map(); - - if (options?.schema !== 'UserLite') { - const meId = me ? me.id : null; - if (meId) { - userMemos = await this.userMemosRepository.findBy({ userId: meId }) - .then(memos => new Map(memos.map(memo => [memo.targetUserId, memo.memo]))); - - if (_userIds.length > 0) { - userRelations = await this.getRelations(meId, _userIds); - pinNotes = await this.userNotePiningsRepository.createQueryBuilder('pin') - .where('pin.userId IN (:...userIds)', { userIds: _userIds }) - .innerJoinAndSelect('pin.note', 'note') - .getMany() - .then(pinsNotes => { - const map = new Map<MiUser['id'], MiUserNotePining[]>(); - for (const note of pinsNotes) { - const notes = map.get(note.userId) ?? []; - notes.push(note); - map.set(note.userId, notes); - } - for (const [, notes] of map.entries()) { - // pack側ではDESCで取得しているので、それに合わせて降順に並び替えておく - notes.sort((a, b) => b.id.localeCompare(a.id)); - } - return map; - }); - } - } - } - - return Promise.all( - _users.map(u => this.pack( - u, - me, - { - ...options, - userProfile: profilesMap.get(u.id), - userRelations: userRelations, - userMemos: userMemos, - pinNotes: pinNotes, - }, - )), - ); + return Promise.all(users.map(u => this.pack(u, me, options))); } } diff --git a/packages/backend/src/server/api/endpoints/users/relation.ts b/packages/backend/src/server/api/endpoints/users/relation.ts index 1d75437b81..6a5b2262fa 100644 --- a/packages/backend/src/server/api/endpoints/users/relation.ts +++ b/packages/backend/src/server/api/endpoints/users/relation.ts @@ -132,9 +132,11 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { - return Array.isArray(ps.userId) - ? await this.userEntityService.getRelations(me.id, ps.userId).then(it => [...it.values()]) - : await this.userEntityService.getRelation(me.id, ps.userId).then(it => [it]); + const ids = Array.isArray(ps.userId) ? ps.userId : [ps.userId]; + + const relations = await Promise.all(ids.map(id => this.userEntityService.getRelation(me.id, id))); + + return Array.isArray(ps.userId) ? relations : relations[0]; }); } } diff --git a/packages/backend/test/unit/entities/UserEntityService.ts b/packages/backend/test/unit/entities/UserEntityService.ts deleted file mode 100644 index ee16d421c4..0000000000 --- a/packages/backend/test/unit/entities/UserEntityService.ts +++ /dev/null @@ -1,528 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { Test, TestingModule } from '@nestjs/testing'; -import { UserEntityService } from '@/core/entities/UserEntityService.js'; -import { GlobalModule } from '@/GlobalModule.js'; -import { CoreModule } from '@/core/CoreModule.js'; -import type { MiUser } from '@/models/User.js'; -import { secureRndstr } from '@/misc/secure-rndstr.js'; -import { genAidx } from '@/misc/id/aidx.js'; -import { - BlockingsRepository, - FollowingsRepository, FollowRequestsRepository, - MiUserProfile, MutingsRepository, RenoteMutingsRepository, - UserMemoRepository, - UserProfilesRepository, - UsersRepository, -} from '@/models/_.js'; -import { DI } from '@/di-symbols.js'; -import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; -import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; -import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; -import { PageEntityService } from '@/core/entities/PageEntityService.js'; -import { CustomEmojiService } from '@/core/CustomEmojiService.js'; -import { AnnouncementService } from '@/core/AnnouncementService.js'; -import { RoleService } from '@/core/RoleService.js'; -import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; -import { IdService } from '@/core/IdService.js'; -import { UtilityService } from '@/core/UtilityService.js'; -import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; -import { ModerationLogService } from '@/core/ModerationLogService.js'; -import { GlobalEventService } from '@/core/GlobalEventService.js'; -import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; -import { MetaService } from '@/core/MetaService.js'; -import { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; -import { CacheService } from '@/core/CacheService.js'; -import { ApResolverService } from '@/core/activitypub/ApResolverService.js'; -import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js'; -import { ApImageService } from '@/core/activitypub/models/ApImageService.js'; -import { ApMfmService } from '@/core/activitypub/ApMfmService.js'; -import { MfmService } from '@/core/MfmService.js'; -import { HashtagService } from '@/core/HashtagService.js'; -import UsersChart from '@/core/chart/charts/users.js'; -import { ChartLoggerService } from '@/core/chart/ChartLoggerService.js'; -import InstanceChart from '@/core/chart/charts/instance.js'; -import { ApLoggerService } from '@/core/activitypub/ApLoggerService.js'; -import { AccountMoveService } from '@/core/AccountMoveService.js'; -import { ReactionService } from '@/core/ReactionService.js'; -import { NotificationService } from '@/core/NotificationService.js'; - -process.env.NODE_ENV = 'test'; - -describe('UserEntityService', () => { - describe('pack/packMany', () => { - let app: TestingModule; - let service: UserEntityService; - let usersRepository: UsersRepository; - let userProfileRepository: UserProfilesRepository; - let userMemosRepository: UserMemoRepository; - let followingRepository: FollowingsRepository; - let followingRequestRepository: FollowRequestsRepository; - let blockingRepository: BlockingsRepository; - let mutingRepository: MutingsRepository; - let renoteMutingsRepository: RenoteMutingsRepository; - - async function createUser(userData: Partial<MiUser> = {}, profileData: Partial<MiUserProfile> = {}) { - const un = secureRndstr(16); - const user = await usersRepository - .insert({ - ...userData, - id: genAidx(Date.now()), - username: un, - usernameLower: un, - }) - .then(x => usersRepository.findOneByOrFail(x.identifiers[0])); - - await userProfileRepository.insert({ - ...profileData, - userId: user.id, - }); - - return user; - } - - async function memo(writer: MiUser, target: MiUser, memo: string) { - await userMemosRepository.insert({ - id: genAidx(Date.now()), - userId: writer.id, - targetUserId: target.id, - memo, - }); - } - - async function follow(follower: MiUser, followee: MiUser) { - await followingRepository.insert({ - id: genAidx(Date.now()), - followerId: follower.id, - followeeId: followee.id, - }); - } - - async function requestFollow(requester: MiUser, requestee: MiUser) { - await followingRequestRepository.insert({ - id: genAidx(Date.now()), - followerId: requester.id, - followeeId: requestee.id, - }); - } - - async function block(blocker: MiUser, blockee: MiUser) { - await blockingRepository.insert({ - id: genAidx(Date.now()), - blockerId: blocker.id, - blockeeId: blockee.id, - }); - } - - async function mute(mutant: MiUser, mutee: MiUser) { - await mutingRepository.insert({ - id: genAidx(Date.now()), - muterId: mutant.id, - muteeId: mutee.id, - }); - } - - async function muteRenote(mutant: MiUser, mutee: MiUser) { - await renoteMutingsRepository.insert({ - id: genAidx(Date.now()), - muterId: mutant.id, - muteeId: mutee.id, - }); - } - - function randomIntRange(weight = 10) { - return [...Array(Math.floor(Math.random() * weight))].map((it, idx) => idx); - } - - beforeAll(async () => { - const services = [ - UserEntityService, - ApPersonService, - NoteEntityService, - PageEntityService, - CustomEmojiService, - AnnouncementService, - RoleService, - FederatedInstanceService, - IdService, - AvatarDecorationService, - UtilityService, - EmojiEntityService, - ModerationLogService, - GlobalEventService, - DriveFileEntityService, - MetaService, - FetchInstanceMetadataService, - CacheService, - ApResolverService, - ApNoteService, - ApImageService, - ApMfmService, - MfmService, - HashtagService, - UsersChart, - ChartLoggerService, - InstanceChart, - ApLoggerService, - AccountMoveService, - ReactionService, - NotificationService, - ]; - - app = await Test.createTestingModule({ - imports: [GlobalModule, CoreModule], - providers: [ - ...services, - ...services.map(x => ({ provide: x.name, useExisting: x })), - ], - }).compile(); - await app.init(); - app.enableShutdownHooks(); - - service = app.get<UserEntityService>(UserEntityService); - usersRepository = app.get<UsersRepository>(DI.usersRepository); - userProfileRepository = app.get<UserProfilesRepository>(DI.userProfilesRepository); - userMemosRepository = app.get<UserMemoRepository>(DI.userMemosRepository); - followingRepository = app.get<FollowingsRepository>(DI.followingsRepository); - followingRequestRepository = app.get<FollowRequestsRepository>(DI.followRequestsRepository); - blockingRepository = app.get<BlockingsRepository>(DI.blockingsRepository); - mutingRepository = app.get<MutingsRepository>(DI.mutingsRepository); - renoteMutingsRepository = app.get<RenoteMutingsRepository>(DI.renoteMutingsRepository); - }); - - afterAll(async () => { - await app.close(); - }); - - test('UserLite', async() => { - const me = await createUser(); - const who = await createUser(); - - await memo(me, who, 'memo'); - - const actual = await service.pack(who, me, { schema: 'UserLite' }) as any; - // no detail - expect(actual.memo).toBeUndefined(); - // no detail and me - expect(actual.birthday).toBeUndefined(); - // no detail and me - expect(actual.achievements).toBeUndefined(); - }); - - test('UserDetailedNotMe', async() => { - const me = await createUser(); - const who = await createUser({}, { birthday: '2000-01-01' }); - - await memo(me, who, 'memo'); - - const actual = await service.pack(who, me, { schema: 'UserDetailedNotMe' }) as any; - // is detail - expect(actual.memo).toBe('memo'); - // is detail - expect(actual.birthday).toBe('2000-01-01'); - // no detail and me - expect(actual.achievements).toBeUndefined(); - }); - - test('MeDetailed', async() => { - const achievements = [{ name: 'achievement', unlockedAt: new Date().getTime() }]; - const me = await createUser({}, { - birthday: '2000-01-01', - achievements: achievements, - }); - await memo(me, me, 'memo'); - - const actual = await service.pack(me, me, { schema: 'MeDetailed' }) as any; - // is detail - expect(actual.memo).toBe('memo'); - // is detail - expect(actual.birthday).toBe('2000-01-01'); - // is detail and me - expect(actual.achievements).toEqual(achievements); - }); - - describe('packManyによるpreloadがある時、preloadが無い時とpackの結果が同じになるか見たい', () => { - test('no-preload', async() => { - const me = await createUser(); - // meがフォローしてる人たち - const followeeMe = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of followeeMe) { - await follow(me, who); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(true); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - - // meをフォローしてる人たち - const followerMe = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of followerMe) { - await follow(who, me); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(true); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - - // meがフォローリクエストを送った人たち - const requestsFromYou = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of requestsFromYou) { - await requestFollow(me, who); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(true); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - - // meにフォローリクエストを送った人たち - const requestsToYou = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of requestsToYou) { - await requestFollow(who, me); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(true); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - - // meがブロックしてる人たち - const blockingYou = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of blockingYou) { - await block(me, who); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(true); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - - // meをブロックしてる人たち - const blockingMe = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of blockingMe) { - await block(who, me); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(true); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - - // meがミュートしてる人たち - const muters = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of muters) { - await mute(me, who); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(true); - expect(actual.isRenoteMuted).toBe(false); - } - - // meがリノートミュートしてる人たち - const renoteMuters = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of renoteMuters) { - await muteRenote(me, who); - const actual = await service.pack(who, me, { schema: 'UserDetailed' }) as any; - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(true); - } - }); - - test('preload', async() => { - const me = await createUser(); - - { - // meがフォローしてる人たち - const followeeMe = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of followeeMe) { - await follow(me, who); - } - const actualList = await service.packMany(followeeMe, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(true); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meをフォローしてる人たち - const followerMe = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of followerMe) { - await follow(who, me); - } - const actualList = await service.packMany(followerMe, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(true); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meがフォローリクエストを送った人たち - const requestsFromYou = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of requestsFromYou) { - await requestFollow(me, who); - } - const actualList = await service.packMany(requestsFromYou, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(true); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meにフォローリクエストを送った人たち - const requestsToYou = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of requestsToYou) { - await requestFollow(who, me); - } - const actualList = await service.packMany(requestsToYou, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(true); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meがブロックしてる人たち - const blockingYou = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of blockingYou) { - await block(me, who); - } - const actualList = await service.packMany(blockingYou, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(true); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meをブロックしてる人たち - const blockingMe = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of blockingMe) { - await block(who, me); - } - const actualList = await service.packMany(blockingMe, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(true); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meがミュートしてる人たち - const muters = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of muters) { - await mute(me, who); - } - const actualList = await service.packMany(muters, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(true); - expect(actual.isRenoteMuted).toBe(false); - } - } - - { - // meがリノートミュートしてる人たち - const renoteMuters = await Promise.all(randomIntRange().map(() => createUser())); - for (const who of renoteMuters) { - await muteRenote(me, who); - } - const actualList = await service.packMany(renoteMuters, me, { schema: 'UserDetailed' }) as any; - for (const actual of actualList) { - expect(actual.isFollowing).toBe(false); - expect(actual.isFollowed).toBe(false); - expect(actual.hasPendingFollowRequestFromYou).toBe(false); - expect(actual.hasPendingFollowRequestToYou).toBe(false); - expect(actual.isBlocking).toBe(false); - expect(actual.isBlocked).toBe(false); - expect(actual.isMuted).toBe(false); - expect(actual.isRenoteMuted).toBe(true); - } - } - }); - }); - }); -}); From 8bbe7033835dfd149e9dcdfb62f9bcf7a7af777c Mon Sep 17 00:00:00 2001 From: caipira113 <caipira@libnare.net> Date: Fri, 27 Oct 2023 21:53:55 +0900 Subject: [PATCH 407/501] enhance(backend): inbox queue error in update note (cherry picked from commit 1312c4f944b235d77275a51047275f84e4904de8) --- .../backend/src/core/activitypub/ApInboxService.ts | 6 +++--- .../src/core/activitypub/models/ApNoteService.ts | 11 ++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts index 0210ff09b9..2c062223f8 100644 --- a/packages/backend/src/core/activitypub/ApInboxService.ts +++ b/packages/backend/src/core/activitypub/ApInboxService.ts @@ -785,9 +785,9 @@ export class ApInboxService { const unlock = await this.appLockService.getApLock(uri); try { - //const exist = await this.apNoteService.fetchNote(note); - //if (exist) return 'skip: note exists'; - await this.apNoteService.updateNote(note, resolver, silent); + const target = await this.notesRepository.findOneBy({uri: uri}); + if (!target) return `skip: target note not located: ${uri}`; + await this.apNoteService.updateNote(note, target, resolver, silent); return 'ok'; } catch (err) { if (err instanceof StatusError && err.isClientError) { diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index e98a15effb..0bcc353232 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -335,7 +335,7 @@ export class ApNoteService { } @bindThis - public async updateNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<MiNote | null> { + public async updateNote(value: string | IObject, target: MiNote, resolver?: Resolver, silent = false): Promise<MiNote | null> { if (resolver == null) resolver = this.apResolverService.createResolver(); const object = await resolver.resolve(value); @@ -365,13 +365,6 @@ export class ApNoteService { throw new Error('actor has been suspended'); } - const b_note = await this.notesRepository.findOneBy({ - uri: entryUri, - }).then(x => { - if (x == null) throw new Error('note not found'); - return x; - }); - const limit = promiseLimit<MiDriveFile>(2); const files = (await Promise.all(toArray(note.attachment).map(attach => ( limit(() => this.apImageService.resolveImage(actor, { @@ -413,7 +406,7 @@ export class ApNoteService { apHashtags, apEmojis, poll, - }, b_note, silent); + }, target, silent); } catch (err: any) { this.logger.warn(`note update failed: ${err}`); return err; From 77a91384e67c1e0d479ee1d6e5d573173b5c32e8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 22 Apr 2024 21:01:20 +0900 Subject: [PATCH 408/501] aaa --- packages/backend/src/server/api/endpoints/i/update.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 022e9645da..37a36cd7c1 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -460,10 +460,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // ハッシュタグ更新 this.hashtagService.updateUsertags(user, tags); //#endregion - if (Object.keys(updates).length > 0) { await this.usersRepository.update(user.id, updates); - this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); + //this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } await this.userProfilesRepository.update(user.id, { @@ -481,7 +480,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.cacheService.userProfileCache.set(user.id, updatedProfile); // Publish meUpdated event - this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); + //this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { @@ -489,7 +488,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // フォロワーにUpdateを配信 - this.accountUpdateService.publishToFollowers(user.id); + //this.accountUpdateService.publishToFollowers(user.id); const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://')); for (const url of urls) { From a9e6c8bb9c1cd4d22969d09a735c3a96499573bf Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 22 Apr 2024 21:09:11 +0900 Subject: [PATCH 409/501] aaa --- packages/backend/src/core/GlobalEventService.ts | 4 ---- packages/backend/src/server/api/endpoints/i/update.ts | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 0f2e20fecc..bfd0cc8a4f 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -334,9 +334,7 @@ export class GlobalEventService { @bindThis public publishInternalEvent<K extends keyof InternalEventTypes>(type: K, value?: InternalEventTypes[K]): void { - console.time('time GlobalEventService.publishInternalEvent'); this.publish('internal', type, typeof value === 'undefined' ? null : value); - console.timeEnd('time GlobalEventService.publishInternalEvent'); } @bindThis @@ -346,9 +344,7 @@ export class GlobalEventService { @bindThis public publishMainStream<K extends keyof MainEventTypes>(userId: MiUser['id'], type: K, value?: MainEventTypes[K]): void { - console.time('time GlobalEventService.publishMainStream'); this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); - console.timeEnd('time GlobalEventService.publishMainStream'); } @bindThis diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 37a36cd7c1..18bf82454b 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -462,7 +462,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- //#endregion if (Object.keys(updates).length > 0) { await this.usersRepository.update(user.id, updates); - //this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); + this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } await this.userProfilesRepository.update(user.id, { @@ -488,7 +488,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } // フォロワーにUpdateを配信 - //this.accountUpdateService.publishToFollowers(user.id); + this.accountUpdateService.publishToFollowers(user.id); const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://')); for (const url of urls) { From f51a4b51b391e5b1c0af461bdfe759e5aa055fba Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 22 Apr 2024 21:13:56 +0900 Subject: [PATCH 410/501] aaa --- packages/backend/src/server/api/endpoints/i/update.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 18bf82454b..1d02d71685 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -462,7 +462,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- //#endregion if (Object.keys(updates).length > 0) { await this.usersRepository.update(user.id, updates); - this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); + //this.globalEventService.publishInternalEvent('localUserUpdated', { id: user.id }); } await this.userProfilesRepository.update(user.id, { From 7d61f8a3d9ba901de71aac89764596ad37d1a2b9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 03:41:45 +0900 Subject: [PATCH 411/501] aaa --- packages/backend/src/core/NoteCreateService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 7c09988837..9a7ca13724 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -308,7 +308,7 @@ export class NoteCreateService implements OnApplicationShutdown { body: JSON.stringify(data_disc), }); } - throw new NoteCreateService.ContainsProhibitedWordsError(); + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); } const inSilencedInstance = this.utilityService.isSilencedHost(meta.silencedHosts, user.host); From 5ec474a701ab5cef0f145da0c3d8fdc1407c7f47 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 03:45:16 +0900 Subject: [PATCH 412/501] aaa --- .../backend/src/core/NoteCreateService.ts | 35 +------------------ 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 9a7ca13724..5924e01057 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -385,40 +385,7 @@ export class NoteCreateService implements OnApplicationShutdown { let tags = data.apHashtags; let emojis = data.apEmojis; let mentionedUsers = data.apMentions; - let tmp:string | null = null; - if (mentionedUsers !== null && mentionedUsers !== undefined && mentionedUsers.length > 0) { - await Promise.all(mentionedUsers.map(async (u) => { - if (data.text != null) { - const regex = new RegExp(`@${u.username}@${u.host ?? this.config.host}`, 'g'); - tmp = data.text.replace(regex, ''); - } - })); - if ( tmp !== null && tmp.trim() === '') { - const { DiscordWebhookUrlWordBlock } = (await this.metaService.fetch()); - if (DiscordWebhookUrlWordBlock) { - await fetch(DiscordWebhookUrlWordBlock, { - 'method': 'post', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ 'username': 'ノートブロックお知らせ', - 'content': - 'ユーザー名 :' + user.username + '\n' + - 'url : ' + user.host + '\n' + - 'contents : ' + data.text + '\n' + - '引っかかった原因 メンションしかない', - - 'allowed_mentions': { - 'parse': [], - }, - }), - }); - } - console.log('メンションしかない'); - throw new NoteCreateService.ContainsProhibitedWordsError(); - } - } // Parse MFM if needed if (!tags || !emojis || !mentionedUsers) { const tokens = (data.text ? mfm.parse(data.text)! : []); @@ -441,7 +408,7 @@ export class NoteCreateService implements OnApplicationShutdown { if (user.host !== null && willCauseNotification) { const userEntity = await this.usersRepository.findOneBy({ id: user.id }); if ((userEntity?.followersCount ?? 0) === 0) { - throw new NoteCreateService.ContainsProhibitedWordsError(); + throw new IdentifiableError('689ee33f-f97c-479a-ac49-1b9f8140af99', 'Note contains prohibited words'); } } From 814eea2513d75e96be1b168d3e226cfdbfd93a28 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 03:49:23 +0900 Subject: [PATCH 413/501] aaa --- packages/frontend/src/pages/admin/roles.editor.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index cee32f54e8..7e9a7b9416 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -99,7 +99,12 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRange> </div> </MkFolder> - + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <template #suffix>{{ role.policies.mentionLimit }}</template> + <MkInput v-model="role.policies.mentionLimit" type="number"> + </MkInput> + </MkFolder> <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> <template #suffix> From aa633a12d8b222c4773bb5e3ed079cae12bc662e Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 03:51:52 +0900 Subject: [PATCH 414/501] aaa --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8e4391b095..e0fab69a1e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.3.1-PrisMisskey.3", + "version": "2024.3.1-PrisMisskey.4", "codename": "nasubi", "repository": { "type": "git", From 6c2b755b9997a8b9fbaa1e4935c0b5b529bafdd2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 03:57:01 +0900 Subject: [PATCH 415/501] aaa --- packages/frontend/src/pages/admin/roles.editor.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 7e9a7b9416..d76c04f7fd 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -102,7 +102,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> <template #label>{{ i18n.ts._role._options.mentionMax }}</template> <template #suffix>{{ role.policies.mentionLimit }}</template> - <MkInput v-model="role.policies.mentionLimit" type="number"> + <MkInput v-model="role.policies.mentionLimit.value" type="number"> </MkInput> </MkFolder> <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> From 2c61ec4d2831d82f9222ddeab417b5fb1a24b22b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 04:02:03 +0900 Subject: [PATCH 416/501] aaa --- .../frontend/src/pages/admin/roles.editor.vue | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index d76c04f7fd..7378e874fd 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -101,9 +101,21 @@ SPDX-License-Identifier: AGPL-3.0-only </MkFolder> <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> <template #label>{{ i18n.ts._role._options.mentionMax }}</template> - <template #suffix>{{ role.policies.mentionLimit }}</template> - <MkInput v-model="role.policies.mentionLimit.value" type="number"> - </MkInput> + <template #suffix> + <span v-if="role.policies.mentionMax.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.mentionMax.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionMax)"></i></span> + </template> + <div class="_gaps"> + <MkSwitch v-model="role.policies.mentionMax.useDefault" :readonly="readonly"> + <template #label>{{ i18n.ts._role.useBaseValue }}</template> + </MkSwitch> + <MkInput v-model="role.policies.mentionMax.value" :disabled="role.policies.mentionMax.useDefault" type="number" :readonly="readonly"> + </MkInput> + <MkRange v-model="role.policies.mentionMax.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <template #label>{{ i18n.ts._role.priority }}</template> + </MkRange> + </div> </MkFolder> <MkFolder v-if="matchQuery([i18n.ts._role._options.gtlAvailable, 'gtlAvailable'])"> <template #label>{{ i18n.ts._role._options.gtlAvailable }}</template> From a7ae2160ac0246c8b82f47b732dd43e67ea0a40a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 04:11:27 +0900 Subject: [PATCH 417/501] aaa --- packages/frontend/src/pages/admin/roles.editor.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 7378e874fd..bfcce4d27a 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -99,18 +99,19 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRange> </div> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionMax'])"> <template #label>{{ i18n.ts._role._options.mentionMax }}</template> <template #suffix> <span v-if="role.policies.mentionMax.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.mentionMax.value }}</span> + <span v-else>{{ role.policies.listPinnedLimit.value }}</span> <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionMax)"></i></span> </template> <div class="_gaps"> <MkSwitch v-model="role.policies.mentionMax.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="role.policies.mentionMax.value" :disabled="role.policies.mentionMax.useDefault" type="number" :readonly="readonly"> + <MkInput v-model="role.policies.mentionMax.value" type="number" :min="0" :disabled="role.policies.mentionMax.useDefault" > + <template #label>{{ i18n.ts._role._options.mentionMax }}</template> </MkInput> <MkRange v-model="role.policies.mentionMax.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> From ecface102a1021729a21b681806dfa34ec5fbb66 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 24 Apr 2024 04:18:58 +0900 Subject: [PATCH 418/501] aaa --- .../frontend/src/pages/admin/roles.editor.vue | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index bfcce4d27a..fd4ed39f58 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -99,21 +99,20 @@ SPDX-License-Identifier: AGPL-3.0-only </MkRange> </div> </MkFolder> - <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionMax'])"> + <MkFolder v-if="matchQuery([i18n.ts._role._options.mentionMax, 'mentionLimit'])"> <template #label>{{ i18n.ts._role._options.mentionMax }}</template> <template #suffix> - <span v-if="role.policies.mentionMax.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> - <span v-else>{{ role.policies.listPinnedLimit.value }}</span> - <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionMax)"></i></span> + <span v-if="role.policies.mentionLimit.useDefault" :class="$style.useDefaultLabel">{{ i18n.ts._role.useBaseValue }}</span> + <span v-else>{{ role.policies.mentionLimit.value }}</span> + <span :class="$style.priorityIndicator"><i :class="getPriorityIcon(role.policies.mentionLimit)"></i></span> </template> <div class="_gaps"> - <MkSwitch v-model="role.policies.mentionMax.useDefault" :readonly="readonly"> + <MkSwitch v-model="role.policies.mentionLimit.useDefault" :readonly="readonly"> <template #label>{{ i18n.ts._role.useBaseValue }}</template> </MkSwitch> - <MkInput v-model="role.policies.mentionMax.value" type="number" :min="0" :disabled="role.policies.mentionMax.useDefault" > - <template #label>{{ i18n.ts._role._options.mentionMax }}</template> + <MkInput v-model="role.policies.mentionLimit.value" :disabled="role.policies.mentionLimit.useDefault" type="number" :readonly="readonly"> </MkInput> - <MkRange v-model="role.policies.mentionMax.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> + <MkRange v-model="role.policies.mentionLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''"> <template #label>{{ i18n.ts._role.priority }}</template> </MkRange> </div> From 206d7c9a18e31a041751cff7f4b834557b7e33d1 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 4 May 2024 23:21:05 +0900 Subject: [PATCH 419/501] outsideprismisskey --- .../src/core/entities/MetaEntityService.ts | 4 +++ packages/backend/src/models/Meta.ts | 24 ++++++++++++++++ .../src/server/api/endpoints/admin/meta.ts | 8 ++++++ .../server/api/endpoints/admin/update-meta.ts | 17 ++++++++++- .../frontend/src/pages/admin/branding.vue | 28 +++++++++++++++++++ packages/frontend/src/store.ts | 8 ++---- 6 files changed, 83 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 9d054ab6a1..50669e1a3c 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -69,6 +69,10 @@ export class MetaEntityService { privacyPolicyUrl: instance.privacyPolicyUrl, disableRegistration: instance.disableRegistration, emailRequiredForSignup: instance.emailRequiredForSignup, + bannerDark: instance.bannerDark, + bannerLight: instance.bannerLight, + iconDark: instance.iconDark, + iconLight: instance.iconLight, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, enableMcaptcha: instance.enableMcaptcha, diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 72ef88b4d1..1b3992abe7 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -433,6 +433,30 @@ export class MiMeta { }) public objectStorageBaseUrl: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public bannerDark: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public bannerLight: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public iconDark: string | null; + + @Column('varchar', { + length: 1024, + nullable: true, + }) + public iconLight: string | null; + @Column('varchar', { length: 1024, nullable: true, diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts index ce2f9d26dc..102eae2898 100644 --- a/packages/backend/src/server/api/endpoints/admin/meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/meta.ts @@ -496,6 +496,10 @@ export const meta = { type: 'string', optional: false, nullable: true, }, + iconLight: { type: 'string', nullable: true }, + iconDark: { type: 'string', nullable: true }, + bannerLight: { type: 'string', nullable: true }, + bannerDark: { type: 'string', nullable: true }, }, }, } as const; @@ -636,6 +640,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- urlPreviewRequireContentLength: instance.urlPreviewRequireContentLength, urlPreviewUserAgent: instance.urlPreviewUserAgent, urlPreviewSummaryProxyUrl: instance.urlPreviewSummaryProxyUrl, + iconLight: instance.iconLight, + iconDark: instance.iconDark, + bannerLight: instance.bannerLight, + bannerDark: instance.bannerDark, }; }); } diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index 98a474bb37..dad4ac7287 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -172,6 +172,10 @@ export const paramDef = { proxyCheckioApiKey: { type: 'string', nullable: true, }, + iconLight: { type: 'string', nullable: true }, + iconDark: { type: 'string', nullable: true }, + bannerLight: { type: 'string', nullable: true }, + bannerDark: { type: 'string', nullable: true }, }, required: [], } as const; @@ -650,7 +654,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const value = ((ps.urlPreviewSummaryProxyUrl ?? ps.summalyProxy) ?? '').trim(); set.urlPreviewSummaryProxyUrl = value === '' ? null : value; } - + if (ps.bannerDark !== undefined) { + set.bannerDark = ps.bannerDark; + } + if (ps.bannerLight !== undefined) { + set.bannerLight = ps.bannerLight; + } + if (ps.iconDark !== undefined) { + set.iconDark = ps.iconDark; + } + if (ps.iconLight !== undefined) { + set.iconLight = ps.iconLight; + } const before = await this.metaService.fetch(true); await this.metaService.update(set); diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index fe1b7c561d..c785df1b7a 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -14,6 +14,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ti ti-link"></i></template> <template #label>{{ i18n.ts._serverSettings.iconUrl }}</template> </MkInput> + <MkInput v-model="iconDark" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }} (Dark)</template> + </MkInput> + <MkInput v-model="iconLight" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts._serverSettings.iconUrl }} (Light)</template> + </MkInput> <MkInput v-model="app192IconUrl" type="url"> <template #prefix><i class="ti ti-link"></i></template> @@ -41,6 +49,14 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ti ti-link"></i></template> <template #label>{{ i18n.ts.bannerUrl }}</template> </MkInput> + <MkInput v-model="bannerDark" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.bannerUrl }} (Dark)</template> + </MkInput> + <MkInput v-model="bannerLight" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>{{ i18n.ts.bannerUrl }} (Light)</template> + </MkInput> <MkInput v-model="backgroundImageUrl" type="url"> <template #prefix><i class="ti ti-link"></i></template> @@ -132,6 +148,10 @@ const infoImageUrl = ref<string | null>(null); const notFoundImageUrl = ref<string | null>(null); const repositoryUrl = ref<string | null>(null); const feedbackUrl = ref<string | null>(null); +const iconDark = ref<string | null>(null); +const iconLight = ref<string | null>(null); +const bannerDark = ref<string | null>(null); +const bannerLight = ref<string | null>(null); const manifestJsonOverride = ref<string>('{}'); async function init() { @@ -150,6 +170,10 @@ async function init() { repositoryUrl.value = meta.repositoryUrl; feedbackUrl.value = meta.feedbackUrl; manifestJsonOverride.value = meta.manifestJsonOverride === '' ? '{}' : JSON.stringify(JSON.parse(meta.manifestJsonOverride), null, '\t'); + iconDark.value = meta.iconDark; + iconLight.value = meta.iconLight; + bannerDark.value = meta.bannerDark; + bannerLight.value = meta.bannerLight; } function save() { @@ -168,6 +192,10 @@ function save() { repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value, feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), + iconDark: iconDark.value === '' ? null : iconDark.value, + iconLight: iconLight.value === '' ? null : iconLight.value, + bannerDark: bannerDark.value === '' ? null : bannerDark.value, + bannerLight: bannerLight.value === '' ? null : bannerLight.value, }).then(() => { fetchInstance(true); }); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index b242366902..ab4c06412a 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -9,6 +9,7 @@ import { miLocalStorage } from './local-storage.js'; import type { SoundType } from '@/scripts/sound.js'; import { Storage } from '@/pizzax.js'; import { hemisphere } from '@/scripts/intl-const.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; interface PostFormAction { title: string, @@ -52,17 +53,14 @@ export type SoundStore = { volume: number; } - +import { instance } from '@/instance.js'; export const postFormActions: PostFormAction[] = []; export const userActions: UserAction[] = []; export const noteActions: NoteAction[] = []; export const noteViewInterruptors: NoteViewInterruptor[] = []; export const notePostInterruptors: NotePostInterruptor[] = []; export const pageViewInterruptors: PageViewInterruptor[] = []; -export const bannerDark = 'https://files.prismisskey.space/misskey/e088c6d1-b07f-4312-8d41-fee2f64071e9.png'; -export const bannerLight = 'https://files.prismisskey.space/misskey/85500d2f-41a9-48ff-a737-65d6fdf74604.png'; -export const iconDark = 'https://files.prismisskey.space/misskey/484efc68-de41-4786-b2b6-e5085c31c2c4.webp'; -export const iconLight = 'https://files.prismisskey.space/misskey/c3d722fe-379f-4c85-9414-90c232d53237.webp'; +export const { bannerDark, bannerLight, iconDark, iconLight } = instance; // TODO: それぞれいちいちwhereとかdefaultというキーを付けなきゃいけないの冗長なのでなんとかする(ただ型定義が面倒になりそう) // あと、現行の定義の仕方なら「whereが何であるかに関わらずキー名の重複不可」という制約を付けられるメリットもあるからそのメリットを引き継ぐ方法も考えないといけない From 836592c8fa3eea237ceeb3e565a779337a4963ea Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 4 May 2024 23:28:23 +0900 Subject: [PATCH 420/501] update --- package.json | 2 +- pnpm-lock.yaml | 46 +++++++++++++++++++--------------------------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/package.json b/package.json index 260a41213b..206ddc0288 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.3.1-PrisMisskey.4", + "version": "2024.3.1-PrisMisskey.5", "codename": "nasubi", "repository": { "type": "git", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75cddc5768..91b6c6948f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -340,7 +340,7 @@ importers: version: 2.7.0 proxycheck-ts: specifier: ^0.0.9 - version: 0.0.9 + version: 0.0.9(encoding@0.1.13) pug: specifier: 3.0.2 version: 3.0.2 @@ -922,7 +922,7 @@ importers: version: 8.0.9(bufferutil@4.0.7)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(utf-8-validate@6.0.3)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))(vue@3.4.26(typescript@5.4.5)) '@testing-library/vue': specifier: 8.0.3 - version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5)) + version: 8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5)) '@types/escape-regexp': specifier: 0.0.3 version: 0.0.3 @@ -4822,9 +4822,6 @@ packages: '@vue/compiler-sfc@3.4.26': resolution: {integrity: sha512-It1dp+FAOCgluYSVYlDn5DtZBxk1NCiJJfu2mlQqa/b+k8GL6NG/3/zRbJnHdhV2VhxFghaDq5L4K+1dakW6cw==} - '@vue/compiler-ssr@3.4.25': - resolution: {integrity: sha512-H2ohvM/Pf6LelGxDBnfbbXFPyM4NE3hrw0e/EpwuSiYu8c819wx+SVGdJ65p/sFrYDd6OnSDxN1MB2mN07hRSQ==} - '@vue/compiler-ssr@3.4.26': resolution: {integrity: sha512-FNwLfk7LlEPRY/g+nw2VqiDKcnDTVdCfBREekF8X74cPLiWHUX6oldktf/Vx28yh4STNy7t+/yuLoMBBF7YDiQ==} @@ -4848,11 +4845,6 @@ packages: '@vue/runtime-dom@3.4.26': resolution: {integrity: sha512-UftYA2hUXR2UOZD/Fc3IndZuCOOJgFxJsWOxDkhfVcwLbsfh2CdXE2tG4jWxBZuDAs9J9PzRTUFt1PgydEtItw==} - '@vue/server-renderer@3.4.25': - resolution: {integrity: sha512-8VTwq0Zcu3K4dWV0jOwIVINESE/gha3ifYCOKEhxOj6MEl5K5y8J8clQncTcDhKF+9U765nRw4UdUEXvrGhyVQ==} - peerDependencies: - vue: 3.4.25 - '@vue/server-renderer@3.4.26': resolution: {integrity: sha512-xoGAqSjYDPGAeRWxeoYwqJFD/gw7mpgzOvSxEmjWaFO2rE6qpbD1PC172YRpvKhrihkyHJkNDADFXTfCyVGhKw==} peerDependencies: @@ -9258,6 +9250,9 @@ packages: proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + proxycheck-ts@0.0.9: + resolution: {integrity: sha512-qxdMgLB01kPksBAeAHI2Zt5q/fW0glZKZK9d9eNm0/KyowKuKzVmvirF2ztsGcIzU7r6s6YvPwXJuPTeQ+LBTg==} + ps-list@8.1.1: resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -15220,11 +15215,11 @@ snapshots: dependencies: '@testing-library/dom': 9.3.4 - '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))': + '@testing-library/vue@8.0.3(@vue/compiler-sfc@3.4.26)(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))': dependencies: '@babel/runtime': 7.23.4 '@testing-library/dom': 9.3.3 - '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5)) + '@vue/test-utils': 2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5)) vue: 3.4.26(typescript@5.4.5) optionalDependencies: '@vue/compiler-sfc': 3.4.26 @@ -16015,12 +16010,6 @@ snapshots: postcss: 8.4.38 source-map-js: 1.2.0 - '@vue/compiler-ssr@3.4.25': - dependencies: - '@vue/compiler-dom': 3.4.25 - '@vue/shared': 3.4.25 - optional: true - '@vue/compiler-ssr@3.4.26': dependencies: '@vue/compiler-dom': 3.4.26 @@ -16055,13 +16044,6 @@ snapshots: '@vue/shared': 3.4.26 csstype: 3.1.3 - '@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5))': - dependencies: - '@vue/compiler-ssr': 3.4.25 - '@vue/shared': 3.4.25 - vue: 3.4.26(typescript@5.4.5) - optional: true - '@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5))': dependencies: '@vue/compiler-ssr': 3.4.26 @@ -16074,13 +16056,13 @@ snapshots: '@vue/shared@3.4.26': {} - '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.25(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))': + '@vue/test-utils@2.4.1(@vue/server-renderer@3.4.26(vue@3.4.26(typescript@5.4.5)))(vue@3.4.26(typescript@5.4.5))': dependencies: js-beautify: 1.14.9 vue: 3.4.26(typescript@5.4.5) vue-component-type-helpers: 1.8.4 optionalDependencies: - '@vue/server-renderer': 3.4.25(vue@3.4.26(typescript@5.4.5)) + '@vue/server-renderer': 3.4.26(vue@3.4.26(typescript@5.4.5)) '@webgpu/types@0.1.30': {} @@ -21488,10 +21470,20 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 + proxycheck-ts@0.0.9: + dependencies: + cross-fetch: 4.0.0 + proxy-from-env@1.0.0: {} proxy-from-env@1.1.0: {} + proxycheck-ts@0.0.9(encoding@0.1.13): + dependencies: + cross-fetch: 4.0.0(encoding@0.1.13) + transitivePeerDependencies: + - encoding + ps-list@8.1.1: {} ps-tree@1.2.0: From d27a23292916aba805b396d3d27d11f67ec2ebe5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 4 May 2024 23:31:01 +0900 Subject: [PATCH 421/501] update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 206ddc0288..abfffb9e93 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.3.1-PrisMisskey.5", + "version": "2024.3.1-mattyatea.5", "codename": "nasubi", "repository": { "type": "git", From 024ce3c881562e6d66df4ebd1f966509e3d0d183 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 4 May 2024 23:32:43 +0900 Subject: [PATCH 422/501] update --- .../1714831133155-outsideprismisskey.js | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 packages/backend/migration/1714831133155-outsideprismisskey.js diff --git a/packages/backend/migration/1714831133155-outsideprismisskey.js b/packages/backend/migration/1714831133155-outsideprismisskey.js new file mode 100644 index 0000000000..0da10f603d --- /dev/null +++ b/packages/backend/migration/1714831133155-outsideprismisskey.js @@ -0,0 +1,17 @@ +export class Outsideprismisskey1714831133155 { + name = 'Outsideprismisskey1714831133155' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "bannerDark" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "bannerLight" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "iconDark" character varying(1024)`); + await queryRunner.query(`ALTER TABLE "meta" ADD "iconLight" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "iconLight"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "iconDark"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bannerLight"`); + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "bannerDark"`); + } +} From 9f60e186b0238a8f2e0860978ad7693c93ebfcf8 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 6 May 2024 07:41:47 +0900 Subject: [PATCH 423/501] update --- .../1714831133156-avatardecoration_fed.js | 11 +++ .../activitypub/models/ApPersonService.ts | 76 +++---------------- .../backend/src/models/AvatarDecoration.ts | 5 -- .../admin/avatar-decorations/list.ts | 6 +- .../api/endpoints/get-avatar-decorations.ts | 7 +- 5 files changed, 24 insertions(+), 81 deletions(-) create mode 100644 packages/backend/migration/1714831133156-avatardecoration_fed.js diff --git a/packages/backend/migration/1714831133156-avatardecoration_fed.js b/packages/backend/migration/1714831133156-avatardecoration_fed.js new file mode 100644 index 0000000000..94093092bd --- /dev/null +++ b/packages/backend/migration/1714831133156-avatardecoration_fed.js @@ -0,0 +1,11 @@ +export class AvatardecorationFed1714831133156 { + name = 'AvatardecorationFed1714831133156' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "avatar_decoration" DROP COLUMN "host"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "avatar_decoration" ADD "host" character varying(256)`); + } +} diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 07e04ca8cf..d13ab78b2b 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -39,8 +39,6 @@ import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.j import type { AccountMoveService } from '@/core/AccountMoveService.js'; import { checkHttps } from '@/misc/check-https.js'; import { isNotNull } from '@/misc/is-not-null.js'; -import { HttpRequestService } from '@/core/HttpRequestService.js'; -import { AvatarDecorationService } from '@/core/AvatarDecorationService.js'; import { getApId, getApType, getOneApHrefNullable, isActor, isCollection, isCollectionOrOrderedCollection, isPropertyValue } from '../type.js'; import { extractApHashtags } from './tag.js'; import type { OnModuleInit } from '@nestjs/common'; @@ -79,8 +77,6 @@ export class ApPersonService implements OnModuleInit { private apLoggerService: ApLoggerService; private accountMoveService: AccountMoveService; private logger: Logger; - private httpRequestService: HttpRequestService; - private avatarDecorationService: AvatarDecorationService; constructor( private moduleRef: ModuleRef, @@ -130,8 +126,6 @@ export class ApPersonService implements OnModuleInit { this.apLoggerService = this.moduleRef.get('ApLoggerService'); this.accountMoveService = this.moduleRef.get('AccountMoveService'); this.logger = this.apLoggerService.logger; - this.httpRequestService = this.moduleRef.get('HttpRequestService'); - this.avatarDecorationService = this.moduleRef.get('AvatarDecorationService'); } private punyHost(url: string): string { @@ -233,73 +227,21 @@ export class ApPersonService implements OnModuleInit { return null; } - private async resolveAvatarAndBanner(user: MiRemoteUser, host: string | null, icon: any, image: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>>> { - if (user == null) throw new Error('failed to create user: user is null'); - + private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { const [avatar, banner] = await Promise.all([icon, image].map(img => { - // if we have an explicitly missing image, return an - // explicitly-null set of values - if ((img == null) || (typeof img === 'object' && img.url == null)) { - return { id: null, url: null, blurhash: null }; - } + if (img == null) return null; + if (user == null) throw new Error('failed to create user: user is null'); return this.apImageService.resolveImage(user, img).catch(() => null); })); - /* - we don't want to return nulls on errors! if the database fields - are already null, nothing changes; if the database has old - values, we should keep those. The exception is if the remote has - actually removed the images: in that case, the block above - returns the special {id:null}&c value, and we return those - */ return { - ...( avatar ? { - avatarId: avatar.id, - avatarUrl: avatar.url ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, - avatarBlurhash: avatar.blurhash, - } : {}), - ...( banner ? { - bannerId: banner.id, - bannerUrl: banner.url ? this.driveFileEntityService.getPublicUrl(banner) : null, - bannerBlurhash: banner.blurhash, - } : {}), + avatarId: avatar?.id ?? null, + bannerId: banner?.id ?? null, + avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, + bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null, + avatarBlurhash: avatar?.blurhash ?? null, + bannerBlurhash: banner?.blurhash ?? null, }; - - if (host) { - const i = await this.federatedInstanceService.fetch(host); - console.log('avatarDecorationFetch: start'); - if (i.softwareName === 'misskey') { - const remoteUserId = user.uri.split('/users/')[1]; - const userMetaRequest = await this.httpRequestService.send(`https://${i.host}/api/users/show`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - 'userId': remoteUserId, - }), - }); - const res: any = await userMetaRequest.json(); - if (res.avatarDecorations) { - const localDecos = await this.avatarDecorationService.getAll(); - // ローカルのデコレーションとして登録する - for (const deco of res.avatarDecorations) { - if (localDecos.some((v) => v.id === deco.id)) continue; - await this.avatarDecorationService.create({ - id: deco.id, - updatedAt: null, - url: deco.url, - name: `import_${host}_${deco.id}`, - description: `Imported from ${host}`, - host: host, - }); - } - Object.assign(returnData, { avatarDecorations: res.avatarDecorations }); - } - } - } - - return returnData; } /** diff --git a/packages/backend/src/models/AvatarDecoration.ts b/packages/backend/src/models/AvatarDecoration.ts index 4d469824d2..6414ff9823 100644 --- a/packages/backend/src/models/AvatarDecoration.ts +++ b/packages/backend/src/models/AvatarDecoration.ts @@ -36,11 +36,6 @@ export class MiAvatarDecoration { default: '', }) public category: string; - @Column('varchar', { - length: 256, - nullable: true, - }) - public host: string; // TODO: 定期ジョブで存在しなくなったロールIDを除去するようにする @Column('varchar', { diff --git a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts index b9a8347fc1..39297de536 100644 --- a/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts +++ b/packages/backend/src/server/api/endpoints/admin/avatar-decorations/list.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-FileCopyrightText: syuilo and other misskey contributors * SPDX-License-Identifier: AGPL-3.0-only */ @@ -88,9 +88,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- super(meta, paramDef, async (ps, me) => { const avatarDecorations = await this.avatarDecorationService.getAll(true); - const filteredAvatarDecorations = avatarDecorations.filter(avatarDecoration => avatarDecoration.host === null); - console.log(filteredAvatarDecorations); - return filteredAvatarDecorations.map(avatarDecoration => ({ + return avatarDecorations.map(avatarDecoration => ({ id: avatarDecoration.id, createdAt: this.idService.parse(avatarDecoration.id).date.toISOString(), updatedAt: avatarDecoration.updatedAt?.toISOString() ?? null, diff --git a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts index 8d43c71072..028d647393 100644 --- a/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts +++ b/packages/backend/src/server/api/endpoints/get-avatar-decorations.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-FileCopyrightText: syuilo and other misskey contributors * SPDX-License-Identifier: AGPL-3.0-only */ @@ -70,10 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const decorations = await this.avatarDecorationService.getAll(true); const allRoles = await this.roleService.getRoles(); - // Filter decorations where host is null - const filteredDecorations = decorations.filter(decoration => decoration.host === null); - - return filteredDecorations.map(decoration => ({ + return decorations.map(decoration => ({ id: decoration.id, name: decoration.name, description: decoration.description, From b0d4294644a70d8f540f1355f8305b8de0aac492 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 6 May 2024 23:26:31 +0900 Subject: [PATCH 424/501] update --- packages/backend/src/core/activitypub/models/ApPersonService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index d13ab78b2b..6a01385ae9 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -382,7 +382,7 @@ export class ApPersonService implements OnModuleInit { //#region アバターとヘッダー画像をフェッチ try { - const updates = await this.resolveAvatarAndBanner(user, host, person.icon, person.image); + const updates = await this.resolveAvatarAndBanner(user, person.icon, person.image); await this.usersRepository.update(user.id, updates); user = { ...user, ...updates }; From b2757484df2fe5080e355f92845fa1fdb0c737d3 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 6 May 2024 23:31:07 +0900 Subject: [PATCH 425/501] update --- packages/backend/src/core/activitypub/models/ApPersonService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index 6a01385ae9..fc91fcf5ae 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -468,7 +468,7 @@ export class ApPersonService implements OnModuleInit { movedToUri: person.movedTo ?? null, alsoKnownAs: person.alsoKnownAs ?? null, isExplorable: person.discoverable, - ...(await this.resolveAvatarAndBanner(exist, host, person.icon, person.image).catch(() => ({}))), + ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), } as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>; const moving = ((): boolean => { From ef0827fee996effcfbd34eb7894fc6e4cabd54ec Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 7 May 2024 14:50:28 +0900 Subject: [PATCH 426/501] update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abfffb9e93..6310884de8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.3.1-mattyatea.5", + "version": "2024.5.0-beta1-mattyatea1", "codename": "nasubi", "repository": { "type": "git", From ec41d0d1dca6d1d59e23c820cbfc58278a394e3c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 11 May 2024 00:05:48 +0900 Subject: [PATCH 427/501] update --- packages/backend/src/core/AccountUpdateService.ts | 2 -- packages/backend/src/misc/cache.ts | 2 -- packages/backend/src/server/api/endpoints/i/update.ts | 4 ++-- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/backend/src/core/AccountUpdateService.ts b/packages/backend/src/core/AccountUpdateService.ts index 3178730b45..69a57b4854 100644 --- a/packages/backend/src/core/AccountUpdateService.ts +++ b/packages/backend/src/core/AccountUpdateService.ts @@ -28,7 +28,6 @@ export class AccountUpdateService { @bindThis public async publishToFollowers(userId: MiUser['id']) { - console.time('time AccountUpdateService.publishToFollowers'); const user = await this.usersRepository.findOneBy({ id: userId }); if (user == null) throw new Error('user not found'); @@ -38,6 +37,5 @@ export class AccountUpdateService { this.apDeliverManagerService.deliverToFollowers(user, content); this.relayService.deliverToRelays(user, content); } - console.timeEnd('time AccountUpdateService.publishToFollowers'); } } diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index d4154ca16a..bba64a06ef 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -33,7 +33,6 @@ export class RedisKVCache<T> { @bindThis public async set(key: string, value: T): Promise<void> { - console.time('time RedisKVCache.set'); this.memoryCache.set(key, value); if (this.lifetime === Infinity) { await this.redisClient.set( @@ -47,7 +46,6 @@ export class RedisKVCache<T> { 'EX', Math.round(this.lifetime / 1000), ); } - console.timeEnd('time RedisKVCache.set'); } @bindThis diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 1d02d71685..ee51930036 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -362,7 +362,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const [myRoles, myPolicies] = await Promise.all([this.roleService.getUserRoles(user.id), this.roleService.getUserPolicies(user.id)]); const allRoles = await this.roleService.getRoles(); const decorationIds = decorations - .filter(d => d.host === null && (d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))) + .filter(d => (d.roleIdsThatCanBeUsedThisDecoration.filter(roleId => allRoles.some(r => r.id === roleId)).length === 0 || myRoles.some(r => d.roleIdsThatCanBeUsedThisDecoration.includes(r.id)))) .map(d => d.id); if (ps.avatarDecorations.length > myPolicies.avatarDecorationLimit) throw new ApiError(meta.errors.restrictedByRole); @@ -480,7 +480,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- await this.cacheService.userProfileCache.set(user.id, updatedProfile); // Publish meUpdated event - //this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); + this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { From 48414d751ffaf69698dc4c1e62449a26bf0a66bf Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 11 May 2024 00:11:45 +0900 Subject: [PATCH 428/501] update --- packages/backend/src/core/entities/UserEntityService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 165f0201b8..b80a1ec206 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -7,6 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; import _Ajv from 'ajv'; import { ModuleRef } from '@nestjs/core'; +import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import type { Packed } from '@/misc/json-schema.js'; From 54b074b947534a01cb82c92cb22bc915f8432363 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 11 May 2024 00:16:31 +0900 Subject: [PATCH 429/501] update --- .../activitypub/models/ApPersonService.ts | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/packages/backend/src/core/activitypub/models/ApPersonService.ts b/packages/backend/src/core/activitypub/models/ApPersonService.ts index fc91fcf5ae..744b1ea683 100644 --- a/packages/backend/src/core/activitypub/models/ApPersonService.ts +++ b/packages/backend/src/core/activitypub/models/ApPersonService.ts @@ -101,7 +101,6 @@ export class ApPersonService implements OnModuleInit { @Inject(DI.followingsRepository) private followingsRepository: FollowingsRepository, - ) { } @@ -227,20 +226,37 @@ export class ApPersonService implements OnModuleInit { return null; } - private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { + private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Partial<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>>> { + if (user == null) throw new Error('failed to create user: user is null'); + const [avatar, banner] = await Promise.all([icon, image].map(img => { - if (img == null) return null; - if (user == null) throw new Error('failed to create user: user is null'); + // if we have an explicitly missing image, return an + // explicitly-null set of values + if ((img == null) || (typeof img === 'object' && img.url == null)) { + return { id: null, url: null, blurhash: null }; + } + return this.apImageService.resolveImage(user, img).catch(() => null); })); + /* + we don't want to return nulls on errors! if the database fields + are already null, nothing changes; if the database has old + values, we should keep those. The exception is if the remote has + actually removed the images: in that case, the block above + returns the special {id:null}&c value, and we return those + */ return { - avatarId: avatar?.id ?? null, - bannerId: banner?.id ?? null, - avatarUrl: avatar ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, - bannerUrl: banner ? this.driveFileEntityService.getPublicUrl(banner) : null, - avatarBlurhash: avatar?.blurhash ?? null, - bannerBlurhash: banner?.blurhash ?? null, + ...( avatar ? { + avatarId: avatar.id, + avatarUrl: avatar.url ? this.driveFileEntityService.getPublicUrl(avatar, 'avatar') : null, + avatarBlurhash: avatar.blurhash, + } : {}), + ...( banner ? { + bannerId: banner.id, + bannerUrl: banner.url ? this.driveFileEntityService.getPublicUrl(banner) : null, + bannerBlurhash: banner.blurhash, + } : {}), }; } @@ -444,10 +460,6 @@ export class ApPersonService implements OnModuleInit { const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/); const url = getOneApHrefNullable(person.url); - let host = null; - if (url) { - host = new URL(url).host; - } if (url && !checkHttps(url)) { throw new Error('unexpected schema of person url: ' + url); From 99f2be556df57dfe4368135932bd4fd387993829 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 11 May 2024 00:20:47 +0900 Subject: [PATCH 430/501] update --- packages/backend/src/core/activitypub/models/ApNoteService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts index becfe42842..bbded118d0 100644 --- a/packages/backend/src/core/activitypub/models/ApNoteService.ts +++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts @@ -5,6 +5,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { In } from 'typeorm'; +import promiseLimit from 'promise-limit'; import { DI } from '@/di-symbols.js'; import type { PollsRepository, EmojisRepository, NotesRepository } from '@/models/_.js'; import type { Config } from '@/config.js'; From 55c80db66d32e0e94a4ceb8bcfbb2ae761e5986a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 11 May 2024 01:12:26 +0900 Subject: [PATCH 431/501] update --- packages/backend/src/core/CacheService.ts | 13 ------------- .../src/core/FanoutTimelineEndpointService.ts | 6 ++++-- packages/backend/src/core/NotificationService.ts | 7 +++++-- .../src/core/entities/NotificationEntityService.ts | 13 ++++++++++--- .../backend/src/server/api/endpoints/i/update.ts | 2 -- .../backend/src/server/api/stream/Connection.ts | 9 ++++++--- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index d008e7ec52..28f14504ae 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -20,7 +20,6 @@ export class CacheService implements OnApplicationShutdown { public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null>; public localUserByIdCache: MemoryKVCache<MiLocalUser>; public uriPersonCache: MemoryKVCache<MiUser | null>; - public userProfileCache: RedisKVCache<MiUserProfile>; public userMutingsCache: RedisKVCache<Set<string>>; public userBlockingCache: RedisKVCache<Set<string>>; public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ @@ -37,9 +36,6 @@ export class CacheService implements OnApplicationShutdown { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, @@ -61,14 +57,6 @@ export class CacheService implements OnApplicationShutdown { this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(Infinity); this.uriPersonCache = new MemoryKVCache<MiUser | null>(Infinity); - this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', { - lifetime: 1000 * 60 * 30, // 30m - memoryCacheLifetime: 1000 * 60, // 1m - fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }), - toRedisConverter: (value) => JSON.stringify(value), - fromRedisConverter: (value) => JSON.parse(value), // TODO: date型の考慮 - }); - this.userMutingsCache = new RedisKVCache<Set<string>>(this.redisClient, 'userMutings', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m @@ -186,7 +174,6 @@ export class CacheService implements OnApplicationShutdown { this.localUserByNativeTokenCache.dispose(); this.localUserByIdCache.dispose(); this.uriPersonCache.dispose(); - this.userProfileCache.dispose(); this.userMutingsCache.dispose(); this.userBlockingCache.dispose(); this.userBlockedCache.dispose(); diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 884723ff81..fa52c7a2b8 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -9,7 +9,7 @@ import { bindThis } from '@/decorators.js'; import type { MiUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; import { Packed } from '@/misc/json-schema.js'; -import type { NotesRepository } from '@/models/_.js'; +import type { NotesRepository, UserProfilesRepository } from '@/models/_.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; @@ -41,6 +41,8 @@ export class FanoutTimelineEndpointService { constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, private noteEntityService: NoteEntityService, private cacheService: CacheService, @@ -109,7 +111,7 @@ export class FanoutTimelineEndpointService { this.cacheService.userMutingsCache.fetch(ps.me.id), this.cacheService.renoteMutingsCache.fetch(ps.me.id), this.cacheService.userBlockedCache.fetch(ps.me.id), - this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)), + (await this.userProfilesRepository.findOneByOrFail({ userId: me.id }).then(p => new Set(p.mutedInstances))), ]); const parentFilter = filter; diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index 68ad92f396..faccc76ab9 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -8,7 +8,7 @@ import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UsersRepository } from '@/models/_.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; import type { MiNotification } from '@/models/Notification.js'; import { bindThis } from '@/decorators.js'; @@ -36,6 +36,9 @@ export class NotificationService implements OnApplicationShutdown { @Inject(DI.usersRepository) private usersRepository: UsersRepository, + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + private notificationEntityService: NotificationEntityService, private idService: IdService, private globalEventService: GlobalEventService, @@ -92,7 +95,7 @@ export class NotificationService implements OnApplicationShutdown { data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>, notifierId?: MiUser['id'] | null, ): Promise<MiNotification | null> { - const profile = await this.cacheService.userProfileCache.fetch(notifieeId); + const profile = await this.userProfilesRepository.findOneByOrFail({ userId: notifieeId }); // 古いMisskeyバージョンのキャッシュが残っている可能性がある // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 94d56c883b..96c097bfd0 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -7,7 +7,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { FollowRequestsRepository, NotesRepository, MiUser, UsersRepository } from '@/models/_.js'; +import type { + FollowRequestsRepository, + NotesRepository, + MiUser, + UsersRepository, + UserProfilesRepository, +} from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiGroupedNotification, MiNotification } from '@/models/Notification.js'; import type { MiNote } from '@/models/Note.js'; @@ -42,7 +48,8 @@ export class NotificationEntityService implements OnModuleInit { private followRequestsRepository: FollowRequestsRepository, private cacheService: CacheService, - + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, ) { @@ -302,7 +309,7 @@ export class NotificationEntityService implements OnModuleInit { userMutedInstances, ] = await Promise.all([ this.cacheService.userMutingsCache.fetch(meId), - this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)), + await this.userProfilesRepository.findOneByOrFail({ userId: meId }).then(p => new Set(p.mutedInstances)), ]); const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(isNotNull); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index ee51930036..1b2468cdcb 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -477,8 +477,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const updatedProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); - await this.cacheService.userProfileCache.set(user.id, updatedProfile); - // Publish meUpdated event this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index 41c0feccc7..f6912ce869 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -4,6 +4,7 @@ */ import * as WebSocket from 'ws'; +import { Inject } from '@nestjs/common'; import type { MiUser } from '@/models/User.js'; import type { MiAccessToken } from '@/models/AccessToken.js'; import type { Packed } from '@/misc/json-schema.js'; @@ -11,9 +12,10 @@ import type { NoteReadService } from '@/core/NoteReadService.js'; import type { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; -import { MiFollowing, MiUserProfile } from '@/models/_.js'; +import { MiFollowing, MiUserProfile, type UserProfilesRepository } from '@/models/_.js'; import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js'; import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; +import { DI } from '@/di-symbols.js'; import type { ChannelsService } from './ChannelsService.js'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; @@ -45,7 +47,8 @@ export default class Connection { private notificationService: NotificationService, private cacheService: CacheService, private channelFollowingService: ChannelFollowingService, - + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, user: MiUser | null | undefined, token: MiAccessToken | null | undefined, ) { @@ -57,7 +60,7 @@ export default class Connection { public async fetch() { if (this.user == null) return; const [userProfile, following, followingChannels, userIdsWhoMeMuting, userIdsWhoBlockingMe, userIdsWhoMeMutingRenotes] = await Promise.all([ - this.cacheService.userProfileCache.fetch(this.user.id), + await this.userProfilesRepository.findOneByOrFail({ userId: this.user.id }), this.cacheService.userFollowingsCache.fetch(this.user.id), this.channelFollowingService.userFollowingChannelsCache.fetch(this.user.id), this.cacheService.userMutingsCache.fetch(this.user.id), From bc3234510c1d00e834be5a3b6addf525e5398c5f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 11 May 2024 01:16:47 +0900 Subject: [PATCH 432/501] Revert "update" This reverts commit 55c80db66d32e0e94a4ceb8bcfbb2ae761e5986a. --- packages/backend/src/core/CacheService.ts | 13 +++++++++++++ .../src/core/FanoutTimelineEndpointService.ts | 6 ++---- packages/backend/src/core/NotificationService.ts | 7 ++----- .../src/core/entities/NotificationEntityService.ts | 13 +++---------- .../backend/src/server/api/endpoints/i/update.ts | 2 ++ .../backend/src/server/api/stream/Connection.ts | 9 +++------ 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index 28f14504ae..d008e7ec52 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -20,6 +20,7 @@ export class CacheService implements OnApplicationShutdown { public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null>; public localUserByIdCache: MemoryKVCache<MiLocalUser>; public uriPersonCache: MemoryKVCache<MiUser | null>; + public userProfileCache: RedisKVCache<MiUserProfile>; public userMutingsCache: RedisKVCache<Set<string>>; public userBlockingCache: RedisKVCache<Set<string>>; public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ @@ -36,6 +37,9 @@ export class CacheService implements OnApplicationShutdown { @Inject(DI.usersRepository) private usersRepository: UsersRepository, + @Inject(DI.userProfilesRepository) + private userProfilesRepository: UserProfilesRepository, + @Inject(DI.mutingsRepository) private mutingsRepository: MutingsRepository, @@ -57,6 +61,14 @@ export class CacheService implements OnApplicationShutdown { this.localUserByIdCache = new MemoryKVCache<MiLocalUser>(Infinity); this.uriPersonCache = new MemoryKVCache<MiUser | null>(Infinity); + this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', { + lifetime: 1000 * 60 * 30, // 30m + memoryCacheLifetime: 1000 * 60, // 1m + fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }), + toRedisConverter: (value) => JSON.stringify(value), + fromRedisConverter: (value) => JSON.parse(value), // TODO: date型の考慮 + }); + this.userMutingsCache = new RedisKVCache<Set<string>>(this.redisClient, 'userMutings', { lifetime: 1000 * 60 * 30, // 30m memoryCacheLifetime: 1000 * 60, // 1m @@ -174,6 +186,7 @@ export class CacheService implements OnApplicationShutdown { this.localUserByNativeTokenCache.dispose(); this.localUserByIdCache.dispose(); this.uriPersonCache.dispose(); + this.userProfileCache.dispose(); this.userMutingsCache.dispose(); this.userBlockingCache.dispose(); this.userBlockedCache.dispose(); diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index fa52c7a2b8..884723ff81 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -9,7 +9,7 @@ import { bindThis } from '@/decorators.js'; import type { MiUser } from '@/models/User.js'; import type { MiNote } from '@/models/Note.js'; import { Packed } from '@/misc/json-schema.js'; -import type { NotesRepository, UserProfilesRepository } from '@/models/_.js'; +import type { NotesRepository } from '@/models/_.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { FanoutTimelineName, FanoutTimelineService } from '@/core/FanoutTimelineService.js'; import { isUserRelated } from '@/misc/is-user-related.js'; @@ -41,8 +41,6 @@ export class FanoutTimelineEndpointService { constructor( @Inject(DI.notesRepository) private notesRepository: NotesRepository, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, private noteEntityService: NoteEntityService, private cacheService: CacheService, @@ -111,7 +109,7 @@ export class FanoutTimelineEndpointService { this.cacheService.userMutingsCache.fetch(ps.me.id), this.cacheService.renoteMutingsCache.fetch(ps.me.id), this.cacheService.userBlockedCache.fetch(ps.me.id), - (await this.userProfilesRepository.findOneByOrFail({ userId: me.id }).then(p => new Set(p.mutedInstances))), + this.cacheService.userProfileCache.fetch(me.id).then(p => new Set(p.mutedInstances)), ]); const parentFilter = filter; diff --git a/packages/backend/src/core/NotificationService.ts b/packages/backend/src/core/NotificationService.ts index faccc76ab9..68ad92f396 100644 --- a/packages/backend/src/core/NotificationService.ts +++ b/packages/backend/src/core/NotificationService.ts @@ -8,7 +8,7 @@ import * as Redis from 'ioredis'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { UsersRepository } from '@/models/_.js'; import type { MiUser } from '@/models/User.js'; import type { MiNotification } from '@/models/Notification.js'; import { bindThis } from '@/decorators.js'; @@ -36,9 +36,6 @@ export class NotificationService implements OnApplicationShutdown { @Inject(DI.usersRepository) private usersRepository: UsersRepository, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, - private notificationEntityService: NotificationEntityService, private idService: IdService, private globalEventService: GlobalEventService, @@ -95,7 +92,7 @@ export class NotificationService implements OnApplicationShutdown { data: Omit<FilterUnionByProperty<MiNotification, 'type', T>, 'type' | 'id' | 'createdAt' | 'notifierId'>, notifierId?: MiUser['id'] | null, ): Promise<MiNotification | null> { - const profile = await this.userProfilesRepository.findOneByOrFail({ userId: notifieeId }); + const profile = await this.cacheService.userProfileCache.fetch(notifieeId); // 古いMisskeyバージョンのキャッシュが残っている可能性がある // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 96c097bfd0..94d56c883b 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -7,13 +7,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { ModuleRef } from '@nestjs/core'; import { In } from 'typeorm'; import { DI } from '@/di-symbols.js'; -import type { - FollowRequestsRepository, - NotesRepository, - MiUser, - UsersRepository, - UserProfilesRepository, -} from '@/models/_.js'; +import type { FollowRequestsRepository, NotesRepository, MiUser, UsersRepository } from '@/models/_.js'; import { awaitAll } from '@/misc/prelude/await-all.js'; import type { MiGroupedNotification, MiNotification } from '@/models/Notification.js'; import type { MiNote } from '@/models/Note.js'; @@ -48,8 +42,7 @@ export class NotificationEntityService implements OnModuleInit { private followRequestsRepository: FollowRequestsRepository, private cacheService: CacheService, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, + //private userEntityService: UserEntityService, //private noteEntityService: NoteEntityService, ) { @@ -309,7 +302,7 @@ export class NotificationEntityService implements OnModuleInit { userMutedInstances, ] = await Promise.all([ this.cacheService.userMutingsCache.fetch(meId), - await this.userProfilesRepository.findOneByOrFail({ userId: meId }).then(p => new Set(p.mutedInstances)), + this.cacheService.userProfileCache.fetch(meId).then(p => new Set(p.mutedInstances)), ]); const notifierIds = notifications.map(notification => 'notifierId' in notification ? notification.notifierId : null).filter(isNotNull); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 1b2468cdcb..ee51930036 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -477,6 +477,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const updatedProfile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); + await this.cacheService.userProfileCache.set(user.id, updatedProfile); + // Publish meUpdated event this.globalEventService.publishMainStream(user.id, 'meUpdated', iObj); diff --git a/packages/backend/src/server/api/stream/Connection.ts b/packages/backend/src/server/api/stream/Connection.ts index f6912ce869..41c0feccc7 100644 --- a/packages/backend/src/server/api/stream/Connection.ts +++ b/packages/backend/src/server/api/stream/Connection.ts @@ -4,7 +4,6 @@ */ import * as WebSocket from 'ws'; -import { Inject } from '@nestjs/common'; import type { MiUser } from '@/models/User.js'; import type { MiAccessToken } from '@/models/AccessToken.js'; import type { Packed } from '@/misc/json-schema.js'; @@ -12,10 +11,9 @@ import type { NoteReadService } from '@/core/NoteReadService.js'; import type { NotificationService } from '@/core/NotificationService.js'; import { bindThis } from '@/decorators.js'; import { CacheService } from '@/core/CacheService.js'; -import { MiFollowing, MiUserProfile, type UserProfilesRepository } from '@/models/_.js'; +import { MiFollowing, MiUserProfile } from '@/models/_.js'; import type { StreamEventEmitter, GlobalEvents } from '@/core/GlobalEventService.js'; import { ChannelFollowingService } from '@/core/ChannelFollowingService.js'; -import { DI } from '@/di-symbols.js'; import type { ChannelsService } from './ChannelsService.js'; import type { EventEmitter } from 'events'; import type Channel from './channel.js'; @@ -47,8 +45,7 @@ export default class Connection { private notificationService: NotificationService, private cacheService: CacheService, private channelFollowingService: ChannelFollowingService, - @Inject(DI.userProfilesRepository) - private userProfilesRepository: UserProfilesRepository, + user: MiUser | null | undefined, token: MiAccessToken | null | undefined, ) { @@ -60,7 +57,7 @@ export default class Connection { public async fetch() { if (this.user == null) return; const [userProfile, following, followingChannels, userIdsWhoMeMuting, userIdsWhoBlockingMe, userIdsWhoMeMutingRenotes] = await Promise.all([ - await this.userProfilesRepository.findOneByOrFail({ userId: this.user.id }), + this.cacheService.userProfileCache.fetch(this.user.id), this.cacheService.userFollowingsCache.fetch(this.user.id), this.channelFollowingService.userFollowingChannelsCache.fetch(this.user.id), this.cacheService.userMutingsCache.fetch(this.user.id), From 9a572eb9f56507fbdfdbd232a14b3529a24c77df Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 12 May 2024 02:00:06 +0900 Subject: [PATCH 433/501] update --- .../src/core/FanoutTimelineEndpointService.ts | 1 + .../frontend/src/pages/avatar-decorations.vue | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts index 884723ff81..ec3e54d32a 100644 --- a/packages/backend/src/core/FanoutTimelineEndpointService.ts +++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts @@ -100,6 +100,7 @@ export class FanoutTimelineEndpointService { if (ps.me) { const me = ps.me; + const [ userIdsWhoMeMuting, userIdsWhoMeMutingRenotes, diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 794bcb8843..3130f844af 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="900"> <MkSwitch v-model="select">SelectMode</MkSwitch> <MkButton @click="setCategoryBulk">Set Category</MkButton> + <MkButton @click="deletes">Delete</MkButton> <div class="_gaps"> <div :class="$style.decorations"> <XDecoration @@ -49,6 +50,7 @@ function add() { category: '', }); } + function selectItems(decorationId) { if (selectItemsId.value.includes(decorationId)) { const index = selectItemsId.value.indexOf(decorationId); @@ -57,6 +59,7 @@ function selectItems(decorationId) { selectItemsId.value.push(decorationId); } } + function del(avatarDecoration) { os.confirm({ type: 'warning', @@ -123,6 +126,22 @@ async function setCategoryBulk() { } } +async function deletes() { + const { canceled, result } = await os.inputText({ + title: 'Category', + }); + if (canceled) return; + if (selectItemsId.value.length > 1) { + for (let i = 0; i < selectItemsId.value.length; i++) { + let decorationId = selectItemsId.value[i]; + await misskeyApi('admin/avatar-decorations/delete', { + id: decorationId, + category: result, + }); + } + } +} + const headerActions = computed(() => [{ asFullButton: true, icon: 'ti ti-plus', From 5672fecaf2415665ee6ac1bf59aeade50f0e637c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sun, 12 May 2024 02:11:18 +0900 Subject: [PATCH 434/501] update --- packages/frontend/src/pages/avatar-decorations.vue | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 3130f844af..023a10e1db 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -127,17 +127,10 @@ async function setCategoryBulk() { } async function deletes() { - const { canceled, result } = await os.inputText({ - title: 'Category', - }); - if (canceled) return; - if (selectItemsId.value.length > 1) { + if (selectItemsId.value.length > 0) { for (let i = 0; i < selectItemsId.value.length; i++) { - let decorationId = selectItemsId.value[i]; - await misskeyApi('admin/avatar-decorations/delete', { - id: decorationId, - category: result, - }); + let decoration = selectItemsId.value[i]; + del(decoration); } } } From ecac24d499a5ea84a6c6cd14a63143d2a87616a9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 13 May 2024 19:46:51 +0900 Subject: [PATCH 435/501] update --- packages/frontend/src/pages/avatar-decorations.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 023a10e1db..673ccd027c 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -94,7 +94,7 @@ function openDecorationCreate() { os.popup(defineAsyncComponent(() => import('@/components/MkAvatarDecoEditDialog.vue')), { }, { del: result => { - window.location.reload(); + avatarDecorations.value.unshift(result); }, }); } From 2c294ff97894ad15144160937a0b50f6e58013f9 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Mon, 13 May 2024 20:09:24 +0900 Subject: [PATCH 436/501] update --- packages/frontend/src/pages/avatar-decorations.vue | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue index 673ccd027c..2e9d1dac0d 100644 --- a/packages/frontend/src/pages/avatar-decorations.vue +++ b/packages/frontend/src/pages/avatar-decorations.vue @@ -128,10 +128,10 @@ async function setCategoryBulk() { async function deletes() { if (selectItemsId.value.length > 0) { - for (let i = 0; i < selectItemsId.value.length; i++) { - let decoration = selectItemsId.value[i]; - del(decoration); - } + selectItemsId.value.forEach(decorationId => { + console.log(decorationId); + misskeyApi('admin/avatar-decorations/delete', { id: decorationId }); + }); } } From 7f9ac61174130d56daf16110705b5c9e9ae10cdc Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 14 May 2024 05:17:18 +0900 Subject: [PATCH 437/501] update --- packages/backend/src/core/CacheService.ts | 2 +- packages/backend/src/server/api/endpoints/i/update.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/backend/src/core/CacheService.ts b/packages/backend/src/core/CacheService.ts index d008e7ec52..653993c3d9 100644 --- a/packages/backend/src/core/CacheService.ts +++ b/packages/backend/src/core/CacheService.ts @@ -130,7 +130,7 @@ export class CacheService implements OnApplicationShutdown { case 'userChangeSuspendedState': case 'userChangeDeletedState': case 'remoteUserUpdated': - case 'localUserUpdated': { + { const user = await this.usersRepository.findOneBy({ id: body.id }); if (user == null) { this.userByIdCache.delete(body.id); diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index ee51930036..8dcfb21991 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -484,15 +484,15 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- // 鍵垢を解除したとき、溜まっていたフォローリクエストがあるならすべて承認 if (user.isLocked && ps.isLocked === false) { - this.userFollowingService.acceptAllFollowRequests(user); + await this.userFollowingService.acceptAllFollowRequests(user); } // フォロワーにUpdateを配信 - this.accountUpdateService.publishToFollowers(user.id); + await this.accountUpdateService.publishToFollowers(user.id); const urls = updatedProfile.fields.filter(x => x.value.startsWith('https://')); for (const url of urls) { - this.verifyLink(url.value, user); + await this.verifyLink(url.value, user); } return iObj; }); From 5e140a7ff5fcea77ed566637edf6791cd23f57c0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 15 May 2024 22:50:18 +0900 Subject: [PATCH 438/501] update --- packages/frontend/src/components/MkClickerGame.vue | 4 ++-- packages/frontend/src/widgets/WidgetClicker.vue | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue index 175fc5da6b..59fde7dab8 100644 --- a/packages/frontend/src/components/MkClickerGame.vue +++ b/packages/frontend/src/components/MkClickerGame.vue @@ -10,13 +10,13 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.count" class=""> <img :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" - src="https://files.prismisskey.space/misskey/59731116-9616-4161-82b3-7eff6c48ea72.apng" + src="https://files.prismisskey.space/misskey/630c737c-e96f-4c10-94a4-73e138278576.webp" /> {{ number(cookies) }} </div> <button v-click-anime class="_button" @click="onClick"> <img - src="https://files.prismisskey.space/misskey/59731116-9616-4161-82b3-7eff6c48ea72.apng" + src="https://files.prismisskey.space/misskey/630c737c-e96f-4c10-94a4-73e138278576.webp" :class="$style.img" > </button> diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue index 79011ab55c..e3dd21f110 100644 --- a/packages/frontend/src/widgets/WidgetClicker.vue +++ b/packages/frontend/src/widgets/WidgetClicker.vue @@ -7,8 +7,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkContainer :showHeader="widgetProps.showHeader" class="mkw-clicker"> <template #icon> <img - :class="[$style.icon,{[$style.dark]:darkMode}]" alt="Cosaque daihuku" - src="https://files.prismisskey.space/misskey/59731116-9616-4161-82b3-7eff6c48ea72.apng" + :class="[$style.icon,{[$style.dark]:darkMode}]" alt="kabocha kawadiri" + src="https://files.prismisskey.space/misskey/630c737c-e96f-4c10-94a4-73e138278576.webp" /> </template> <template #header>Clicker</template> From 509181a6d0644bf5190a6d63e005a02414e80370 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 01:08:01 +0900 Subject: [PATCH 439/501] update --- locales/index.d.ts | 12 + locales/ja-JP.yml | 3 + .../entities/NotificationEntityService.ts | 3 + .../src/core/entities/UserEntityService.ts | 6 +- packages/backend/src/models/UserProfile.ts | 4 + .../backend/src/server/api/endpoints/i.ts | 15 +- packages/backend/src/types.ts | 1 + .../src/components/MkNotification.vue | 9 +- packages/frontend/src/pages/user/home.vue | 464 +++++++++--------- 9 files changed, 288 insertions(+), 229 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 39d5fa492e..af1c2479ea 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -436,6 +436,10 @@ export interface Locale extends ILocale { * フォロワー */ "followers": string; + /** + * プリズム + */ + "points": string; /** * フォローされています */ @@ -9291,6 +9295,10 @@ export interface Locale extends ILocale { * 実績を獲得 */ "achievementEarned": string; + /** + * ログインボーナス + */ + "loginbonus": string; /** * 通知テスト */ @@ -9380,6 +9388,10 @@ export interface Locale extends ILocale { * 実績の獲得 */ "achievementEarned": string; + /** + * ログインボーナス + */ + "loginbonus": string; /** * 連携アプリからの通知 */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index fb157821af..1f44f8e40c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -105,6 +105,7 @@ note: "ノート" notes: "ノート" following: "フォロー" followers: "フォロワー" +points: "プリズム" followsYou: "フォローされています" createList: "リスト作成" manageLists: "リストの管理" @@ -2452,6 +2453,7 @@ _notification: roleAssigned: "ロールが付与されました" emptyPushNotificationMessage: "プッシュ通知の更新をしました" achievementEarned: "実績を獲得" + loginbonus: "ログインボーナス" testNotification: "通知テスト" checkNotificationBehavior: "通知の表示を確かめる" sendTestNotification: "テスト通知を送信する" @@ -2476,6 +2478,7 @@ _notification: followRequestAccepted: "フォローが受理された" roleAssigned: "ロールが付与された" achievementEarned: "実績の獲得" + loginbonus: "ログインボーナス" app: "連携アプリからの通知" _actions: diff --git a/packages/backend/src/core/entities/NotificationEntityService.ts b/packages/backend/src/core/entities/NotificationEntityService.ts index 94d56c883b..e3d4aa75df 100644 --- a/packages/backend/src/core/entities/NotificationEntityService.ts +++ b/packages/backend/src/core/entities/NotificationEntityService.ts @@ -163,6 +163,9 @@ export class NotificationEntityService implements OnModuleInit { ...(notification.type === 'achievementEarned' ? { achievement: notification.achievement, } : {}), + ...(notification.type === 'loginbonus' ? { + loginbonus: notification.loginbonus, + } : {}), ...(notification.type === 'app' ? { body: notification.customBody, header: notification.customHeader, diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index b80a1ec206..9d9c2eaec5 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -404,6 +404,7 @@ export class UserEntityService implements OnModuleInit { userRelations?: Map<MiUser['id'], UserRelation>, userMemos?: Map<MiUser['id'], string | null>, pinNotes?: Map<MiUser['id'], MiUserNotePining[]>, + todayGetPoints?: number, }, ): Promise<Packed<S>> { const opts = Object.assign({ @@ -507,7 +508,7 @@ export class UserEntityService implements OnModuleInit { iconUrl: r.iconUrl, displayOrder: r.displayOrder, }))) : undefined, - + ...(user.host == null ? { getPoints: profile!.getPoints } : {}), ...(isDetailed ? { url: profile!.url, uri: user.uri, @@ -602,6 +603,9 @@ export class UserEntityService implements OnModuleInit { achievements: profile!.achievements, loggedInDays: profile!.loggedInDates.length, policies: this.roleService.getUserPolicies(user.id), + ...(opts.todayGetPoints ? { + todayGetPoints: opts.todayGetPoints, + } : {}), } : {}), ...(opts.includeSecrets ? { diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 7dbe0b3717..ef70a297a9 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -261,6 +261,10 @@ export class MiUserProfile { length: 32, array: true, default: '{}', }) public loggedInDates: string[]; + @Column('integer', { + default: '0', + }) + public getPoints: number; @Column('jsonb', { default: [], diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index d324e3e64a..399aa385ef 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -8,13 +8,14 @@ import type { UserProfilesRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; +import { NotificationService } from '@/core/NotificationService.js'; import { ApiError } from '../error.js'; export const meta = { tags: ['account'], requireCredential: true, - kind: "read:account", + kind: 'read:account', res: { type: 'object', @@ -43,7 +44,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, - + private notificationService: NotificationService, private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, user, token) => { @@ -51,7 +52,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const now = new Date(); const today = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`; - + let todayGetPoints = 0; // 渡ってきている user はキャッシュされていて古い可能性があるので改めて取得 const userProfile = await this.userProfilesRepository.findOne({ where: { @@ -65,9 +66,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } if (!userProfile.loggedInDates.includes(today)) { + todayGetPoints = Math.floor(Math.random() * 5) + 1; this.userProfilesRepository.update({ userId: user.id }, { loggedInDates: [...userProfile.loggedInDates, today], }); + this.userProfilesRepository.update({ userId: user.id }, { + getPoints: userProfile.getPoints + todayGetPoints, + }); + this.notificationService.createNotification(user.id, 'loginbonus', { + loginbonus: todayGetPoints, + }); userProfile.loggedInDates = [...userProfile.loggedInDates, today]; } @@ -75,6 +83,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- schema: 'MeDetailed', includeSecrets: isSecure, userProfile, + ...(todayGetPoints && { todayGetPoints }), }); }); } diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index 01d04c5ca8..18fc67f8eb 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -42,6 +42,7 @@ export const notificationTypes = [ 'achievementEarned', 'app', 'test', + 'loginbonus', ] as const; export const groupedNotificationTypes = [ diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index 73cd7cd5b3..c010e2ba7e 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.root"> <div :class="$style.head"> <MkAvatar v-if="['pollEnded', 'note'].includes(notification.type) && notification.note" :class="$style.icon" :user="notification.note.user" link preview/> - <MkAvatar v-else-if="['roleAssigned', 'achievementEarned'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/> + <MkAvatar v-else-if="['roleAssigned', 'achievementEarned', 'loginbonus'].includes(notification.type)" :class="$style.icon" :user="$i" link preview/> <div v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'" :class="[$style.icon, $style.icon_reactionGroupHeart]"><i class="ti ti-heart" style="line-height: 1;"></i></div> <div v-else-if="notification.type === 'reaction:grouped'" :class="[$style.icon, $style.icon_reactionGroup]"><i class="ti ti-plus" style="line-height: 1;"></i></div> <div v-else-if="notification.type === 'renote:grouped'" :class="[$style.icon, $style.icon_renoteGroup]"><i class="ti ti-repeat" style="line-height: 1;"></i></div> @@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only [$style.t_quote]: notification.type === 'quote', [$style.t_pollEnded]: notification.type === 'pollEnded', [$style.t_achievementEarned]: notification.type === 'achievementEarned', + [$style.t_achievementEarned]: notification.type === 'loginbonus', [$style.t_roleAssigned]: notification.type === 'roleAssigned' && notification.role.iconUrl == null, }]" > @@ -37,6 +38,8 @@ SPDX-License-Identifier: AGPL-3.0-only <i v-else-if="notification.type === 'quote'" class="ti ti-quote"></i> <i v-else-if="notification.type === 'pollEnded'" class="ti ti-chart-arrows"></i> <i v-else-if="notification.type === 'achievementEarned'" class="ti ti-medal"></i> + <i v-else-if="notification.type === 'loginbonus'" class="ti ti-medal"></i> + <template v-else-if="notification.type === 'roleAssigned'"> <img v-if="notification.role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="notification.role.iconUrl" alt=""/> <i v-else class="ti ti-badges"></i> @@ -56,6 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else-if="notification.type === 'note'">{{ i18n.ts._notification.newNote }}: <MkUserName :user="notification.note.user"/></span> <span v-else-if="notification.type === 'roleAssigned'">{{ i18n.ts._notification.roleAssigned }}</span> <span v-else-if="notification.type === 'achievementEarned'">{{ i18n.ts._notification.achievementEarned }}</span> + <span v-else-if="notification.type === 'loginbonus'">{{ i18n.ts._notification.loginbonus }}</span> <span v-else-if="notification.type === 'test'">{{ i18n.ts._notification.testNotification }}</span> <MkA v-else-if="notification.type === 'follow' || notification.type === 'mention' || notification.type === 'reply' || notification.type === 'renote' || notification.type === 'quote' || notification.type === 'reaction' || notification.type === 'receiveFollowRequest' || notification.type === 'followRequestAccepted'" v-user-preview="notification.user.id" :class="$style.headerName" :to="userPage(notification.user)"><MkUserName :user="notification.user"/></MkA> <span v-else-if="notification.type === 'reaction:grouped' && notification.note.reactionAcceptance === 'likeOnly'">{{ i18n.tsx._notification.likedBySomeUsers({ n: getActualReactedUsersCount(notification) }) }}</span> @@ -94,10 +98,13 @@ SPDX-License-Identifier: AGPL-3.0-only </MkA> <div v-else-if="notification.type === 'roleAssigned'" :class="$style.text"> {{ notification.role.name }} + </div> <div v-else-if="notification.type === 'loginbonus'" :class="$style.text"> + {{ notification.loginbonus }}プリズム入手しました! </div> <MkA v-else-if="notification.type === 'achievementEarned'" :class="$style.text" to="/my/achievements"> {{ i18n.ts._achievements._types['_' + notification.achievement].title }} </MkA> + <template v-else-if="notification.type === 'follow'"> <span :class="$style.text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}</span> </template> diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 1b26dada99..d2acc2edae 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -11,159 +11,175 @@ SPDX-License-Identifier: AGPL-3.0-only <!-- <div class="punished" v-if="user.isSuspended"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSuspended }}</div> --> <!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> --> - <div class="profile _gaps"> - <MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/> - <MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/> - <MkRemoteInfoUpdate v-if="user.host != null" :UserId="user.id" class="warn"/> - <div :key="user.id" class="main _panel"> - <div class="banner-container" :style="style"> - <div ref="bannerEl" class="banner" :style="style"></div> - <div class="fade"></div> - <div class="title"> - <MkUserName class="name" :user="user" :nowrap="true"/> - <div class="bottom"> - <span class="username"><MkAcct :user="user" :detail="true"/></span> - <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i - class="ti ti-shield"></i></span> - <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> - <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> - <button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea"> - <i class="ti ti-edit"/> {{ i18n.ts.addMemo }} - </button> - </div> - </div> - <span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> - <div v-if="$i" class="actions"> - <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> - <MkNotifyButton v-if="$i.id != user.id " :user="user"></MkNotifyButton> - <MkFollowButton v-if="$i.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" - class="koudoku"/> - </div> - </div> - <MkAvatar class="avatar" :user="user" indicator/> - <div class="title"> - <MkUserName :user="user" :nowrap="false" class="name"/> - <div class="bottom"> - <span class="username"><MkAcct :user="user" :detail="true"/></span> - <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i - class="ti ti-shield"></i></span> - <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> - <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> - </div> - </div> - <div v-if="user.roles.length > 0" class="roles"> - <span v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" - :style="{ '--color': role.color }"> + <div class="profile _gaps"> + <MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/> + <MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/> + <MkRemoteInfoUpdate v-if="user.host != null" :UserId="user.id" class="warn"/> + <div :key="user.id" class="main _panel"> + <div class="banner-container" :style="style"> + <div ref="bannerEl" class="banner" :style="style"></div> + <div class="fade"></div> + <div class="title"> + <MkUserName class="name" :user="user" :nowrap="true"/> + <div class="bottom"> + <span class="username"><MkAcct :user="user" :detail="true"/></span> + <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i + class="ti ti-shield" + ></i></span> + <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> + <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> + <button v-if="$i && !isEditingMemo && !memoDraft" class="_button add-note-button" @click="showMemoTextarea"> + <i class="ti ti-edit"/> {{ i18n.ts.addMemo }} + </button> + </div> + </div> + <span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> + <div v-if="$i" class="actions"> + <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> + <MkNotifyButton v-if="$i.id != user.id " :user="user"></MkNotifyButton> + <MkFollowButton + v-if="$i.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" + class="koudoku" + /> + </div> + </div> + <MkAvatar class="avatar" :user="user" indicator/> + <div class="title"> + <MkUserName :user="user" :nowrap="false" class="name"/> + <div class="bottom"> + <span class="username"><MkAcct :user="user" :detail="true"/></span> + <span v-if="user.isAdmin" :title="i18n.ts.isAdmin" style="color: var(--badge);"><i + class="ti ti-shield" + ></i></span> + <span v-if="user.isLocked" :title="i18n.ts.isLocked"><i class="ti ti-lock"></i></span> + <span v-if="user.isBot" :title="i18n.ts.isBot"><i class="ti ti-robot"></i></span> + </div> + </div> + <div v-if="user.roles.length > 0" class="roles"> + <span + v-for="role in user.roles" :key="role.id" v-tooltip="role.description" class="role" + :style="{ '--color': role.color }" + > <MkA v-adaptive-bg :to="`/roles/${role.id}`"> <img v-if="role.iconUrl" style="height: 1.3em; vertical-align: -22%;" :src="role.iconUrl"/> {{ role.name }} </MkA> </span> - </div> - <div v-if="iAmModerator" class="moderationNote"> - <MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" - v-model="moderationNote" manualSave> - <template #label>{{ i18n.ts.moderationNote }}</template> - </MkTextarea> - <div v-else> - <MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton> - </div> - </div> - <div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}"> - <div class="heading" v-text="i18n.ts.memo"/> - <textarea - ref="memoTextareaEl" - v-model="memoDraft" - rows="1" - @focus="isEditingMemo = true" - @blur="updateMemo" - @input="adjustMemoTextarea" - /> - </div> - <div class="description"> - <MkOmit> - <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user"/> - <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> - </MkOmit> - </div> - <div class="fields system"> - <dl v-if="user.location" class="field"> - <dt class="name"><i class="ti ti-map-pin ti-fw"></i> {{ i18n.ts.location }}</dt> - <dd class="value">{{ user.location }}</dd> - </dl> - <dl v-if="user.birthday" class="field"> - <dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt> - <dd class="value">{{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ - i18n.tsx.yearsOld({age}) - }}) - </dd> - </dl> - <dl class="field"> - <dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt> - <dd class="value">{{ dateString(user.createdAt) }} ( - <MkTime :time="user.createdAt"/> - ) - </dd> - </dl> - </div> - <div v-if="user.fields.length > 0" class="fields"> - <dl v-for="(field, i) in user.fields" :key="i" class="field"> - <dt class="name"> - <Mfm :text="field.name" :plain="true" :colored="false"/> - </dt> - <dd class="value"> - <Mfm :text="field.value" :author="user" :colored="false"/> - <i v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" - class="ti ti-circle-check" :class="$style.verifiedLink"></i> - </dd> - </dl> - </div> - <div class="status"> - <MkA :to="userPage(user)"> - <b>{{ number(user.notesCount) }}</b> - <span>{{ i18n.ts.notes }}</span> - </MkA> - <MkA v-if="isFollowingVisibleForMe(user)" :to="userPage(user, 'following')"> - <b>{{ number(user.followingCount) }}</b> - <span>{{ i18n.ts.following }}</span> - </MkA> - <MkA v-if="isFollowersVisibleForMe(user)" :to="userPage(user, 'followers')"> - <b>{{ number(user.followersCount) }}</b> - <span>{{ i18n.ts.followers }}</span> - </MkA> - </div> - </div> - </div> + </div> + <div v-if="iAmModerator" class="moderationNote"> + <MkTextarea + v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" + v-model="moderationNote" manualSave + > + <template #label>{{ i18n.ts.moderationNote }}</template> + </MkTextarea> + <div v-else> + <MkButton small @click="editModerationNote = true">{{ i18n.ts.addModerationNote }}</MkButton> + </div> + </div> + <div v-if="isEditingMemo || memoDraft" class="memo" :class="{'no-memo': !memoDraft}"> + <div class="heading" v-text="i18n.ts.memo"/> + <textarea + ref="memoTextareaEl" + v-model="memoDraft" + rows="1" + @focus="isEditingMemo = true" + @blur="updateMemo" + @input="adjustMemoTextarea" + /> + </div> + <div class="description"> + <MkOmit> + <Mfm v-if="user.description" :text="user.description" :isNote="false" :author="user"/> + <p v-else class="empty">{{ i18n.ts.noAccountDescription }}</p> + </MkOmit> + </div> + <div class="fields system"> + <dl v-if="user.location" class="field"> + <dt class="name"><i class="ti ti-map-pin ti-fw"></i> {{ i18n.ts.location }}</dt> + <dd class="value">{{ user.location }}</dd> + </dl> + <dl v-if="user.birthday" class="field"> + <dt class="name"><i class="ti ti-cake ti-fw"></i> {{ i18n.ts.birthday }}</dt> + <dd class="value"> + {{ user.birthday.replace('-', '/').replace('-', '/') }} ({{ + i18n.tsx.yearsOld({age}) + }}) + </dd> + </dl> + <dl class="field"> + <dt class="name"><i class="ti ti-calendar ti-fw"></i> {{ i18n.ts.registeredDate }}</dt> + <dd class="value"> + {{ dateString(user.createdAt) }} ( + <MkTime :time="user.createdAt"/> + ) + </dd> + </dl> + </div> + <div v-if="user.fields.length > 0" class="fields"> + <dl v-for="(field, i) in user.fields" :key="i" class="field"> + <dt class="name"> + <Mfm :text="field.name" :plain="true" :colored="false"/> + </dt> + <dd class="value"> + <Mfm :text="field.value" :author="user" :colored="false"/> + <i + v-if="user.verifiedLinks.includes(field.value)" v-tooltip:dialog="i18n.ts.verifiedLink" + class="ti ti-circle-check" :class="$style.verifiedLink" + ></i> + </dd> + </dl> + </div> + <div class="status"> + <MkA :to="userPage(user)"> + <b>{{ number(user.notesCount) }}</b> + <span>{{ i18n.ts.notes }}</span> + </MkA> + <MkA v-if="isFollowingVisibleForMe(user)" :to="userPage(user, 'following')"> + <b>{{ number(user.followingCount) }}</b> + <span>{{ i18n.ts.following }}</span> + </MkA> + <MkA v-if="isFollowersVisibleForMe(user)" :to="userPage(user, 'followers')"> + <b>{{ number(user.followersCount) }}</b> + <span>{{ i18n.ts.followers }}</span> + </MkA> + <MkA> + <b> {{ number(user.getPoints) }}</b> + <span>{{ i18n.ts.points }}</span> + </MkA> + </div> + </div> + </div> - <div class="contents _gaps"> - <div v-if="user.pinnedNotes.length > 0" class="_gaps"> - <MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> - </div> - <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> - <template v-if="narrow"> - <MkLazy> + <div class="contents _gaps"> + <div v-if="user.pinnedNotes.length > 0" class="_gaps"> + <MkNote v-for="note in user.pinnedNotes" :key="note.id" class="note _panel" :note="note" :pinned="true"/> + </div> + <MkInfo v-else-if="$i && $i.id === user.id">{{ i18n.ts.userPagePinTip }}</MkInfo> + <template v-if="narrow"> + <MkLazy> <XFiles :key="user.id" :user="user"/> - </MkLazy> + </MkLazy> <MkLazy> <XActivity :key="user.id" :user="user"/> - </MkLazy> + </MkLazy> </template> - <div> - <div style="margin-bottom: 8px;">{{ i18n.ts._sfx.note }}</div> - <MkNotes :class="$style.tl" :noGap="true" :pagination="Notes"/> - </div> - </div> - </div> - <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> - <XFiles :key="user.id" :user="user"/> - <XActivity :key="user.id" :user="user"/> - </div> - </div> - </MkSpacer> + <div> + <div style="margin-bottom: 8px;">{{ i18n.ts._sfx.note }}</div> + <MkNotes :class="$style.tl" :noGap="true" :pagination="Notes"/> + </div> + </div> + </div> + <div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;"> + <XFiles :key="user.id" :user="user"/> + <XActivity :key="user.id" :user="user"/> + </div> + </div> +</MkSpacer> </template> <script lang="ts" setup> -import {defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue'; +import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkNote from '@/components/MkNote.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; @@ -173,36 +189,36 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkOmit from '@/components/MkOmit.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkButton from '@/components/MkButton.vue'; -import {getScrollPosition} from '@/scripts/scroll.js'; -import {getUserMenu} from '@/scripts/get-user-menu.js'; +import { getScrollPosition } from '@/scripts/scroll.js'; +import { getUserMenu } from '@/scripts/get-user-menu.js'; import number from '@/filters/number.js'; -import {userPage} from '@/filters/user.js'; +import { userPage } from '@/filters/user.js'; import * as os from '@/os.js'; import { useRouter } from '@/router/supplier.js'; -import {i18n} from '@/i18n.js'; -import {$i, iAmModerator} from '@/account.js'; -import {dateString} from '@/filters/date.js'; -import {confetti} from '@/scripts/confetti.js'; -import {misskeyApi} from '@/scripts/misskey-api.js'; -import {isFollowingVisibleForMe, isFollowersVisibleForMe} from '@/scripts/isFfVisibleForMe.js'; -import MkNotifyButton from "@/components/MkNotifyButton.vue"; -import MkRemoteInfoUpdate from "@/components/MkRemoteInfoUpdate.vue"; -import MkNotes from "@/components/MkNotes.vue"; -import MkLazy from "@/components/global/MkLazy.vue"; +import { i18n } from '@/i18n.js'; +import { $i, iAmModerator } from '@/account.js'; +import { dateString } from '@/filters/date.js'; +import { confetti } from '@/scripts/confetti.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; +import MkNotifyButton from '@/components/MkNotifyButton.vue'; +import MkRemoteInfoUpdate from '@/components/MkRemoteInfoUpdate.vue'; +import MkNotes from '@/components/MkNotes.vue'; +import MkLazy from '@/components/global/MkLazy.vue'; function calcAge(birthdate: string): number { - const date = new Date(birthdate); - const now = new Date(); + const date = new Date(birthdate); + const now = new Date(); - let yearDiff = now.getFullYear() - date.getFullYear(); - const monthDiff = now.getMonth() - date.getMonth(); - const pastDate = now.getDate() < date.getDate(); + let yearDiff = now.getFullYear() - date.getFullYear(); + const monthDiff = now.getMonth() - date.getMonth(); + const pastDate = now.getDate() < date.getDate(); - if (monthDiff < 0 || (monthDiff === 0 && pastDate)) { - yearDiff--; - } + if (monthDiff < 0 || (monthDiff === 0 && pastDate)) { + yearDiff--; + } - return yearDiff; + return yearDiff; } const XFiles = defineAsyncComponent(() => import('./index.files.vue')); @@ -214,7 +230,7 @@ const props = withDefaults(defineProps<{ /** Test only; MkNotes currently causes problems in vitest */ disableNotes: boolean; }>(), { - disableNotes: false, + disableNotes: false, }); const router = useRouter(); @@ -231,107 +247,107 @@ const moderationNote = ref(props.user.moderationNote); const editModerationNote = ref(false); watch(moderationNote, async () => { - await misskeyApi('admin/update-user-note', {userId: props.user.id, text: moderationNote.value }); + await misskeyApi('admin/update-user-note', { userId: props.user.id, text: moderationNote.value }); }); const pagination = { - endpoint: 'users/featured-notes' as const, - limit: 10, - params: computed(() => ({ - userId: props.user.id, - })), + endpoint: 'users/featured-notes' as const, + limit: 10, + params: computed(() => ({ + userId: props.user.id, + })), +}; +const Notes = { + endpoint: 'users/notes' as const, + limit: 10, + params: computed(() => ({ + userId: props.user.id, + })), }; -const Notes ={ - endpoint: 'users/notes' as const, - limit: 10, - params: computed(() => ({ - userId: props.user.id, - })), -} const style = computed(() => { - if (props.user.bannerUrl == null) return {}; - return { - backgroundImage: `url(${props.user.bannerUrl})`, - }; + if (props.user.bannerUrl == null) return {}; + return { + backgroundImage: `url(${props.user.bannerUrl})`, + }; }); const age = computed(() => { - return calcAge(props.user.birthday); + return calcAge(props.user.birthday); }); function menu(ev: MouseEvent) { - const {menu, cleanup} = getUserMenu(user.value, router); - os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); + const { menu, cleanup } = getUserMenu(user.value, router); + os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); } function parallaxLoop() { - parallaxAnimationId.value = window.requestAnimationFrame(parallaxLoop); - parallax(); + parallaxAnimationId.value = window.requestAnimationFrame(parallaxLoop); + parallax(); } function parallax() { - const banner = bannerEl.value as any; - if (banner == null) return; + const banner = bannerEl.value as any; + if (banner == null) return; - const top = getScrollPosition(rootEl.value); + const top = getScrollPosition(rootEl.value); - if (top < 0) return; + if (top < 0) return; - const z = 1.75; // 奥行き(小さいほど奥) - const pos = -(top / z); - banner.style.backgroundPosition = `center calc(50% - ${pos}px)`; + const z = 1.75; // 奥行き(小さいほど奥) + const pos = -(top / z); + banner.style.backgroundPosition = `center calc(50% - ${pos}px)`; } function showMemoTextarea() { - isEditingMemo.value = true; - nextTick(() => { - memoTextareaEl.value?.focus(); - }); + isEditingMemo.value = true; + nextTick(() => { + memoTextareaEl.value?.focus(); + }); } function adjustMemoTextarea() { - if (!memoTextareaEl.value) return; - memoTextareaEl.value.style.height = '0px'; - memoTextareaEl.value.style.height = `${memoTextareaEl.value.scrollHeight}px`; + if (!memoTextareaEl.value) return; + memoTextareaEl.value.style.height = '0px'; + memoTextareaEl.value.style.height = `${memoTextareaEl.value.scrollHeight}px`; } async function updateMemo() { - await misskeyApi('users/update-memo', { - memo: memoDraft.value, - userId: props.user.id, - }); - isEditingMemo.value = false; + await misskeyApi('users/update-memo', { + memo: memoDraft.value, + userId: props.user.id, + }); + isEditingMemo.value = false; } watch([props.user], () => { - memoDraft.value = props.user.memo; + memoDraft.value = props.user.memo; }); onMounted(() => { - window.requestAnimationFrame(parallaxLoop); - narrow.value = rootEl.value!.clientWidth < 1000; + window.requestAnimationFrame(parallaxLoop); + narrow.value = rootEl.value!.clientWidth < 1000; - if (props.user.birthday) { - const m = new Date().getMonth() + 1; - const d = new Date().getDate(); - const bm = parseInt(props.user.birthday.split('-')[1]); - const bd = parseInt(props.user.birthday.split('-')[2]); - if (m === bm && d === bd) { - confetti({ - duration: 1000 * 4, - }); - } - } - nextTick(() => { - adjustMemoTextarea(); - }); + if (props.user.birthday) { + const m = new Date().getMonth() + 1; + const d = new Date().getDate(); + const bm = parseInt(props.user.birthday.split('-')[1]); + const bd = parseInt(props.user.birthday.split('-')[2]); + if (m === bm && d === bd) { + confetti({ + duration: 1000 * 4, + }); + } + } + nextTick(() => { + adjustMemoTextarea(); + }); }); onUnmounted(() => { - if (parallaxAnimationId.value) { - window.cancelAnimationFrame(parallaxAnimationId.value); - } + if (parallaxAnimationId.value) { + window.cancelAnimationFrame(parallaxAnimationId.value); + } }); </script> From 6a07fa78dd23dd617440fb8eb69441b577265b47 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 01:08:17 +0900 Subject: [PATCH 440/501] update --- packages/backend/migration/1715787239605-loginbonus.js | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 packages/backend/migration/1715787239605-loginbonus.js diff --git a/packages/backend/migration/1715787239605-loginbonus.js b/packages/backend/migration/1715787239605-loginbonus.js new file mode 100644 index 0000000000..ce7410277f --- /dev/null +++ b/packages/backend/migration/1715787239605-loginbonus.js @@ -0,0 +1,9 @@ +export class Loginbonus1715787239605 { + name = 'Loginbonus1715787239605' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "getPoints" integer NOT NULL DEFAULT '0'`); } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "getPoints"`); } +} From ed14ddad0c087671cdef4428620cc078d6364ae7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 01:15:57 +0900 Subject: [PATCH 441/501] update --- packages/backend/src/core/entities/UserEntityService.ts | 6 +----- packages/backend/src/server/api/endpoints/i.ts | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 9d9c2eaec5..3cdd66b53b 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -404,7 +404,6 @@ export class UserEntityService implements OnModuleInit { userRelations?: Map<MiUser['id'], UserRelation>, userMemos?: Map<MiUser['id'], string | null>, pinNotes?: Map<MiUser['id'], MiUserNotePining[]>, - todayGetPoints?: number, }, ): Promise<Packed<S>> { const opts = Object.assign({ @@ -508,7 +507,7 @@ export class UserEntityService implements OnModuleInit { iconUrl: r.iconUrl, displayOrder: r.displayOrder, }))) : undefined, - ...(user.host == null ? { getPoints: profile!.getPoints } : {}), + ...(user.host == null ? { getPoints: user.getPoints } : {}), ...(isDetailed ? { url: profile!.url, uri: user.uri, @@ -603,9 +602,6 @@ export class UserEntityService implements OnModuleInit { achievements: profile!.achievements, loggedInDays: profile!.loggedInDates.length, policies: this.roleService.getUserPolicies(user.id), - ...(opts.todayGetPoints ? { - todayGetPoints: opts.todayGetPoints, - } : {}), } : {}), ...(opts.includeSecrets ? { diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 399aa385ef..08bf8b70c2 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -83,7 +83,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- schema: 'MeDetailed', includeSecrets: isSecure, userProfile, - ...(todayGetPoints && { todayGetPoints }), }); }); } From 4a0138297e60c36f6d72b510c0ebfa7211260b41 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 01:50:40 +0900 Subject: [PATCH 442/501] update --- package.json | 2 +- .../backend/migration/1715787239605-loginbonus.js | 6 ++++-- .../backend/migration/1715791271605-loginbonus1.js | 13 +++++++++++++ .../backend/src/core/entities/UserEntityService.ts | 3 +-- packages/backend/src/models/User.ts | 5 +++++ packages/backend/src/models/UserProfile.ts | 4 ---- packages/backend/src/server/api/endpoints/i.ts | 7 +++++-- 7 files changed, 29 insertions(+), 11 deletions(-) create mode 100644 packages/backend/migration/1715791271605-loginbonus1.js diff --git a/package.json b/package.json index 6310884de8..6165b68e55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea1", + "version": "2024.5.0-beta1-mattyatea2", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/migration/1715787239605-loginbonus.js b/packages/backend/migration/1715787239605-loginbonus.js index ce7410277f..b12fa3c76c 100644 --- a/packages/backend/migration/1715787239605-loginbonus.js +++ b/packages/backend/migration/1715787239605-loginbonus.js @@ -1,9 +1,11 @@ +import {Column} from "typeorm"; + export class Loginbonus1715787239605 { name = 'Loginbonus1715787239605' async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "getPoints" integer NOT NULL DEFAULT '0'`); } + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "getPoints"`); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "getPoints"`); } + await queryRunner.query(`ALTER TABLE "user_profile" ADD "getPoints" integer NOT NULL DEFAULT '0'`); } } diff --git a/packages/backend/migration/1715791271605-loginbonus1.js b/packages/backend/migration/1715791271605-loginbonus1.js new file mode 100644 index 0000000000..7dc8e1e709 --- /dev/null +++ b/packages/backend/migration/1715791271605-loginbonus1.js @@ -0,0 +1,13 @@ +export class Loginbonus11715791271605 { + name = 'Loginbonus11715791271605' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "getPoints"`); + await queryRunner.query(`ALTER TABLE "user" ADD "getPoints" integer NOT NULL DEFAULT '0'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "getPoints"`); + await queryRunner.query(`ALTER TABLE "user_profile" ADD "getPoints" integer NOT NULL DEFAULT '0'`); + } +} diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index 3cdd66b53b..b8c8253126 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -471,9 +471,8 @@ export class UserEntityService implements OnModuleInit { createdAt: this.idService.parse(announcement.id).date.toISOString(), ...announcement, })) : null; - + console.log(user.getPoints); const notificationsInfo = isMe && isDetailed ? await this.getNotificationsInfo(user.id) : null; - const packed = { id: user.id, name: user.name, diff --git a/packages/backend/src/models/User.ts b/packages/backend/src/models/User.ts index 837e6bc966..7bfba85b95 100644 --- a/packages/backend/src/models/User.ts +++ b/packages/backend/src/models/User.ts @@ -25,6 +25,11 @@ export class MiUser { }) public lastFetchedAt: Date | null; + @Column('integer', { + default: '0', + }) + public getPoints: number; + @Index() @Column('timestamp with time zone', { nullable: true, diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index ef70a297a9..7dbe0b3717 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -261,10 +261,6 @@ export class MiUserProfile { length: 32, array: true, default: '{}', }) public loggedInDates: string[]; - @Column('integer', { - default: '0', - }) - public getPoints: number; @Column('jsonb', { default: [], diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 08bf8b70c2..475468dac3 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -4,7 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import type { UserProfilesRepository } from '@/models/_.js'; +import type { UserProfilesRepository, UsersRepository } from '@/models/_.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -44,6 +44,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- constructor( @Inject(DI.userProfilesRepository) private userProfilesRepository: UserProfilesRepository, + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private notificationService: NotificationService, private userEntityService: UserEntityService, ) { @@ -70,7 +73,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.userProfilesRepository.update({ userId: user.id }, { loggedInDates: [...userProfile.loggedInDates, today], }); - this.userProfilesRepository.update({ userId: user.id }, { + this.usersRepository.update({ userId: user.id }, { getPoints: userProfile.getPoints + todayGetPoints, }); this.notificationService.createNotification(user.id, 'loginbonus', { From db588acce0fe341dd6acbc2a5aca48efe07e74e7 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 03:21:57 +0900 Subject: [PATCH 443/501] update --- packages/frontend/src/pages/user/home.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index d2acc2edae..65c5517898 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -143,7 +143,7 @@ SPDX-License-Identifier: AGPL-3.0-only <b>{{ number(user.followersCount) }}</b> <span>{{ i18n.ts.followers }}</span> </MkA> - <MkA> + <MkA v-if="!user.host"> <b> {{ number(user.getPoints) }}</b> <span>{{ i18n.ts.points }}</span> </MkA> From 514595c5a78db6ed0c4624633bd09d933030249b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 06:25:32 +0900 Subject: [PATCH 444/501] update --- .../backend/src/server/api/endpoints/i.ts | 4 +-- .../frontend/src/components/MkMediaAudio.vue | 33 ++++++++++++++----- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 475468dac3..4284670499 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -73,8 +73,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.userProfilesRepository.update({ userId: user.id }, { loggedInDates: [...userProfile.loggedInDates, today], }); - this.usersRepository.update({ userId: user.id }, { - getPoints: userProfile.getPoints + todayGetPoints, + this.usersRepository.update( user.id, { + getPoints: user.getPoints + todayGetPoints, }); this.notificationService.createNotification(user.id, 'loginbonus', { loginbonus: todayGetPoints, diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 5d2edf467e..c38754a371 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div + :id="(audio.url).slice(32).replace('-','')" ref="playerEl" v-hotkey="keymap" tabindex="0" @@ -29,9 +30,9 @@ SPDX-License-Identifier: AGPL-3.0-only preload="metadata" controls :class="$style.nativeAudio" + :src="audio.url" @keydown.prevent > - <source :src="audio.url"> </audio> </div> @@ -39,8 +40,8 @@ SPDX-License-Identifier: AGPL-3.0-only <audio ref="audioEl" preload="metadata" + :src="audio.url" > - <source :src="audio.url"> </audio> <div :class="[$style.controlsChild, $style.controlsLeft]"> <button class="_button" :class="$style.controlButton" @click="togglePlayPause"> @@ -64,10 +65,24 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.volumeSeekbar" /> </div> + {{ audioEl }} + <WaveSurferPlayer + :class="$style.seekbarRoot" + :options="{ media: audioEl, + height: 32, + waveColor: 'gray', + progressColor: 'red', + barGap: 5, + barWidth: 5, + barRadius: 8, + duration: 80, + backend: 'WebAudio' + }" + ></WaveSurferPlayer> <MkMediaRange + v-if="defaultStore.state.dataSaver.media && !hide" v-model="rangePercent" :class="$style.seekbarRoot" - :buffer="bufferedDataRatio" /> </div> </div> @@ -76,6 +91,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; +import { WaveSurferPlayer } from '@meersagor/wavesurfer-vue'; +import type WaveSurfer from 'wavesurfer.js'; import type { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -84,7 +101,6 @@ import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; import { $i, iAmModerator } from '@/account.js'; - const props = defineProps<{ audio: Misskey.entities.DriveFile; }>(); @@ -132,6 +148,8 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.data // Menu const menuShowing = ref(false); +const waveSuferOptions = ref(); + function showMenu(ev: MouseEvent) { let menu: MenuItem[] = []; @@ -226,10 +244,7 @@ const volume = ref(.25); const speed = ref(1); const loop = ref(false); // TODO: ドライブファイルのフラグに置き換える const bufferedEnd = ref(0); -const bufferedDataRatio = computed(() => { - if (!audioEl.value) return 0; - return bufferedEnd.value / audioEl.value.duration; -}); +let audioContext = new AudioContext(); // MediaControl Events function togglePlayPause() { @@ -325,7 +340,7 @@ watch(loop, (to) => { if (audioEl.value) audioEl.value.loop = to; }); -onMounted(() => { +onMounted(async () => { init(); }); From a6e8743071cdb4d841b60f82897e2aa83905bd85 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 06:26:15 +0900 Subject: [PATCH 445/501] update --- .../frontend/src/components/MkMediaAudio.vue | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index c38754a371..5d2edf467e 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -5,7 +5,6 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div - :id="(audio.url).slice(32).replace('-','')" ref="playerEl" v-hotkey="keymap" tabindex="0" @@ -30,9 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only preload="metadata" controls :class="$style.nativeAudio" - :src="audio.url" @keydown.prevent > + <source :src="audio.url"> </audio> </div> @@ -40,8 +39,8 @@ SPDX-License-Identifier: AGPL-3.0-only <audio ref="audioEl" preload="metadata" - :src="audio.url" > + <source :src="audio.url"> </audio> <div :class="[$style.controlsChild, $style.controlsLeft]"> <button class="_button" :class="$style.controlButton" @click="togglePlayPause"> @@ -65,24 +64,10 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.volumeSeekbar" /> </div> - {{ audioEl }} - <WaveSurferPlayer - :class="$style.seekbarRoot" - :options="{ media: audioEl, - height: 32, - waveColor: 'gray', - progressColor: 'red', - barGap: 5, - barWidth: 5, - barRadius: 8, - duration: 80, - backend: 'WebAudio' - }" - ></WaveSurferPlayer> <MkMediaRange - v-if="defaultStore.state.dataSaver.media && !hide" v-model="rangePercent" :class="$style.seekbarRoot" + :buffer="bufferedDataRatio" /> </div> </div> @@ -91,8 +76,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; -import { WaveSurferPlayer } from '@meersagor/wavesurfer-vue'; -import type WaveSurfer from 'wavesurfer.js'; import type { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -101,6 +84,7 @@ import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; import { $i, iAmModerator } from '@/account.js'; + const props = defineProps<{ audio: Misskey.entities.DriveFile; }>(); @@ -148,8 +132,6 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.data // Menu const menuShowing = ref(false); -const waveSuferOptions = ref(); - function showMenu(ev: MouseEvent) { let menu: MenuItem[] = []; @@ -244,7 +226,10 @@ const volume = ref(.25); const speed = ref(1); const loop = ref(false); // TODO: ドライブファイルのフラグに置き換える const bufferedEnd = ref(0); -let audioContext = new AudioContext(); +const bufferedDataRatio = computed(() => { + if (!audioEl.value) return 0; + return bufferedEnd.value / audioEl.value.duration; +}); // MediaControl Events function togglePlayPause() { @@ -340,7 +325,7 @@ watch(loop, (to) => { if (audioEl.value) audioEl.value.loop = to; }); -onMounted(async () => { +onMounted(() => { init(); }); From c0480ac26da2c8740579a31fc4fae327909de037 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 06:51:50 +0900 Subject: [PATCH 446/501] update --- packages/frontend/package.json | 4 +- .../frontend/src/components/MkMediaAudio.vue | 35 ++++-- pnpm-lock.yaml | 114 +++++++++++------- 3 files changed, 96 insertions(+), 57 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index a482373200..32b73ec7e2 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -20,6 +20,7 @@ "@discordapp/twemoji": "15.0.3", "@github/webauthn-json": "2.1.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", + "@meersagor/wavesurfer-vue": "^0.1.0", "@misskey-dev/browser-image-resizer": "2024.1.0", "@rollup/plugin-json": "6.1.0", "@rollup/plugin-replace": "5.0.5", @@ -73,7 +74,8 @@ "v-code-diff": "1.11.0", "vite": "5.2.11", "vue": "3.4.26", - "vuedraggable": "next" + "vuedraggable": "next", + "wavesurfer.js": "^7.7.14" }, "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 5d2edf467e..960fe47d81 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -5,6 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div + :id="(audio.url).slice(32).replace('-','')" ref="playerEl" v-hotkey="keymap" tabindex="0" @@ -29,9 +30,9 @@ SPDX-License-Identifier: AGPL-3.0-only preload="metadata" controls :class="$style.nativeAudio" + :src="audio.url" @keydown.prevent > - <source :src="audio.url"> </audio> </div> @@ -39,8 +40,8 @@ SPDX-License-Identifier: AGPL-3.0-only <audio ref="audioEl" preload="metadata" + :src="audio.url" > - <source :src="audio.url"> </audio> <div :class="[$style.controlsChild, $style.controlsLeft]"> <button class="_button" :class="$style.controlButton" @click="togglePlayPause"> @@ -64,10 +65,25 @@ SPDX-License-Identifier: AGPL-3.0-only :class="$style.volumeSeekbar" /> </div> + + <WaveSurferPlayer + v-if="!defaultStore.state.dataSaver.media && audioEl" + :class="$style.seekbarRoot" + :options="{ media: audioEl, + height: 32, + waveColor: 'gray', + progressColor: accent, + barGap: 3, + barWidth: 3, + barRadius: 5, + duration: 80, + cursorWidth: 0, + }" + ></WaveSurferPlayer> <MkMediaRange + v-if="defaultStore.state.dataSaver.media && !hide" v-model="rangePercent" :class="$style.seekbarRoot" - :buffer="bufferedDataRatio" /> </div> </div> @@ -76,6 +92,8 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; +import { WaveSurferPlayer } from '@meersagor/wavesurfer-vue'; +import tinycolor from 'tinycolor2'; import type { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -84,11 +102,9 @@ import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; import { $i, iAmModerator } from '@/account.js'; - const props = defineProps<{ audio: Misskey.entities.DriveFile; }>(); - const keymap = { 'up': () => { if (hasFocus() && audioEl.value) { @@ -131,6 +147,7 @@ const hide = ref((defaultStore.state.nsfw === 'force' || defaultStore.state.data // Menu const menuShowing = ref(false); +const accent = ref(); function showMenu(ev: MouseEvent) { let menu: MenuItem[] = []; @@ -226,10 +243,6 @@ const volume = ref(.25); const speed = ref(1); const loop = ref(false); // TODO: ドライブファイルのフラグに置き換える const bufferedEnd = ref(0); -const bufferedDataRatio = computed(() => { - if (!audioEl.value) return 0; - return bufferedEnd.value / audioEl.value.duration; -}); // MediaControl Events function togglePlayPause() { @@ -325,7 +338,9 @@ watch(loop, (to) => { if (audioEl.value) audioEl.value.loop = to; }); -onMounted(() => { +onMounted(async () => { + accent.value = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); + init(); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 91b6c6948f..52839285d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -697,6 +697,9 @@ importers: '@mcaptcha/vanilla-glue': specifier: 0.1.0-alpha-3 version: 0.1.0-alpha-3 + '@meersagor/wavesurfer-vue': + specifier: ^0.1.0 + version: 0.1.0(typescript@5.4.5) '@misskey-dev/browser-image-resizer': specifier: 2024.1.0 version: 2024.1.0 @@ -859,6 +862,9 @@ importers: vuedraggable: specifier: next version: 4.1.0(vue@3.4.26(typescript@5.4.5)) + wavesurfer.js: + specifier: ^7.7.14 + version: 7.7.14 devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 @@ -3042,6 +3048,9 @@ packages: '@types/react': '>=16' react: '>=16' + '@meersagor/wavesurfer-vue@0.1.0': + resolution: {integrity: sha512-DjOkssn3IgUxNLFlT4X9jpgxdTo+ykSHV762vwfJjRfKwaIna0cEUmOGpKDNDESP4XEHZKuWgrFWp2iEzPkGww==} + '@microsoft/api-extractor-model@7.28.14': resolution: {integrity: sha512-Bery/c8A8SsKPSvA82cTTuy/+OcxZbLRmKhPkk91/AJOQzxZsShcrmHFAGeiEqSIrv1nPZ3tKq9kfMLdCHmsqg==} @@ -10873,6 +10882,9 @@ packages: vue-component-type-helpers@2.0.16: resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==} + vue-component-type-helpers@2.0.18: + resolution: {integrity: sha512-zi1QaDBhSb3oeHJh55aTCrosFNKEQsOL9j3XCAjpF9dwxDUUtd85RkJVzO+YpJqy1LNoCWLU8gwuZ7HW2iDN/A==} + vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} engines: {node: '>=12'} @@ -10944,6 +10956,9 @@ packages: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} + wavesurfer.js@7.7.14: + resolution: {integrity: sha512-sbd48yHnOVDEbZwsnD3dWzBj4SF2q2rsPysmPE8spoR2XwKVkU/POtZg/L0wPi6ypqXb7brQLftSeOovJNToPQ==} + wcwidth@1.0.1: resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} @@ -11659,7 +11674,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11679,7 +11694,7 @@ snapshots: '@babel/traverse': 7.24.0 '@babel/types': 7.24.0 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -11749,7 +11764,7 @@ snapshots: '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -12520,7 +12535,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.9 '@babel/types': 7.23.5 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12535,7 +12550,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.24.0 '@babel/types': 7.24.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -12927,7 +12942,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) espree: 9.6.1 globals: 13.19.0 ignore: 5.2.4 @@ -13078,7 +13093,7 @@ snapshots: '@humanwhocodes/config-array@0.11.13': dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -13086,7 +13101,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -13470,6 +13485,13 @@ snapshots: '@types/react': 18.0.28 react: 18.3.1 + '@meersagor/wavesurfer-vue@0.1.0(typescript@5.4.5)': + dependencies: + vue: 3.4.26(typescript@5.4.5) + wavesurfer.js: 7.7.14 + transitivePeerDependencies: + - typescript + '@microsoft/api-extractor-model@7.28.14(@types/node@20.12.7)': dependencies: '@microsoft/tsdoc': 0.14.2 @@ -14954,7 +14976,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.4.26(typescript@5.4.5) - vue-component-type-helpers: 2.0.16 + vue-component-type-helpers: 2.0.18 transitivePeerDependencies: - encoding - supports-color @@ -15617,7 +15639,7 @@ snapshots: '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.53.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -15637,7 +15659,7 @@ snapshots: '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -15657,7 +15679,7 @@ snapshots: '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -15675,7 +15697,7 @@ snapshots: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.53.0 optionalDependencies: typescript: 5.3.3 @@ -15688,7 +15710,7 @@ snapshots: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 optionalDependencies: typescript: 5.3.3 @@ -15701,7 +15723,7 @@ snapshots: '@typescript-eslint/types': 7.7.1 '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 optionalDependencies: typescript: 5.4.5 @@ -15727,7 +15749,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.53.0 ts-api-utils: 1.0.1(typescript@5.3.3) optionalDependencies: @@ -15739,7 +15761,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 ts-api-utils: 1.0.1(typescript@5.3.3) optionalDependencies: @@ -15751,7 +15773,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: @@ -15769,7 +15791,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -15783,7 +15805,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -15798,7 +15820,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.7.1 '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 @@ -16124,13 +16146,13 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color agent-base@7.1.0: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -16360,7 +16382,7 @@ snapshots: dependencies: '@fastify/error': 3.4.0 archy: 1.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) fastq: 1.17.1 transitivePeerDependencies: - supports-color @@ -17468,7 +17490,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -17684,7 +17706,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.20.2): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) esbuild: 0.20.2 transitivePeerDependencies: - supports-color @@ -17954,7 +17976,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -17997,7 +18019,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -18459,7 +18481,7 @@ snapshots: follow-redirects@1.15.2(debug@4.3.4): optionalDependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) for-each@0.3.3: dependencies: @@ -18911,7 +18933,7 @@ snapshots: http-proxy-agent@7.0.0: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -18950,14 +18972,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -19038,7 +19060,7 @@ snapshots: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -19291,7 +19313,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -20355,7 +20377,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -21470,10 +21492,6 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - proxycheck-ts@0.0.9: - dependencies: - cross-fetch: 4.0.0 - proxy-from-env@1.0.0: {} proxy-from-env@1.1.0: {} @@ -22191,7 +22209,7 @@ snapshots: dependencies: '@hapi/hoek': 10.0.1 '@hapi/wreck': 18.0.1 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) joi: 17.11.0 transitivePeerDependencies: - supports-color @@ -22289,7 +22307,7 @@ snapshots: socks-proxy-agent@8.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -22386,7 +22404,7 @@ snapshots: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -22899,7 +22917,7 @@ snapshots: chalk: 4.1.2 cli-highlight: 2.1.11 dayjs: 1.11.10 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) dotenv: 16.0.3 glob: 10.3.10 mkdirp: 2.1.6 @@ -23109,7 +23127,7 @@ snapshots: vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3): dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) mlly: 1.5.0 pathe: 1.1.2 picocolors: 1.0.0 @@ -23158,7 +23176,7 @@ snapshots: acorn-walk: 8.3.2 cac: 6.7.14 chai: 4.3.10 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) local-pkg: 0.4.3 magic-string: 0.30.7 pathe: 1.1.2 @@ -23218,6 +23236,8 @@ snapshots: vue-component-type-helpers@2.0.16: {} + vue-component-type-helpers@2.0.18: {} + vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)): dependencies: vue: 3.4.26(typescript@5.4.5) @@ -23239,7 +23259,7 @@ snapshots: vue-eslint-parser@9.4.2(eslint@8.57.0): dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.4(supports-color@5.5.0) eslint: 8.57.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 @@ -23311,6 +23331,8 @@ snapshots: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 + wavesurfer.js@7.7.14: {} + wcwidth@1.0.1: dependencies: defaults: 1.0.4 From 696c50c5522590d4d17d3d1a6bc172fe290c8311 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 08:16:59 +0900 Subject: [PATCH 447/501] update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6165b68e55..ee43be0a00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea2", + "version": "2024.5.0-beta1-mattyatea3", "codename": "nasubi", "repository": { "type": "git", From 3468af92e1effc728fe8ff07fa57d7840a3b823a Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 09:12:48 +0900 Subject: [PATCH 448/501] update --- packages/frontend/src/components/MkMediaAudio.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 960fe47d81..d9247afa24 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -77,7 +77,6 @@ SPDX-License-Identifier: AGPL-3.0-only barWidth: 3, barRadius: 5, duration: 80, - cursorWidth: 0, }" ></WaveSurferPlayer> <MkMediaRange @@ -93,7 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; import { WaveSurferPlayer } from '@meersagor/wavesurfer-vue'; -import tinycolor from 'tinycolor2'; +import type WaveSurfer from 'wavesurfer.js'; import type { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; @@ -105,6 +104,7 @@ import { $i, iAmModerator } from '@/account.js'; const props = defineProps<{ audio: Misskey.entities.DriveFile; }>(); + const keymap = { 'up': () => { if (hasFocus() && audioEl.value) { From c13c85c8a9d36216aef1e4d96edee2255e9baccd Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 09:13:23 +0900 Subject: [PATCH 449/501] update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ee43be0a00..672c0db53d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea3", + "version": "2024.5.0-beta1-mattyatea4", "codename": "nasubi", "repository": { "type": "git", From 94bca758ab331f58272d9bb9d0f9ea597473df2b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 16 May 2024 09:20:28 +0900 Subject: [PATCH 450/501] update --- packages/frontend/src/components/MkMediaAudio.vue | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index d9247afa24..570569ecd0 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -101,6 +101,7 @@ import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; import { $i, iAmModerator } from '@/account.js'; +import tinycolor from "tinycolor2"; const props = defineProps<{ audio: Misskey.entities.DriveFile; }>(); @@ -243,6 +244,7 @@ const volume = ref(.25); const speed = ref(1); const loop = ref(false); // TODO: ドライブファイルのフラグに置き換える const bufferedEnd = ref(0); +let audioContext = new AudioContext(); // MediaControl Events function togglePlayPause() { @@ -273,7 +275,8 @@ let stopAudioElWatch: () => void; function init() { if (onceInit) return; onceInit = true; - + const computedStyle = getComputedStyle(document.documentElement); + accent.value = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString(); stopAudioElWatch = watch(audioEl, () => { if (audioEl.value) { isReady.value = true; @@ -339,8 +342,6 @@ watch(loop, (to) => { }); onMounted(async () => { - accent.value = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--accent')); - init(); }); From 26bb2d42f91decaee4674f8c221a6eb5d1c70ad5 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 01:17:16 +0900 Subject: [PATCH 451/501] update --- .../backend/src/server/api/EndpointsModule.ts | 5 +- packages/backend/src/server/api/endpoints.ts | 6 ++- .../admin/accounts/present-points.ts | 52 +++++++++++++++++++ .../backend/src/server/api/endpoints/i.ts | 2 +- packages/frontend/src/pages/admin-user.vue | 12 +++++ 5 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/admin/accounts/present-points.ts diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index f7d7c5ed9e..c9834c2821 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -388,13 +388,14 @@ import * as ep___reversi_surrender from './endpoints/reversi/surrender.js'; import * as ep___reversi_verify from './endpoints/reversi/verify.js'; import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; +import * as ep___admin_accounts_present_points from './endpoints/admin/accounts/present-points.js'; import type { Provider } from '@nestjs/common'; - const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }; const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }; const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }; const $admin_accounts_delete: Provider = { provide: 'ep:admin/accounts/delete', useClass: ep___admin_accounts_delete.default }; const $admin_accounts_findByEmail: Provider = { provide: 'ep:admin/accounts/find-by-email', useClass: ep___admin_accounts_findByEmail.default }; +const $admin_accounts_present_points: Provider = { provide: 'ep:admin/accounts/present-points', useClass: ep___admin_accounts_present_points.default }; const $admin_ad_create: Provider = { provide: 'ep:admin/ad/create', useClass: ep___admin_ad_create.default }; const $admin_ad_delete: Provider = { provide: 'ep:admin/ad/delete', useClass: ep___admin_ad_delete.default }; const $admin_ad_list: Provider = { provide: 'ep:admin/ad/list', useClass: ep___admin_ad_list.default }; @@ -783,6 +784,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_accounts_create, $admin_accounts_delete, $admin_accounts_findByEmail, + $admin_accounts_present_points, $admin_ad_create, $admin_ad_delete, $admin_ad_list, @@ -1165,6 +1167,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_accounts_create, $admin_accounts_delete, $admin_accounts_findByEmail, + $admin_accounts_present_points, $admin_ad_create, $admin_ad_delete, $admin_ad_list, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index d65c6271d3..4c835f25e2 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -4,7 +4,7 @@ */ import { permissions } from 'misskey-js'; -import type { KeyOf,Schema } from '@/misc/json-schema.js'; +import type { KeyOf, Schema } from '@/misc/json-schema.js'; import { RolePolicies } from '@/core/RoleService.js'; import * as ep___admin_emoji_setlocalOnlyBulk from './endpoints/admin/emoji/set-localonly-bulk.js'; import * as ep___admin_emoji_setisSensitiveBulk from './endpoints/admin/emoji/set-issensitive-bulk.js'; @@ -13,6 +13,8 @@ import * as ep___admin_abuseUserReports from './endpoints/admin/abuse-user-repor import * as ep___admin_accounts_create from './endpoints/admin/accounts/create.js'; import * as ep___admin_accounts_delete from './endpoints/admin/accounts/delete.js'; import * as ep___admin_accounts_findByEmail from './endpoints/admin/accounts/find-by-email.js'; +import * as ep___admin_accounts_present_points from './endpoints/admin/accounts/present-points.js'; + import * as ep___admin_ad_create from './endpoints/admin/ad/create.js'; import * as ep___admin_ad_delete from './endpoints/admin/ad/delete.js'; import * as ep___admin_ad_list from './endpoints/admin/ad/list.js'; @@ -386,13 +388,13 @@ import * as ep___reversi_invitations from './endpoints/reversi/invitations.js'; import * as ep___reversi_showGame from './endpoints/reversi/show-game.js'; import * as ep___reversi_surrender from './endpoints/reversi/surrender.js'; import * as ep___reversi_verify from './endpoints/reversi/verify.js'; - const eps = [ ['admin/meta', ep___admin_meta], ['admin/abuse-user-reports', ep___admin_abuseUserReports], ['admin/accounts/create', ep___admin_accounts_create], ['admin/accounts/delete', ep___admin_accounts_delete], ['admin/accounts/find-by-email', ep___admin_accounts_findByEmail], + ['admin/accounts/present-points', ep___admin_accounts_present_points], ['admin/ad/create', ep___admin_ad_create], ['admin/ad/delete', ep___admin_ad_delete], ['admin/ad/list', ep___admin_ad_list], diff --git a/packages/backend/src/server/api/endpoints/admin/accounts/present-points.ts b/packages/backend/src/server/api/endpoints/admin/accounts/present-points.ts new file mode 100644 index 0000000000..d69c6b83c7 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/accounts/present-points.ts @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import type { UsersRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { NotificationService } from '@/core/NotificationService.js'; + +export const meta = { + tags: ['admin'], + + requireCredential: true, + requireAdmin: true, + kind: 'write:admin:account', +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id' }, + points: { type: 'number' }, + }, + required: ['userId', 'points'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + private notificationService: NotificationService, + ) { + super(meta, paramDef, async (ps, me) => { + const user = await this.usersRepository.findOneBy({ id: ps.userId }); + + if (user == null) { + throw new Error('user not found'); + } + this.usersRepository.update( user.id, { + getPoints: user.getPoints + ps.points, + }); + this.notificationService.createNotification(user.id, 'loginbonus', { + loginbonus: ps.points, + }); + + return {}; + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 4284670499..6f36acb0b8 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -76,7 +76,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.usersRepository.update( user.id, { getPoints: user.getPoints + todayGetPoints, }); - this.notificationService.createNotification(user.id, 'loginbonus', { + this.notificationService.createNotification(user. id, 'loginbonus', { loginbonus: todayGetPoints, }); userProfile.loggedInDates = [...userProfile.loggedInDates, today]; diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue index f57aa51b5b..305d89395d 100644 --- a/packages/frontend/src/pages/admin-user.vue +++ b/packages/frontend/src/pages/admin-user.vue @@ -98,6 +98,9 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <MkButton v-if="user.host == null" inline style="margin-right: 8px;" @click="resetPassword"><i class="ti ti-key"></i> {{ i18n.ts.resetPassword }}</MkButton> </div> + <div> + <MkButton v-if="user.host == null" inline style="margin-right: 8px;" @click="presentsPoints">ぷりずむを付与する</MkButton> + </div> <MkFolder> <template #icon><i class="ti ti-license"></i></template> @@ -312,6 +315,15 @@ async function resetPassword() { } } +async function presentsPoints() { + const { canceled, result } = await os.inputText({ + title: 'ポイント', + }); + if (canceled) return; + if (result === null) return; + await misskeyApi('admin/accounts/present-points', { userId: user.value.id, points: parseInt(result) }); +} + async function toggleSuspend(v) { const confirm = await os.confirm({ type: 'warning', From d4b17a16e8ee140b6fa9bd2ddad2997a3f4aeb62 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 01:29:34 +0900 Subject: [PATCH 452/501] update --- packages/frontend/src/components/MkMediaAudio.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue index 570569ecd0..90be2cfda0 100644 --- a/packages/frontend/src/components/MkMediaAudio.vue +++ b/packages/frontend/src/components/MkMediaAudio.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div - :id="(audio.url).slice(32).replace('-','')" + ref="playerEl" v-hotkey="keymap" tabindex="0" @@ -92,6 +92,7 @@ SPDX-License-Identifier: AGPL-3.0-only import { shallowRef, watch, computed, ref, onDeactivated, onActivated, onMounted } from 'vue'; import * as Misskey from 'misskey-js'; import { WaveSurferPlayer } from '@meersagor/wavesurfer-vue'; +import tinycolor from 'tinycolor2'; import type WaveSurfer from 'wavesurfer.js'; import type { MenuItem } from '@/types/menu.js'; import { defaultStore } from '@/store.js'; @@ -101,7 +102,6 @@ import bytes from '@/filters/bytes.js'; import { hms } from '@/filters/hms.js'; import MkMediaRange from '@/components/MkMediaRange.vue'; import { $i, iAmModerator } from '@/account.js'; -import tinycolor from "tinycolor2"; const props = defineProps<{ audio: Misskey.entities.DriveFile; }>(); From f0d93755e9a2e6ff113933ed56b8e78e156d45bf Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 11:12:20 +0900 Subject: [PATCH 453/501] update --- .../backend/src/server/api/EndpointsModule.ts | 4 + packages/backend/src/server/api/endpoints.ts | 2 + .../api/endpoints/admin/emoji/speedtest.ts | 78 + packages/frontend/package.json | 10 +- .../src/components/MkEmojiEditDialog.vue | 151 +- .../frontend/src/components/MkNoteSimple.vue | 2 +- .../src/components/MkSubNoteContent.vue | 1 + .../global/MkMisskeyFlavoredMarkdown.ts | 13 +- .../src/scripts/emojiColorChangeSecond.ts | 135 + pnpm-lock.yaml | 2474 ++++++++++++++++- 10 files changed, 2720 insertions(+), 150 deletions(-) create mode 100644 packages/backend/src/server/api/endpoints/admin/emoji/speedtest.ts create mode 100644 packages/frontend/src/scripts/emojiColorChangeSecond.ts diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index c9834c2821..734e0e12dc 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -390,6 +390,7 @@ import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import * as ep___admin_accounts_present_points from './endpoints/admin/accounts/present-points.js'; import type { Provider } from '@nestjs/common'; +import * as ep___emoji_speedtest from './endpoints/admin/emoji/speedtest.js'; const $admin_meta: Provider = { provide: 'ep:admin/meta', useClass: ep___admin_meta.default }; const $admin_abuseUserReports: Provider = { provide: 'ep:admin/abuse-user-reports', useClass: ep___admin_abuseUserReports.default }; const $admin_accounts_create: Provider = { provide: 'ep:admin/accounts/create', useClass: ep___admin_accounts_create.default }; @@ -411,6 +412,7 @@ const $admin_avatarDecorations_update: Provider = { provide: 'ep:admin/avatar-de const $admin_deleteAllFilesOfAUser: Provider = { provide: 'ep:admin/delete-all-files-of-a-user', useClass: ep___admin_deleteAllFilesOfAUser.default }; const $admin_unsetUserAvatar: Provider = { provide: 'ep:admin/unset-user-avatar', useClass: ep___admin_unsetUserAvatar.default }; const $admin_unsetUserBanner: Provider = { provide: 'ep:admin/unset-user-banner', useClass: ep___admin_unsetUserBanner.default }; +const $emoji_speedtest: Provider = { provide: 'ep:emoji/speedtest', useClass: ep___emoji_speedtest.default }; const $admin_drive_cleanRemoteFiles: Provider = { provide: 'ep:admin/drive/clean-remote-files', useClass: ep___admin_drive_cleanRemoteFiles.default }; const $admin_drive_cleanup: Provider = { provide: 'ep:admin/drive/cleanup', useClass: ep___admin_drive_cleanup.default }; const $admin_drive_files: Provider = { provide: 'ep:admin/drive/files', useClass: ep___admin_drive_files.default }; @@ -799,6 +801,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_avatarDecorations_update, $admin_deleteAllFilesOfAUser, $admin_unsetUserAvatar, + $emoji_speedtest, $admin_unsetUserBanner, $admin_drive_cleanRemoteFiles, $admin_drive_cleanup, @@ -1183,6 +1186,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $admin_deleteAllFilesOfAUser, $admin_unsetUserAvatar, $admin_unsetUserBanner, + $emoji_speedtest, $admin_drive_cleanRemoteFiles, $admin_drive_cleanup, $admin_drive_files, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 4c835f25e2..16b230fbb2 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -50,6 +50,7 @@ import * as ep___admin_emoji_setCategoryBulk from './endpoints/admin/emoji/set-c import * as ep___admin_emoji_setLicenseBulk from './endpoints/admin/emoji/set-license-bulk.js'; import * as ep___admin_emoji_update from './endpoints/admin/emoji/update.js'; import * as ep___admin_emoji_updateRequest from './endpoints/admin/emoji/update-request.js'; +import * as ep___emoji_speedtest from './endpoints/admin/emoji/speedtest.js'; import * as ep___admin_federation_deleteAllFiles from './endpoints/admin/federation/delete-all-files.js'; import * as ep___admin_federation_refreshRemoteInstanceMetadata from './endpoints/admin/federation/refresh-remote-instance-metadata.js'; import * as ep___admin_federation_removeAllFollowing from './endpoints/admin/federation/remove-all-following.js'; @@ -432,6 +433,7 @@ const eps = [ ['admin/emoji/set-license-bulk', ep___admin_emoji_setLicenseBulk], ['admin/emoji/update', ep___admin_emoji_update], ['admin/emoji/update-request', ep___admin_emoji_updateRequest], + ['emoji/speedtest', ep___emoji_speedtest], ['admin/federation/delete-all-files', ep___admin_federation_deleteAllFiles], ['admin/federation/refresh-remote-instance-metadata', ep___admin_federation_refreshRemoteInstanceMetadata], ['admin/federation/remove-all-following', ep___admin_federation_removeAllFollowing], diff --git a/packages/backend/src/server/api/endpoints/admin/emoji/speedtest.ts b/packages/backend/src/server/api/endpoints/admin/emoji/speedtest.ts new file mode 100644 index 0000000000..ca2a4599db --- /dev/null +++ b/packages/backend/src/server/api/endpoints/admin/emoji/speedtest.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@nestjs/common'; +import sharp from 'sharp'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { CustomEmojiService } from '@/core/CustomEmojiService.js'; + +export const meta = { + tags: ['admin'], + requireCredential: true, + requireRolePolicy: 'canManageCustomEmojis', + kind: 'write:admin:emoji', +} as const; + +export const paramDef = { + type: 'object', + properties: { + url: { + type: 'string', + }, + }, + required: ['url'], +} as const; + +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + private customEmojiService: CustomEmojiService, + ) { + super(meta, paramDef, async (ps, me) => { + const response = await fetch(ps.url, { + 'headers': { + 'accept': 'image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8', + 'cache-control': 'no-cache', + 'pragma': 'no-cache', + 'priority': 'u=1, i', + }, + 'method': 'GET', + }); + + if (!response.ok) { + throw new Error(`Failed to fetch image: ${response.status} ${response.statusText}`); + } + const buffer = await response.arrayBuffer(); + const metadata = await sharp(buffer).metadata(); + + if (!metadata.pages) { + throw new Error('Invalid image format or no animation frames found.'); + } + + const frameRate = metadata.delay && metadata.delay.length > 0 + ? 1000 / metadata.delay[0] + : 30; // Fallback to 30 FPS if no delay information is present + + const colorsPerFrame: number[] = []; + for (let i = 0; i < metadata.pages; i++) { + const { data, info } = await sharp(buffer, { page: i }).raw().toBuffer({ resolveWithObject: true }); + const uniqueColors = new Set<string>(); + for (let y = 0; y < info.height; y++) { + for (let x = 0; x < info.width; x++) { + const offset = (y * info.width + x) * info.channels; + const color = `${data[offset]}-${data[offset + 1]}-${data[offset + 2]}`; + uniqueColors.add(color); + } + } + colorsPerFrame.push(uniqueColors.size); + } + + const colorChanges = colorsPerFrame.map((colorCount, index, arr) => { + if (index === 0) return 0; + return Math.abs(colorCount - arr[index - 1]); + }); + + const averageColorChangePerSecond = colorChanges.reduce((sum, change) => sum + change, 0) / colorsPerFrame.length; + console.log('Average color change per second:', 10 < averageColorChangePerSecond); + return Boolean(10 < averageColorChangePerSecond); + // You can store or use this information as needed + }); + } +} diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 32b73ec7e2..1cc8602785 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -17,6 +17,7 @@ "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { + "@caed0/webp-conv": "^1.1.0", "@discordapp/twemoji": "15.0.3", "@github/webauthn-json": "2.1.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", @@ -31,6 +32,7 @@ "@vitejs/plugin-vue": "5.0.4", "@vue/compiler-sfc": "3.4.26", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4", + "apng-js": "^1.1.1", "astring": "1.8.6", "broadcast-channel": "7.0.0", "buraha": "0.0.1", @@ -47,10 +49,14 @@ "escape-regexp": "0.0.1", "estree-walker": "3.0.3", "eventemitter3": "5.0.1", + "gif-frames": "^1.0.1", + "gifshot": "^0.4.5", + "gifuct-js": "^2.1.2", "idb-keyval": "6.2.1", "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", + "libwebpjs": "^0.0.1", "matter-js": "0.19.0", "mfm-js": "0.24.0", "misskey-bubble-game": "workspace:*", @@ -75,7 +81,9 @@ "vite": "5.2.11", "vue": "3.4.26", "vuedraggable": "next", - "wavesurfer.js": "^7.7.14" + "wavesurfer.js": "^7.7.14", + "webm-wasm": "^0.4.1", + "webp-hero": "^0.0.2" }, "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index 1262c59bb5..f0ab622f0e 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -6,9 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <MkWindow ref="windowEl" - :initialWidth="400" - :initialHeight="500" - :canResize="false" + :initialWidth="600" + :initialHeight="600" + :canResize="true" @close="windowEl.close()" @closed="$emit('closed')" > @@ -18,63 +18,75 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <MkSpacer :marginMin="20" :marginMax="28"> - <div class="_gaps_m"> - <div v-if="imgUrl != null" :class="$style.imgs"> - <div style="background: #000;" :class="$style.imgContainer"> - <img :src="imgUrl" :class="$style.img"/> - </div> - <div style="background: #222;" :class="$style.imgContainer"> - <img :src="imgUrl" :class="$style.img"/> - </div> - <div style="background: #ddd;" :class="$style.imgContainer"> - <img :src="imgUrl" :class="$style.img"/> - </div> - <div style="background: #fff;" :class="$style.imgContainer"> - <img :src="imgUrl" :class="$style.img"/> - </div> - </div> - <MkButton rounded style="margin: 0 auto;" @click="changeImage">{{ i18n.ts.selectFile }}</MkButton> - <MkInput v-model="name" pattern="[a-z0-9_]" autocapitalize="off"> - <template #label>{{ i18n.ts.name }}</template> - <template #caption>{{ i18n.ts.emojiNameValidation }}</template> - </MkInput> - <MkInput v-model="category" :datalist="customEmojiCategories"> - <template #label>{{ i18n.ts.category }}</template> - </MkInput> - <MkInput v-model="aliases" autocapitalize="off"> - <template #label>{{ i18n.ts.tags }}</template> - <template #caption> - {{ i18n.ts.theKeywordWhenSearchingForCustomEmoji }}<br/> - {{ i18n.ts.setMultipleBySeparatingWithSpace }} - </template> - </MkInput> - <MkInput v-model="license" :mfmAutocomplete="true"> - <template #label>{{ i18n.ts.license }}</template> - </MkInput> - <MkFolder v-if="!isRequest"> - <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> - <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> - - <div class="_gaps"> - <MkButton rounded @click="addRole"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> - - <div v-for="role in rolesThatCanBeUsedThisEmojiAsReaction" :key="role.id" :class="$style.roleItem"> - <MkRolePreview :class="$style.role" :role="role" :forModeration="true" :detailed="false" style="pointer-events: none;"/> - <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="removeRole(role, $event)"><i class="ti ti-x"></i></button> - <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button> + <div class="_gaps_m" style="display: flex; flex-direction: row"> + <div> + <div v-if="imgUrl != null" :class="$style.imgs"> + <div style="background: #000;" :class="$style.imgContainer"> + <img :src="imgUrl" :class="$style.img"/> + </div> + <div style="background: #222;" :class="$style.imgContainer"> + <img :src="imgUrl" :class="$style.img"/> + </div> + <div style="background: #ddd;" :class="$style.imgContainer"> + <img :src="imgUrl" :class="$style.img"/> + </div> + <div style="background: #fff;" :class="$style.imgContainer"> + <img :src="imgUrl" :class="$style.img"/> </div> - - <MkInfo>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription }}</MkInfo> - <MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo> </div> - </MkFolder> - <MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch> - <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> - <MkSwitch v-model="isNotifyIsHome"> - {{ i18n.ts.isNotifyIsHome }} - </MkSwitch> + <MkButton rounded style="margin: 0 auto;" @click="changeImage">{{ i18n.ts.selectFile }}</MkButton> + <MkInput v-model="name" pattern="[a-z0-9_]" autocapitalize="off"> + <template #label>{{ i18n.ts.name }}</template> + <template #caption>{{ i18n.ts.emojiNameValidation }}</template> + </MkInput> + <MkInput v-model="category" :datalist="customEmojiCategories"> + <template #label>{{ i18n.ts.category }}</template> + </MkInput> + <MkInput v-model="aliases" autocapitalize="off"> + <template #label>{{ i18n.ts.tags }}</template> + <template #caption> + {{ i18n.ts.theKeywordWhenSearchingForCustomEmoji }}<br/> + {{ i18n.ts.setMultipleBySeparatingWithSpace }} + </template> + </MkInput> + <MkInput v-model="license" :mfmAutocomplete="true"> + <template #label>{{ i18n.ts.license }}</template> + </MkInput> + <MkFolder v-if="!isRequest"> + <template #label>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReaction }}</template> + <template #suffix>{{ rolesThatCanBeUsedThisEmojiAsReaction.length === 0 ? i18n.ts.all : rolesThatCanBeUsedThisEmojiAsReaction.length }}</template> + + <div class="_gaps"> + <MkButton rounded @click="addRole"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton> + + <div v-for="role in rolesThatCanBeUsedThisEmojiAsReaction" :key="role.id" :class="$style.roleItem"> + <MkRolePreview :class="$style.role" :role="role" :forModeration="true" :detailed="false" style="pointer-events: none;"/> + <button v-if="role.target === 'manual'" class="_button" :class="$style.roleUnassign" @click="removeRole(role, $event)"><i class="ti ti-x"></i></button> + <button v-else class="_button" :class="$style.roleUnassign" disabled><i class="ti ti-ban"></i></button> + </div> + + <MkInfo>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription }}</MkInfo> + <MkInfo warn>{{ i18n.ts.rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn }}</MkInfo> + </div> + </MkFolder> + <MkSwitch v-model="isSensitive">{{ i18n.ts.isSensitive }}</MkSwitch> + <MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch> + <MkSwitch v-model="isNotifyIsHome"> + {{ i18n.ts.isNotifyIsHome }} + </MkSwitch> + </div> + <div v-if="imgUrl"> + <MkInput v-model="text"> + <template #label>テスト文章</template> + </MkInput><br/> + <MkNoteSimple :note="{isHidden:false,replyId:null,renoteId:null,files:[],user: $i,text:text,cw:null, emojis: {[name]: imgUrl}}"/> + <p v-if="speed ">基準より眩しい可能性があります。</p> + <p v-if="!speed">問題は見つかりませんでした。</p> + <p>※上記の物は問題がないことを保証するものではありません。</p> + </div> </div> </MkSpacer> + <div :class="$style.footer"> <div :class="$style.footerButtons"> <MkButton v-if="!isRequest" danger rounded style="margin: 0 auto;" @click="del()"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> @@ -87,9 +99,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref, watch } from 'vue'; +import { computed, onMounted, ref, watch } from 'vue'; import * as Misskey from 'misskey-js'; -import { DriveFile } from 'misskey-js/built/entities.js'; import MkWindow from '@/components/MkWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; @@ -102,12 +113,14 @@ import { customEmojiCategories } from '@/custom-emojis.js'; import MkSwitch from '@/components/MkSwitch.vue'; import { selectFile, selectFiles } from '@/scripts/select-file.js'; import MkRolePreview from '@/components/MkRolePreview.vue'; - +import { $i } from '@/account.js'; +import MkNoteSimple from '@/components/MkNoteSimple.vue'; const props = defineProps<{ emoji?: any, isRequest: boolean, }>(); - +const text = ref<string>('テスト文章'); +const speed = ref<boolean>(false); const windowEl = ref<InstanceType<typeof MkWindow> | null>(null); const name = ref<string>(props.emoji ? props.emoji.name : ''); const category = ref<string>(props.emoji ? props.emoji.category : ''); @@ -132,6 +145,12 @@ const emit = defineEmits<{ (ev: 'closed'): void }>(); +const colorChanges = ref<number | null>(null); + +watch(colorChanges, (value) => { + console.log(value); +}); + async function changeImage(ev) { file.value = await selectFile(ev.currentTarget ?? ev.target, null); const candidate = file.value.name.replace(/\.(.+)$/, ''); @@ -222,6 +241,12 @@ async function del() { windowEl.value.close(); }); } + +watch(imgUrl, async (value) => { + speed.value = await misskeyApi('emoji/speedtest', { + url: value, + }); +}); </script> <style lang="scss" module> @@ -243,6 +268,12 @@ async function del() { width: 64px; object-fit: contain; } +.preview { + display: block; + height: 16px; + + object-fit: contain; +} .roleItem { display: flex; diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 17f32b0042..45410c26d6 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -34,7 +34,7 @@ import MkSubNoteContent from '@/components/MkSubNoteContent.vue'; import MkCwButton from '@/components/MkCwButton.vue'; import MkButton from '@/components/MkButton.vue'; import * as os from '@/os.js'; -import {misskeyApi} from "@/scripts/misskey-api.js"; +import { misskeyApi } from '@/scripts/misskey-api.js'; const isDeleted = ref(false); const props = defineProps<{ note: Misskey.entities.Note & { diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 9a07826f1a..45967e2337 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -9,6 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="note.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> <span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span> <MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> + <Mfm v-if="note.text" :text="note.text" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> <MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> </div> diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 413f74e435..1d9b3939ce 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -6,6 +6,7 @@ import { VNode, h, SetupContext, provide } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; +import { ID, Instance } from 'misskey-js/built/entities.js'; import MkUrl from '@/components/global/MkUrl.vue'; import MkTime from '@/components/global/MkTime.vue'; import MkLink from '@/components/MkLink.vue'; @@ -23,7 +24,6 @@ import { defaultStore } from '@/store'; import { mixEmoji } from '@/scripts/emojiKitchen/emojiMixer'; import { nyaize as doNyaize } from '@/scripts/nyaize.js'; import { uhoize as doUhoize } from '@/scripts/uhoize.js'; -import {ID, Instance} from "misskey-js/built/entities.js"; import { safeParseFloat } from '@/scripts/safe-parse.js'; const QUOTE_STYLE = ` @@ -73,7 +73,7 @@ type MfmProps = { emojiUrls?: Record<string, string>; rootScale?: number; nyaize?: boolean | 'respect'; - uhoize: boolean | 'respect'; + uhoize?: boolean | 'respect'; parsedNodes?: mfm.MfmNode[] | null; enableEmojiMenu?: boolean; enableEmojiMenuReaction?: boolean; @@ -246,7 +246,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven const radius = parseFloat(token.props.args.rad ?? '6'); return h('span', { class: '_mfm_blur_', - style: `--blur-px: ${radius}px;` + style: `--blur-px: ${radius}px;`, }, genEl(token.children, scale)); } case 'rainbow': { @@ -363,7 +363,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven key: Math.random(), name: emoji1 + emoji2, normal: props.plain, - url: mixedEmojiUrl + url: mixedEmojiUrl, }); } case 'unixtime': { @@ -474,7 +474,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven case 'emojiCode': { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (props.author?.host == null) { + if (props.author?.host == null && !props.emojiUrls) { return [h(MkCustomEmoji, { key: Math.random(), name: token.props.name, @@ -487,6 +487,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven })]; } else { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + console.log(props.emojiUrls, props.emojiUrls[token.props.name], token.props.name); if (props.emojiUrls && (props.emojiUrls[token.props.name] == null)) { return [h('span', `:${token.props.name}:`)]; } else { @@ -495,7 +496,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven name: token.props.name, url: props.emojiUrls && props.emojiUrls[token.props.name], normal: props.plain, - host: props.author.host, + host: props.author.host ? props.author.host : null, useOriginalSize: scale >= 2.5, })]; } diff --git a/packages/frontend/src/scripts/emojiColorChangeSecond.ts b/packages/frontend/src/scripts/emojiColorChangeSecond.ts new file mode 100644 index 0000000000..433c611ed1 --- /dev/null +++ b/packages/frontend/src/scripts/emojiColorChangeSecond.ts @@ -0,0 +1,135 @@ +const imageUrl = ref<string>('path_to_your_image.gif'); // または '.apng' + +// 1秒あたりの色変化数を保持するリアクティブ変数 +const colorChangesPerSecond = ref<number | null>(null); + +// コンポーネントがマウントされたときに画像を取得して解析する +onMounted(() => { + fetchImage(imageUrl.value); +}); + +// 画像を取得する関数 +async function fetchImage(url: string) { + try { + const response = await fetch(url); + const blob = await response.blob(); + const arrayBuffer = await blob.arrayBuffer(); + const bytes = new Uint8Array(arrayBuffer); + + if (url.endsWith('.gif')) { + analyzeGif(bytes); + } else if (url.endsWith('.apng')) { + analyzeApng(bytes); + } + } catch (error) { + console.error('Error fetching the image:', error); + } +} + +// GIFの解析関数 +function analyzeGif(bytes: Uint8Array) { + const frames = extractGifFrames(bytes); + calculateColorChanges(frames); +} + +// APNGの解析関数 +function analyzeApng(bytes: Uint8Array) { + const frames = extractApngFrames(bytes); + calculateColorChanges(frames); +} + +// GIFフレーム抽出関数 +function extractGifFrames(bytes: Uint8Array) { + const frames = []; + let i = 0; + while (i < bytes.length) { + // GIFのヘッダーとロジカルスクリーンディスクリプタをスキップ + if (i === 0) i += 13; + // グローバルカラーテーブルのスキップ + if (i === 13) i += (bytes[10] & 0x80 ? 3 * (2 ** ((bytes[10] & 0x07) + 1)) : 0); + + // イメージディスクリプタを探す + if (bytes[i] === 0x2C) { + const imageLeft = bytes[i + 1] + (bytes[i + 2] << 8); + const imageTop = bytes[i + 3] + (bytes[i + 4] << 8); + const imageWidth = bytes[i + 5] + (bytes[i + 6] << 8); + const imageHeight = bytes[i + 7] + (bytes[i + 8] << 8); + const localColorTableFlag = bytes[i + 9] & 0x80; + const localColorTableSize = 2 ** ((bytes[i + 9] & 0x07) + 1); + + i += 10; + + if (localColorTableFlag) { + i += 3 * localColorTableSize; + } + + while (bytes[i] !== 0x00) { + const blockSize = bytes[i]; + i += blockSize + 1; + } + + i++; + const frame = extractPixelColor(bytes, imageLeft, imageTop, imageWidth, imageHeight); + frames.push(frame); + } else { + i++; + } + } + + return frames; +} + +// APNGフレーム抽出関数 +function extractApngFrames(bytes: Uint8Array) { + const frames = []; + let i = 8; // PNGシグネチャをスキップ + + while (i < bytes.length) { + const length = (bytes[i] << 24) + (bytes[i + 1] << 16) + (bytes[i + 2] << 8) + bytes[i + 3]; + const type = String.fromCharCode(bytes[i + 4], bytes[i + 5], bytes[i + 6], bytes[i + 7]); + + if (type === 'IDAT' || type === 'fdAT') { + const imageLeft = 0; + const imageTop = 0; + const imageWidth = 0; + const imageHeight = 0; + + const frame = extractPixelColor(bytes, imageLeft, imageTop, imageWidth, imageHeight); + frames.push(frame); + } + + i += length + 12; + } + + return frames; +} + +// 中央ピクセルの色を抽出する関数 +function extractPixelColor(bytes: Uint8Array, left: number, top: number, width: number, height: number) { + const centerX = left + Math.floor(width / 2); + const centerY = top + Math.floor(height / 2); + const index = (centerY * width + centerX) * 4; + + return { + r: bytes[index], + g: bytes[index + 1], + b: bytes[index + 2], + }; +} + +// 色の変化を計算する関数 +function calculateColorChanges(frames: { r: number; g: number; b: number }[]) { + let colorChangeCount = 0; + for (let i = 1; i < frames.length; i++) { + const prevFrame = frames[i - 1]; + const currFrame = frames[i]; + if (prevFrame.r !== currFrame.r || prevFrame.g !== currFrame.g || prevFrame.b !== currFrame.b) { + colorChangeCount++; + } + } + + // 仮にFPSが30と仮定し、1秒あたりの色変化回数を計算 + const fps = 30; + const durationInSeconds = frames.length / fps; + colorChangesPerSecond.value = colorChangeCount / durationInSeconds; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 52839285d9..10c066c91f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,7 +178,7 @@ importers: version: 2.0.5 body-parser: specifier: 1.20.2 - version: 1.20.2 + version: 1.20.2(supports-color@3.2.3) bullmq: specifier: 5.7.8 version: 5.7.8 @@ -262,7 +262,7 @@ importers: version: 4.1.0 jsdom: specifier: 24.0.0 - version: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) + version: 24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3) json5: specifier: 2.2.3 version: 2.2.3 @@ -688,6 +688,9 @@ importers: packages/frontend: dependencies: + '@caed0/webp-conv': + specifier: ^1.1.0 + version: 1.1.0(encoding@0.1.13) '@discordapp/twemoji': specifier: 15.0.3 version: 15.0.3 @@ -730,6 +733,9 @@ importers: aiscript-vscode: specifier: github:aiscript-dev/aiscript-vscode#v0.1.4 version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424 + apng-js: + specifier: ^1.1.1 + version: 1.1.1 astring: specifier: 1.8.6 version: 1.8.6 @@ -778,6 +784,15 @@ importers: eventemitter3: specifier: 5.0.1 version: 5.0.1 + gif-frames: + specifier: ^1.0.1 + version: 1.0.1 + gifshot: + specifier: ^0.4.5 + version: 0.4.5 + gifuct-js: + specifier: ^2.1.2 + version: 2.1.2 idb-keyval: specifier: 6.2.1 version: 6.2.1 @@ -790,6 +805,9 @@ importers: json5: specifier: 2.2.3 version: 2.2.3 + libwebpjs: + specifier: ^0.0.1 + version: 0.0.1 matter-js: specifier: 0.19.0 version: 0.19.0 @@ -865,6 +883,12 @@ importers: wavesurfer.js: specifier: ^7.7.14 version: 7.7.14 + webm-wasm: + specifier: ^0.4.1 + version: 0.4.1 + webp-hero: + specifier: ^0.0.2 + version: 0.0.2 devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 @@ -880,7 +904,7 @@ importers: version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@storybook/addon-interactions': specifier: 8.0.9 - version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) + version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) '@storybook/addon-links': specifier: 8.0.9 version: 8.0.9(react@18.3.1) @@ -913,7 +937,7 @@ importers: version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)) '@storybook/test': specifier: 8.0.9 - version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) + version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) '@storybook/theming': specifier: 8.0.9 version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -970,7 +994,7 @@ importers: version: 7.7.1(eslint@8.57.0)(typescript@5.4.5) '@vitest/coverage-v8': specifier: 0.34.6 - version: 0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) + version: 0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) '@vue/runtime-core': specifier: 3.4.26 version: 3.4.26 @@ -1036,10 +1060,10 @@ importers: version: 1.0.3 vitest: specifier: 0.34.6 - version: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) + version: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) vitest-fetch-mock: specifier: 0.2.2 - version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) + version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) vue-component-type-helpers: specifier: 2.0.16 version: 2.0.16 @@ -2147,6 +2171,9 @@ packages: '@bundled-es-modules/statuses@1.0.1': resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} + '@caed0/webp-conv@1.1.0': + resolution: {integrity: sha512-R+zJgRB9UUVQODBF/NQsjjEZZDa7ZoMv4Z042xNarXPod0zAaS2kl95i3kL6tO68MXCvAhscxAxK+MFRY5raIg==} + '@canvas/image-data@1.0.0': resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==} @@ -4925,6 +4952,11 @@ packages: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} + acorn@3.3.0: + resolution: {integrity: sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==} + engines: {node: '>=0.4.0'} + hasBin: true + acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} @@ -4990,6 +5022,21 @@ packages: ajv@8.13.0: resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} + align-text@0.1.4: + resolution: {integrity: sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==} + engines: {node: '>=0.10.0'} + + almond@0.2.5: + resolution: {integrity: sha512-Y37372p4xHRrlqPIERtDWE+W3gjDnLvMCTZwo2R5q/brF/QIvfC48ABE0uUrbxn6LPeY2FnlnjFIsWZVFwA6gA==} + engines: {node: '>=0.4.0'} + + alphanum-sort@1.0.2: + resolution: {integrity: sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==} + + amdefine@1.0.1: + resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} + engines: {node: '>=0.4.2'} + ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -4998,6 +5045,10 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -5006,6 +5057,10 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -5029,6 +5084,9 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} + apng-js@1.1.1: + resolution: {integrity: sha512-UWaloDssWCE8Bj0wipyNxEXPnMadYS0VAjghCLas5nKGqfiBMNdQJhg8Fawq2+jZ50IOM1feKwjiqPAC/bvKgg==} + app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -5072,6 +5130,14 @@ packages: aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} + arr-diff@2.0.0: + resolution: {integrity: sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -5086,6 +5152,10 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} + array-unique@0.2.1: + resolution: {integrity: sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==} + engines: {node: '>=0.10.0'} + array.prototype.findlastindex@1.2.3: resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} @@ -5126,6 +5196,9 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} + assert@1.5.1: + resolution: {integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==} + assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} @@ -5147,6 +5220,18 @@ packages: async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} + async@0.1.22: + resolution: {integrity: sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==} + + async@0.2.10: + resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==} + + async@0.9.2: + resolution: {integrity: sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==} + + async@1.5.2: + resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} + async@3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} @@ -5161,6 +5246,9 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} + autoprefixer@6.7.7: + resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==} + available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -5186,6 +5274,9 @@ packages: b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} + babel-code-frame@6.26.0: + resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} + babel-core@7.0.0-bridge.0: resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: @@ -5238,12 +5329,18 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + balanced-match@0.4.2: + resolution: {integrity: sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg==} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + batch@0.6.1: + resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} + bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} @@ -5258,6 +5355,12 @@ packages: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} + big.js@3.2.0: + resolution: {integrity: sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==} + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + bin-check@4.1.0: resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==} engines: {node: '>=4'} @@ -5280,6 +5383,10 @@ packages: blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} + block-stream@0.0.9: + resolution: {integrity: sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==} + engines: {node: 0.4 || >=0.5.8} + bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -5313,6 +5420,10 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + braces@1.8.5: + resolution: {integrity: sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==} + engines: {node: '>=0.10.0'} + braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -5323,9 +5434,17 @@ packages: browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} + browserify-aes@0.4.0: + resolution: {integrity: sha512-hnvbMhZ/Ete34qnoKKyjikiYQfZbl89d5UZ29cz3EG13cv/8VRyM8Zs84luB/O7BRzC3qSng9dVovJ6jghcAvg==} + browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} + browserslist@1.7.7: + resolution: {integrity: sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==} + deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools. + hasBin: true + browserslist@4.22.2: resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5352,6 +5471,9 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + buffer@4.9.2: + resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} + buffer@5.6.0: resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} @@ -5365,6 +5487,9 @@ packages: resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==} engines: {node: '>=6.14.2'} + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + bullmq@5.7.8: resolution: {integrity: sha512-F/Haeu6AVHkFrfeaU/kLOjhfrH6x3CaKAZlQQ+76fa8l3kfI9oaUHeFMW+1mYVz0NtYPF7PNTWFq4ylAHYcCgA==} @@ -5414,6 +5539,10 @@ packages: call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} @@ -5425,6 +5554,10 @@ packages: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} + camelcase@1.2.1: + resolution: {integrity: sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==} + engines: {node: '>=0.10.0'} + camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -5433,9 +5566,15 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} + caniuse-api@1.6.1: + resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==} + caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + caniuse-db@1.0.30001620: + resolution: {integrity: sha512-dYQIgCcUpy2l/IfiEA6xgNHCgr5jmDWF4i89MRv6DqCiEt4MNJguYsVeVZSxyqWfb8GfhWEZEMkjI3vhIYRrvw==} + caniuse-lite@1.0.30001566: resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==} @@ -5448,6 +5587,10 @@ packages: canvas-confetti@1.9.3: resolution: {integrity: sha512-rFfTURMvmVEX1gyXFgn5QMn81bYk70qa0HLzcIOSVEyl57n6o9ItHeBtUSWdvKAPY0xlvBHno4/v3QPrT83q9g==} + canvas@2.11.2: + resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} + engines: {node: '>=6'} + caseless@0.12.0: resolution: {integrity: sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==} @@ -5458,6 +5601,10 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + center-align@0.1.3: + resolution: {integrity: sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==} + engines: {node: '>=0.10.0'} + chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} @@ -5466,6 +5613,10 @@ packages: resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} engines: {node: '>=14.16'} + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -5561,6 +5712,10 @@ packages: cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} + clap@1.2.3: + resolution: {integrity: sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==} + engines: {node: '>=0.10.0'} + clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -5598,6 +5753,9 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} + cliui@2.1.0: + resolution: {integrity: sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==} + cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} @@ -5627,6 +5785,10 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + coa@1.0.4: + resolution: {integrity: sha512-KAGck/eNAmCL0dcT3BiuYwLbExK6lduR8DxM3C1TyDzaXhZHyZ8ooX5I5+na2e3dPFuibfxrGdorr0/Lr7RYCQ==} + engines: {node: '>= 0.8.0'} + code-error-fragment@0.0.230: resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==} engines: {node: '>= 4'} @@ -5647,6 +5809,9 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + color-string@0.3.0: + resolution: {integrity: sha512-sz29j1bmSDfoAxKIEU6zwoIZXN6BrFbAMIhfYCNyiZXBDuU/aiHlN84lp/xDzL2ubyFhLDobHIlU1X70XRrMDA==} + color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} @@ -5654,6 +5819,9 @@ packages: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true + color@0.11.4: + resolution: {integrity: sha512-Ajpjd8asqZ6EdxQeqGzU5WBhhTfJ/0cA4Wlbre7e5vXfmDSmda7Ov6jeKoru+b0vHcb1CqvuroTHp5zIWzhVMA==} + color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} @@ -5664,6 +5832,17 @@ packages: colorette@2.0.19: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} + colormin@1.1.2: + resolution: {integrity: sha512-XSEQUUQUR/lXqGyddiNH3XYFUPYlYr1vXy9rTFMsSOw+J7Q6EQkdlQIrTlYn4TccpsOaUE1PYQNjBn20gwCdgQ==} + + colors@0.6.2: + resolution: {integrity: sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==} + engines: {node: '>=0.1.90'} + + colors@1.1.2: + resolution: {integrity: sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==} + engines: {node: '>=0.1.90'} + combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -5726,15 +5905,25 @@ packages: config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + connect-history-api-fallback@1.6.0: + resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} + engines: {node: '>=0.8'} + consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -5743,6 +5932,10 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} + contentstream@1.0.0: + resolution: {integrity: sha512-jqWbfFZFG9tZbdej7+TzXI4kanABh3BLtTWY6NxqTK5zo6iTIeo5aq4iRVfYsLQ0y8ccQqmJR/J4NeMmEdnR2w==} + engines: {node: '>= 0.8.0'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -5816,19 +6009,32 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} + crypto-browserify@3.3.0: + resolution: {integrity: sha512-9n5nGl6D8zb29Ui8Ji8pVdUIE3RUe6A9zQf2iLPjFKftnkkkJBCGb7TkYAFNjt9nfsvZTLL52CwxzS9Tw7Bujw==} + crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} + css-color-names@0.0.4: + resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} + css-declaration-sorter@7.2.0: resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 + css-loader@0.26.4: + resolution: {integrity: sha512-BpErUP1CcAdW8IQ0834qG+5yGMzCRKEXH+HmJ/rXNAfOeOY4GwNDOG16Oil7G7MpeP+1QQ9NGCjzI6MebqO+rA==} + engines: {node: '>=0.12.0 || >=4.3.0 <5.0.0 || >=5.10'} + css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + css-selector-tokenizer@0.7.3: + resolution: {integrity: sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==} + css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -5861,12 +6067,20 @@ packages: peerDependencies: postcss: ^8.4.31 + cssnano@3.10.0: + resolution: {integrity: sha512-0o0IMQE0Ezo4b41Yrm8U6Rp9/Ag81vNXY1gZMnT1XhO4DpjEf2utKERqWJbOoz3g1Wdc1d3QSta/cIuJ1wSTEg==} + cssnano@6.1.2: resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + csso@2.3.2: + resolution: {integrity: sha512-FmCI/hmqDeHHLaIQckMhMZneS84yzUZdrWDAvJVVxOwcKE1P1LF9FGmzr1ktIQSxOw6fl3PaQsmfg+GN+VvR3w==} + engines: {node: '>=0.10.0'} + hasBin: true + csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -5881,6 +6095,10 @@ packages: cwise-compiler@1.1.3: resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==} + cycle@1.0.3: + resolution: {integrity: sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==} + engines: {node: '>=0.4.0'} + cypress@13.7.3: resolution: {integrity: sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -5963,6 +6181,10 @@ packages: decode-named-character-reference@1.0.2: resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==} + decompress-response@4.2.1: + resolution: {integrity: sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==} + engines: {node: '>=8'} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -5982,6 +6204,10 @@ packages: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} + deep-equal@1.1.2: + resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==} + engines: {node: '>= 0.4'} + deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} @@ -6003,6 +6229,10 @@ packages: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} @@ -6011,6 +6241,13 @@ packages: resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} engines: {node: '>= 0.4'} + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + defined@1.0.1: + resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -6029,6 +6266,10 @@ packages: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} + depd@1.1.2: + resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} + engines: {node: '>= 0.6'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -6109,6 +6350,10 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + domain-browser@1.2.0: + resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} + engines: {node: '>=0.4', npm: '>=1.2'} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -6171,6 +6416,14 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + emojis-list@2.1.0: + resolution: {integrity: sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==} + engines: {node: '>= 0.10'} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} @@ -6184,6 +6437,10 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + enhanced-resolve@0.9.1: + resolution: {integrity: sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==} + engines: {node: '>=0.6'} + enquirer@2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -6207,6 +6464,10 @@ packages: err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -6214,6 +6475,14 @@ packages: resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} engines: {node: '>= 0.4'} + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} @@ -6360,6 +6629,11 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + esprima@2.7.3: + resolution: {integrity: sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==} + engines: {node: '>=0.10.0'} + hasBin: true + esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -6407,10 +6681,18 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + events@1.1.1: + resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} + engines: {node: '>=0.4.x'} + events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource@2.0.2: + resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} + engines: {node: '>=12.0.0'} + execa@0.7.0: resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} engines: {node: '>=4'} @@ -6439,6 +6721,14 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} + expand-brackets@0.1.5: + resolution: {integrity: sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==} + engines: {node: '>=0.10.0'} + + expand-range@1.8.2: + resolution: {integrity: sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==} + engines: {node: '>=0.10.0'} + expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6465,6 +6755,10 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + extglob@0.3.2: + resolution: {integrity: sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==} + engines: {node: '>=0.10.0'} + extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -6474,6 +6768,10 @@ packages: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} + eyes@0.1.8: + resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} + engines: {node: '> 0.1.90'} + fast-content-type-parse@1.1.0: resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} @@ -6490,6 +6788,10 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} + fast-json-patch@1.2.2: + resolution: {integrity: sha512-Fp8ZmUPKCH1CzeHWAjTbYfQKkny8+QODP7NqXGkx0Z/4+6A4ommqus22/J2i1hgo1bbD/uGUgr8P6TUZyFsn2g==} + engines: {node: '>= 0.4.0'} + fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -6526,12 +6828,19 @@ packages: fastify@4.26.2: resolution: {integrity: sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==} + fastparse@1.1.2: + resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} + fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -6571,6 +6880,10 @@ packages: filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} + filename-regex@2.0.1: + resolution: {integrity: sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==} + engines: {node: '>=0.10.0'} + filename-reserved-regex@3.0.0: resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6579,6 +6892,10 @@ packages: resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==} engines: {node: '>=12.20'} + fill-range@2.2.4: + resolution: {integrity: sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==} + engines: {node: '>=0.10.0'} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -6629,6 +6946,10 @@ packages: flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + flatten@1.0.3: + resolution: {integrity: sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==} + deprecated: flatten is deprecated in favor of utility frameworks such as lodash. + flow-parser@0.202.0: resolution: {integrity: sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw==} engines: {node: '>=0.4.0'} @@ -6649,6 +6970,14 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + + for-own@0.1.5: + resolution: {integrity: sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==} + engines: {node: '>=0.10.0'} + foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -6729,6 +7058,13 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] + fstream-ignore@0.0.10: + resolution: {integrity: sha512-x3RZbvYrL8mMyaJrg6Kip+YkljUFnCiafRR94RQY0vBFOjbWTRehlPg3iTqcPauayQxs9WvUByJ1v48o3QSLsQ==} + + fstream@0.1.31: + resolution: {integrity: sha512-N1pLGEHoDyCoI8uMmPXJXhn238L4nk41iipXCrqs4Ss0ooYSr5sNj2ucMo5AqJVC4OaOa7IztpBhOaaYTGZVuA==} + engines: {node: '>=0.6'} + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -6757,6 +7093,10 @@ packages: get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + get-npm-tarball-url@2.0.3: resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==} engines: {node: '>=12.17'} @@ -6797,10 +7137,22 @@ packages: getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} + gif-encoder-2@1.0.5: + resolution: {integrity: sha512-fsRAKbZuUoZ7FYGjpFElmflTkKwsn/CzAmL/xDl4558aTAgysIDCUF6AXWO8dmai/ApfZACbPVAM+vPezJXlFg==} + gif-encoder@0.4.1: resolution: {integrity: sha512-++rNGpDBgWQ9eXj9JfTBLHMUEd7lDOdzIvFyHQM9yL8ffxkcg4G6jWmsgu/r59Uq6nHc3wcVwtgy3geLnIWunQ==} engines: {node: '>= 0.8.0'} + gif-frames@1.0.1: + resolution: {integrity: sha512-9ddxnrEbAjdv0R6Ib8DHhd3TIsaulrm55qWpiH1dsVp4X/QPE8FxtK2YvuYrj+y+YhiBHRHRXo5Gei9GzdMS3g==} + + gifshot@0.4.5: + resolution: {integrity: sha512-oaOTT7patjxFFv7ptR0R0NNhqy3ZAmcLUQCjM/sTsvsQaUAlB2fHirLajcNAKJ6ufoVhdP+ZkXYvmUycHP1FNg==} + + gifuct-js@2.1.2: + resolution: {integrity: sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==} + giget@1.1.2: resolution: {integrity: sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A==} hasBin: true @@ -6808,6 +7160,13 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} + glob-base@0.3.0: + resolution: {integrity: sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==} + engines: {node: '>=0.10.0'} + + glob-parent@2.0.0: + resolution: {integrity: sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==} + glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -6884,6 +7243,15 @@ packages: resolution: {integrity: sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==} engines: {node: '>=20'} + graceful-fs@1.1.14: + resolution: {integrity: sha512-JUrvoFoQbLZpOZilKTXZX2e1EV0DTnuG5vsRFNFv4mPf/mnYbwNAFw/5x0rxeyaJslIdObGSgTTsMnM/acRaVw==} + engines: {node: '>=0.4.0'} + deprecated: please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js + + graceful-fs@3.0.12: + resolution: {integrity: sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==} + engines: {node: '>=0.4.0'} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -6927,9 +7295,17 @@ packages: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + has-flag@1.0.0: + resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==} + engines: {node: '>=0.10.0'} + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -6941,6 +7317,9 @@ packages: has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} @@ -7004,6 +7383,9 @@ packages: resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} engines: {node: '>=14'} + html-comment-regex@1.1.2: + resolution: {integrity: sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==} + html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -7028,6 +7410,10 @@ packages: http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + http-errors@1.6.3: + resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} + engines: {node: '>= 0.6'} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -7036,10 +7422,20 @@ packages: resolution: {integrity: sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==} engines: {node: '>=6.0.0'} + http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + http-proxy-agent@7.0.0: resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} engines: {node: '>= 14'} + http-proxy-middleware@0.17.4: + resolution: {integrity: sha512-JtH3UZju4oXDdca28/kknbm/CFmt35vy0YV0PNOMWWWpn3rT9WV95IXN451xwBGSjy9L0Cah1O9TCMytboLdfw==} + + http-proxy@1.18.1: + resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} + engines: {node: '>=8.0.0'} + http-signature@1.2.0: resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} engines: {node: '>=0.8', npm: '>=1.3.7'} @@ -7060,6 +7456,9 @@ packages: resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==} engines: {node: '>=16'} + https-browserify@0.0.1: + resolution: {integrity: sha512-EjDQFbgJr1vDD/175UJeSX3ncQ3+RUnCL5NkthQGHvF4VNHlzTy8ifJfTqz47qiPRqaFH58+CbuG3x51WuB1XQ==} + https-proxy-agent@2.2.4: resolution: {integrity: sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==} engines: {node: '>= 4.5.0'} @@ -7088,6 +7487,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + i@0.3.7: + resolution: {integrity: sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==} + engines: {node: '>=0.4'} + iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -7096,6 +7499,9 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} + icss-replace-symbols@1.1.0: + resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} + idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} @@ -7117,6 +7523,9 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + image-conversion@2.1.1: + resolution: {integrity: sha512-hnMOmP7q2jxA+52FZ+wHNhg3fdFRlgfngsQH2JQHEQkafY7tj/8F15e6Rv/RxDegc872jvyaRHwMbkTZK1Cjbg==} + immutable@4.2.2: resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==} @@ -7145,9 +7554,21 @@ packages: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} + indexes-of@1.0.1: + resolution: {integrity: sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==} + + indexof@0.0.1: + resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==} + inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + inherits@1.0.2: + resolution: {integrity: sha512-Al67oatbRSo3RV5hRqIoln6Y5yMVbJSIn4jEJNL7VCImzq/kLr7vvb6sFRJXqr8rpHc/2kJOM+y0sPKN47VdzA==} + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -7169,6 +7590,9 @@ packages: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} + interpret@0.6.6: + resolution: {integrity: sha512-Vg6X07U0AOZb4HF6CWHa+jnJU8j71buKQ9Pc0C75qBXgvCYbxWBkGo4jnTS3O0MIc9FZtt0mB7h+uclojqdw1Q==} + intersection-observer@0.12.2: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} @@ -7206,6 +7630,10 @@ packages: resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} engines: {node: '>=8'} + is-absolute-url@2.1.0: + resolution: {integrity: sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==} + engines: {node: '>=0.10.0'} + is-absolute-url@4.0.1: resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7260,9 +7688,25 @@ packages: engines: {node: '>=8'} hasBin: true + is-dotfile@1.0.3: + resolution: {integrity: sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==} + engines: {node: '>=0.10.0'} + + is-equal-shallow@0.1.3: + resolution: {integrity: sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==} + engines: {node: '>=0.10.0'} + is-expression@4.0.0: resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==} + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extglob@1.0.0: + resolution: {integrity: sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==} + engines: {node: '>=0.10.0'} + is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -7282,6 +7726,14 @@ packages: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} + is-glob@2.0.1: + resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} + engines: {node: '>=0.10.0'} + + is-glob@3.1.0: + resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} + engines: {node: '>=0.10.0'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -7323,6 +7775,14 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} + is-number@2.1.0: + resolution: {integrity: sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==} + engines: {node: '>=0.10.0'} + + is-number@4.0.0: + resolution: {integrity: sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==} + engines: {node: '>=0.10.0'} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -7351,9 +7811,17 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-posix-bracket@0.1.1: + resolution: {integrity: sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==} + engines: {node: '>=0.10.0'} + is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + is-primitive@2.0.0: + resolution: {integrity: sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==} + engines: {node: '>=0.10.0'} + is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} @@ -7383,6 +7851,10 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} + is-svg@2.1.0: + resolution: {integrity: sha512-Ya1giYJUkcL/94quj0+XGcmts6cETPBW1MiFz1ReJrnDJ680F52qpAEGAEGU0nq96FRGIGPx6Yo1CyPXcOoyGw==} + engines: {node: '>=0.10.0'} + is-svg@5.0.0: resolution: {integrity: sha512-sRl7J0oX9yUNamSdc8cwgzh9KBLnQXNzGmW0RVHwg/jEYjGNYHC6UvnYD8+hAeut9WwxRvhG9biK7g/wDGxcMw==} engines: {node: '>=14.16'} @@ -7431,6 +7903,10 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} @@ -7475,6 +7951,11 @@ packages: engines: {node: '>=10'} hasBin: true + jamjs@0.2.17: + resolution: {integrity: sha512-bJrZ1NEtuL1u3hjLMbVSOQAHvoP7g7wZtOSya+6/KFIvcgYz0uExlKQbMe3LOzFJp9vGk8ilXpHZWEwyLeg4YQ==} + engines: {node: '>= 0.6.x'} + hasBin: true + jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -7619,14 +8100,23 @@ packages: jpeg-js@0.3.7: resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==} + js-base64@2.6.4: + resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} + js-beautify@1.14.9: resolution: {integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==} engines: {node: '>=12'} hasBin: true + js-binary-schema-parser@2.0.3: + resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} + js-stringify@1.0.2: resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} + js-tokens@3.0.2: + resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -7634,6 +8124,10 @@ packages: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true + js-yaml@3.7.0: + resolution: {integrity: sha512-eIlkGty7HGmntbV6P/ZlAsoncFLGsNoM27lkTzS+oneY/EiNhj+geqD9ezg/ip+SW6Var0BJU2JtV0vEUZpWVQ==} + hasBin: true + js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -7678,6 +8172,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-merge-patch@0.2.3: + resolution: {integrity: sha512-mjd5eObNGOhWkKCztwVuF25KOzLj2T4TJaWXLBgCQPeoPRJrMxKNgjNBE8sPmXoWRT0WDlo4Itd/gTlFh29TFw==} + json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -7700,6 +8197,10 @@ packages: resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==} engines: {node: '>= 4'} + json5@0.5.1: + resolution: {integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==} + hasBin: true + json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -7758,6 +8259,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -7784,6 +8289,10 @@ packages: resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} engines: {node: '> 0.8'} + lazy-cache@1.0.4: + resolution: {integrity: sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==} + engines: {node: '>=0.10.0'} + lazy-universal-dotenv@4.0.0: resolution: {integrity: sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==} engines: {node: '>=14.0.0'} @@ -7800,6 +8309,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libwebpjs@0.0.1: + resolution: {integrity: sha512-HSxZmhSsLoL8eKGIxPkSHTp7UeTSNhm4gLdlIQij7uJrhpBMoUiYMJFQeE+Rs+aJ/i963m68hH19Jvq6bJqXYg==} + light-my-request@5.11.0: resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==} @@ -7819,6 +8331,13 @@ packages: enquirer: optional: true + loader-utils@0.2.17: + resolution: {integrity: sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==} + + loader-utils@1.4.2: + resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} + engines: {node: '>=4.0.0'} + local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} @@ -7835,6 +8354,9 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -7879,6 +8401,10 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + longest@1.0.1: + resolution: {integrity: sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==} + engines: {node: '>=0.10.0'} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -7902,6 +8428,9 @@ packages: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} + lru-cache@2.7.3: + resolution: {integrity: sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==} + lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -7980,6 +8509,12 @@ packages: peerDependencies: react: '>= 0.14.0' + math-expression-evaluator@1.4.0: + resolution: {integrity: sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==} + + math-random@1.0.4: + resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==} + matter-js@0.19.0: resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==} @@ -8032,6 +8567,15 @@ packages: memoizerific@1.11.3: resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} + memory-fs@0.2.0: + resolution: {integrity: sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==} + + memory-fs@0.3.0: + resolution: {integrity: sha512-QTNXnl79X97kZ9jJk/meJrtDuvgvRakX5LU7HZW1L7MsXHuSTwoMIzN9tOLLH3Xfsj/gbsSqX/ovnsqz246zKQ==} + + memory-fs@0.4.1: + resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==} + meow@9.0.0: resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} engines: {node: '>=10'} @@ -8141,6 +8685,10 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} + micromatch@2.3.11: + resolution: {integrity: sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==} + engines: {node: '>=0.10.0'} + micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -8153,6 +8701,9 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime@1.2.11: + resolution: {integrity: sha512-Ysa2F/nqTNGHhhm9MV8ure4+Hc+Y8AWiqUdHxsO7xu8zc92ND9f3kpALHjaP026Ft17UfxrMt95c50PLUeynBw==} + mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -8175,6 +8726,10 @@ packages: resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} engines: {node: '>=4'} + mimic-response@2.1.0: + resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==} + engines: {node: '>=8'} + mimic-response@3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} @@ -8190,6 +8745,14 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + minimatch@0.2.14: + resolution: {integrity: sha512-zZ+Jy8lVWlvqqeM8iZB7w7KmQkoJn8djM585z88rywrEbzoqawVa9FR5p2hwD+y74nfuKOjmNvi9gtWJNLqHvA==} + deprecated: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue + + minimatch@0.3.0: + resolution: {integrity: sha512-WFX1jI1AaxNTZVOHLBVazwTWKaQjoykSzCBNXB72vDTCzopQGtyP91tKdFK5cv1+qMwPyiTu1HqUriqplI8pcA==} + deprecated: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue + minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -8216,6 +8779,9 @@ packages: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} + minimist@0.0.10: + resolution: {integrity: sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -8264,6 +8830,10 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mkdirp@0.3.5: + resolution: {integrity: sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==} + deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -8361,9 +8931,17 @@ packages: engines: {node: ^18 || >=20} hasBin: true + natives@1.1.6: + resolution: {integrity: sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==} + deprecated: This module relies on Node.js's internals and will break at some point. Do not use it, and update to graceful-fs@4.x. + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + ncp@0.2.7: + resolution: {integrity: sha512-wPUepcV37u3Mw+ktjrUbl3azxwAkcD9RrVLQGlpSapWcEQM5jL0g8zwKo6ukOjVQAAEjqpRdLeojOalqqySpCg==} + hasBin: true + ncp@2.0.0: resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} hasBin: true @@ -8465,9 +9043,15 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + node-libs-browser@0.7.0: + resolution: {integrity: sha512-V0EeBff5/nauAta4yGYMdn/CYXpn2KYcE8r6rwU9qJDXG6wMrBhtWVfoKWphSvsnX+mZk6DzaGSO+Yz/MGBAGQ==} + node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} + node-webpmux@3.2.0: + resolution: {integrity: sha512-Rmc7MIS7zxDRPkfC9B15malsd1uStX2N6mYqHZNZeCJAVRDjmnxxTsyDh6POvWdNC7nmAYBW6CcHVY62DlnzRg==} + nodemailer@6.9.13: resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==} engines: {node: '>=6.0.0'} @@ -8512,10 +9096,22 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@1.9.1: + resolution: {integrity: sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==} + engines: {node: '>=4'} + normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} @@ -8547,6 +9143,9 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + num2fraction@1.2.2: + resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} + nwsapi@2.2.9: resolution: {integrity: sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==} @@ -8570,6 +9169,9 @@ packages: object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + object-inspect@1.13.1: + resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} + object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} @@ -8589,6 +9191,10 @@ packages: object.groupby@1.0.1: resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} + object.omit@2.0.1: + resolution: {integrity: sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==} + engines: {node: '>=0.10.0'} + object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -8628,6 +9234,10 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} + open@0.0.5: + resolution: {integrity: sha512-+X/dJYLapVO1VbC620DhtNZK9U4/kQVaTQp/Gh7cb6UTLYfGZzzU2ZXkWrOA/wBrf4UqAFwtLqXYTxe4tSnWQQ==} + engines: {node: '>= 0.6.0'} + open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -8639,6 +9249,9 @@ packages: resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==} hasBin: true + optimist@0.6.1: + resolution: {integrity: sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==} + optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -8647,6 +9260,9 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} + os-browserify@0.2.1: + resolution: {integrity: sha512-vHbnbzdqWJWvGOm7aOMDXHVUykPG0GdhfLkn5ZDmvbRI+wPj/XoB0/CRAkP9v28eZ7REIPPHJa+8ZEYixsWKmQ==} + os-filter-obj@2.0.0: resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==} engines: {node: '>=4'} @@ -8729,6 +9345,10 @@ packages: parse-data-uri@0.2.0: resolution: {integrity: sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==} + parse-glob@3.0.4: + resolution: {integrity: sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==} + engines: {node: '>=0.10.0'} + parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -8755,6 +9375,9 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} + path-browserify@0.0.0: + resolution: {integrity: sha512-WA3pxi1olUQcsl82W576vkqhUSGp0uBtr/381pxx5WXLp3NC+AB99hUG3aGW7H0Kg9PFr1D8wv1iJeICe+9Mhw==} + path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -8818,6 +9441,9 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} + pbkdf2-compat@2.0.1: + resolution: {integrity: sha512-JYubxYhymODUUWVq9/Tmo9VQFZ8LyrD/pbXVpwmt1Npr2z29KZwp7+IBT3/PRjr1xpecX4W1EcbjFjp8nE3stQ==} + peek-readable@5.0.0: resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} engines: {node: '>=14.16'} @@ -8935,6 +9561,14 @@ packages: pkg-types@1.0.3: resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + pkginfo@0.2.3: + resolution: {integrity: sha512-7W7wTrE/NsY8xv/DTGjwNIyNah81EQH0MWcTzrHL6pOpMocOGZc0Mbdz9aXxSrp+U0mSmkU8jrNCDCfUs3sOBg==} + engines: {node: '>= 0.4.0'} + + pkginfo@0.4.1: + resolution: {integrity: sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ==} + engines: {node: '>= 0.4.0'} + plimit-lit@1.5.0: resolution: {integrity: sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==} @@ -8954,88 +9588,158 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + polished@4.2.2: resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} engines: {node: '>=10'} + postcss-calc@5.3.1: + resolution: {integrity: sha512-iBcptYFq+QUh9gzP7ta2btw50o40s4uLI4UDVgd5yRAZtUDWc5APdl5yQDd2h/TyiZNbJrv0HiYhT102CMgN7Q==} + postcss-calc@9.0.1: resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.2 + postcss-colormin@2.2.2: + resolution: {integrity: sha512-XXitQe+jNNPf+vxvQXIQ1+pvdQKWKgkx8zlJNltcMEmLma1ypDRDQwlLt+6cP26fBreihNhZxohh1rcgCH2W5w==} + postcss-colormin@6.1.0: resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-convert-values@2.6.1: + resolution: {integrity: sha512-SE7mf25D3ORUEXpu3WUqQqy0nCbMuM5BEny+ULE/FXdS/0UMA58OdzwvzuHJRpIFlk1uojt16JhaEogtP6W2oA==} + postcss-convert-values@6.1.0: resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-discard-comments@2.0.4: + resolution: {integrity: sha512-yGbyBDo5FxsImE90LD8C87vgnNlweQkODMkUZlDVM/CBgLr9C5RasLGJxxh9GjVOBeG8NcCMatoqI1pXg8JNXg==} + postcss-discard-comments@6.0.2: resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-discard-duplicates@2.1.0: + resolution: {integrity: sha512-+lk5W1uqO8qIUTET+UETgj9GWykLC3LOldr7EehmymV0Wu36kyoHimC4cILrAAYpHQ+fr4ypKcWcVNaGzm0reA==} + postcss-discard-duplicates@6.0.3: resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-discard-empty@2.1.0: + resolution: {integrity: sha512-IBFoyrwk52dhF+5z/ZAbzq5Jy7Wq0aLUsOn69JNS+7YeuyHaNzJwBIYE0QlUH/p5d3L+OON72Fsexyb7OK/3og==} + postcss-discard-empty@6.0.3: resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-discard-overridden@0.1.1: + resolution: {integrity: sha512-IyKoDL8QNObOiUc6eBw8kMxBHCfxUaERYTUe2QF8k7j/xiirayDzzkmlR6lMQjrAM1p1DDRTvWrS7Aa8lp6/uA==} + postcss-discard-overridden@6.0.2: resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-discard-unused@2.2.3: + resolution: {integrity: sha512-nCbFNfqYAbKCw9J6PSJubpN9asnrwVLkRDFc4KCwyUEdOtM5XDE/eTW3OpqHrYY1L4fZxgan7LLRAAYYBzwzrg==} + + postcss-filter-plugins@2.0.3: + resolution: {integrity: sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==} + + postcss-merge-idents@2.1.7: + resolution: {integrity: sha512-9DHmfCZ7/hNHhIKnNkz4CU0ejtGen5BbTRJc13Z2uHfCedeCUsK2WEQoAJRBL+phs68iWK6Qf8Jze71anuysWA==} + + postcss-merge-longhand@2.0.2: + resolution: {integrity: sha512-ma7YvxjdLQdifnc1HFsW/AW6fVfubGyR+X4bE3FOSdBVMY9bZjKVdklHT+odknKBB7FSCfKIHC3yHK7RUAqRPg==} + postcss-merge-longhand@6.0.5: resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-merge-rules@2.1.2: + resolution: {integrity: sha512-Wgg2FS6W3AYBl+5L9poL6ZUISi5YzL+sDCJfM7zNw/Q1qsyVQXXZ2cbVui6mu2cYJpt1hOKCGj1xA4mq/obz/Q==} + postcss-merge-rules@6.1.1: resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-message-helpers@2.0.0: + resolution: {integrity: sha512-tPLZzVAiIJp46TBbpXtrUAKqedXSyW5xDEo1sikrfEfnTs+49SBZR/xDdqCiJvSSbtr615xDsaMF3RrxS2jZlA==} + + postcss-minify-font-values@1.0.5: + resolution: {integrity: sha512-vFSPzrJhNe6/8McOLU13XIsERohBJiIFFuC1PolgajOZdRWqRgKITP/A4Z/n4GQhEmtbxmO9NDw3QLaFfE1dFQ==} + postcss-minify-font-values@6.1.0: resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-minify-gradients@1.0.5: + resolution: {integrity: sha512-DZhT0OE+RbVqVyGsTIKx84rU/5cury1jmwPa19bViqYPQu499ZU831yMzzsyC8EhiZVd73+h5Z9xb/DdaBpw7Q==} + postcss-minify-gradients@6.0.3: resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-minify-params@1.2.2: + resolution: {integrity: sha512-hhJdMVgP8vasrHbkKAk+ab28vEmPYgyuDzRl31V3BEB3QOR3L5TTIVEWLDNnZZ3+fiTi9d6Ker8GM8S1h8p2Ow==} + postcss-minify-params@6.1.0: resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-minify-selectors@2.1.1: + resolution: {integrity: sha512-e13vxPBSo3ZaPne43KVgM+UETkx3Bs4/Qvm6yXI9HQpQp4nyb7HZ0gKpkF+Wn2x+/dbQ+swNpCdZSbMOT7+TIA==} + postcss-minify-selectors@6.0.4: resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-modules-extract-imports@1.2.1: + resolution: {integrity: sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==} + + postcss-modules-local-by-default@1.2.0: + resolution: {integrity: sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==} + + postcss-modules-scope@1.1.0: + resolution: {integrity: sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==} + + postcss-modules-values@1.3.0: + resolution: {integrity: sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==} + + postcss-normalize-charset@1.1.1: + resolution: {integrity: sha512-RKgjEks83l8w4yEhztOwNZ+nLSrJ+NvPNhpS+mVDzoaiRHZQVoG7NF2TP5qjwnaN9YswUhj6m1E0S0Z+WDCgEQ==} + postcss-normalize-charset@6.0.2: resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} engines: {node: ^14 || ^16 || >=18.0} @@ -9078,6 +9782,9 @@ packages: peerDependencies: postcss: ^8.4.31 + postcss-normalize-url@3.0.8: + resolution: {integrity: sha512-WqtWG6GV2nELsQEFES0RzfL2ebVwmGl/M8VmMbshKto/UClBo+mznX8Zi4/hkThdqx7ijwv+O8HWPdpK7nH/Ig==} + postcss-normalize-url@6.0.2: resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} engines: {node: ^14 || ^16 || >=18.0} @@ -9090,24 +9797,39 @@ packages: peerDependencies: postcss: ^8.4.31 + postcss-ordered-values@2.2.3: + resolution: {integrity: sha512-5RB1IUZhkxDCfa5fx/ogp/A82mtq+r7USqS+7zt0e428HJ7+BHCxyeY39ClmkkUtxdOd3mk8gD6d9bjH2BECMg==} + postcss-ordered-values@6.0.2: resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-reduce-idents@2.4.0: + resolution: {integrity: sha512-0+Ow9e8JLtffjumJJFPqvN4qAvokVbdQPnijUDSOX8tfTwrILLP4ETvrZcXZxAtpFLh/U0c+q8oRMJLr1Kiu4w==} + + postcss-reduce-initial@1.0.1: + resolution: {integrity: sha512-jJFrV1vWOPCQsIVitawGesRgMgunbclERQ/IRGW7r93uHrVzNQQmHQ7znsOIjJPZ4yWMzs5A8NFhp3AkPHPbDA==} + postcss-reduce-initial@6.1.0: resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-reduce-transforms@1.0.4: + resolution: {integrity: sha512-lGgRqnSuAR5i5uUg1TA33r9UngfTadWxOyL2qx1KuPoCQzfmtaHjp9PuwX7yVyRxG3BWBzeFUaS5uV9eVgnEgQ==} + postcss-reduce-transforms@6.0.2: resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-selector-parser@2.2.3: + resolution: {integrity: sha512-3pqyakeGhrO0BQ5+/tGTfvi5IAUAhHRayGK8WFSu06aEv2BmHoXw/Mhb+w7VY5HERIuC+QoUI7wgrCcq2hqCVA==} + postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -9116,21 +9838,41 @@ packages: resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} engines: {node: '>=4'} + postcss-svgo@2.1.6: + resolution: {integrity: sha512-y5AdQdgBoF4rbpdbeWAJuxE953g/ylRfVNp6mvAi61VCN/Y25Tu9p5mh3CyI42WbTRIiwR9a1GdFtmDnNPeskQ==} + postcss-svgo@6.0.3: resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} engines: {node: ^14 || ^16 || >= 18} peerDependencies: postcss: ^8.4.31 + postcss-unique-selectors@2.0.2: + resolution: {integrity: sha512-WZX8r1M0+IyljoJOJleg3kYm10hxNYF9scqAT7v/xeSX1IdehutOM85SNO0gP9K+bgs86XERr7Ud5u3ch4+D8g==} + postcss-unique-selectors@6.0.4: resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + postcss-value-parser@3.3.1: + resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} + postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + postcss-zindex@2.2.0: + resolution: {integrity: sha512-uhRZ2hRgj0lorxm9cr62B01YzpUe63h0RXMXQ4gWW3oa2rpJh+FJAiEAytaFCPU/VgaBS+uW2SJ1XKyDNz1h4w==} + + postcss@5.2.18: + resolution: {integrity: sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==} + engines: {node: '>=0.12'} + + postcss@6.0.23: + resolution: {integrity: sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==} + engines: {node: '>=4.0.0'} + postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -9174,6 +9916,14 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} + prepend-http@1.0.4: + resolution: {integrity: sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==} + engines: {node: '>=0.10.0'} + + preserve@0.2.0: + resolution: {integrity: sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==} + engines: {node: '>=0.10.0'} + prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} @@ -9239,6 +9989,10 @@ packages: promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + prompt@0.2.1: + resolution: {integrity: sha512-HTqCMaVQXu7SzcaF1PJJxPMgTR3My8a6ZZTH5wHzi0X+fotszg9A0Ra6ZDCFX1i/0q9QCS2rMmljVOBGksYsaQ==} + engines: {node: '>= 0.4.0'} + prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -9262,6 +10016,9 @@ packages: proxycheck-ts@0.0.9: resolution: {integrity: sha512-qxdMgLB01kPksBAeAHI2Zt5q/fW0glZKZK9d9eNm0/KyowKuKzVmvirF2ztsGcIzU7r6s6YvPwXJuPTeQ+LBTg==} + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + ps-list@8.1.1: resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -9325,6 +10082,9 @@ packages: pumpify@1.5.1: resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -9339,6 +10099,10 @@ packages: resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} engines: {node: '>=6.0.0'} + q@1.5.1: + resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} + engines: {node: '>=0.6.0', teleport: '>=0.2.0'} + qrcode@1.5.3: resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} engines: {node: '>=10.13.0'} @@ -9356,10 +10120,22 @@ packages: resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} engines: {node: '>=0.6'} + qs@6.12.1: + resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} + engines: {node: '>=0.6'} + qs@6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} + query-string@4.3.4: + resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==} + engines: {node: '>=0.10.0'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -9390,6 +10166,10 @@ packages: resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==} engines: {node: '>= 0.6.0'} + randomatic@3.1.1: + resolution: {integrity: sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==} + engines: {node: '>= 0.10.0'} + range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -9462,6 +10242,9 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} + readable-stream@1.0.34: + resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} + readable-stream@1.1.14: resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} @@ -9521,6 +10304,12 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} + reduce-css-calc@1.3.0: + resolution: {integrity: sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==} + + reduce-function-call@1.0.3: + resolution: {integrity: sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==} + reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -9540,10 +10329,18 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + regex-cache@0.4.4: + resolution: {integrity: sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==} + engines: {node: '>=0.10.0'} + regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} + regexp.prototype.flags@1.5.2: + resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} + engines: {node: '>= 0.4'} + regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -9567,9 +10364,20 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + rename@1.0.4: resolution: {integrity: sha512-YMM6Fn3lrFOCjhORKjj+z/yizj8WSzv3F3YUlpJA20fteWCb0HbJU19nvuRBPUM5dWgxJcHP+kix3M+5NowJyA==} + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -9578,6 +10386,11 @@ packages: engines: {node: '>= 6'} deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + request@2.9.203: + resolution: {integrity: sha512-OWtna9w7yRI/gcfu3VaURgIwE1FHgbz5+fHGQ9GJTHcJ4+uvHnDjXd+N7mVDOv5+1fp1CRPzUSY2wcM345Z2Fw==} + engines: {'0': node >= 0.3.6} + deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -9589,6 +10402,11 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + requirejs@2.1.4: + resolution: {integrity: sha512-i7EPWQSHAVh+UNGUzenTR7FeZIQWK8Lo5U9G/PRWAnj7MvZaYD4Bo6QmgK0biPaTvjuQCuqf5JzUxTRq833Ebg==} + engines: {node: '>=0.4.0'} + hasBin: true + requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -9644,9 +10462,23 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + revalidator@0.1.8: + resolution: {integrity: sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==} + engines: {node: '>= 0.4.0'} + rfdc@1.3.0: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} + right-align@0.1.3: + resolution: {integrity: sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==} + engines: {node: '>=0.10.0'} + + rimraf@1.0.9: + resolution: {integrity: sha512-vMboELGrBSgcKZ1fVCCfLEvRdhIeOhDAK4XhjuKXFFs/TOkvWxjT9RRHw2kDhM139zfU3wUQbJkRanMXLl7zFA==} + + rimraf@2.0.3: + resolution: {integrity: sha512-uR09PSoW2+1hW0hquRqxb+Ae2h6R5ls3OAy2oNekQFtqbSJkltkhKRa+OhZKoxWsN9195Gp1vg7sELDRoJ8a3w==} + rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} hasBin: true @@ -9659,6 +10491,9 @@ packages: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true + ripemd160@0.2.0: + resolution: {integrity: sha512-JJsJ74Mw4sUDDisXGDnNNyN9xWmt5HcH6Kwvb/0m/IvTKjnLAtZfzeoLdpxk44AxQZki54oCCd+Kt0nPQ2AF2g==} + rollup@4.17.2: resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -9707,6 +10542,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + save-pixels-jpeg-js-upgrade@2.3.4-jpeg-js-upgrade.0: + resolution: {integrity: sha512-mFeQrydaAVTYQjywMvuNtjHmYZwAXZlo96Xouh3I7wTYDdUhMttoEPQsfk6EP+Wxt+fo/B8SW/A8dfhBImhKIw==} + sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -9731,6 +10569,10 @@ packages: resolution: {integrity: sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==} engines: {node: '>=8'} + semver@1.0.14: + resolution: {integrity: sha512-edb8Hl6pnVrKQauQHTqQkRlpZB5RZ/pEe2ir3C3Ztdst0qIayag31dSLsxexLRe80NiWkCffTF5MB7XrGydhSQ==} + hasBin: true + semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true @@ -9753,6 +10595,10 @@ packages: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} + serve-index@1.9.1: + resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} + engines: {node: '>= 0.8.0'} + serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -9763,12 +10609,27 @@ packages: set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + setprototypeof@1.1.0: + resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} + setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.2.6: + resolution: {integrity: sha512-GC+qN4sf/O6bDwz6CHaz8HVQfLbbNyIsXpTZLiD5c1badnWA63WVAH1msoCq+fXcV0dZ50jxTqKA8seu40845A==} + hasBin: true + sha.js@2.4.11: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true @@ -9803,9 +10664,16 @@ packages: side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + side-channel@1.0.6: + resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} + engines: {node: '>= 0.4'} + siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + sigmund@1.0.1: + resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} + signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -9813,6 +10681,12 @@ packages: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@3.1.1: + resolution: {integrity: sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==} + simple-oauth2@5.0.0: resolution: {integrity: sha512-8291lo/z5ZdpmiOFzOs1kF3cxn22bMj5FFH+DNUppLJrpoIlM1QnFiE7KpshHu3J3i21TVcx4yW+gXYjdCKDLQ==} @@ -9926,6 +10800,13 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} + sockjs-client@1.6.1: + resolution: {integrity: sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==} + engines: {node: '>=12'} + + sockjs@0.3.24: + resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} + socks-proxy-agent@8.0.2: resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==} engines: {node: '>= 14'} @@ -9948,6 +10829,9 @@ packages: sortablejs@1.14.0: resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + source-list-map@0.1.8: + resolution: {integrity: sha512-cabwdhnSNf/tTDMh/DXZXlkeQLvdYT5xfGYBohqHG7wb3bBQrQlHQNWM9NWSOboXXK1zgwz6JzS5e4hZq9vxMw==} + source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -9962,6 +10846,14 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + source-map@0.4.4: + resolution: {integrity: sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==} + engines: {node: '>=0.8.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -10007,6 +10899,9 @@ packages: resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -10022,6 +10917,10 @@ packages: engines: {node: '>=16'} hasBin: true + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -10059,12 +10958,21 @@ packages: resolution: {integrity: sha512-/Mvij0Br5bUwJpCvqAUZMEDIWmdRxEyllvVj8Ukw5lIWJePxfpSsz4px5jg9+R6B9tO8sQSqjg4HJvQ/pZk8Tg==} hasBin: true + stream-browserify@2.0.2: + resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==} + stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} + stream-cache@0.0.2: + resolution: {integrity: sha512-FsMTiRi4aXOcbL3M2lh7yAOWqM7kfVWQfkJ6kelrhdKNpJJVm0IebICQ2LURsbC5w9XfPSRwd9DkfqDHR9OP3g==} + stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} + stream-http@2.8.3: + resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==} + stream-parser@0.3.1: resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} @@ -10088,6 +10996,10 @@ packages: strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} + strict-uri-encode@1.1.0: + resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} + engines: {node: '>=0.10.0'} + string-argv@0.3.1: resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} engines: {node: '>=0.6.19'} @@ -10126,6 +11038,10 @@ packages: stringz@2.1.0: resolution: {integrity: sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==} + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -10180,12 +11096,27 @@ packages: resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} engines: {node: '>=14.16'} + style-loader@0.13.2: + resolution: {integrity: sha512-0lN0o7DS1G/HRoYJQMEO3yP+tNCuAnNuX1mt/2Yw4edSok45vebtyJoHUyBREasuPYBtZpC3d8wvgY/WD68ZJg==} + + style@0.0.3: + resolution: {integrity: sha512-HDhQ0NTbt7xR9jj9b5zWTelA9iHbg8PtdarlceLzMMQ2gEZjd/Rpmd8PJzkxey5q/fC0cwFKJ18BmW9KGmRTcA==} + engines: {node: '>=0.2.0'} + stylehacks@6.1.1: resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + + supports-color@3.2.3: + resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==} + engines: {node: '>=0.8.0'} + supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -10210,6 +11141,12 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} + svgo@0.7.2: + resolution: {integrity: sha512-jT/g9FFMoe9lu2IT6HtAxTA7RR2XOrmcrmCtGnyB/+GQnV6ZjNn+KOHZbZ35yL81+1F/aB6OeEsJztzBQ2EEwA==} + engines: {node: '>=0.10.0'} + deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. + hasBin: true + svgo@3.2.0: resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} engines: {node: '>=14.0.0'} @@ -10224,6 +11161,10 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true + tapable@0.1.10: + resolution: {integrity: sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==} + engines: {node: '>=0.6'} + tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} @@ -10234,6 +11175,10 @@ packages: tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} + tar@0.1.20: + resolution: {integrity: sha512-RW79YhJI8vSyOcVv0OzOr7DVGkhtFhyz6cufNETPF7dmCJ/kQH+ubpCZkV8Wb7RvQlPuGplQPy0o2ihElzkEZA==} + deprecated: This version of tar is no longer supported, and will not receive security updates. Please upgrade asap. + tar@4.4.19: resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==} engines: {node: '>=4.5'} @@ -10305,6 +11250,14 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + time-stamp@2.2.0: + resolution: {integrity: sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==} + engines: {node: '>=0.10.0'} + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + tiny-invariant@1.3.1: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} @@ -10336,6 +11289,9 @@ packages: tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + to-arraybuffer@1.0.1: + resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} + to-data-view@1.1.0: resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==} @@ -10452,6 +11408,9 @@ packages: engines: {node: '>=18.0.0'} hasBin: true + tty-browserify@0.0.0: + resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} + tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -10596,11 +11555,19 @@ packages: ufo@1.3.2: resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} + uglify-js@2.7.5: + resolution: {integrity: sha512-RvbIYn4DIadCg1MV7YP7OrpxnVrtEieZzbK0KSQvwWGAHojqWJxInkQhmtYGRo9PTwwkJkljIgzMyA1VitEc4Q==} + engines: {node: '>=0.8.0'} + hasBin: true + uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} hasBin: true + uglify-to-browserify@1.0.2: + resolution: {integrity: sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==} + uid2@0.0.4: resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} @@ -10618,6 +11585,9 @@ packages: undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} + underscore@1.3.3: + resolution: {integrity: sha512-ddgUaY7xyrznJ0tbSUZgvNdv5qbiF6XcUBTrHgdCOVUrxJYWozD5KyiRjtIwds1reZ7O1iPLv5rIyqnVAcS6gg==} + undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -10647,6 +11617,9 @@ packages: uniq@1.0.1: resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==} + uniqs@2.0.0: + resolution: {integrity: sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==} + unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -10709,6 +11682,9 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + url@0.11.3: + resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} + utf-8-validate@6.0.3: resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==} engines: {node: '>=6.14.2'} @@ -10716,9 +11692,16 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + utile@0.1.7: + resolution: {integrity: sha512-ODuep9+cqmpklRDjEPS1JtY27+zg3MUfeWf8/NOVIJeMJB2nZBOemPXnIeseVkFcvGq9euiIMkS4HiUAEIFbow==} + engines: {node: '>= 0.6.4'} + utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -10760,6 +11743,9 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} + vendors@1.0.4: + resolution: {integrity: sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==} + verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} @@ -10843,6 +11829,9 @@ packages: webdriverio: optional: true + vm-browserify@0.0.4: + resolution: {integrity: sha512-NyZNR3WDah+NPkjh/YmhuWSsT4a0mF0BJYgUmvrJ70zxjTXh5Y2Asobxlh0Nfs0PCFB5FVpRJft7NozAWFMwLQ==} + void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} @@ -10882,8 +11871,8 @@ packages: vue-component-type-helpers@2.0.16: resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==} - vue-component-type-helpers@2.0.18: - resolution: {integrity: sha512-zi1QaDBhSb3oeHJh55aTCrosFNKEQsOL9j3XCAjpF9dwxDUUtd85RkJVzO+YpJqy1LNoCWLU8gwuZ7HW2iDN/A==} + vue-component-type-helpers@2.0.19: + resolution: {integrity: sha512-cN3f1aTxxKo4lzNeQAkVopswuImUrb5Iurll9Gaw5cqpnbTAxtEMM1mgi6ou4X79OCyqYv1U1mzBHJkzmiK82w==} vue-demi@0.14.7: resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==} @@ -10952,6 +11941,9 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + watchpack@0.2.9: + resolution: {integrity: sha512-hmLWdxNfe0Ou1xaRj+ublbOYUaZJfVz1VuHQfERLVlUrLS21gUaGa2gWRl8L5Ej1aUS3KxFN+1qoWK4kZLMvKw==} + watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -10978,6 +11970,32 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} + webm-wasm@0.4.1: + resolution: {integrity: sha512-FOKtpyY8tXcaWEarq2+o9v+DvnVqeuJPXDSfgp99UWMavkSUNsSh1T4By6Q9BBgTFTWxrcJNlwxVvHGQ3/xP5Q==} + + webp-hero@0.0.2: + resolution: {integrity: sha512-XDN8k2DZerXAawblkGKbcRXAz3WjY6Id5fTmrsOvblzFg5jELfoDCOxRDHD3zIGJo3OPEjLRsVS6Kzl36HxjqA==} + + webpack-core@0.6.9: + resolution: {integrity: sha512-P6ZUGXn5buTEZyTStCHHLwtWGKSm/jA629Zgp4pcHSsy60CCsT9MaHDxNIPL+GGJ2KwOgI6ORwQtHcrYHAt2UQ==} + engines: {node: '>=0.6'} + + webpack-dev-middleware@1.12.2: + resolution: {integrity: sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==} + engines: {node: '>=0.6'} + peerDependencies: + webpack: ^1.0.0 || ^2.0.0 || ^3.0.0 + + webpack-dev-server@1.16.5: + resolution: {integrity: sha512-on9j8SBuJXa2lzyIAv0DasJT8SteshUrEjjKc/mc8D68U7RN0mIBZksAcjnPW72RSJa9scWZ+C+Dme76LDH+lA==} + hasBin: true + peerDependencies: + webpack: '>=1.3.0 <3' + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -10985,6 +12003,27 @@ packages: webpack-virtual-modules@0.5.0: resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + webpack@1.15.0: + resolution: {integrity: sha512-+8bxNSHMZCWBa6hi++2A2pw9GmLUWY6lII+aIXlgUPpB+ClNrUKgP8hx0w+hxjWhX81hclUYPGFg+7NxgLTUYQ==} + engines: {node: '>=0.6'} + hasBin: true + peerDependencies: + webpack-cli: '*' + webpack-command: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + webpack-command: + optional: true + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -11004,6 +12043,10 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + whet.extend@0.9.9: + resolution: {integrity: sha512-mmIPAft2vTgEILgPeZFqE/wWh24SEsR/k+N9fJ3Jxrz44iDFy9aemCxdksfURSHYFCLmvs/d/7Iso5XjPpNfrA==} + engines: {node: '>=0.6.0'} + which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -11039,10 +12082,26 @@ packages: wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} + window-size@0.1.0: + resolution: {integrity: sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==} + engines: {node: '>= 0.8.0'} + + winston@0.6.2: + resolution: {integrity: sha512-BzHNq8X415XGFkGPT+ACKTj95ghSAbR4eThAtKg4OC4PYVn5FLIdTETYUv76f4QxUcG1ps6yqnbO1K/81hGIzQ==} + engines: {node: '>= 0.4.0'} + with@7.0.2: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} + wordwrap@0.0.2: + resolution: {integrity: sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==} + engines: {node: '>=0.4.0'} + + wordwrap@0.0.3: + resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==} + engines: {node: '>=0.4.0'} + wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -11150,6 +12209,9 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} + yargs@3.10.0: + resolution: {integrity: sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==} + yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} @@ -12596,6 +13658,17 @@ snapshots: dependencies: statuses: 2.0.1 + '@caed0/webp-conv@1.1.0(encoding@0.1.13)': + dependencies: + canvas: 2.11.2(encoding@0.1.13) + gif-encoder-2: 1.0.5 + image-conversion: 2.1.1 + node-webpmux: 3.2.0 + pngjs: 7.0.0 + transitivePeerDependencies: + - encoding + - supports-color + '@canvas/image-data@1.0.0': {} '@colors/colors@1.5.0': @@ -12990,7 +14063,7 @@ snapshots: '@fastify/express@3.0.0': dependencies: - express: 4.19.2 + express: 4.19.2(supports-color@3.2.3) fastify-plugin: 4.5.0 transitivePeerDependencies: - supports-color @@ -13471,7 +14544,6 @@ snapshots: transitivePeerDependencies: - encoding - supports-color - optional: true '@mcaptcha/core-glue@0.1.0-alpha-5': {} @@ -13680,9 +14752,9 @@ snapshots: dependencies: '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) - body-parser: 1.20.2 + body-parser: 1.20.2(supports-color@3.2.3) cors: 2.8.5 - express: 4.19.2 + express: 4.19.2(supports-color@3.2.3) multer: 1.4.4-lts.1 tslib: 2.6.2 transitivePeerDependencies: @@ -14400,11 +15472,11 @@ snapshots: dependencies: '@storybook/global': 5.0.0 - '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': + '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': dependencies: '@storybook/global': 5.0.0 '@storybook/instrumenter': 8.0.9 - '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) + '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) '@storybook/types': 8.0.9 polished: 4.2.2 ts-dedent: 2.2.0 @@ -14499,7 +15571,7 @@ snapshots: ejs: 3.1.9 esbuild: 0.20.2 esbuild-plugin-alias: 0.2.1 - express: 4.19.2 + express: 4.19.2(supports-color@3.2.3) fs-extra: 11.1.1 process: 0.11.10 util: 0.12.5 @@ -14692,7 +15764,7 @@ snapshots: better-opn: 3.0.2 chalk: 4.1.2 cli-table3: 0.6.3 - compression: 1.7.4 + compression: 1.7.4(supports-color@3.2.3) detect-port: 1.5.1 express: 4.18.2 fs-extra: 11.1.1 @@ -14908,14 +15980,14 @@ snapshots: - encoding - supports-color - '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': + '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': dependencies: '@storybook/client-logger': 8.0.9 '@storybook/core-events': 8.0.9 '@storybook/instrumenter': 8.0.9 '@storybook/preview-api': 8.0.9 '@testing-library/dom': 9.3.4 - '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) + '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)) '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4) '@vitest/expect': 1.3.1 '@vitest/spy': 1.6.0 @@ -14976,7 +16048,7 @@ snapshots: ts-dedent: 2.2.0 type-fest: 2.19.0 vue: 3.4.26(typescript@5.4.5) - vue-component-type-helpers: 2.0.18 + vue-component-type-helpers: 2.0.19 transitivePeerDependencies: - encoding - supports-color @@ -15217,7 +16289,7 @@ snapshots: lz-string: 1.5.0 pretty-format: 27.5.1 - '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': + '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': dependencies: '@adobe/css-tools': 4.3.3 '@babel/runtime': 7.23.4 @@ -15231,7 +16303,7 @@ snapshots: '@jest/globals': 29.7.0 '@types/jest': 29.5.12 jest: 29.7.0(@types/node@20.12.7) - vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) + vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) '@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4)': dependencies: @@ -15895,7 +16967,7 @@ snapshots: vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3) vue: 3.4.26(typescript@5.4.5) - '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': + '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))': dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 @@ -15908,7 +16980,7 @@ snapshots: std-env: 3.7.0 test-exclude: 6.0.0 v8-to-istanbul: 9.2.0 - vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) + vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) transitivePeerDependencies: - supports-color @@ -16130,6 +17202,8 @@ snapshots: acorn-walk@8.3.2: {} + acorn@3.3.0: {} + acorn@7.4.1: {} acorn@8.11.3: {} @@ -16193,16 +17267,32 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 + align-text@0.1.4: + dependencies: + kind-of: 3.2.2 + longest: 1.0.1 + repeat-string: 1.6.1 + + almond@0.2.5: {} + + alphanum-sort@1.0.2: {} + + amdefine@1.0.1: {} + ansi-colors@4.1.3: {} ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 + ansi-regex@2.1.1: {} + ansi-regex@5.0.1: {} ansi-regex@6.0.1: {} + ansi-styles@2.2.1: {} + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -16222,14 +17312,15 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 + apng-js@1.1.1: {} + app-root-dir@1.0.2: {} app-root-path@3.1.0: {} append-field@1.0.0: {} - aproba@2.0.0: - optional: true + aproba@2.0.0: {} arch@2.2.0: {} @@ -16259,7 +17350,6 @@ snapshots: dependencies: delegates: 1.0.0 readable-stream: 3.6.0 - optional: true arg@5.0.2: {} @@ -16273,6 +17363,12 @@ snapshots: dependencies: deep-equal: 2.2.0 + arr-diff@2.0.0: + dependencies: + arr-flatten: 1.1.0 + + arr-flatten@1.1.0: {} + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.2 @@ -16290,6 +17386,8 @@ snapshots: array-union@2.1.0: {} + array-unique@0.2.1: {} + array.prototype.findlastindex@1.2.3: dependencies: call-bind: 1.0.2 @@ -16346,6 +17444,11 @@ snapshots: assert-plus@1.0.0: {} + assert@1.5.1: + dependencies: + object.assign: 4.1.4 + util: 0.10.4 + assert@2.1.0: dependencies: call-bind: 1.0.2 @@ -16368,6 +17471,14 @@ snapshots: dependencies: tslib: 2.6.2 + async@0.1.22: {} + + async@0.2.10: {} + + async@0.9.2: {} + + async@1.5.2: {} + async@3.2.4: {} asynckit@0.4.0: {} @@ -16376,6 +17487,15 @@ snapshots: atomic-sleep@1.0.0: {} + autoprefixer@6.7.7: + dependencies: + browserslist: 1.7.7 + caniuse-db: 1.0.30001620 + normalize-range: 0.1.2 + num2fraction: 1.2.2 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + available-typed-arrays@1.0.5: {} avvio@8.3.0: @@ -16413,6 +17533,12 @@ snapshots: b4a@1.6.4: {} + babel-code-frame@6.26.0: + dependencies: + chalk: 1.1.3 + esutils: 2.0.3 + js-tokens: 3.0.2 + babel-core@7.0.0-bridge.0(@babel/core@7.24.0): dependencies: '@babel/core': 7.24.0 @@ -16499,10 +17625,14 @@ snapshots: bail@2.0.2: {} + balanced-match@0.4.2: {} + balanced-match@1.0.2: {} base64-js@1.5.1: {} + batch@0.6.1: {} + bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 @@ -16515,6 +17645,10 @@ snapshots: big-integer@1.6.51: {} + big.js@3.2.0: {} + + big.js@5.2.2: {} + bin-check@4.1.0: dependencies: execa: 0.7.0 @@ -16541,6 +17675,10 @@ snapshots: blob-util@2.0.2: {} + block-stream@0.0.9: + dependencies: + inherits: 2.0.4 + bluebird@3.7.2: {} blurhash@2.0.5: {} @@ -16551,7 +17689,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) depd: 2.0.0 destroy: 1.2.0 http-errors: 2.0.0 @@ -16564,11 +17702,11 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@1.20.2: + body-parser@1.20.2(supports-color@3.2.3): dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) depd: 2.0.0 destroy: 1.2.0 http-errors: 2.0.0 @@ -16598,6 +17736,12 @@ snapshots: dependencies: balanced-match: 1.0.2 + braces@1.8.5: + dependencies: + expand-range: 1.8.2 + preserve: 0.2.0 + repeat-element: 1.1.4 + braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -16611,10 +17755,19 @@ snapshots: browser-assert@1.2.1: {} + browserify-aes@0.4.0: + dependencies: + inherits: 2.0.4 + browserify-zlib@0.1.4: dependencies: pako: 0.2.9 + browserslist@1.7.7: + dependencies: + caniuse-db: 1.0.30001620 + electron-to-chromium: 1.4.686 + browserslist@4.22.2: dependencies: caniuse-lite: 1.0.30001566 @@ -16641,6 +17794,12 @@ snapshots: buffer-from@1.1.2: {} + buffer@4.9.2: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + isarray: 1.0.0 + buffer@5.6.0: dependencies: base64-js: 1.5.1 @@ -16661,6 +17820,8 @@ snapshots: node-gyp-build: 4.6.0 optional: true + builtin-status-codes@3.0.0: {} + bullmq@5.7.8: dependencies: cron-parser: 4.8.1 @@ -16731,6 +17892,14 @@ snapshots: function-bind: 1.1.2 get-intrinsic: 1.2.1 + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + call-me-maybe@1.0.2: {} callsites@3.1.0: {} @@ -16741,10 +17910,19 @@ snapshots: map-obj: 4.3.0 quick-lru: 4.0.1 + camelcase@1.2.1: {} + camelcase@5.3.1: {} camelcase@6.3.0: {} + caniuse-api@1.6.1: + dependencies: + browserslist: 1.7.7 + caniuse-db: 1.0.30001620 + lodash.memoize: 4.1.2 + lodash.uniq: 4.5.0 + caniuse-api@3.0.0: dependencies: browserslist: 4.23.0 @@ -16752,6 +17930,8 @@ snapshots: lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 + caniuse-db@1.0.30001620: {} + caniuse-lite@1.0.30001566: {} caniuse-lite@1.0.30001591: {} @@ -16760,6 +17940,15 @@ snapshots: canvas-confetti@1.9.3: {} + canvas@2.11.2(encoding@0.1.13): + dependencies: + '@mapbox/node-pre-gyp': 1.0.9(encoding@0.1.13) + nan: 2.18.0 + simple-get: 3.1.1 + transitivePeerDependencies: + - encoding + - supports-color + caseless@0.12.0: {} cbor@9.0.2: @@ -16768,6 +17957,11 @@ snapshots: ccount@2.0.1: {} + center-align@0.1.3: + dependencies: + align-text: 0.1.4 + lazy-cache: 1.0.4 + chai@4.3.10: dependencies: assertion-error: 1.1.0 @@ -16782,6 +17976,14 @@ snapshots: dependencies: chalk: 5.3.0 + chalk@1.1.3: + dependencies: + ansi-styles: 2.2.1 + escape-string-regexp: 1.0.5 + has-ansi: 2.0.0 + strip-ansi: 3.0.1 + supports-color: 2.0.0 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -16877,6 +18079,10 @@ snapshots: cjs-module-lexer@1.2.2: {} + clap@1.2.3: + dependencies: + chalk: 1.1.3 + clean-stack@2.2.0: {} clean-stack@5.2.0: @@ -16913,6 +18119,12 @@ snapshots: cli-width@4.1.0: {} + cliui@2.1.0: + dependencies: + center-align: 0.1.3 + right-align: 0.1.3 + wordwrap: 0.0.2 + cliui@6.0.0: dependencies: string-width: 4.2.3 @@ -16947,6 +18159,10 @@ snapshots: co@4.6.0: {} + coa@1.0.4: + dependencies: + q: 1.5.1 + code-error-fragment@0.0.230: {} collect-v8-coverage@1.0.1: {} @@ -16963,13 +18179,22 @@ snapshots: color-name@1.1.4: {} + color-string@0.3.0: + dependencies: + color-name: 1.1.4 + color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - color-support@1.1.3: - optional: true + color-support@1.1.3: {} + + color@0.11.4: + dependencies: + clone: 1.0.4 + color-convert: 1.9.3 + color-string: 0.3.0 color@4.2.3: dependencies: @@ -16980,6 +18205,16 @@ snapshots: colorette@2.0.19: {} + colormin@1.1.2: + dependencies: + color: 0.11.4 + css-color-names: 0.0.4 + has: 1.0.3 + + colors@0.6.2: {} + + colors@1.1.2: {} + combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -17014,12 +18249,12 @@ snapshots: dependencies: mime-db: 1.52.0 - compression@1.7.4: + compression@1.7.4(supports-color@3.2.3): dependencies: accepts: 1.3.8 bytes: 3.0.0 compressible: 2.0.18 - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) on-headers: 1.0.2 safe-buffer: 5.1.2 vary: 1.1.2 @@ -17042,22 +18277,31 @@ snapshots: ini: 1.3.8 proto-list: 1.2.4 + connect-history-api-fallback@1.6.0: {} + consola@2.15.3: {} - console-control-strings@1.1.0: - optional: true + console-browserify@1.2.0: {} + + console-control-strings@1.1.0: {} constantinople@4.0.1: dependencies: '@babel/parser': 7.23.9 '@babel/types': 7.23.5 + constants-browserify@1.0.0: {} + content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 content-type@1.0.5: {} + contentstream@1.0.0: + dependencies: + readable-stream: 1.0.34 + convert-source-map@2.0.0: {} cookie-signature@1.0.6: {} @@ -17142,12 +18386,36 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 + crypto-browserify@3.3.0: + dependencies: + browserify-aes: 0.4.0 + pbkdf2-compat: 2.0.1 + ripemd160: 0.2.0 + sha.js: 2.2.6 + crypto-random-string@2.0.0: {} + css-color-names@0.0.4: {} + css-declaration-sorter@7.2.0(postcss@8.4.38): dependencies: postcss: 8.4.38 + css-loader@0.26.4: + dependencies: + babel-code-frame: 6.26.0 + css-selector-tokenizer: 0.7.3 + cssnano: 3.10.0 + loader-utils: 1.4.2 + lodash.camelcase: 4.3.0 + object-assign: 4.1.1 + postcss: 5.2.18 + postcss-modules-extract-imports: 1.2.1 + postcss-modules-local-by-default: 1.2.0 + postcss-modules-scope: 1.1.0 + postcss-modules-values: 1.3.0 + source-list-map: 0.1.8 + css-select@5.1.0: dependencies: boolbase: 1.0.0 @@ -17156,6 +18424,11 @@ snapshots: domutils: 3.0.1 nth-check: 2.1.1 + css-selector-tokenizer@0.7.3: + dependencies: + cssesc: 3.0.0 + fastparse: 1.1.2 + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 @@ -17210,12 +18483,52 @@ snapshots: dependencies: postcss: 8.4.38 + cssnano@3.10.0: + dependencies: + autoprefixer: 6.7.7 + decamelize: 1.2.0 + defined: 1.0.1 + has: 1.0.3 + object-assign: 4.1.1 + postcss: 5.2.18 + postcss-calc: 5.3.1 + postcss-colormin: 2.2.2 + postcss-convert-values: 2.6.1 + postcss-discard-comments: 2.0.4 + postcss-discard-duplicates: 2.1.0 + postcss-discard-empty: 2.1.0 + postcss-discard-overridden: 0.1.1 + postcss-discard-unused: 2.2.3 + postcss-filter-plugins: 2.0.3 + postcss-merge-idents: 2.1.7 + postcss-merge-longhand: 2.0.2 + postcss-merge-rules: 2.1.2 + postcss-minify-font-values: 1.0.5 + postcss-minify-gradients: 1.0.5 + postcss-minify-params: 1.2.2 + postcss-minify-selectors: 2.1.1 + postcss-normalize-charset: 1.1.1 + postcss-normalize-url: 3.0.8 + postcss-ordered-values: 2.2.3 + postcss-reduce-idents: 2.4.0 + postcss-reduce-initial: 1.0.1 + postcss-reduce-transforms: 1.0.4 + postcss-svgo: 2.1.6 + postcss-unique-selectors: 2.0.2 + postcss-value-parser: 3.3.1 + postcss-zindex: 2.2.0 + cssnano@6.1.2(postcss@8.4.38): dependencies: cssnano-preset-default: 6.1.2(postcss@8.4.38) lilconfig: 3.1.1 postcss: 8.4.38 + csso@2.3.2: + dependencies: + clap: 1.2.3 + source-map: 0.5.7 + csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -17230,6 +18543,8 @@ snapshots: dependencies: uniq: 1.0.1 + cycle@1.0.3: {} + cypress@13.7.3: dependencies: '@cypress/request': 3.0.0 @@ -17341,9 +18656,17 @@ snapshots: de-indent@1.0.2: {} - debug@2.6.9: + debug@2.6.9(supports-color@3.2.3): dependencies: ms: 2.0.0 + optionalDependencies: + supports-color: 3.2.3 + + debug@3.2.7(supports-color@3.2.3): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 3.2.3 debug@3.2.7(supports-color@8.1.1): dependencies: @@ -17387,6 +18710,10 @@ snapshots: dependencies: character-entities: 2.0.2 + decompress-response@4.2.1: + dependencies: + mimic-response: 2.1.0 + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -17406,6 +18733,15 @@ snapshots: dependencies: type-detect: 4.0.8 + deep-equal@1.1.2: + dependencies: + is-arguments: 1.1.1 + is-date-object: 1.0.5 + is-regex: 1.1.4 + object-is: 1.1.5 + object-keys: 1.1.1 + regexp.prototype.flags: 1.5.2 + deep-equal@2.2.0: dependencies: call-bind: 1.0.2 @@ -17441,6 +18777,12 @@ snapshots: defer-to-connect@2.0.1: {} + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + define-lazy-prop@2.0.0: {} define-properties@1.2.0: @@ -17448,6 +18790,14 @@ snapshots: has-property-descriptors: 1.0.0 object-keys: 1.1.1 + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.0 + object-keys: 1.1.1 + + defined@1.0.1: {} + defu@6.1.4: {} del@6.1.1: @@ -17463,11 +18813,12 @@ snapshots: delayed-stream@1.0.0: {} - delegates@1.0.0: - optional: true + delegates@1.0.0: {} denque@2.1.0: {} + depd@1.1.2: {} + depd@2.0.0: {} dequal@2.0.3: {} @@ -17476,8 +18827,7 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.2: - optional: true + detect-libc@2.0.2: {} detect-libc@2.0.3: {} @@ -17532,6 +18882,8 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 + domain-browser@1.2.0: {} + domelementtype@2.3.0: {} domhandler@5.0.3: @@ -17591,6 +18943,10 @@ snapshots: emoji-regex@9.2.2: {} + emojis-list@2.1.0: {} + + emojis-list@3.0.0: {} + encode-utf8@1.0.3: {} encodeurl@1.0.2: {} @@ -17604,6 +18960,12 @@ snapshots: dependencies: once: 1.4.0 + enhanced-resolve@0.9.1: + dependencies: + graceful-fs: 4.2.11 + memory-fs: 0.2.0 + tapable: 0.1.10 + enquirer@2.3.6: dependencies: ansi-colors: 4.1.3 @@ -17618,6 +18980,10 @@ snapshots: err-code@2.0.3: {} + errno@0.1.8: + dependencies: + prr: 1.0.1 + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -17664,6 +19030,12 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.11 + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + es-get-iterator@1.1.3: dependencies: call-bind: 1.0.2 @@ -17823,7 +19195,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -17831,7 +19203,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.53.0): dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) optionalDependencies: '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3) eslint: 8.53.0 @@ -17841,7 +19213,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) optionalDependencies: '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: 8.57.0 @@ -17851,7 +19223,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) optionalDependencies: '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 @@ -17865,7 +19237,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) doctrine: 2.1.0 eslint: 8.53.0 eslint-import-resolver-node: 0.3.9 @@ -17892,7 +19264,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -17919,7 +19291,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -18055,6 +19427,8 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.11.3) eslint-visitor-keys: 3.4.3 + esprima@2.7.3: {} + esprima@4.0.1: {} esquery@1.4.2: @@ -18095,8 +19469,12 @@ snapshots: eventemitter3@5.0.1: {} + events@1.1.1: {} + events@3.3.0: {} + eventsource@2.0.2: {} + execa@0.7.0: dependencies: cross-spawn: 5.1.0 @@ -18161,6 +19539,14 @@ snapshots: exit@0.1.2: {} + expand-brackets@0.1.5: + dependencies: + is-posix-bracket: 0.1.1 + + expand-range@1.8.2: + dependencies: + fill-range: 2.2.4 + expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -18180,12 +19566,12 @@ snapshots: content-type: 1.0.5 cookie: 0.5.0 cookie-signature: 1.0.6 - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) depd: 2.0.0 encodeurl: 1.0.2 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0 + finalhandler: 1.2.0(supports-color@3.2.3) fresh: 0.5.2 http-errors: 2.0.0 merge-descriptors: 1.0.1 @@ -18197,8 +19583,8 @@ snapshots: qs: 6.11.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.18.0(supports-color@3.2.3) + serve-static: 1.15.0(supports-color@3.2.3) setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -18207,21 +19593,21 @@ snapshots: transitivePeerDependencies: - supports-color - express@4.19.2: + express@4.19.2(supports-color@3.2.3): dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.2 + body-parser: 1.20.2(supports-color@3.2.3) content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.6.0 cookie-signature: 1.0.6 - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) depd: 2.0.0 encodeurl: 1.0.2 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0 + finalhandler: 1.2.0(supports-color@3.2.3) fresh: 0.5.2 http-errors: 2.0.0 merge-descriptors: 1.0.1 @@ -18233,8 +19619,8 @@ snapshots: qs: 6.11.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0 - serve-static: 1.15.0 + send: 0.18.0(supports-color@3.2.3) + serve-static: 1.15.0(supports-color@3.2.3) setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -18254,6 +19640,10 @@ snapshots: extend@3.0.2: {} + extglob@0.3.2: + dependencies: + is-extglob: 1.0.0 + extract-zip@2.0.1(supports-color@8.1.1): dependencies: debug: 4.3.4(supports-color@8.1.1) @@ -18266,6 +19656,8 @@ snapshots: extsprintf@1.3.0: {} + eyes@0.1.8: {} + fast-content-type-parse@1.1.0: {} fast-decode-uri-component@1.0.1: {} @@ -18282,6 +19674,8 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.5 + fast-json-patch@1.2.2: {} + fast-json-stable-stringify@2.1.0: {} fast-json-stringify@5.8.0: @@ -18338,6 +19732,8 @@ snapshots: transitivePeerDependencies: - supports-color + fastparse@1.1.2: {} + fastq@1.15.0: dependencies: reusify: 1.0.4 @@ -18346,6 +19742,10 @@ snapshots: dependencies: reusify: 1.0.4 + faye-websocket@0.11.4: + dependencies: + websocket-driver: 0.7.4 + fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -18394,6 +19794,8 @@ snapshots: dependencies: minimatch: 5.1.2 + filename-regex@2.0.1: {} + filename-reserved-regex@3.0.0: {} filenamify@5.1.1: @@ -18402,13 +19804,21 @@ snapshots: strip-outer: 2.0.0 trim-repeated: 2.0.0 + fill-range@2.2.4: + dependencies: + is-number: 2.1.0 + isobject: 2.1.0 + randomatic: 3.1.1 + repeat-element: 1.1.4 + repeat-string: 1.6.1 + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 - finalhandler@1.2.0: + finalhandler@1.2.0(supports-color@3.2.3): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.4.1 @@ -18472,6 +19882,8 @@ snapshots: flatted@3.2.7: {} + flatten@1.0.3: {} + flow-parser@0.202.0: {} fluent-ffmpeg@2.1.2: @@ -18487,6 +19899,12 @@ snapshots: dependencies: is-callable: 1.2.7 + for-in@1.0.2: {} + + for-own@0.1.5: + dependencies: + for-in: 1.0.2 + foreground-child@3.1.1: dependencies: cross-spawn: 7.0.3 @@ -18571,6 +19989,19 @@ snapshots: fsevents@2.3.3: optional: true + fstream-ignore@0.0.10: + dependencies: + fstream: 0.1.31 + inherits: 2.0.4 + minimatch: 0.3.0 + + fstream@0.1.31: + dependencies: + graceful-fs: 3.0.12 + inherits: 2.0.4 + mkdirp: 0.5.6 + rimraf: 2.7.1 + function-bind@1.1.2: {} function.prototype.name@1.1.5: @@ -18593,7 +20024,6 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 - optional: true gensync@1.0.0-beta.2: {} @@ -18608,6 +20038,14 @@ snapshots: has-proto: 1.0.1 has-symbols: 1.0.3 + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.1 + has-symbols: 1.0.3 + hasown: 2.0.0 + get-npm-tarball-url@2.0.3: {} get-package-type@0.1.0: {} @@ -18653,10 +20091,24 @@ snapshots: dependencies: assert-plus: 1.0.0 + gif-encoder-2@1.0.5: {} + gif-encoder@0.4.1: dependencies: readable-stream: 1.1.14 + gif-frames@1.0.1: + dependencies: + get-pixels-frame-info-update: 3.3.2 + multi-integer-range: 3.0.0 + save-pixels-jpeg-js-upgrade: 2.3.4-jpeg-js-upgrade.0 + + gifshot@0.4.5: {} + + gifuct-js@2.1.2: + dependencies: + js-binary-schema-parser: 2.0.3 + giget@1.1.2: dependencies: colorette: 2.0.19 @@ -18671,6 +20123,15 @@ snapshots: github-slugger@2.0.0: {} + glob-base@0.3.0: + dependencies: + glob-parent: 2.0.0 + is-glob: 2.0.1 + + glob-parent@2.0.0: + dependencies: + is-glob: 2.0.1 + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -18795,6 +20256,13 @@ snapshots: p-cancelable: 4.0.1 responselike: 3.0.0 + graceful-fs@1.1.14: + optional: true + + graceful-fs@3.0.12: + dependencies: + natives: 1.1.6 + graceful-fs@4.2.11: {} grapheme-splitter@1.0.4: {} @@ -18838,8 +20306,14 @@ snapshots: hard-rejection@2.1.0: {} + has-ansi@2.0.0: + dependencies: + ansi-regex: 2.1.1 + has-bigints@1.0.2: {} + has-flag@1.0.0: {} + has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -18848,6 +20322,10 @@ snapshots: dependencies: get-intrinsic: 1.2.1 + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + has-proto@1.0.1: {} has-symbols@1.0.3: {} @@ -18856,8 +20334,7 @@ snapshots: dependencies: has-symbols: 1.0.3 - has-unicode@2.0.1: - optional: true + has-unicode@2.0.1: {} has@1.0.3: dependencies: @@ -18899,6 +20376,8 @@ snapshots: hpagent@1.2.0: {} + html-comment-regex@1.1.2: {} + html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -18920,6 +20399,13 @@ snapshots: http-cache-semantics@4.1.1: {} + http-errors@1.6.3: + dependencies: + depd: 1.1.2 + inherits: 2.0.3 + setprototypeof: 1.1.0 + statuses: 1.5.0 + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -18930,6 +20416,8 @@ snapshots: http-link-header@1.1.3: {} + http-parser-js@0.5.8: {} + http-proxy-agent@7.0.0: dependencies: agent-base: 7.1.0 @@ -18937,6 +20425,23 @@ snapshots: transitivePeerDependencies: - supports-color + http-proxy-middleware@0.17.4: + dependencies: + http-proxy: 1.18.1 + is-glob: 3.1.0 + lodash: 4.17.21 + micromatch: 2.3.11 + transitivePeerDependencies: + - debug + + http-proxy@1.18.1: + dependencies: + eventemitter3: 4.0.7 + follow-redirects: 1.15.2(debug@4.3.4) + requires-port: 1.0.0 + transitivePeerDependencies: + - debug + http-signature@1.2.0: dependencies: assert-plus: 1.0.0 @@ -18961,10 +20466,12 @@ snapshots: http_ece@1.2.0: {} + https-browserify@0.0.1: {} + https-proxy-agent@2.2.4: dependencies: agent-base: 4.3.0 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) transitivePeerDependencies: - supports-color optional: true @@ -18991,6 +20498,8 @@ snapshots: human-signals@5.0.0: {} + i@0.3.7: {} + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -18999,6 +20508,8 @@ snapshots: dependencies: safer-buffer: 2.1.2 + icss-replace-symbols@1.1.0: {} + idb-keyval@6.2.1: {} ieee754@1.2.1: {} @@ -19013,6 +20524,8 @@ snapshots: ignore@5.3.1: {} + image-conversion@2.1.1: {} + immutable@4.2.2: {} import-fresh@3.3.0: @@ -19033,11 +20546,19 @@ snapshots: indent-string@5.0.0: {} + indexes-of@1.0.1: {} + + indexof@0.0.1: {} + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 + inherits@1.0.2: {} + + inherits@2.0.3: {} + inherits@2.0.4: {} ini@1.3.8: {} @@ -19054,6 +20575,8 @@ snapshots: has: 1.0.3 side-channel: 1.0.4 + interpret@0.6.6: {} + intersection-observer@0.12.2: {} ioredis@5.4.1: @@ -19092,6 +20615,8 @@ snapshots: irregular-plurals@3.5.0: {} + is-absolute-url@2.1.0: {} + is-absolute-url@4.0.1: {} is-arguments@1.1.1: @@ -19142,11 +20667,21 @@ snapshots: is-docker@2.2.1: {} + is-dotfile@1.0.3: {} + + is-equal-shallow@0.1.3: + dependencies: + is-primitive: 2.0.0 + is-expression@4.0.0: dependencies: acorn: 7.4.1 object-assign: 4.1.1 + is-extendable@0.1.1: {} + + is-extglob@1.0.0: {} + is-extglob@2.1.1: {} is-file-animated@1.0.2: {} @@ -19159,6 +20694,14 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-glob@2.0.1: + dependencies: + is-extglob: 1.0.0 + + is-glob@3.1.0: + dependencies: + is-extglob: 2.1.1 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -19193,6 +20736,12 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-number@2.1.0: + dependencies: + kind-of: 3.2.2 + + is-number@4.0.0: {} + is-number@7.0.0: {} is-path-cwd@2.2.0: {} @@ -19209,8 +20758,12 @@ snapshots: is-plain-object@5.0.0: {} + is-posix-bracket@0.1.1: {} + is-potential-custom-element-name@1.0.1: {} + is-primitive@2.0.0: {} + is-promise@2.2.2: {} is-regex@1.1.4: @@ -19234,6 +20787,10 @@ snapshots: dependencies: has-tostringtag: 1.0.0 + is-svg@2.1.0: + dependencies: + html-comment-regex: 1.1.2 + is-svg@5.0.0: dependencies: fast-xml-parser: 4.2.5 @@ -19279,6 +20836,10 @@ snapshots: isexe@3.1.1: {} + isobject@2.1.0: + dependencies: + isarray: 1.0.0 + isobject@3.0.1: {} isstream@0.1.2: {} @@ -19339,6 +20900,25 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.2 + jamjs@0.2.17: + dependencies: + almond: 0.2.5 + async: 0.1.22 + fstream: 0.1.31 + fstream-ignore: 0.0.10 + inherits: 1.0.2 + mime: 1.2.11 + minimatch: 0.2.14 + mkdirp: 0.3.5 + ncp: 0.2.7 + prompt: 0.2.1 + request: 2.9.203 + requirejs: 2.1.4 + rimraf: 2.0.3 + semver: 1.0.14 + tar: 0.1.20 + underscore: 1.3.3 + jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -19671,6 +21251,8 @@ snapshots: jpeg-js@0.3.7: {} + js-base64@2.6.4: {} + js-beautify@1.14.9: dependencies: config-chain: 1.1.13 @@ -19678,8 +21260,12 @@ snapshots: glob: 8.1.0 nopt: 6.0.0 + js-binary-schema-parser@2.0.3: {} + js-stringify@1.0.2: {} + js-tokens@3.0.2: {} + js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -19687,6 +21273,11 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 + js-yaml@3.7.0: + dependencies: + argparse: 1.0.10 + esprima: 2.7.3 + js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -19724,7 +21315,7 @@ snapshots: transitivePeerDependencies: - supports-color - jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3): + jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3): dependencies: cssstyle: 4.0.1 data-urls: 5.0.0 @@ -19747,6 +21338,8 @@ snapshots: whatwg-url: 14.0.0 ws: 8.17.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) xml-name-validator: 5.0.0 + optionalDependencies: + canvas: 2.11.2(encoding@0.1.13) transitivePeerDependencies: - bufferutil - supports-color @@ -19758,6 +21351,10 @@ snapshots: json-buffer@3.0.1: {} + json-merge-patch@0.2.3: + dependencies: + deep-equal: 1.1.2 + json-parse-even-better-errors@2.3.1: {} json-schema-traverse@0.4.1: {} @@ -19775,6 +21372,8 @@ snapshots: code-error-fragment: 0.0.230 grapheme-splitter: 1.0.4 + json5@0.5.1: {} + json5@1.0.2: dependencies: minimist: 1.2.8 @@ -19850,6 +21449,10 @@ snapshots: dependencies: json-buffer: 3.0.1 + kind-of@3.2.2: + dependencies: + is-buffer: 1.1.6 + kind-of@6.0.3: {} kleur@3.0.3: {} @@ -19866,6 +21469,8 @@ snapshots: lazy-ass@1.6.0: {} + lazy-cache@1.0.4: {} + lazy-universal-dotenv@4.0.0: dependencies: app-root-dir: 1.0.2 @@ -19883,6 +21488,21 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libwebpjs@0.0.1: + dependencies: + css-loader: 0.26.4 + fast-json-patch: 1.2.2 + jamjs: 0.2.17 + json-merge-patch: 0.2.3 + style: 0.0.3 + style-loader: 0.13.2 + webpack: 1.15.0 + webpack-dev-server: 1.16.5(webpack@1.15.0) + transitivePeerDependencies: + - debug + - webpack-cli + - webpack-command + light-my-request@5.11.0: dependencies: cookie: 0.5.0 @@ -19906,6 +21526,19 @@ snapshots: optionalDependencies: enquirer: 2.3.6 + loader-utils@0.2.17: + dependencies: + big.js: 3.2.0 + emojis-list: 2.1.0 + json5: 0.5.1 + object-assign: 4.1.1 + + loader-utils@1.4.2: + dependencies: + big.js: 5.2.2 + emojis-list: 3.0.0 + json5: 1.0.2 + local-pkg@0.4.3: {} locate-path@3.0.0: @@ -19921,6 +21554,8 @@ snapshots: dependencies: p-locate: 5.0.0 + lodash.camelcase@4.3.0: {} + lodash.debounce@4.0.8: {} lodash.defaults@4.2.0: {} @@ -19957,6 +21592,8 @@ snapshots: longest-streak@3.1.0: {} + longest@1.0.1: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -19975,6 +21612,8 @@ snapshots: lru-cache@10.2.2: {} + lru-cache@2.7.3: {} + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 @@ -20055,6 +21694,10 @@ snapshots: dependencies: react: 18.3.1 + math-expression-evaluator@1.4.0: {} + + math-random@1.0.4: {} + matter-js@0.19.0: {} mdast-util-find-and-replace@3.0.1: @@ -20174,6 +21817,18 @@ snapshots: dependencies: map-or-similar: 1.5.0 + memory-fs@0.2.0: {} + + memory-fs@0.3.0: + dependencies: + errno: 0.1.8 + readable-stream: 2.3.7 + + memory-fs@0.4.1: + dependencies: + errno: 0.1.8 + readable-stream: 2.3.7 + meow@9.0.0: dependencies: '@types/minimist': 1.2.2 @@ -20396,6 +22051,22 @@ snapshots: transitivePeerDependencies: - supports-color + micromatch@2.3.11: + dependencies: + arr-diff: 2.0.0 + array-unique: 0.2.1 + braces: 1.8.5 + expand-brackets: 0.1.5 + extglob: 0.3.2 + filename-regex: 2.0.1 + is-extglob: 1.0.0 + is-glob: 2.0.1 + kind-of: 3.2.2 + normalize-path: 2.1.1 + object.omit: 2.0.1 + parse-glob: 3.0.4 + regex-cache: 0.4.4 + micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -20407,6 +22078,8 @@ snapshots: dependencies: mime-db: 1.52.0 + mime@1.2.11: {} + mime@1.6.0: {} mime@3.0.0: {} @@ -20417,6 +22090,8 @@ snapshots: mimic-response@1.0.1: {} + mimic-response@2.1.0: {} + mimic-response@3.1.0: {} mimic-response@4.0.0: {} @@ -20425,6 +22100,16 @@ snapshots: minimalistic-assert@1.0.1: {} + minimatch@0.2.14: + dependencies: + lru-cache: 2.7.3 + sigmund: 1.0.1 + + minimatch@0.3.0: + dependencies: + lru-cache: 2.7.3 + sigmund: 1.0.1 + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -20455,6 +22140,8 @@ snapshots: is-plain-obj: 1.1.0 kind-of: 6.0.3 + minimist@0.0.10: {} + minimist@1.2.8: {} minipass-collect@1.0.2: @@ -20507,6 +22194,8 @@ snapshots: mkdirp-classic@0.5.3: {} + mkdirp@0.3.5: {} + mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -20611,8 +22300,12 @@ snapshots: nanoid@5.0.7: {} + natives@1.1.6: {} + natural-compare@1.4.0: {} + ncp@0.2.7: {} + ncp@2.0.0: {} ndarray-ops@1.2.2: @@ -20636,7 +22329,7 @@ snapshots: needle@2.9.1: dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@3.2.3) iconv-lite: 0.4.24 sax: 1.2.4 transitivePeerDependencies: @@ -20720,8 +22413,36 @@ snapshots: node-int64@0.4.0: {} + node-libs-browser@0.7.0: + dependencies: + assert: 1.5.1 + browserify-zlib: 0.1.4 + buffer: 4.9.2 + console-browserify: 1.2.0 + constants-browserify: 1.0.0 + crypto-browserify: 3.3.0 + domain-browser: 1.2.0 + events: 1.1.1 + https-browserify: 0.0.1 + os-browserify: 0.2.1 + path-browserify: 0.0.0 + process: 0.11.10 + punycode: 1.4.1 + querystring-es3: 0.2.1 + readable-stream: 2.3.7 + stream-browserify: 2.0.2 + stream-http: 2.8.3 + string_decoder: 0.10.31 + timers-browserify: 2.0.12 + tty-browserify: 0.0.0 + url: 0.11.3 + util: 0.10.4 + vm-browserify: 0.0.4 + node-releases@2.0.14: {} + node-webpmux@3.2.0: {} + nodemailer@6.9.13: {} nodemon@3.0.2: @@ -20759,7 +22480,6 @@ snapshots: nopt@5.0.0: dependencies: abbrev: 1.1.1 - optional: true nopt@6.0.0: dependencies: @@ -20783,8 +22503,21 @@ snapshots: semver: 7.6.0 validate-npm-package-license: 3.0.4 + normalize-path@2.1.1: + dependencies: + remove-trailing-separator: 1.1.0 + normalize-path@3.0.0: {} + normalize-range@0.1.2: {} + + normalize-url@1.9.1: + dependencies: + object-assign: 4.1.1 + prepend-http: 1.0.4 + query-string: 4.3.4 + sort-keys: 1.1.2 + normalize-url@6.1.0: {} normalize-url@8.0.0: {} @@ -20807,7 +22540,6 @@ snapshots: console-control-strings: 1.1.0 gauge: 3.0.2 set-blocking: 2.0.0 - optional: true nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)): dependencies: @@ -20818,6 +22550,8 @@ snapshots: dependencies: boolbase: 1.0.0 + num2fraction@1.2.2: {} + nwsapi@2.2.9: {} oauth-sign@0.9.0: {} @@ -20826,7 +22560,7 @@ snapshots: oauth2orize@1.12.0: dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) uid2: 0.0.4 utils-merge: 1.0.1 transitivePeerDependencies: @@ -20838,6 +22572,8 @@ snapshots: object-inspect@1.12.3: {} + object-inspect@1.13.1: {} + object-is@1.1.5: dependencies: call-bind: 1.0.2 @@ -20865,6 +22601,11 @@ snapshots: es-abstract: 1.22.1 get-intrinsic: 1.2.1 + object.omit@2.0.1: + dependencies: + for-own: 0.1.5 + is-extendable: 0.1.1 + object.values@1.1.7: dependencies: call-bind: 1.0.2 @@ -20899,6 +22640,8 @@ snapshots: dependencies: mimic-fn: 4.0.0 + open@0.0.5: {} + open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -20916,6 +22659,11 @@ snapshots: undici: 5.28.2 yargs-parser: 21.1.1 + optimist@0.6.1: + dependencies: + minimist: 0.0.10 + wordwrap: 0.0.3 + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -20937,6 +22685,8 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 + os-browserify@0.2.1: {} + os-filter-obj@2.0.0: dependencies: arch: 2.2.0 @@ -21008,6 +22758,13 @@ snapshots: dependencies: data-uri-to-buffer: 0.0.3 + parse-glob@3.0.4: + dependencies: + glob-base: 0.3.0 + is-dotfile: 1.0.3 + is-extglob: 1.0.0 + is-glob: 2.0.1 + parse-json@5.2.0: dependencies: '@babel/code-frame': 7.23.5 @@ -21036,6 +22793,8 @@ snapshots: parseurl@1.3.3: {} + path-browserify@0.0.0: {} + path-browserify@1.0.1: {} path-exists@3.0.0: {} @@ -21082,6 +22841,8 @@ snapshots: dependencies: through: 2.3.8 + pbkdf2-compat@2.0.1: {} + peek-readable@5.0.0: {} peek-stream@1.1.3: @@ -21204,6 +22965,10 @@ snapshots: mlly: 1.5.0 pathe: 1.1.2 + pkginfo@0.2.3: {} + + pkginfo@0.4.1: {} + plimit-lit@1.5.0: dependencies: queue-lit: 1.5.0 @@ -21218,16 +22983,30 @@ snapshots: pngjs@5.0.0: {} + pngjs@7.0.0: {} + polished@4.2.2: dependencies: '@babel/runtime': 7.23.4 + postcss-calc@5.3.1: + dependencies: + postcss: 5.2.18 + postcss-message-helpers: 2.0.0 + reduce-css-calc: 1.3.0 + postcss-calc@9.0.1(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-selector-parser: 6.0.15 postcss-value-parser: 4.2.0 + postcss-colormin@2.2.2: + dependencies: + colormin: 1.1.2 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-colormin@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 @@ -21236,34 +23015,82 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-convert-values@2.6.1: + dependencies: + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-convert-values@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-discard-comments@2.0.4: + dependencies: + postcss: 5.2.18 + postcss-discard-comments@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 + postcss-discard-duplicates@2.1.0: + dependencies: + postcss: 5.2.18 + postcss-discard-duplicates@6.0.3(postcss@8.4.38): dependencies: postcss: 8.4.38 + postcss-discard-empty@2.1.0: + dependencies: + postcss: 5.2.18 + postcss-discard-empty@6.0.3(postcss@8.4.38): dependencies: postcss: 8.4.38 + postcss-discard-overridden@0.1.1: + dependencies: + postcss: 5.2.18 + postcss-discard-overridden@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 + postcss-discard-unused@2.2.3: + dependencies: + postcss: 5.2.18 + uniqs: 2.0.0 + + postcss-filter-plugins@2.0.3: + dependencies: + postcss: 5.2.18 + + postcss-merge-idents@2.1.7: + dependencies: + has: 1.0.3 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + + postcss-merge-longhand@2.0.2: + dependencies: + postcss: 5.2.18 + postcss-merge-longhand@6.0.5(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 stylehacks: 6.1.1(postcss@8.4.38) + postcss-merge-rules@2.1.2: + dependencies: + browserslist: 1.7.7 + caniuse-api: 1.6.1 + postcss: 5.2.18 + postcss-selector-parser: 2.2.3 + vendors: 1.0.4 + postcss-merge-rules@6.1.1(postcss@8.4.38): dependencies: browserslist: 4.23.0 @@ -21272,11 +23099,24 @@ snapshots: postcss: 8.4.38 postcss-selector-parser: 6.0.16 + postcss-message-helpers@2.0.0: {} + + postcss-minify-font-values@1.0.5: + dependencies: + object-assign: 4.1.1 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-minify-font-values@6.1.0(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-minify-gradients@1.0.5: + dependencies: + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-minify-gradients@6.0.3(postcss@8.4.38): dependencies: colord: 2.9.3 @@ -21284,6 +23124,13 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-minify-params@1.2.2: + dependencies: + alphanum-sort: 1.0.2 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + uniqs: 2.0.0 + postcss-minify-params@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 @@ -21291,11 +23138,41 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-minify-selectors@2.1.1: + dependencies: + alphanum-sort: 1.0.2 + has: 1.0.3 + postcss: 5.2.18 + postcss-selector-parser: 2.2.3 + postcss-minify-selectors@6.0.4(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-selector-parser: 6.0.16 + postcss-modules-extract-imports@1.2.1: + dependencies: + postcss: 6.0.23 + + postcss-modules-local-by-default@1.2.0: + dependencies: + css-selector-tokenizer: 0.7.3 + postcss: 6.0.23 + + postcss-modules-scope@1.1.0: + dependencies: + css-selector-tokenizer: 0.7.3 + postcss: 6.0.23 + + postcss-modules-values@1.3.0: + dependencies: + icss-replace-symbols: 1.1.0 + postcss: 6.0.23 + + postcss-normalize-charset@1.1.1: + dependencies: + postcss: 5.2.18 + postcss-normalize-charset@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 @@ -21331,6 +23208,13 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-normalize-url@3.0.8: + dependencies: + is-absolute-url: 2.1.0 + normalize-url: 1.9.1 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-normalize-url@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 @@ -21341,23 +23225,49 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-ordered-values@2.2.3: + dependencies: + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-ordered-values@6.0.2(postcss@8.4.38): dependencies: cssnano-utils: 4.0.2(postcss@8.4.38) postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-reduce-idents@2.4.0: + dependencies: + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + + postcss-reduce-initial@1.0.1: + dependencies: + postcss: 5.2.18 + postcss-reduce-initial@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 caniuse-api: 3.0.0 postcss: 8.4.38 + postcss-reduce-transforms@1.0.4: + dependencies: + has: 1.0.3 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + postcss-reduce-transforms@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 + postcss-selector-parser@2.2.3: + dependencies: + flatten: 1.0.3 + indexes-of: 1.0.1 + uniq: 1.0.1 + postcss-selector-parser@6.0.15: dependencies: cssesc: 3.0.0 @@ -21368,19 +23278,53 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 + postcss-svgo@2.1.6: + dependencies: + is-svg: 2.1.0 + postcss: 5.2.18 + postcss-value-parser: 3.3.1 + svgo: 0.7.2 + postcss-svgo@6.0.3(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 svgo: 3.2.0 + postcss-unique-selectors@2.0.2: + dependencies: + alphanum-sort: 1.0.2 + postcss: 5.2.18 + uniqs: 2.0.0 + postcss-unique-selectors@6.0.4(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-selector-parser: 6.0.16 + postcss-value-parser@3.3.1: {} + postcss-value-parser@4.2.0: {} + postcss-zindex@2.2.0: + dependencies: + has: 1.0.3 + postcss: 5.2.18 + uniqs: 2.0.0 + + postcss@5.2.18: + dependencies: + chalk: 1.1.3 + js-base64: 2.6.4 + source-map: 0.5.7 + supports-color: 3.2.3 + + postcss@6.0.23: + dependencies: + chalk: 2.4.2 + source-map: 0.6.1 + supports-color: 5.5.0 + postcss@8.4.38: dependencies: nanoid: 3.3.7 @@ -21411,6 +23355,10 @@ snapshots: prelude-ls@1.2.1: {} + prepend-http@1.0.4: {} + + preserve@0.2.0: {} + prettier@3.2.5: {} pretty-bytes@5.6.0: {} @@ -21474,6 +23422,14 @@ snapshots: dependencies: asap: 2.0.6 + prompt@0.2.1: + dependencies: + colors: 0.6.2 + pkginfo: 0.4.1 + revalidator: 0.1.8 + utile: 0.1.7 + winston: 0.6.2 + prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -21502,6 +23458,8 @@ snapshots: transitivePeerDependencies: - encoding + prr@1.0.1: {} + ps-list@8.1.1: {} ps-tree@1.2.0: @@ -21597,6 +23555,8 @@ snapshots: inherits: 2.0.4 pump: 2.0.1 + punycode@1.4.1: {} + punycode@2.3.1: {} pure-rand@6.0.0: {} @@ -21607,6 +23567,8 @@ snapshots: pvutils@1.1.3: {} + q@1.5.1: {} + qrcode@1.5.3: dependencies: dijkstrajs: 1.0.2 @@ -21626,8 +23588,19 @@ snapshots: dependencies: side-channel: 1.0.4 + qs@6.12.1: + dependencies: + side-channel: 1.0.6 + qs@6.5.3: {} + query-string@4.3.4: + dependencies: + object-assign: 4.1.1 + strict-uri-encode: 1.1.0 + + querystring-es3@0.2.1: {} + querystringify@2.2.0: {} queue-lit@1.5.0: {} @@ -21648,6 +23621,12 @@ snapshots: dependencies: json-stringify-safe: 5.0.1 + randomatic@3.1.1: + dependencies: + is-number: 4.0.0 + kind-of: 6.0.3 + math-random: 1.0.4 + range-parser@1.2.1: {} ratelimiter@3.4.1: {} @@ -21741,6 +23720,13 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 + readable-stream@1.0.34: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 0.0.1 + string_decoder: 0.10.31 + readable-stream@1.1.14: dependencies: core-util-is: 1.0.3 @@ -21820,6 +23806,16 @@ snapshots: dependencies: redis-errors: 1.2.0 + reduce-css-calc@1.3.0: + dependencies: + balanced-match: 0.4.2 + math-expression-evaluator: 1.4.0 + reduce-function-call: 1.0.3 + + reduce-function-call@1.0.3: + dependencies: + balanced-match: 1.0.2 + reflect-metadata@0.2.2: {} regenerate-unicode-properties@10.1.0: @@ -21836,12 +23832,23 @@ snapshots: dependencies: '@babel/runtime': 7.23.4 + regex-cache@0.4.4: + dependencies: + is-equal-shallow: 0.1.3 + regexp.prototype.flags@1.5.0: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 functions-have-names: 1.2.3 + regexp.prototype.flags@1.5.2: + dependencies: + call-bind: 1.0.7 + define-properties: 1.2.1 + es-errors: 1.3.0 + set-function-name: 2.0.2 + regexpu-core@5.3.2: dependencies: '@babel/regjsgen': 0.8.0 @@ -21898,12 +23905,18 @@ snapshots: mdast-util-to-markdown: 2.1.0 unified: 11.0.4 + remove-trailing-separator@1.1.0: {} + rename@1.0.4: dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) transitivePeerDependencies: - supports-color + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + request-progress@3.0.0: dependencies: throttleit: 1.0.0 @@ -21931,12 +23944,16 @@ snapshots: tunnel-agent: 0.6.0 uuid: 3.4.0 + request@2.9.203: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} require-main-filename@2.0.0: {} + requirejs@2.1.4: {} + requires-port@1.0.0: {} resolve-alpn@1.2.1: {} @@ -21983,8 +24000,20 @@ snapshots: reusify@1.0.4: {} + revalidator@0.1.8: {} + rfdc@1.3.0: {} + right-align@0.1.3: + dependencies: + align-text: 0.1.4 + + rimraf@1.0.9: {} + + rimraf@2.0.3: + optionalDependencies: + graceful-fs: 1.1.14 + rimraf@2.6.3: dependencies: glob: 7.2.3 @@ -21992,12 +24021,13 @@ snapshots: rimraf@2.7.1: dependencies: glob: 7.2.3 - optional: true rimraf@3.0.2: dependencies: glob: 7.2.3 + ripemd160@0.2.0: {} + rollup@4.17.2: dependencies: '@types/estree': 1.0.5 @@ -22075,6 +24105,16 @@ snapshots: immutable: 4.2.2 source-map-js: 1.2.0 + save-pixels-jpeg-js-upgrade@2.3.4-jpeg-js-upgrade.0: + dependencies: + contentstream: 1.0.0 + gif-encoder: 0.4.1 + jpeg-js: 0.3.7 + ndarray: 1.0.19 + ndarray-ops: 1.2.2 + pngjs-nozlib: 1.0.0 + through: 2.3.8 + sax@1.2.4: {} saxes@6.0.0: @@ -22095,6 +24135,8 @@ snapshots: dependencies: semver: 6.3.1 + semver@1.0.14: {} + semver@5.7.1: {} semver@6.3.1: {} @@ -22107,9 +24149,9 @@ snapshots: dependencies: lru-cache: 6.0.0 - send@0.18.0: + send@0.18.0(supports-color@3.2.3): dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) depd: 2.0.0 destroy: 1.2.0 encodeurl: 1.0.2 @@ -22125,12 +24167,24 @@ snapshots: transitivePeerDependencies: - supports-color - serve-static@1.15.0: + serve-index@1.9.1(supports-color@3.2.3): + dependencies: + accepts: 1.3.8 + batch: 0.6.1 + debug: 2.6.9(supports-color@3.2.3) + escape-html: 1.0.3 + http-errors: 1.6.3 + mime-types: 2.1.35 + parseurl: 1.3.3 + transitivePeerDependencies: + - supports-color + + serve-static@1.15.0(supports-color@3.2.3): dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.18.0(supports-color@3.2.3) transitivePeerDependencies: - supports-color @@ -22138,10 +24192,30 @@ snapshots: set-cookie-parser@2.6.0: {} + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + setimmediate@1.0.5: {} + setprototypeof@1.1.0: {} + setprototypeof@1.2.0: {} + sha.js@2.2.6: {} + sha.js@2.4.11: dependencies: inherits: 2.0.4 @@ -22199,12 +24273,29 @@ snapshots: get-intrinsic: 1.2.1 object-inspect: 1.12.3 + side-channel@1.0.6: + dependencies: + call-bind: 1.0.7 + es-errors: 1.3.0 + get-intrinsic: 1.2.4 + object-inspect: 1.13.1 + siginfo@2.0.0: {} + sigmund@1.0.1: {} + signal-exit@3.0.7: {} signal-exit@4.1.0: {} + simple-concat@1.0.1: {} + + simple-get@3.1.1: + dependencies: + decompress-response: 4.2.1 + once: 1.4.0 + simple-concat: 1.0.1 + simple-oauth2@5.0.0: dependencies: '@hapi/hoek': 10.0.1 @@ -22304,6 +24395,22 @@ snapshots: smart-buffer@4.2.0: {} + sockjs-client@1.6.1(supports-color@3.2.3): + dependencies: + debug: 3.2.7(supports-color@3.2.3) + eventsource: 2.0.2 + faye-websocket: 0.11.4 + inherits: 2.0.4 + url-parse: 1.5.10 + transitivePeerDependencies: + - supports-color + + sockjs@0.3.24: + dependencies: + faye-websocket: 0.11.4 + uuid: 8.3.2 + websocket-driver: 0.7.4 + socks-proxy-agent@8.0.2: dependencies: agent-base: 7.1.0 @@ -22331,6 +24438,8 @@ snapshots: sortablejs@1.14.0: {} + source-list-map@0.1.8: {} + source-map-js@1.0.2: {} source-map-js@1.2.0: {} @@ -22345,6 +24454,12 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 + source-map@0.4.4: + dependencies: + amdefine: 1.0.1 + + source-map@0.5.7: {} + source-map@0.6.1: {} source-map@0.7.4: {} @@ -22391,6 +24506,8 @@ snapshots: dependencies: minipass: 5.0.0 + stack-trace@0.0.10: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -22412,6 +24529,8 @@ snapshots: transitivePeerDependencies: - supports-color + statuses@1.5.0: {} + statuses@2.0.1: {} std-env@3.7.0: {} @@ -22447,18 +24566,33 @@ snapshots: - supports-color - utf-8-validate + stream-browserify@2.0.2: + dependencies: + inherits: 2.0.4 + readable-stream: 2.3.7 + stream-browserify@3.0.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.0 + stream-cache@0.0.2: {} + stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 + stream-http@2.8.3: + dependencies: + builtin-status-codes: 3.0.0 + inherits: 2.0.4 + readable-stream: 2.3.7 + to-arraybuffer: 1.0.1 + xtend: 4.0.2 + stream-parser@0.3.1: dependencies: - debug: 2.6.9 + debug: 2.6.9(supports-color@3.2.3) transitivePeerDependencies: - supports-color @@ -22477,6 +24611,8 @@ snapshots: strict-event-emitter@0.5.1: {} + strict-uri-encode@1.1.0: {} + string-argv@0.3.1: {} string-length@4.0.2: @@ -22528,6 +24664,10 @@ snapshots: dependencies: char-regex: 1.0.2 + strip-ansi@3.0.1: + dependencies: + ansi-regex: 2.1.1 + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -22569,12 +24709,24 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 5.0.0 + style-loader@0.13.2: + dependencies: + loader-utils: 1.4.2 + + style@0.0.3: {} + stylehacks@6.1.1(postcss@8.4.38): dependencies: browserslist: 4.23.0 postcss: 8.4.38 postcss-selector-parser: 6.0.16 + supports-color@2.0.0: {} + + supports-color@3.2.3: + dependencies: + has-flag: 1.0.0 + supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -22596,6 +24748,16 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} + svgo@0.7.2: + dependencies: + coa: 1.0.4 + colors: 1.1.2 + csso: 2.3.2 + js-yaml: 3.7.0 + mkdirp: 0.5.6 + sax: 1.2.4 + whet.extend: 0.9.9 + svgo@3.2.0: dependencies: '@trysound/sax': 0.2.0 @@ -22610,6 +24772,8 @@ snapshots: systeminformation@5.22.7: {} + tapable@0.1.10: {} + tar-fs@2.1.1: dependencies: chownr: 1.1.4 @@ -22631,6 +24795,12 @@ snapshots: fast-fifo: 1.3.0 streamx: 2.15.0 + tar@0.1.20: + dependencies: + block-stream: 0.0.9 + fstream: 0.1.31 + inherits: 2.0.4 + tar@4.4.19: dependencies: chownr: 1.1.4 @@ -22717,6 +24887,12 @@ snapshots: through@2.3.8: {} + time-stamp@2.2.0: {} + + timers-browserify@2.0.12: + dependencies: + setimmediate: 1.0.5 + tiny-invariant@1.3.1: {} tiny-invariant@1.3.3: {} @@ -22735,6 +24911,8 @@ snapshots: tmpl@1.0.5: {} + to-arraybuffer@1.0.1: {} + to-data-view@1.1.0: {} to-fast-properties@2.0.0: {} @@ -22847,6 +25025,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + tty-browserify@0.0.0: {} + tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 @@ -22940,9 +25120,18 @@ snapshots: ufo@1.3.2: {} + uglify-js@2.7.5: + dependencies: + async: 0.2.10 + source-map: 0.5.7 + uglify-to-browserify: 1.0.2 + yargs: 3.10.0 + uglify-js@3.17.4: optional: true + uglify-to-browserify@1.0.2: {} + uid2@0.0.4: {} uid@2.0.2: @@ -22960,6 +25149,8 @@ snapshots: undefsafe@2.0.5: {} + underscore@1.3.3: {} + undici-types@5.26.5: {} undici@5.28.2: @@ -22989,6 +25180,8 @@ snapshots: uniq@1.0.1: {} + uniqs@2.0.0: {} + unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 @@ -23060,6 +25253,11 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 + url@0.11.3: + dependencies: + punycode: 1.4.1 + qs: 6.12.1 + utf-8-validate@6.0.3: dependencies: node-gyp-build: 4.6.0 @@ -23067,6 +25265,10 @@ snapshots: util-deprecate@1.0.2: {} + util@0.10.4: + dependencies: + inherits: 2.0.3 + util@0.12.5: dependencies: inherits: 2.0.4 @@ -23075,6 +25277,15 @@ snapshots: is-typed-array: 1.1.10 which-typed-array: 1.1.11 + utile@0.1.7: + dependencies: + async: 0.1.22 + deep-equal: 2.2.0 + i: 0.3.7 + mkdirp: 0.5.6 + ncp: 0.2.7 + rimraf: 1.0.9 + utils-merge@1.0.1: {} uuid@3.4.0: {} @@ -23107,6 +25318,8 @@ snapshots: vary@1.1.2: {} + vendors@1.0.4: {} + verror@1.10.0: dependencies: assert-plus: 1.0.0 @@ -23155,14 +25368,14 @@ snapshots: sass: 1.76.0 terser: 5.30.3 - vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)): + vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)): dependencies: cross-fetch: 3.1.6(encoding@0.1.13) - vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) + vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3) transitivePeerDependencies: - encoding - vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3): + vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3): dependencies: '@types/chai': 4.3.11 '@types/chai-subset': 1.3.5 @@ -23190,7 +25403,7 @@ snapshots: why-is-node-running: 2.2.2 optionalDependencies: happy-dom: 14.7.1 - jsdom: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3) + jsdom: 24.0.0(bufferutil@4.0.7)(canvas@2.11.2(encoding@0.1.13))(utf-8-validate@6.0.3) transitivePeerDependencies: - less - lightningcss @@ -23200,6 +25413,10 @@ snapshots: - supports-color - terser + vm-browserify@0.0.4: + dependencies: + indexof: 0.0.1 + void-elements@3.1.0: {} vscode-jsonrpc@8.2.0: {} @@ -23236,7 +25453,7 @@ snapshots: vue-component-type-helpers@2.0.16: {} - vue-component-type-helpers@2.0.18: {} + vue-component-type-helpers@2.0.19: {} vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)): dependencies: @@ -23326,6 +25543,12 @@ snapshots: dependencies: makeerror: 1.0.12 + watchpack@0.2.9: + dependencies: + async: 0.9.2 + chokidar: 3.5.3 + graceful-fs: 4.2.11 + watchpack@2.4.0: dependencies: glob-to-regexp: 0.4.1 @@ -23353,10 +25576,73 @@ snapshots: webidl-conversions@7.0.0: {} + webm-wasm@0.4.1: {} + + webp-hero@0.0.2: {} + + webpack-core@0.6.9: + dependencies: + source-list-map: 0.1.8 + source-map: 0.4.4 + + webpack-dev-middleware@1.12.2(webpack@1.15.0): + dependencies: + memory-fs: 0.4.1 + mime: 1.6.0 + path-is-absolute: 1.0.1 + range-parser: 1.2.1 + time-stamp: 2.2.0 + webpack: 1.15.0 + + webpack-dev-server@1.16.5(webpack@1.15.0): + dependencies: + compression: 1.7.4(supports-color@3.2.3) + connect-history-api-fallback: 1.6.0 + express: 4.19.2(supports-color@3.2.3) + http-proxy-middleware: 0.17.4 + open: 0.0.5 + optimist: 0.6.1 + serve-index: 1.9.1(supports-color@3.2.3) + sockjs: 0.3.24 + sockjs-client: 1.6.1(supports-color@3.2.3) + stream-cache: 0.0.2 + strip-ansi: 3.0.1 + supports-color: 3.2.3 + webpack: 1.15.0 + webpack-dev-middleware: 1.12.2(webpack@1.15.0) + transitivePeerDependencies: + - debug + webpack-sources@3.2.3: {} webpack-virtual-modules@0.5.0: {} + webpack@1.15.0: + dependencies: + acorn: 3.3.0 + async: 1.5.2 + clone: 1.0.4 + enhanced-resolve: 0.9.1 + interpret: 0.6.6 + loader-utils: 0.2.17 + memory-fs: 0.3.0 + mkdirp: 0.5.6 + node-libs-browser: 0.7.0 + optimist: 0.6.1 + supports-color: 3.2.3 + tapable: 0.1.10 + uglify-js: 2.7.5 + watchpack: 0.2.9 + webpack-core: 0.6.9 + + websocket-driver@0.7.4: + dependencies: + http-parser-js: 0.5.8 + safe-buffer: 5.2.1 + websocket-extensions: 0.1.4 + + websocket-extensions@0.1.4: {} + whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -23375,6 +25661,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + whet.extend@0.9.9: {} + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 @@ -23420,7 +25708,18 @@ snapshots: wide-align@1.1.5: dependencies: string-width: 4.2.3 - optional: true + + window-size@0.1.0: {} + + winston@0.6.2: + dependencies: + async: 0.1.22 + colors: 0.6.2 + cycle: 1.0.3 + eyes: 0.1.8 + pkginfo: 0.2.3 + request: 2.9.203 + stack-trace: 0.0.10 with@7.0.2: dependencies: @@ -23429,6 +25728,10 @@ snapshots: assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 + wordwrap@0.0.2: {} + + wordwrap@0.0.3: {} + wordwrap@1.0.0: {} wrap-ansi@6.2.0: @@ -23541,6 +25844,13 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 + yargs@3.10.0: + dependencies: + camelcase: 1.2.1 + cliui: 2.1.0 + decamelize: 1.2.0 + window-size: 0.1.0 + yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 From 4cd51435a7627905aa1f357a34139e5d8deb161c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 11:14:56 +0900 Subject: [PATCH 454/501] update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 672c0db53d..246d2a0ce8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea4", + "version": "2024.5.0-beta1-mattyatea5", "codename": "nasubi", "repository": { "type": "git", From 17debc4727598b0f3cd1da08111864f263335285 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 11:38:35 +0900 Subject: [PATCH 455/501] update --- packages/frontend/package.json | 8 -------- .../src/components/global/MkMisskeyFlavoredMarkdown.ts | 3 +-- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 1cc8602785..8e99993721 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -17,7 +17,6 @@ "lint": "pnpm typecheck && pnpm eslint" }, "dependencies": { - "@caed0/webp-conv": "^1.1.0", "@discordapp/twemoji": "15.0.3", "@github/webauthn-json": "2.1.1", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3", @@ -32,7 +31,6 @@ "@vitejs/plugin-vue": "5.0.4", "@vue/compiler-sfc": "3.4.26", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4", - "apng-js": "^1.1.1", "astring": "1.8.6", "broadcast-channel": "7.0.0", "buraha": "0.0.1", @@ -49,14 +47,10 @@ "escape-regexp": "0.0.1", "estree-walker": "3.0.3", "eventemitter3": "5.0.1", - "gif-frames": "^1.0.1", - "gifshot": "^0.4.5", - "gifuct-js": "^2.1.2", "idb-keyval": "6.2.1", "insert-text-at-cursor": "0.3.0", "is-file-animated": "1.0.2", "json5": "2.2.3", - "libwebpjs": "^0.0.1", "matter-js": "0.19.0", "mfm-js": "0.24.0", "misskey-bubble-game": "workspace:*", @@ -82,8 +76,6 @@ "vue": "3.4.26", "vuedraggable": "next", "wavesurfer.js": "^7.7.14", - "webm-wasm": "^0.4.1", - "webp-hero": "^0.0.2" }, "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 1d9b3939ce..895cb87183 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -474,7 +474,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven case 'emojiCode': { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (props.author?.host == null && !props.emojiUrls) { + if (props.author?.host == null ) { return [h(MkCustomEmoji, { key: Math.random(), name: token.props.name, @@ -487,7 +487,6 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven })]; } else { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - console.log(props.emojiUrls, props.emojiUrls[token.props.name], token.props.name); if (props.emojiUrls && (props.emojiUrls[token.props.name] == null)) { return [h('span', `:${token.props.name}:`)]; } else { From ab94d15d387d28c0e82d009e3ff70ae89f5eba60 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 11:39:19 +0900 Subject: [PATCH 456/501] update --- packages/frontend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 8e99993721..32b73ec7e2 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -75,7 +75,7 @@ "vite": "5.2.11", "vue": "3.4.26", "vuedraggable": "next", - "wavesurfer.js": "^7.7.14", + "wavesurfer.js": "^7.7.14" }, "devDependencies": { "@misskey-dev/eslint-plugin": "1.0.0", From ce3a019d8116a30b9bd005064388144486c7a53d Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 11:44:57 +0900 Subject: [PATCH 457/501] update --- packages/frontend/src/components/MkEmojiEditDialog.vue | 2 +- packages/frontend/src/components/MkNoteSimple.vue | 3 ++- .../src/components/global/MkMisskeyFlavoredMarkdown.ts | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index f0ab622f0e..c185683b51 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInput v-model="text"> <template #label>テスト文章</template> </MkInput><br/> - <MkNoteSimple :note="{isHidden:false,replyId:null,renoteId:null,files:[],user: $i,text:text,cw:null, emojis: {[name]: imgUrl}}"/> + <MkNoteSimple :emojireq="true" :note="{isHidden:false,replyId:null,renoteId:null,files:[],user: $i,text:text,cw:null, emojis: {[name]: imgUrl}}"/> <p v-if="speed ">基準より眩しい可能性があります。</p> <p v-if="!speed">問題は見つかりませんでした。</p> <p>※上記の物は問題がないことを保証するものではありません。</p> diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 45410c26d6..4227b47fa2 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkNoteHeader :class="$style.header" :note="note" :mini="true"/> <div> <p v-if="note.cw != null" :class="$style.cw"> - <Mfm v-if="note.cw != ''" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> + <Mfm v-if="note.cw != ''" :emojireq="emojireq" style="margin-right: 8px;" :text="note.cw" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/> </p> <div v-show="note.cw == null || showContent"> @@ -42,6 +42,7 @@ const props = defineProps<{ isSchedule?: boolean; scheduledNoteId?: string; }; + emojireq:boolean; }>(); const emit = defineEmits<{ diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 895cb87183..6fbd2a68e3 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -39,6 +39,7 @@ type MfmProps = { text: string; plain?: boolean; nowrap?: boolean; + emojireq?: boolean; author?: { id: ID; username: string; @@ -474,7 +475,7 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven case 'emojiCode': { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (props.author?.host == null ) { + if (props.author?.host == null && props.emojireq ) { return [h(MkCustomEmoji, { key: Math.random(), name: token.props.name, From 4fe08913740c238d4617efbcf1d2eeeea5e24349 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 11:51:41 +0900 Subject: [PATCH 458/501] update --- packages/frontend/src/components/MkNoteSimple.vue | 2 +- packages/frontend/src/components/MkSubNoteContent.vue | 3 ++- .../src/components/global/MkMisskeyFlavoredMarkdown.ts | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue index 4227b47fa2..370d254228 100644 --- a/packages/frontend/src/components/MkNoteSimple.vue +++ b/packages/frontend/src/components/MkNoteSimple.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkCwButton v-model="showContent" :text="note.text" :files="note.files" :poll="note.poll"/> </p> <div v-show="note.cw == null || showContent"> - <MkSubNoteContent :class="$style.text" :note="note"/> + <MkSubNoteContent :emojireq="emojireq" :class="$style.text" :note="note"/> </div> <div v-if="note.isSchedule" style="margin-top: 10px;"> <MkButton :class="$style.button" inline @click="editScheduleNote()"><i class="ti ti-pencil"></i> {{ i18n.ts.deleteAndEdit }}</MkButton> diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue index 45967e2337..5f947bae87 100644 --- a/packages/frontend/src/components/MkSubNoteContent.vue +++ b/packages/frontend/src/components/MkSubNoteContent.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="note.deletedAt" style="opacity: 0.5">({{ i18n.ts.deletedNote }})</span> <MkA v-if="note.replyId" :class="$style.reply" :to="`/notes/${note.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> - <Mfm v-if="note.text" :text="note.text" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> + <Mfm v-if="note.text" :emojireq="emojireq" :text="note.text" :author="note.user" :nyaize="'respect'" :emojiUrls="note.emojis"/> <MkA v-if="note.renoteId" :class="$style.rp" :to="`/notes/${note.renoteId}`">RN: ...</MkA> </div> <details v-if="note.files && note.files.length > 0"> @@ -40,6 +40,7 @@ import { shouldCollapsed } from '@/scripts/collapsed.js'; const props = defineProps<{ note: Misskey.entities.Note; + emojireq: boolean; }>(); const isLong = shouldCollapsed(props.note, []); diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts index 6fbd2a68e3..32e9778c72 100644 --- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts +++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts @@ -475,7 +475,8 @@ export default function (props: MfmProps, { emit }: { emit: SetupContext<MfmEven case 'emojiCode': { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - if (props.author?.host == null && props.emojireq ) { + console.log('emojiCode', props.emojireq); + if (props.author?.host == null && !props.emojireq ) { return [h(MkCustomEmoji, { key: Math.random(), name: token.props.name, From 4da6ae53e9c7f702898a917ca27a59781a282d72 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 12:16:43 +0900 Subject: [PATCH 459/501] update --- package.json | 2 +- .../migration/1715787239605-loginbonus.js | 11 ----------- .../src/components/MkEmojiEditDialog.vue | 2 +- packages/frontend/src/scripts/get-user-menu.ts | 16 ++++++++++++++++ 4 files changed, 18 insertions(+), 13 deletions(-) delete mode 100644 packages/backend/migration/1715787239605-loginbonus.js diff --git a/package.json b/package.json index 246d2a0ce8..d64b476527 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea5", + "version": "2024.5.0-beta1-mattyatea6", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/migration/1715787239605-loginbonus.js b/packages/backend/migration/1715787239605-loginbonus.js deleted file mode 100644 index b12fa3c76c..0000000000 --- a/packages/backend/migration/1715787239605-loginbonus.js +++ /dev/null @@ -1,11 +0,0 @@ -import {Column} from "typeorm"; - -export class Loginbonus1715787239605 { - name = 'Loginbonus1715787239605' - - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "getPoints"`); } - - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "getPoints" integer NOT NULL DEFAULT '0'`); } -} diff --git a/packages/frontend/src/components/MkEmojiEditDialog.vue b/packages/frontend/src/components/MkEmojiEditDialog.vue index c185683b51..2cd685d01a 100644 --- a/packages/frontend/src/components/MkEmojiEditDialog.vue +++ b/packages/frontend/src/components/MkEmojiEditDialog.vue @@ -75,7 +75,7 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.isNotifyIsHome }} </MkSwitch> </div> - <div v-if="imgUrl"> + <div v-if="imgUrl" style="width: 30%"> <MkInput v-model="text"> <template #label>テスト文章</template> </MkInput><br/> diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 3e031d232f..c8bf8af255 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -81,6 +81,18 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter }); } + async function toggleBlockAndMute() { + if (user.isBlocking && user.isMuted || !user.isBlocking && !user.isMuted) { + toggleMute(); + toggleBlock(); + } + if (user.isMuted && !user.isBlocking) { + toggleMute(); + } else if (!user.isMuted && user.isBlocking) { + toggleBlock(); + } + } + async function toggleWithReplies() { os.apiWithDialog('following/update', { userId: user.id, @@ -327,6 +339,10 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter icon: 'ti ti-ban', text: user.isBlocking ? i18n.ts.unblock : i18n.ts.block, action: toggleBlock, + }, { + icon: 'ti ti-user-off', + text: user.isBlocking || user.isMuted ? i18n.ts.unblock + '&' + i18n.ts.unmute : i18n.ts.block + '&' + i18n.ts.mute, + action: toggleBlockAndMute, }]); if (user.isFollowed) { From fb6154957f0b9e0158d5057854580fa45539dd37 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 12:18:30 +0900 Subject: [PATCH 460/501] update --- packages/backend/migration/1715791271605-loginbonus1.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/backend/migration/1715791271605-loginbonus1.js b/packages/backend/migration/1715791271605-loginbonus1.js index 7dc8e1e709..6d9407a5b1 100644 --- a/packages/backend/migration/1715791271605-loginbonus1.js +++ b/packages/backend/migration/1715791271605-loginbonus1.js @@ -2,12 +2,10 @@ export class Loginbonus11715791271605 { name = 'Loginbonus11715791271605' async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "getPoints"`); await queryRunner.query(`ALTER TABLE "user" ADD "getPoints" integer NOT NULL DEFAULT '0'`); } async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "getPoints"`); await queryRunner.query(`ALTER TABLE "user_profile" ADD "getPoints" integer NOT NULL DEFAULT '0'`); } } From ceee4800b8695ff3dcfbdb0ee4ac5e7608e9bffd Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 19:23:34 +0900 Subject: [PATCH 461/501] update --- .../frontend/src/components/MkPostForm.vue | 7 ++-- .../frontend/src/components/MkUrlPreview.vue | 32 +++++++++++++++---- .../frontend/src/pages/settings/general.vue | 6 ++++ packages/frontend/src/store.ts | 8 +++++ 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index ccb33dc30c..0a97030fb4 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -274,12 +274,12 @@ const maxTextLength = computed((): number => { const canPost = computed((): boolean => { return !props.mock && !posting.value && !posted.value && ( - 1 <= textLength.value || + 1 <= textLength.value || 1 <= files.value.length || poll.value != null || props.renote != null || (props.reply != null && quoteId.value != null) - ) && + ) && (textLength.value <= maxTextLength.value) && (!poll.value || poll.value.choices.length >= 2); }); @@ -857,7 +857,7 @@ async function post(ev?: MouseEvent) { if (notesCount === 1) { claimAchievement('notes1'); } - poll.value = null; + if (postData.schedule?.scheduledAt) { const d = new Date(postData.schedule.scheduledAt); @@ -901,6 +901,7 @@ async function post(ev?: MouseEvent) { if (m === 0 && s === 0) { claimAchievement('postedAt0min0sec'); } + clear(); }); }).catch(err => { posting.value = false; diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 6954f1f6ff..9b1640e0a4 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -13,10 +13,11 @@ SPDX-License-Identifier: AGPL-3.0-only v-if="player.url.startsWith('http://') || player.url.startsWith('https://')" sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin" scrolling="no" - :allow="player.allow == null ? 'autoplay;encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')" + :allow="player.allow == null ? 'encrypted-media;fullscreen' : player.allow.filter(x => ['autoplay', 'clipboard-write', 'fullscreen', 'encrypted-media', 'picture-in-picture', 'web-share'].includes(x)).join(';')" :class="$style.playerIframe" - :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" - :style="{ border: 0 }" + :src="player.url" + :style="{ border: 0, backgroundColor: 'transparent' }" + allowtransparency="true" ></iframe> <span v-else>invalid url</span> </div> @@ -27,14 +28,18 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </template> <template v-else-if="tweetId && tweetExpanded"> - <div ref="twitter"> + <div ref="twitter" :class="$style.twitter"> <iframe ref="tweet" allow="fullscreen;web-share" sandbox="allow-popups allow-popups-to-escape-sandbox allow-scripts allow-same-origin" scrolling="no" - :style="{ position: 'relative', width: '100%', height: `${tweetHeight}px`, border: 0 }" + data-transparent="true" + + :style="{ position: 'relative', width: '100%', height: `${tweetHeight}px`, border: 0,borderRadius: '14px'}" :src="`https://platform.twitter.com/embed/index.html?embedId=${embedId}&hideCard=false&hideThread=false&lang=en&theme=${defaultStore.state.darkMode ? 'dark' : 'light'}&id=${tweetId}`" + frameborder="0" + allowtransparency="true" ></iframe> </div> <div :class="$style.action"> @@ -83,7 +88,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, onDeactivated, onUnmounted, ref } from 'vue'; +import { defineAsyncComponent, onDeactivated, onMounted, onUnmounted, ref } from 'vue'; import type { summaly } from '@misskey-dev/summaly'; import { url as local } from '@/config.js'; import { i18n } from '@/i18n.js'; @@ -135,6 +140,15 @@ onDeactivated(() => { playerEnabled.value = false; }); +onMounted(() => { + if (defaultStore.state.alwaysShowPlayer) { + playerEnabled.value = true; + } + if (defaultStore.state.alwaysExpandTweet) { + tweetExpanded.value = true; + } +}); + const requestUrl = new URL(props.url); if (!['http:', 'https:'].includes(requestUrl.protocol)) throw new Error('invalid url'); @@ -206,7 +220,13 @@ onUnmounted(() => { position: relative; width: 100%; } +.twitter{ + width: 70%; +} +.app{ + background: red; +} .disablePlayer { position: absolute; top: -1.5em; diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 7e4d8dab00..0ae5b7590f 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -36,6 +36,8 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps_s"> <MkSwitch v-model="showFixedPostForm">{{ i18n.ts.showFixedPostForm }}</MkSwitch> <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> + <MkSwitch v-model="alwaysShowPlayer">Youtube.comや、nicovideo.jpのプレイヤーを全て開いた状態にする</MkSwitch> + <MkSwitch v-model="alwaysExpandTweet">Xのポストを常時表示させる</MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> <div v-for="pinnedLists in defaultStore.reactiveState.pinnedUserLists.value" class="_margin"> @@ -433,6 +435,8 @@ const imageNewTab = computed(defaultStore.makeGetterSetter('imageNewTab')); const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); +const alwaysShowPlayer = computed(defaultStore.makeGetterSetter('alwaysShowPlayer')); +const alwaysExpandTweet = computed(defaultStore.makeGetterSetter('alwaysExpandTweet')); const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); const numberOfGamingSpeed = computed(defaultStore.makeGetterSetter('numberOfGamingSpeed')); const homeColor = computed(defaultStore.makeGetterSetter('homeColor')); @@ -562,6 +566,8 @@ watch([ disableStreamingTimeline, enableSeasonalScreenEffect, alwaysConfirmFollow, + alwaysShowPlayer, + alwaysExpandTweet, ], async () => { await reloadAsk(); }); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index f8b2919d78..dbdd6531db 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -314,6 +314,14 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: false, }, + alwaysShowPlayer: { + where: 'device', + default: true, + }, + alwaysExpandTweet: { + where: 'device', + default: false, + }, enableQuickAddMfmFunction: { where: 'device', default: false, From 83aca6e7ec5fbe6916fcd8d5935e5ed8f6f7014a Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:28:10 +0900 Subject: [PATCH 462/501] =?UTF-8?q?add:=20=E4=B8=8B=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E9=96=A2=E6=95=B0=E7=AD=89?= =?UTF-8?q?=E3=81=AE=E5=88=87=E3=82=8A=E9=9B=A2=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/note-drafts.ts | 44 ++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 packages/frontend/src/scripts/note-drafts.ts diff --git a/packages/frontend/src/scripts/note-drafts.ts b/packages/frontend/src/scripts/note-drafts.ts new file mode 100644 index 0000000000..0b886f912c --- /dev/null +++ b/packages/frontend/src/scripts/note-drafts.ts @@ -0,0 +1,44 @@ +import * as Misskey from 'misskey-js'; +import type { PollEditorModelValue } from '@/components/MkPollEditor.vue'; +import type { DeleteScheduleEditorModelValue } from '@/components/MkDeleteScheduleEditor.vue'; +import { miLocalStorage } from '@/local-storage.js'; + +export type NoteDraft = { + updatedAt: Date; + data: { + text: string; + useCw: boolean; + cw: string | null; + visibility: (typeof Misskey.noteVisibilities)[number]; + localOnly: boolean; + files: Misskey.entities.DriveFile[]; + poll: PollEditorModelValue | null; + scheduledNoteDelete: DeleteScheduleEditorModelValue | null; + }; +}; + +export function getAll() { + const drafts = miLocalStorage.getItem('drafts'); + if (!drafts) return {}; + return JSON.parse(drafts) as Record<string, NoteDraft | undefined>; +} + +export function get(key: string) { + const draft = getAll()[key]; + return draft ?? null; +} + +export function set(key: string, draft: NoteDraft['data']) { + const drafts = getAll(); + drafts[key] = { + updatedAt: new Date(), + data: draft, + }; + miLocalStorage.setItem('drafts', JSON.stringify(drafts)); +} + +export function remove(key: string) { + const drafts = getAll(); + delete drafts[key]; + miLocalStorage.setItem('drafts', JSON.stringify(drafts)); +} From 5cac3fe5175d88a9d7f402c16d86c3ef55b2a880 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 14:29:52 +0900 Subject: [PATCH 463/501] =?UTF-8?q?feat:=20=E4=B8=8B=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkPostFormDrafts.vue | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 packages/frontend/src/components/MkPostFormDrafts.vue diff --git a/packages/frontend/src/components/MkPostFormDrafts.vue b/packages/frontend/src/components/MkPostFormDrafts.vue new file mode 100644 index 0000000000..c1eaf0c64e --- /dev/null +++ b/packages/frontend/src/components/MkPostFormDrafts.vue @@ -0,0 +1,102 @@ +<template> +<MkModalWindow + ref="dialog" + :width="500" + :height="600" + @close="dialog?.close()" + @closed="$emit('closed')" +> + <template #header>{{ i18n.ts.drafts }}</template> + + <div :class="$style.container"> + <template v-for="(note, key) of notes" :key="key"> + <div v-if="note && noteFilter(key)" class="_panel" :class="$style.note" @click="() => select(key)"> + <div v-if="key.startsWith('renote:')" :class="$style.subtext"><i class="ti ti-quote"></i> {{ i18n.ts.quote }}</div> + <div v-if="key.startsWith('reply:')" :class="$style.subtext"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.reply }}</div> + <Mfm v-if="note.data.text" :text="note.data.text" :nyaize="'respect'"/> + <div :class="[$style.subtext, $style.bottom]"> + <MkTime :time="note.updatedAt"/> + <div v-if="note.data.files.length"><i class="ti ti-photo-plus" :class="$style.icon"></i>{{ note.data.files.length }}</div> + </div> + </div> + </template> + </div> +</MkModalWindow> +</template> + +<script lang="ts" setup> +import { shallowRef, computed } from 'vue'; +import * as noteDrafts from '@/scripts/note-drafts.js'; +import MkModalWindow from '@/components/MkModalWindow.vue'; +import { i18n } from '@/i18n.js'; +import { $i } from '@/account'; + +const props = withDefaults(defineProps<{ + channel: boolean; +}>(), { + channel: false, +}); + +const emit = defineEmits<{ + (ev: 'selected', res: string): void; + (ev: 'closed'): void; +}>(); + +const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); +const notes = computed(() => noteDrafts.getAll()); + +function noteFilter(key: string) { + // チャンネルモードの場合はチャンネル内での下書きのみを表示 + if (props.channel) return key.startsWith('channel:'); + + // チャンネル外ならチャンネル内の下書きは表示しない + if (key.startsWith('channel:')) return false; + if (key.startsWith('note:')) return key.startsWith(`note:${$i?.id}`); + + return true; +} + +function select(key: string) { + emit('selected', key); + dialog.value?.close(); +} +</script> + +<style lang="scss" module> +.container { + display: flex; + flex-direction: column; + gap: 16px; + overflow-x: clip; + padding: 16px; +} + +.note { + display: flex; + flex-direction: column; + padding: 10px; + gap: 6px; + border-radius: 12px; + background-color: var(--buttonBg); + cursor: pointer; + + &:hover { + background-color: var(--buttonHoverBg); + } +} + +.subtext { + font-size: 0.8em; + opacity: 0.7; + user-select: none; +} + +.bottom { + display: flex; + gap: 12px; +} + +.icon { + margin-right: 4px; +} +</style> From 0022e11dce61d4b5954cf146fa79b9f4f3c05bce Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 16:34:51 +0900 Subject: [PATCH 464/501] =?UTF-8?q?refactor:=20indexedDb=E3=81=AB=E7=A7=BB?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/note-drafts.ts | 81 +++++++++++++++++--- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/packages/frontend/src/scripts/note-drafts.ts b/packages/frontend/src/scripts/note-drafts.ts index 0b886f912c..c5f1376deb 100644 --- a/packages/frontend/src/scripts/note-drafts.ts +++ b/packages/frontend/src/scripts/note-drafts.ts @@ -2,9 +2,13 @@ import * as Misskey from 'misskey-js'; import type { PollEditorModelValue } from '@/components/MkPollEditor.vue'; import type { DeleteScheduleEditorModelValue } from '@/components/MkDeleteScheduleEditor.vue'; import { miLocalStorage } from '@/local-storage.js'; +import { get as idbGet, set as idbSet } from '@/scripts/idb-proxy.js'; export type NoteDraft = { updatedAt: Date; + type: keyof NoteKeys; + uniqueId: string; + auxId: string | null; data: { text: string; useCw: boolean; @@ -17,28 +21,83 @@ export type NoteDraft = { }; }; -export function getAll() { - const drafts = miLocalStorage.getItem('drafts'); +type NoteKeys = { + note: () => unknown, + reply: (replyId: string) => unknown, + quote: (renoteId: string) => unknown, + channel: (channelId: string) => unknown, +} + +export async function migrate(userId: string) { + const raw = miLocalStorage.getItem('drafts'); + if (!raw) return; + + const drafts = JSON.parse(raw) as Record<string, NoteDraft>; + const newDrafts: Record<string, NoteDraft> = {}; + + for (let i = 0; i < Object.keys(drafts).length; i++) { + const key = Object.keys(drafts)[i]; + const [type, id] = key.split(':'); + if (type === 'note' && id !== userId) continue; + const keyType = type === 'renote' ? 'quote' : type as keyof NoteKeys; + const keyId = type === 'note' ? null : id; + const uniqueId = Date.now().toString() + i.toString(); + const newKey = getKey(keyType, uniqueId, false, keyId as string); + newDrafts[newKey] = { + ...drafts[key], + uniqueId, + type: keyType, + auxId: keyId, + }; + delete drafts[key]; + } + + await idbSet(`drafts::${userId}`, JSON.stringify(newDrafts)); + miLocalStorage.setItem('drafts', JSON.stringify(drafts)); +} + +function getKey<T extends keyof NoteKeys, U extends boolean>(type: T, uniqueId: string | null, withUniqueId: U, ...args: Parameters<NoteKeys[T]>): U extends true ? { uniqueId: string, key: string } : string { + const id = uniqueId ?? Date.now(); + let key = `${type}:${id}`; + for (const arg of args) { + if (arg != null) key += `:${arg}`; + } + + if (withUniqueId) { + return { uniqueId: id, key } as any; + } else { + return key as any; + } +} + +export async function getAll(userId: string) { + const drafts = await idbGet(`drafts::${userId}`); if (!drafts) return {}; return JSON.parse(drafts) as Record<string, NoteDraft | undefined>; } -export function get(key: string) { - const draft = getAll()[key]; +export async function get<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string | null, ...args: Parameters<NoteKeys[T]>) { + const key = getKey(type, uniqueId, false, ...args); + const draft = await getAll(userId)[key]; return draft ?? null; } -export function set(key: string, draft: NoteDraft['data']) { - const drafts = getAll(); - drafts[key] = { +export async function set<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string | null, draft: NoteDraft['data'], ...args: Parameters<NoteKeys[T]>) { + const drafts = await getAll(userId); + const keys = getKey(type, uniqueId, true, ...args); + drafts[keys.key] = { updatedAt: new Date(), + type, + uniqueId: uniqueId ?? keys.uniqueId, + auxId: args[0] ?? null, data: draft, }; - miLocalStorage.setItem('drafts', JSON.stringify(drafts)); + await idbSet(`drafts::${userId}`, JSON.stringify(drafts)); } -export function remove(key: string) { - const drafts = getAll(); +export async function remove<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string | null, ...args: Parameters<NoteKeys[T]>) { + const drafts = await getAll(userId); + const key = getKey(type, uniqueId, false, ...args); delete drafts[key]; - miLocalStorage.setItem('drafts', JSON.stringify(drafts)); + await idbSet(`drafts::${userId}`, JSON.stringify(drafts)); } From 0b784b8f32bbf1f714255185f000b7893aa8e819 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 17:10:41 +0900 Subject: [PATCH 465/501] =?UTF-8?q?change:=20uniqueId=E3=81=AE=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E5=BF=85=E9=A0=88=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/note-drafts.ts | 42 +++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/frontend/src/scripts/note-drafts.ts b/packages/frontend/src/scripts/note-drafts.ts index c5f1376deb..958b9ae5dc 100644 --- a/packages/frontend/src/scripts/note-drafts.ts +++ b/packages/frontend/src/scripts/note-drafts.ts @@ -42,7 +42,7 @@ export async function migrate(userId: string) { const keyType = type === 'renote' ? 'quote' : type as keyof NoteKeys; const keyId = type === 'note' ? null : id; const uniqueId = Date.now().toString() + i.toString(); - const newKey = getKey(keyType, uniqueId, false, keyId as string); + const newKey = getKey(keyType, uniqueId, keyId as string); newDrafts[newKey] = { ...drafts[key], uniqueId, @@ -52,52 +52,46 @@ export async function migrate(userId: string) { delete drafts[key]; } - await idbSet(`drafts::${userId}`, JSON.stringify(newDrafts)); + await idbSet(`drafts::${userId}`, newDrafts); miLocalStorage.setItem('drafts', JSON.stringify(drafts)); } -function getKey<T extends keyof NoteKeys, U extends boolean>(type: T, uniqueId: string | null, withUniqueId: U, ...args: Parameters<NoteKeys[T]>): U extends true ? { uniqueId: string, key: string } : string { - const id = uniqueId ?? Date.now(); - let key = `${type}:${id}`; +function getKey<T extends keyof NoteKeys>(type: T, uniqueId: string, ...args: Parameters<NoteKeys[T]>) { + let key = `${type}:${uniqueId}`; for (const arg of args) { if (arg != null) key += `:${arg}`; } - - if (withUniqueId) { - return { uniqueId: id, key } as any; - } else { - return key as any; - } + return key; } export async function getAll(userId: string) { const drafts = await idbGet(`drafts::${userId}`); - if (!drafts) return {}; - return JSON.parse(drafts) as Record<string, NoteDraft | undefined>; + return (drafts ?? {}) as Record<string, NoteDraft | undefined>; } -export async function get<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string | null, ...args: Parameters<NoteKeys[T]>) { - const key = getKey(type, uniqueId, false, ...args); +export async function get<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string, ...args: Parameters<NoteKeys[T]>) { + const key = getKey(type, uniqueId, ...args); const draft = await getAll(userId)[key]; return draft ?? null; } -export async function set<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string | null, draft: NoteDraft['data'], ...args: Parameters<NoteKeys[T]>) { +export async function set<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string, draft: NoteDraft['data'], ...args: Parameters<NoteKeys[T]>) { const drafts = await getAll(userId); - const keys = getKey(type, uniqueId, true, ...args); - drafts[keys.key] = { + const key = getKey(type, uniqueId, ...args); + drafts[key] = { updatedAt: new Date(), type, - uniqueId: uniqueId ?? keys.uniqueId, + uniqueId, auxId: args[0] ?? null, - data: draft, + data: JSON.parse(JSON.stringify(draft)) as NoteDraft['data'], }; - await idbSet(`drafts::${userId}`, JSON.stringify(drafts)); + console.log(drafts); + await idbSet(`drafts::${userId}`, drafts); } -export async function remove<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string | null, ...args: Parameters<NoteKeys[T]>) { +export async function remove<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string, ...args: Parameters<NoteKeys[T]>) { const drafts = await getAll(userId); - const key = getKey(type, uniqueId, false, ...args); + const key = getKey(type, uniqueId, ...args); delete drafts[key]; - await idbSet(`drafts::${userId}`, JSON.stringify(drafts)); + await idbSet(`drafts::${userId}`, drafts); } From e7c180ff8929195fe8dc039ae07e5e833995b150 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:29:38 +0900 Subject: [PATCH 466/501] =?UTF-8?q?fix:=20=E3=83=9E=E3=82=A4=E3=82=B0?= =?UTF-8?q?=E3=83=AC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=8C=E6=AD=A3?= =?UTF-8?q?=E5=B8=B8=E3=81=AB=E8=A1=8C=E3=81=88=E3=81=AA=E3=81=84=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/scripts/note-drafts.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/scripts/note-drafts.ts b/packages/frontend/src/scripts/note-drafts.ts index 958b9ae5dc..67abc004a3 100644 --- a/packages/frontend/src/scripts/note-drafts.ts +++ b/packages/frontend/src/scripts/note-drafts.ts @@ -33,10 +33,11 @@ export async function migrate(userId: string) { if (!raw) return; const drafts = JSON.parse(raw) as Record<string, NoteDraft>; + const keys = Object.keys(drafts); const newDrafts: Record<string, NoteDraft> = {}; - for (let i = 0; i < Object.keys(drafts).length; i++) { - const key = Object.keys(drafts)[i]; + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; const [type, id] = key.split(':'); if (type === 'note' && id !== userId) continue; const keyType = type === 'renote' ? 'quote' : type as keyof NoteKeys; @@ -52,6 +53,7 @@ export async function migrate(userId: string) { delete drafts[key]; } + if (Object.keys(newDrafts).length === 0) return; await idbSet(`drafts::${userId}`, newDrafts); miLocalStorage.setItem('drafts', JSON.stringify(drafts)); } @@ -71,8 +73,8 @@ export async function getAll(userId: string) { export async function get<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string, ...args: Parameters<NoteKeys[T]>) { const key = getKey(type, uniqueId, ...args); - const draft = await getAll(userId)[key]; - return draft ?? null; + const draft = await getAll(userId); + return draft[key] ?? null; } export async function set<T extends keyof NoteKeys>(type: T, userId: string, uniqueId: string, draft: NoteDraft['data'], ...args: Parameters<NoteKeys[T]>) { From 07ebc6e5e1761c583f2c0ca171df34276ee1c6e0 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 18:55:09 +0900 Subject: [PATCH 467/501] =?UTF-8?q?enhance:=20=E4=B8=8B=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=82=92=E6=94=B9=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/MkPostFormDrafts.vue | 103 +++++++++++++----- 1 file changed, 78 insertions(+), 25 deletions(-) diff --git a/packages/frontend/src/components/MkPostFormDrafts.vue b/packages/frontend/src/components/MkPostFormDrafts.vue index c1eaf0c64e..27bcbe1324 100644 --- a/packages/frontend/src/components/MkPostFormDrafts.vue +++ b/packages/frontend/src/components/MkPostFormDrafts.vue @@ -9,80 +9,116 @@ <template #header>{{ i18n.ts.drafts }}</template> <div :class="$style.container"> - <template v-for="(note, key) of notes" :key="key"> - <div v-if="note && noteFilter(key)" class="_panel" :class="$style.note" @click="() => select(key)"> - <div v-if="key.startsWith('renote:')" :class="$style.subtext"><i class="ti ti-quote"></i> {{ i18n.ts.quote }}</div> - <div v-if="key.startsWith('reply:')" :class="$style.subtext"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.reply }}</div> + <div v-if="notes === null" :class="$style.center">{{ i18n.ts.loading }}</div> + <div v-else-if="Object.keys(notes).length === 0" :class="$style.center">{{ i18n.ts.nothing }}</div> + <div v-for="(note, key) of notes" v-else :key="key" class="_panel" :class="$style.wrapper" :aria-disabled="!noteFilter(note)"> + <div v-if="note" :class="$style.note" @click="() => select(note)"> + <div v-if="note.type === 'quote'" :class="$style.subtext"><i class="ti ti-quote"></i> {{ i18n.ts.quote }}</div> + <div v-if="note.type === 'reply'" :class="$style.subtext"><i class="ti ti-arrow-back-up"></i> {{ i18n.ts.reply }}</div> + <div v-if="note.type === 'channel'" :class="$style.subtext"><i class="ti ti-device-tv"></i> {{ i18n.ts.channel }}</div> <Mfm v-if="note.data.text" :text="note.data.text" :nyaize="'respect'"/> <div :class="[$style.subtext, $style.bottom]"> <MkTime :time="note.updatedAt"/> <div v-if="note.data.files.length"><i class="ti ti-photo-plus" :class="$style.icon"></i>{{ note.data.files.length }}</div> </div> </div> - </template> + <div :class="$style.trash" @click="() => remove(note)"><i class="ti ti-trash"></i></div> + </div> </div> </MkModalWindow> </template> <script lang="ts" setup> -import { shallowRef, computed } from 'vue'; +import { shallowRef, ref, onMounted } from 'vue'; import * as noteDrafts from '@/scripts/note-drafts.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; -import { $i } from '@/account'; +import { signinRequired } from '@/account.js'; +import * as os from '@/os.js'; -const props = withDefaults(defineProps<{ - channel: boolean; -}>(), { - channel: false, -}); +const $i = signinRequired(); + +const props = defineProps<{ + channelId?: string; +}>(); const emit = defineEmits<{ - (ev: 'selected', res: string): void; + (ev: 'selected', res: noteDrafts.NoteDraft): void; (ev: 'closed'): void; }>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); -const notes = computed(() => noteDrafts.getAll()); +const notes = ref<Record<string, noteDrafts.NoteDraft | undefined> | null>(null); + +function noteFilter(note: noteDrafts.NoteDraft | undefined) { + if (!note) return false; -function noteFilter(key: string) { // チャンネルモードの場合はチャンネル内での下書きのみを表示 - if (props.channel) return key.startsWith('channel:'); + if (props.channelId) return note.type === 'channel' && note.auxId === props.channelId; // チャンネル外ならチャンネル内の下書きは表示しない - if (key.startsWith('channel:')) return false; - if (key.startsWith('note:')) return key.startsWith(`note:${$i?.id}`); + if (note.type === 'channel') return false; return true; } -function select(key: string) { - emit('selected', key); +function select(note: noteDrafts.NoteDraft) { + if (!noteFilter(note)) return; + emit('selected', note); dialog.value?.close(); } + +async function remove(note: noteDrafts.NoteDraft | undefined) { + if (!note) return; + + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.deleteConfirm, + }); + + if (canceled) return; + await noteDrafts.remove(note.type, $i.id, note.uniqueId, note.auxId as string); + notes.value = await noteDrafts.getAll($i.id); +} + +onMounted(async () => { + notes.value = await noteDrafts.getAll($i.id); +}); </script> <style lang="scss" module> .container { display: flex; flex-direction: column; + align-items: stretch; gap: 16px; overflow-x: clip; padding: 16px; } -.note { +.wrapper { display: flex; - flex-direction: column; - padding: 10px; - gap: 6px; border-radius: 12px; background-color: var(--buttonBg); cursor: pointer; - &:hover { + &:hover:not([aria-disabled="true"]) { background-color: var(--buttonHoverBg); } + + &[aria-disabled="true"] { + opacity: 0.5; + cursor: not-allowed; + } +} + +.note { + display: flex; + flex-direction: column; + justify-content: center; + padding: 10px; + gap: 6px; + flex-grow: 1; } .subtext { @@ -99,4 +135,21 @@ function select(key: string) { .icon { margin-right: 4px; } + +.center { + text-align: center; +} + +.trash { + display: flex; + align-items: center; + justify-content: center; + padding: 16px; + color: var(--error); + + &:hover { + background-color: var(--error); + color: white; + } +} </style> From d564bd73bc19b4433ad2cf03a09508653687ce09 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:00:11 +0900 Subject: [PATCH 468/501] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=83=95?= =?UTF-8?q?=E3=82=A9=E3=83=BC=E3=83=A0=E3=81=AB=E4=B8=8B=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E3=82=92=E9=81=A9=E7=94=A8=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/MkPostForm.vue | 297 ++++++++++-------- packages/frontend/src/store.ts | 4 - 2 files changed, 158 insertions(+), 143 deletions(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 0a97030fb4..2d8a341d50 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -32,6 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span :class="$style.headerRightButtonText">{{ channel.name }}</span> </button> </template> + <button v-click-anime v-tooltip="i18n.ts.drafts" class="_button" :class="$style.headerRightItem" @click="chooseDraft"><i class="ti ti-note"></i></button> <button v-click-anime v-tooltip="i18n.ts._visibility.disableFederation" class="_button" :class="[$style.headerRightItem, { [$style.danger]: localOnly }]" :disabled="channel != null || visibility === 'specified'" @click="toggleLocalOnly"> <span v-if="!localOnly"><i class="ti ti-rocket"></i></span> <span v-else><i class="ti ti-rocket-off"></i></span> @@ -115,6 +116,7 @@ import { extractMentions } from '@/scripts/extract-mentions.js'; import { formatTimeString } from '@/scripts/format-time-string.js'; import { Autocomplete } from '@/scripts/autocomplete.js'; import * as os from '@/os.js'; +import * as noteDrafts from '@/scripts/note-drafts.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { selectFiles } from '@/scripts/select-file.js'; import { dateTimeFormat } from '@/scripts/intl-const.js'; @@ -197,6 +199,8 @@ let schedule = ref<{ scheduledAt: string | null; }| null>(null); const useCw = ref<boolean>(!!props.initialCw); +const renote = ref(props.renote); +const reply = ref(props.reply); const showPreview = ref(defaultStore.state.showPreview); watch(showPreview, () => defaultStore.set('showPreview', showPreview.value)); const showAddMfmFunction = ref(defaultStore.state.enableQuickAddMfmFunction); @@ -219,24 +223,19 @@ const imeText = ref(''); const showingOptions = ref(false); const textAreaReadOnly = ref(false); -const draftKey = computed((): string => { - let key = props.channel ? `channel:${props.channel.id}` : ''; - - if (props.renote) { - key += `renote:${props.renote.id}`; - } else if (props.reply) { - key += `reply:${props.reply.id}`; - } else { - key += `note:${$i.id}`; - } - - return key; +const draftType = computed(() => { + if (props.channel) return 'channel'; + if (renote.value) return 'quote'; + if (reply.value) return 'reply'; + return 'note'; }); +const draftAuxId = computed<string | null>(() => props.channel ? props.channel.id : renote.value ? renote.value.id : reply.value ? reply.value.id : null); + const placeholder = computed((): string => { - if (props.renote) { + if (renote.value) { return i18n.ts._postForm.quotePlaceholder; - } else if (props.reply) { + } else if (reply.value) { return i18n.ts._postForm.replyPlaceholder; } else if (props.channel) { return i18n.ts._postForm.channelPlaceholder; @@ -254,9 +253,9 @@ const placeholder = computed((): string => { }); const submitText = computed((): string => { - return props.renote + return renote.value ? i18n.ts.quote - : props.reply + : reply.value ? i18n.ts.reply : schedule.value ? i18n.ts._schedulePost.addSchedule @@ -273,15 +272,9 @@ const maxTextLength = computed((): number => { const canPost = computed((): boolean => { return !props.mock && !posting.value && !posted.value && - ( - 1 <= textLength.value || - 1 <= files.value.length || - poll.value != null || - props.renote != null || - (props.reply != null && quoteId.value != null) - ) && - (textLength.value <= maxTextLength.value) && - (!poll.value || poll.value.choices.length >= 2); + (1 <= textLength.value || 1 <= files.value.length || !!poll.value || !!renote.value) && + (textLength.value <= maxTextLength.value) && + (!poll.value || poll.value.choices.length >= 2); }); const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags')); @@ -313,82 +306,86 @@ watch(visibleUsers, () => { deep: true, }); -if (props.mention) { - 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.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `; -} - -if (props.reply && props.reply.text != null) { - const ast = mfm.parse(props.reply.text); - const otherHost = props.reply.user.host; - - for (const x of extractMentions(ast)) { - const mention = x.host ? - `@${x.username}@${toASCII(x.host)}` : - (otherHost == null || otherHost === host) ? - `@${x.username}` : - `@${x.username}@${toASCII(otherHost)}`; - - // 自分は除外 - if ($i.username === x.username && (x.host == null || x.host === host)) continue; - - // 重複は除外 - if (text.value.includes(`${mention} `)) continue; - - text.value += `${mention} `; +function initialize() { + if (props.mention) { + text.value = props.mention.host ? `@${props.mention.username}@${toASCII(props.mention.host)}` : `@${props.mention.username}`; + text.value += ' '; } -} -if ($i.isSilenced && visibility.value === 'public') { - visibility.value = 'home'; -} + if (reply.value && (reply.value.user.username !== $i.username || (reply.value.user.host != null && reply.value.user.host !== host))) { + text.value = `@${reply.value.user.username}${reply.value.user.host != null ? '@' + toASCII(reply.value.user.host) : ''} `; + } -if (props.channel) { - visibility.value = 'public'; - localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す -} + if (reply.value && reply.value.text != null) { + const ast = mfm.parse(reply.value.text); + const otherHost = reply.value.user.host; -// 公開以外へのリプライ時は元の公開範囲を引き継ぐ -if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { - if (props.reply.visibility === 'home' && visibility.value === 'followers') { - visibility.value = 'followers'; - } else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') { + for (const x of extractMentions(ast)) { + const mention = x.host ? + `@${x.username}@${toASCII(x.host)}` : + (otherHost == null || otherHost === host) ? + `@${x.username}` : + `@${x.username}@${toASCII(otherHost)}`; + + // 自分は除外 + if ($i.username === x.username && (x.host == null || x.host === host)) continue; + + // 重複は除外 + if (text.value.includes(`${mention} `)) continue; + + text.value += `${mention} `; + } + } + + if ($i.isSilenced && visibility.value === 'public') { + visibility.value = 'home'; + } + + if (props.channel) { + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + } + + // 公開以外へのリプライ時は元の公開範囲を引き継ぐ + if (reply.value && ['home', 'followers', 'specified'].includes(reply.value.visibility)) { + if (reply.value.visibility === 'home' && visibility.value === 'followers') { + visibility.value = 'followers'; + } else if (['home', 'followers'].includes(reply.value.visibility) && visibility.value === 'specified') { + visibility.value = 'specified'; + } else { + visibility.value = reply.value.visibility; + } + + if (visibility.value === 'specified') { + if (reply.value.visibleUserIds) { + misskeyApi('users/show', { + userIds: reply.value.visibleUserIds.filter(uid => uid !== $i.id && uid !== reply.value?.userId), + }).then(users => { + users.forEach(pushVisibleUser); + }); + } + + if (reply.value.userId !== $i.id) { + misskeyApi('users/show', { userId: reply.value.userId }).then(user => { + pushVisibleUser(user); + }); + } + } + } + + if (props.specified) { visibility.value = 'specified'; - } else { - visibility.value = props.reply.visibility; + pushVisibleUser(props.specified); } - if (visibility.value === 'specified') { - if (props.reply.visibleUserIds) { - misskeyApi('users/show', { - userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId), - }).then(users => { - users.forEach(pushVisibleUser); - }); - } - - if (props.reply.userId !== $i.id) { - misskeyApi('users/show', { userId: props.reply.userId }).then(user => { - pushVisibleUser(user); - }); - } + // keep cw when reply + if (defaultStore.state.keepCw && reply.value && reply.value.cw) { + useCw.value = true; + cw.value = reply.value.cw; } } -if (props.specified) { - visibility.value = 'specified'; - pushVisibleUser(props.specified); -} - -// keep cw when reply -if (defaultStore.state.keepCw && props.reply && props.reply.cw) { - useCw.value = true; - cw.value = props.reply.cw; -} +initialize(); function watchForDraft() { watch(text, () => saveDraft()); @@ -508,7 +505,7 @@ function setVisibility() { currentVisibility: visibility.value, isSilenced: $i.isSilenced, localOnly: localOnly.value, src: visibilityButton.value, - ...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}), + ...(reply.value ? { isReplyVisibilitySpecified: reply.value.visibility === 'specified' } : {}), }, { changeVisibility: v => { visibility.value = v; @@ -635,7 +632,7 @@ async function onPaste(ev: ClipboardEvent) { const paste = ev.clipboardData.getData('text'); - if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) { + if (!renote.value && !quoteId.value && paste.startsWith(url + '/notes/')) { ev.preventDefault(); os.confirm({ @@ -706,34 +703,73 @@ function onDrop(ev: DragEvent): void { //#endregion } -function saveDraft() { +function saveDraft(auto = true) { if (props.instant || props.mock) return; - const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + if (!auto) { + // 手動での保存の場合は自動保存したものを削除した上で保存 + noteDrafts.remove(draftType.value, $i.id, 'default', draftAuxId.value as string); + } - draftData[draftKey.value] = { - updatedAt: new Date(), - data: { - text: text.value, - useCw: useCw.value, - cw: cw.value, - visibility: visibility.value, - localOnly: localOnly.value, - files: files.value, - poll: poll.value, - visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined, - }, - }; - - miLocalStorage.setItem('drafts', JSON.stringify(draftData)); + noteDrafts.set(draftType.value, $i.id, auto ? 'default' : Date.now().toString(), { + text: text.value, + useCw: useCw.value, + cw: cw.value, + visibility: visibility.value, + localOnly: localOnly.value, + files: files.value, + poll: poll.value, + visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined, + }, draftAuxId.value as string); } function deleteDraft() { - const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + noteDrafts.remove(draftType.value, $i.id, 'default', draftAuxId.value as string); +} - delete draftData[draftKey.value]; +function chooseDraft() { + os.popup(defineAsyncComponent(() => import('@/components/MkPostFormDrafts.vue')), { + channelId: props.channel?.id, + }, { + selected: async (res) => { + const draft = await res as noteDrafts.NoteDraft; + applyDraft(draft); + }, + }, 'closed'); +} - miLocalStorage.setItem('drafts', JSON.stringify(draftData)); +async function applyDraft(draft: noteDrafts.NoteDraft, native = false) { + if (!native) { + switch (draft.type) { + case 'quote': { + await os.apiWithDialog('notes/show', { noteId: draft.auxId as string }).then(note => { + renote.value = note; + }); + break; + } + case 'reply': { + await os.apiWithDialog('notes/show', { noteId: draft.auxId as string }).then(note => { + reply.value = note; + }); + break; + } + } + + initialize(); + } + + 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.value = draft.data.poll; + } + if (draft.data.scheduledNoteDelete) { + scheduledNoteDelete.value = draft.data.scheduledNoteDelete; + } } async function post(ev?: MouseEvent) { @@ -788,8 +824,8 @@ async function post(ev?: MouseEvent) { let postData = { text: text.value === '' ? null : 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.value ? quoteId.value : undefined, + replyId: reply.value ? reply.value.id : undefined, + renoteId: renote.value ? renote.value.id : quoteId.value ? quoteId.value : undefined, channelId: props.channel ? props.channel.id : undefined, schedule: schedule.value, poll: poll.value, @@ -887,7 +923,7 @@ async function post(ev?: MouseEvent) { claimAchievement('brainDiver'); } - if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { + if (renote.value && (renote.value.userId === $i.id) && text.length > 0) { claimAchievement('selfQuote'); } @@ -1033,30 +1069,13 @@ onMounted(() => { if (cwInputEl.value) new Autocomplete(cwInputEl.value, cw); if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags); - nextTick(() => { + nextTick(async () => { + await noteDrafts.migrate($i.id); + // 書きかけの投稿を復元 - if (!props.instant && !props.mention && !props.specified && !props.mock) { - const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value]; - if (draft) { - 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.value = draft.data.poll; - } - if (draft.data.visibleUserIds) { - misskeyApi('users/show', { userIds: draft.data.visibleUserIds }).then(users => { - for (let i = 0; i < users.length; i++) { - if (users[i].id === draft.data.visibleUserIds[i]) { - pushVisibleUser(users[i]); - } - } - }); - } - } + if (!props.instant && !props.mention && !props.specified && !props.mock && !defaultStore.state.disableNoteDrafting) { + const draft = await noteDrafts.get(draftType.value, $i.id, 'default', draftAuxId.value as string); + if (draft) applyDraft(draft, true); } // 削除して編集 diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index dbdd6531db..ecd243f681 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -90,10 +90,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: true, }, - rememberNoteVisibility: { - where: 'account', - default: false, - }, defaultNoteVisibility: { where: 'account', default: 'public' as (typeof Misskey.noteVisibilities)[number], From ab98d149a5b3816ce636b55adc2752b56c799b29 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:17:49 +0900 Subject: [PATCH 469/501] =?UTF-8?q?feat:=20=E4=B8=8B=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E3=82=92=E8=87=AA=E5=8B=95=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=8B=E3=81=A9=E3=81=86=E3=81=8B=E3=82=92=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/MkPostForm.vue | 15 +++++++++++++ .../src/components/MkPostFormDialog.vue | 1 + packages/frontend/src/store.ts | 21 ++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 2d8a341d50..e2f1ab6f3b 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -706,6 +706,8 @@ function onDrop(ev: DragEvent): void { function saveDraft(auto = true) { if (props.instant || props.mock) return; + if (auto && defaultStore.state.draftSavingBehavior !== 'auto') return; + if (!auto) { // 手動での保存の場合は自動保存したものを削除した上で保存 noteDrafts.remove(draftType.value, $i.id, 'default', draftAuxId.value as string); @@ -952,6 +954,18 @@ function cancel() { emit('cancel'); } +async function closed() { + if (defaultStore.state.draftSavingBehavior === 'manual' && text.value !== '') { + os.confirm({ + type: 'question', + text: i18n.ts.saveConfirm, + }).then(({ canceled }) => { + if (canceled) return; + saveDraft(false); + }); + } +} + function insertMention() { os.selectUser({ localOnly: localOnly.value, includeSelf: true }).then(user => { insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' '); @@ -1109,6 +1123,7 @@ onMounted(() => { defineExpose({ clear, + closed, }); </script> diff --git a/packages/frontend/src/components/MkPostFormDialog.vue b/packages/frontend/src/components/MkPostFormDialog.vue index d3de1af75e..075635d67e 100644 --- a/packages/frontend/src/components/MkPostFormDialog.vue +++ b/packages/frontend/src/components/MkPostFormDialog.vue @@ -50,6 +50,7 @@ function onPosted() { } function onModalClosed() { + form.value?.closed(); emit('closed'); } </script> diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index ecd243f681..c20c945aef 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -101,8 +101,27 @@ export const defaultStore = markRaw(new Storage('base', { defaultHomeNoteLocalOnly: { where: 'account', default: false, + }, defaultFollowersNoteLocalOnly: { + where: 'account', + default: false, }, - defaultFollowersNoteLocalOnly: { + draftSavingBehavior: { + where: 'account', + default: 'auto' as 'auto' | 'manual', + }, + rememberNoteVisibility: { + where: 'account', + default: false, + }, + rememberReactionAcceptance: { + where: 'account', + default: false, + }, + defaultNoteVisibility: { + where: 'account', + default: 'home', + }, + defaultNoteLocalOnly: { where: 'account', default: false, }, From e4cc3a5ab3b96075269a625f5cea92722525a1b3 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:34:08 +0900 Subject: [PATCH 470/501] =?UTF-8?q?feat:=20=E3=80=8C=E4=B8=8B=E6=9B=B8?= =?UTF-8?q?=E3=81=8D=E3=81=A8=E3=81=97=E3=81=A6=E4=BF=9D=E5=AD=98=E3=80=8D?= =?UTF-8?q?=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend/src/components/MkPostForm.vue | 69 ++++++++++++++----- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index e2f1ab6f3b..f6440baec0 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -79,15 +79,9 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <footer :class="$style.footer"> <div :class="$style.footerLeft"> - <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> - <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> - <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> - <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> - <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> - <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> - <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> - <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button> - <button v-tooltip="i18n.ts.ruby" :class="['_button', $style.footerButton]" @click="insertRuby"><i class="ti ti-abc"></i></button> + <template v-for="item in defaultStore.state.postFormActions"> + <button v-if="!bottomItemActionDef[item].hide" :key="item" v-tooltip="bottomItemDef[item].title" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: bottomItemActionDef[item].active }]" v-on="bottomItemActionDef[item].action ? { click: bottomItemActionDef[item].action } : {}"><i class="ti" :class="bottomItemDef[item].icon"></i></button> + </template> </div> <div :class="$style.footerRight"> <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> @@ -280,6 +274,48 @@ const canPost = computed((): boolean => { const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags')); const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags')); +const bottomItemActionDef: Record<keyof typeof bottomItemDef, { + hide?: boolean; + active?: any; + action?: any; +}> = reactive({ + attachFile: { + action: chooseFileFrom, + }, + poll: { + active: poll, + action: togglePoll, + }, + useCw: { + active: useCw, + action: () => useCw.value = !useCw.value, + }, + mention: { + action: insertMention, + }, + hashtags: { + active: withHashtags, + action: () => withHashtags.value = !withHashtags.value, + }, + plugins: { + hide: postFormActions.length === 0, + action: showActions, + }, + emoji: { + action: insertEmoji, + }, + addMfmFunction: { + hide: computed(() => !showAddMfmFunction.value), + action: insertMfmFunction, + }, + clearPost: { + action: clear, + }, + saveAsDraft: { + action: () => saveDraft(false), + }, +}); + watch(text, () => { checkMissingMention(); }, { immediate: true }); @@ -703,17 +739,17 @@ function onDrop(ev: DragEvent): void { //#endregion } -function saveDraft(auto = true) { +async function saveDraft(auto = true) { if (props.instant || props.mock) return; if (auto && defaultStore.state.draftSavingBehavior !== 'auto') return; if (!auto) { // 手動での保存の場合は自動保存したものを削除した上で保存 - noteDrafts.remove(draftType.value, $i.id, 'default', draftAuxId.value as string); + await noteDrafts.remove(draftType.value, $i.id, 'default', draftAuxId.value as string); } - noteDrafts.set(draftType.value, $i.id, auto ? 'default' : Date.now().toString(), { + await noteDrafts.set(draftType.value, $i.id, auto ? 'default' : Date.now().toString(), { text: text.value, useCw: useCw.value, cw: cw.value, @@ -723,6 +759,10 @@ function saveDraft(auto = true) { poll: poll.value, visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined, }, draftAuxId.value as string); + + if (!auto) { + clear(); + } } function deleteDraft() { @@ -769,9 +809,6 @@ async function applyDraft(draft: noteDrafts.NoteDraft, native = false) { if (draft.data.poll) { poll.value = draft.data.poll; } - if (draft.data.scheduledNoteDelete) { - scheduledNoteDelete.value = draft.data.scheduledNoteDelete; - } } async function post(ev?: MouseEvent) { @@ -955,7 +992,7 @@ function cancel() { } async function closed() { - if (defaultStore.state.draftSavingBehavior === 'manual' && text.value !== '') { + if (defaultStore.state.draftSavingBehavior === 'manual' && (text.value !== '' || files.value.length > 0)) { os.confirm({ type: 'question', text: i18n.ts.saveConfirm, From d7c5f3119195ba446132df33f7a5e938dc8b7ab7 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:42:11 +0900 Subject: [PATCH 471/501] =?UTF-8?q?enhance:=20=E6=97=A2=E3=81=AB=E5=85=A5?= =?UTF-8?q?=E5=8A=9B=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E3=81=8C=E3=81=82=E3=82=8B=E5=A0=B4=E5=90=88=E3=81=AB?= =?UTF-8?q?=E4=B8=8A=E6=9B=B8=E3=81=8D=E8=AD=A6=E5=91=8A=E3=82=92=E5=87=BA?= =?UTF-8?q?=E3=81=99=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/components/MkPostForm.vue | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index f6440baec0..5379cf7238 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -775,6 +775,15 @@ function chooseDraft() { }, { selected: async (res) => { const draft = await res as noteDrafts.NoteDraft; + + if (text.value !== '' || files.value.length > 0) { + const { canceled } = await os.confirm({ + type: 'warning', + text: i18n.ts.draftOverwriteConfirm, + }); + if (canceled) return; + } + applyDraft(draft); }, }, 'closed'); @@ -786,12 +795,14 @@ async function applyDraft(draft: noteDrafts.NoteDraft, native = false) { case 'quote': { await os.apiWithDialog('notes/show', { noteId: draft.auxId as string }).then(note => { renote.value = note; + reply.value = undefined; }); break; } case 'reply': { await os.apiWithDialog('notes/show', { noteId: draft.auxId as string }).then(note => { reply.value = note; + renote.value = undefined; }); break; } From 9ee4c3d0a82a452f59992a60510d432610033d78 Mon Sep 17 00:00:00 2001 From: yukineko <27853966+hideki0403@users.noreply.github.com> Date: Thu, 18 Apr 2024 19:42:34 +0900 Subject: [PATCH 472/501] =?UTF-8?q?add:=20=E7=BF=BB=E8=A8=B3=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 26 ++++++++++++++++++++++++++ locales/ja-JP.yml | 21 +++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/locales/index.d.ts b/locales/index.d.ts index af1c2479ea..e574e133e6 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -5192,6 +5192,22 @@ export interface Locale extends ILocale { * お問い合わせ */ "inquiry": string; + /** + * 下書き + */ + "drafts": string; + /** + * 下書きの保存に関する動作 + */ + "draftSavingBehavior": string; + /** + * 下書きとして保存 + */ + "saveAsDraft": string; + /** + * 下書きを適用すると現在入力されている内容はリセットされます。よろしいですか? + */ + "draftOverwriteConfirm": string; "_bubbleGame": { /** * 遊び方 @@ -10268,6 +10284,16 @@ export interface Locale extends ILocale { */ "loop": string; }; + "_draftSavingBehavior": { + /** + * 自動的に保存する + */ + "auto": string; + /** + * 都度確認する + */ + "manual": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 1f44f8e40c..21fb6fa105 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1294,6 +1294,23 @@ keepOriginalFilenameDescription: "この設定をオフにすると、アップ noDescription: "説明文はありません" alwaysConfirmFollow: "フォローの際常に確認する" inquiry: "お問い合わせ" +scheduledNoteDelete: "ノートの自己消滅" +noteDeletationAt: "このノートは{time}に消滅します" +cannotScheduleLaterThanOneYear: "1年以上先の日時を指定することはできません" +hideActivity: "アクティビティを非公開にする" +hideActivityDescription: "自分のプロフィールのアクティビティ (概要/アクティビティタブ) を他人が見れないようにします。このオプションを有効にしても、自分であればプロフィールのアクティビティタブから引き続き閲覧できます。" +channelAnnouncementDescription: "このお知らせはチャンネルのタイムライン上部に表示されます。最初の1行がタイトルとして表示され、2行目以降はお知らせをタップすることで表示されるようになります。" +postForm: "投稿フォーム" +postFormBottomSettingsDescription: "投稿フォームの下部に表示される項目の並び替えが出来ます。項目をクリックすると削除できます。" +clearPost: "投稿フォームをリセット" +addToEmojiPicker: "絵文字ピッカーに追加" +hideReactionCount: "リアクション数の非表示" +hideReactionUsers: "誰がリアクションをしたのかを非表示にする" +hideReactionUsersDescription: "リアクションをホバーした際のユーザー一覧と、ノート詳細ページのリアクションタブにあるリアクションをしたユーザー一覧を非表示にします" +drafts: "下書き" +draftSavingBehavior: "下書きの保存に関する動作" +saveAsDraft: "下書きとして保存" +draftOverwriteConfirm: "下書きを適用すると現在入力されている内容はリセットされます。よろしいですか?" _bubbleGame: howToPlay: "遊び方" @@ -2732,3 +2749,7 @@ _mediaControls: pip: "ピクチャインピクチャ" playbackRate: "再生速度" loop: "ループ再生" + +_draftSavingBehavior: + auto: "自動的に保存する" + manual: "都度確認する" From be31cedd986cb6857f49cdf5ed623dde05f7b691 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 23:14:11 +0900 Subject: [PATCH 473/501] update --- locales/index.d.ts | 56 +++++++++++++++++ locales/ja-JP.yml | 2 + .../frontend/src/components/MkPostForm.vue | 62 ++++++++++++++++--- .../frontend/src/pages/settings/general.vue | 13 ++++ 4 files changed, 124 insertions(+), 9 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index e574e133e6..464d6946c4 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -60,6 +60,10 @@ export interface Locale extends ILocale { * OK */ "ok": string; + /** + * ノートの投稿フォームを開き直した際に、下書きを復元しないようにします。 + */ + "disableNoteDraftingDescription": string; /** * このプロファイルをデフォルトにしますか? */ @@ -5192,6 +5196,58 @@ export interface Locale extends ILocale { * お問い合わせ */ "inquiry": string; + /** + * ノートの自己消滅 + */ + "scheduledNoteDelete": string; + /** + * このノートは{time}に消滅します + */ + "noteDeletationAt": ParameterizedString<"time">; + /** + * 1年以上先の日時を指定することはできません + */ + "cannotScheduleLaterThanOneYear": string; + /** + * アクティビティを非公開にする + */ + "hideActivity": string; + /** + * 自分のプロフィールのアクティビティ (概要/アクティビティタブ) を他人が見れないようにします。このオプションを有効にしても、自分であればプロフィールのアクティビティタブから引き続き閲覧できます。 + */ + "hideActivityDescription": string; + /** + * このお知らせはチャンネルのタイムライン上部に表示されます。最初の1行がタイトルとして表示され、2行目以降はお知らせをタップすることで表示されるようになります。 + */ + "channelAnnouncementDescription": string; + /** + * 投稿フォーム + */ + "postForm": string; + /** + * 投稿フォームの下部に表示される項目の並び替えが出来ます。項目をクリックすると削除できます。 + */ + "postFormBottomSettingsDescription": string; + /** + * 投稿フォームをリセット + */ + "clearPost": string; + /** + * 絵文字ピッカーに追加 + */ + "addToEmojiPicker": string; + /** + * リアクション数の非表示 + */ + "hideReactionCount": string; + /** + * 誰がリアクションをしたのかを非表示にする + */ + "hideReactionUsers": string; + /** + * リアクションをホバーした際のユーザー一覧と、ノート詳細ページのリアクションタブにあるリアクションをしたユーザー一覧を非表示にします + */ + "hideReactionUsersDescription": string; /** * 下書き */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 21fb6fa105..72493a9851 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -11,6 +11,8 @@ password: "パスワード" forgotPassword: "パスワードを忘れた" fetchingAsApObject: "連合に照会中" ok: "OK" + +disableNoteDraftingDescription: "ノートの投稿フォームを開き直した際に、下書きを復元しないようにします。" setDefaultProfileConfirm: "このプロファイルをデフォルトにしますか?" emojiPickerProfile: "絵文字ピッカーのプロファイル" notificationIndicator: "通知のインジケーターの数字を表示する" diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 5379cf7238..64219e2d4b 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -79,9 +79,16 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <footer :class="$style.footer"> <div :class="$style.footerLeft"> - <template v-for="item in defaultStore.state.postFormActions"> - <button v-if="!bottomItemActionDef[item].hide" :key="item" v-tooltip="bottomItemDef[item].title" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: bottomItemActionDef[item].active }]" v-on="bottomItemActionDef[item].action ? { click: bottomItemActionDef[item].action } : {}"><i class="ti" :class="bottomItemDef[item].icon"></i></button> - </template> + <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> + <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> + <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> + <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> + <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> + <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> + <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> + <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button> + <button v-tooltip="i18n.ts.ruby" :class="['_button', $style.footerButton]" @click="insertRuby"><i class="ti ti-abc"></i></button> + <button v-tooltip="i18n.ts.saveAsDraft" class="_button" :class="$style.footerButton" @click="saveDraft(false)"><i class="ti ti-device-floppy"></i></button> </div> <div :class="$style.footerRight"> <button v-tooltip="i18n.ts.previewNoteText" class="_button" :class="[$style.footerButton, { [$style.previewButtonActive]: showPreview }]" @click="showPreview = !showPreview"><i class="ti ti-eye"></i></button> @@ -274,11 +281,7 @@ const canPost = computed((): boolean => { const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags')); const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags')); -const bottomItemActionDef: Record<keyof typeof bottomItemDef, { - hide?: boolean; - active?: any; - action?: any; -}> = reactive({ +const bottomItemActionDef = ref({ attachFile: { action: chooseFileFrom, }, @@ -319,6 +322,48 @@ const bottomItemActionDef: Record<keyof typeof bottomItemDef, { watch(text, () => { checkMissingMention(); }, { immediate: true }); +const bottomItemDef = { + attachFile: { + title: i18n.ts.attachFile, + icon: 'ti-photo-plus', + }, + poll: { + title: i18n.ts.poll, + icon: 'ti-chart-arrows', + }, + useCw: { + title: i18n.ts.useCw, + icon: 'ti-eye-off', + }, + mention: { + title: i18n.ts.mention, + icon: 'ti-at', + }, + hashtags: { + title: i18n.ts.hashtags, + icon: 'ti-hash', + }, + plugins: { + title: i18n.ts.plugins, + icon: 'ti-plug', + }, + emoji: { + title: i18n.ts.emoji, + icon: 'ti-mood-happy', + }, + addMfmFunction: { + title: i18n.ts.addMfmFunction, + icon: 'ti-palette', + }, + clearPost: { + title: i18n.ts.clearPost, + icon: 'ti-trash', + }, + saveAsDraft: { + title: i18n.ts.saveAsDraft, + icon: 'ti-note', + }, +}; watch(visibility, () => { switch (visibility.value) { @@ -944,7 +989,6 @@ async function post(ev?: MouseEvent) { claimAchievement('notes1'); } - if (postData.schedule?.scheduledAt) { const d = new Date(postData.schedule.scheduledAt); const str = dateTimeFormat.format(d); diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 0ae5b7590f..224dd2c2db 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -38,6 +38,16 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="showFixedPostFormInChannel">{{ i18n.ts.showFixedPostFormInChannel }}</MkSwitch> <MkSwitch v-model="alwaysShowPlayer">Youtube.comや、nicovideo.jpのプレイヤーを全て開いた状態にする</MkSwitch> <MkSwitch v-model="alwaysExpandTweet">Xのポストを常時表示させる</MkSwitch> + <MkSelect v-model="draftSavingBehavior"> + <template #label>{{ i18n.ts.draftSavingBehavior }}<span class="_beta">{{ i18n.ts.originalFeature }}</span></template> + <option value="auto">{{ i18n.ts._draftSavingBehavior.auto }}</option> + <option value="manual">{{ i18n.ts._draftSavingBehavior.manual }}</option> + </MkSelect> + <MkSwitch v-model="disableNoteDrafting"> + <template #caption>{{ i18n.ts.disableNoteDraftingDescription }}</template> + {{ i18n.ts.disableNoteDrafting }} + <span class="_beta">{{ i18n.ts.originalFeature }}</span> + </MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> <div v-for="pinnedLists in defaultStore.reactiveState.pinnedUserLists.value" class="_margin"> @@ -436,6 +446,9 @@ const nsfw = computed(defaultStore.makeGetterSetter('nsfw')); const showFixedPostForm = computed(defaultStore.makeGetterSetter('showFixedPostForm')); const showFixedPostFormInChannel = computed(defaultStore.makeGetterSetter('showFixedPostFormInChannel')); const alwaysShowPlayer = computed(defaultStore.makeGetterSetter('alwaysShowPlayer')); + +const disableNoteDrafting = computed(defaultStore.makeGetterSetter('disableNoteDrafting')); +const draftSavingBehavior = computed(defaultStore.makeGetterSetter('draftSavingBehavior')); const alwaysExpandTweet = computed(defaultStore.makeGetterSetter('alwaysExpandTweet')); const numberOfPageCache = computed(defaultStore.makeGetterSetter('numberOfPageCache')); const numberOfGamingSpeed = computed(defaultStore.makeGetterSetter('numberOfGamingSpeed')); From b05df30660fbc1e998b04e97e5548ee64b922de0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 23:17:01 +0900 Subject: [PATCH 474/501] update --- packages/frontend/src/store.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index c20c945aef..b03c3d0c96 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -117,14 +117,6 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: false, }, - defaultNoteVisibility: { - where: 'account', - default: 'home', - }, - defaultNoteLocalOnly: { - where: 'account', - default: false, - }, uploadFolder: { where: 'account', default: null as string | null, From 64984b1984529b7735bad6d21f03b264b32d52f0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 23:20:59 +0900 Subject: [PATCH 475/501] update --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d64b476527..ac26d1a0e1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea6", + "version": "2024.5.0-beta1-mattyatea7", "codename": "nasubi", "repository": { "type": "git", From 3f04b5bf8f0b36fe03a1d745c0d1ee591c55846c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 17 May 2024 23:34:14 +0900 Subject: [PATCH 476/501] update --- locales/index.d.ts | 2 ++ locales/ja-JP.yml | 2 ++ package.json | 2 +- packages/frontend/src/pages/settings/general.vue | 2 +- 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 464d6946c4..18a3186081 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2604,6 +2604,8 @@ export interface Locale extends ILocale { * 内容を隠す */ "useCw": string; + "disableNoteDrafting":string; + "kakuregaFeature": string; /** * プレイヤーを開く */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 72493a9851..0319cf11be 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -19,6 +19,8 @@ notificationIndicator: "通知のインジケーターの数字を表示する" hanntenn: "アイコンとバナーを反転させる" hanntennInfo: "ダークだったらライトのアイコンに、ライトだったらダークのアイコンに。" ruby: "ルビ" +disableNoteDrafting: "ノートの下書きの復元を無効化" +kakuregaFeature: "隠れ家" pinnedChannel: "ピン留めされたチャンネル" gotIt: "わかった" cancel: "キャンセル" diff --git a/package.json b/package.json index ac26d1a0e1..22ae2a628c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-beta1-mattyatea7", + "version": "2024.5.0-beta1-mattyatea8", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index 224dd2c2db..a13e06cc83 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="disableNoteDrafting"> <template #caption>{{ i18n.ts.disableNoteDraftingDescription }}</template> {{ i18n.ts.disableNoteDrafting }} - <span class="_beta">{{ i18n.ts.originalFeature }}</span> + <span class="_beta">{{ i18n.ts.kakuregaFeature }}</span> </MkSwitch> <MkFolder> <template #label>{{ i18n.ts.pinnedList }}</template> From 782ff23e9c1910727494e3854a65695bdd472434 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 18 May 2024 01:14:00 +0900 Subject: [PATCH 477/501] update --- locales/index.d.ts | 10 ++++++-- .../backend/src/server/api/endpoints/i.ts | 11 ++++++++- .../frontend/src/components/MkPostForm.vue | 24 ++++++++++++++++--- packages/frontend/src/store.ts | 5 +++- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/locales/index.d.ts b/locales/index.d.ts index 18a3186081..bea61a6d6b 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -88,6 +88,14 @@ export interface Locale extends ILocale { * ルビ */ "ruby": string; + /** + * ノートの下書きの復元を無効化 + */ + "disableNoteDrafting": string; + /** + * 隠れ家 + */ + "kakuregaFeature": string; /** * ピン留めされたチャンネル */ @@ -2604,8 +2612,6 @@ export interface Locale extends ILocale { * 内容を隠す */ "useCw": string; - "disableNoteDrafting":string; - "kakuregaFeature": string; /** * プレイヤーを開く */ diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 6f36acb0b8..2b7a1d30fe 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -55,6 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- const now = new Date(); const today = `${now.getFullYear()}/${now.getMonth() + 1}/${now.getDate()}`; + let todayGetPoints = 0; // 渡ってきている user はキャッシュされていて古い可能性があるので改めて取得 const userProfile = await this.userProfilesRepository.findOne({ @@ -68,8 +69,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- throw new ApiError(meta.errors.userIsDeleted); } + function generateSecureRandomNumber(min, max) { + const range = max - min + 1; + const randomBuffer = new Uint32Array(1); + crypto.getRandomValues(randomBuffer); + const randomNumber = randomBuffer[0] / (0xFFFFFFFF + 1); // 0から1未満の浮動小数点数 + return Math.floor(randomNumber * range) + min; + } + if (!userProfile.loggedInDates.includes(today)) { - todayGetPoints = Math.floor(Math.random() * 5) + 1; + todayGetPoints = generateSecureRandomNumber(1, 5); this.userProfilesRepository.update({ userId: user.id }, { loggedInDates: [...userProfile.loggedInDates, today], }); diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index 64219e2d4b..5b3d0720b8 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -42,8 +42,8 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.submitInner ,{ [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' }]"> <template v-if="posted"></template> <template v-else-if="posting"><MkEllipsis/></template> - <template v-else>{{ submitText }}</template> - <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : schedule ? 'ti ti-clock-hour-4' : 'ti ti-send'"></i> + <template v-else-if="screenWidth >= 355">{{ submitText }}</template> + <i :class="[posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : schedule ? 'ti ti-clock-hour-4' : 'ti ti-send',$style.mgnlft]"></i> </div> </button> </div> @@ -1161,7 +1161,12 @@ function openOtherSettingsMenu(ev: MouseEvent) { }); } +const screenWidth = ref(0); onMounted(() => { + screenWidth.value = window.innerWidth; + window.addEventListener('resize', () => { + screenWidth.value = window.innerWidth; + }); if (props.autofocus) { focus(); @@ -1310,7 +1315,20 @@ defineExpose({ border-radius: 999px; pointer-events: none; } - +.submitInner i::after { + content: ""; +} +@media (width < 355px) { + .submitInner { + min-width: 20px !important; + } + .mgnlft{ + margin-left: 0 !important; + } +} +.mgnlft{ + margin-left: 6px; +} .submitInner { padding: 0 12px; line-height: 34px; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index b03c3d0c96..7fb428802c 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -125,7 +125,10 @@ export const defaultStore = markRaw(new Storage('base', { where: 'account', default: 'yyyy-MM-dd HH-mm-ss [{{number}}]', }, - + disableNoteDrafting: { + where: 'account', + default: false, + }, keepOriginalUploading: { where: 'account', default: false, From b21bd116de4879314961621fa7743cb182ab7a97 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 21 May 2024 20:26:38 +0900 Subject: [PATCH 478/501] update --- packages/backend/package.json | 2 + packages/backend/src/core/MfmService.ts | 7 +- .../frontend/src/pages/settings/general.vue | 2 +- pnpm-lock.yaml | 2487 +---------------- 4 files changed, 125 insertions(+), 2373 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index 597238c98f..b19829e649 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -179,6 +179,7 @@ "typescript": "5.4.5", "ulid": "2.3.0", "vary": "1.1.2", + "w3c-xmlserializer": "^5.0.0", "web-push": "3.6.7", "ws": "8.17.0", "xev": "3.0.2" @@ -225,6 +226,7 @@ "@types/tinycolor2": "1.4.6", "@types/tmp": "0.2.6", "@types/vary": "1.1.3", + "@types/w3c-xmlserializer": "^2.0.4", "@types/web-push": "3.6.3", "@types/ws": "8.5.10", "@typescript-eslint/eslint-plugin": "7.7.1", diff --git a/packages/backend/src/core/MfmService.ts b/packages/backend/src/core/MfmService.ts index 9786f8b8bb..cf30a3ddf1 100644 --- a/packages/backend/src/core/MfmService.ts +++ b/packages/backend/src/core/MfmService.ts @@ -6,7 +6,8 @@ import { URL } from 'node:url'; import { Inject, Injectable } from '@nestjs/common'; import * as parse5 from 'parse5'; -import { Window, XMLSerializer } from 'happy-dom'; +import { JSDOM } from 'jsdom'; +import serialize from 'w3c-xmlserializer'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; import { intersperse } from '@/misc/prelude/array.js'; @@ -243,7 +244,7 @@ export class MfmService { return null; } - const { window } = new Window(); + const { window } = new JSDOM() as unknown as { window: Window }; const doc = window.document; @@ -461,6 +462,6 @@ export class MfmService { appendChildren(nodes, body); - return new XMLSerializer().serializeToString(body); + return serialize(body); } } diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index a13e06cc83..4c09d26f51 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSwitch v-model="alwaysShowPlayer">Youtube.comや、nicovideo.jpのプレイヤーを全て開いた状態にする</MkSwitch> <MkSwitch v-model="alwaysExpandTweet">Xのポストを常時表示させる</MkSwitch> <MkSelect v-model="draftSavingBehavior"> - <template #label>{{ i18n.ts.draftSavingBehavior }}<span class="_beta">{{ i18n.ts.originalFeature }}</span></template> + <template #label>{{ i18n.ts.draftSavingBehavior }}<span class="_beta">{{ i18n.ts.kakuregaFeature }}</span></template> <option value="auto">{{ i18n.ts._draftSavingBehavior.auto }}</option> <option value="manual">{{ i18n.ts._draftSavingBehavior.manual }}</option> </MkSelect> diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10c066c91f..0516a8cc01 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -178,7 +178,7 @@ importers: version: 2.0.5 body-parser: specifier: 1.20.2 - version: 1.20.2(supports-color@3.2.3) + version: 1.20.2 bullmq: specifier: 5.7.8 version: 5.7.8 @@ -419,6 +419,9 @@ importers: vary: specifier: 1.1.2 version: 1.1.2 + w3c-xmlserializer: + specifier: ^5.0.0 + version: 5.0.0 web-push: specifier: 3.6.7 version: 3.6.7 @@ -640,6 +643,9 @@ importers: '@types/vary': specifier: 1.1.3 version: 1.1.3 + '@types/w3c-xmlserializer': + specifier: ^2.0.4 + version: 2.0.4 '@types/web-push': specifier: 3.6.3 version: 3.6.3 @@ -688,9 +694,6 @@ importers: packages/frontend: dependencies: - '@caed0/webp-conv': - specifier: ^1.1.0 - version: 1.1.0(encoding@0.1.13) '@discordapp/twemoji': specifier: 15.0.3 version: 15.0.3 @@ -733,9 +736,6 @@ importers: aiscript-vscode: specifier: github:aiscript-dev/aiscript-vscode#v0.1.4 version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424 - apng-js: - specifier: ^1.1.1 - version: 1.1.1 astring: specifier: 1.8.6 version: 1.8.6 @@ -784,15 +784,6 @@ importers: eventemitter3: specifier: 5.0.1 version: 5.0.1 - gif-frames: - specifier: ^1.0.1 - version: 1.0.1 - gifshot: - specifier: ^0.4.5 - version: 0.4.5 - gifuct-js: - specifier: ^2.1.2 - version: 2.1.2 idb-keyval: specifier: 6.2.1 version: 6.2.1 @@ -805,9 +796,6 @@ importers: json5: specifier: 2.2.3 version: 2.2.3 - libwebpjs: - specifier: ^0.0.1 - version: 0.0.1 matter-js: specifier: 0.19.0 version: 0.19.0 @@ -883,12 +871,6 @@ importers: wavesurfer.js: specifier: ^7.7.14 version: 7.7.14 - webm-wasm: - specifier: ^0.4.1 - version: 0.4.1 - webp-hero: - specifier: ^0.0.2 - version: 0.0.2 devDependencies: '@misskey-dev/eslint-plugin': specifier: 1.0.0 @@ -2171,9 +2153,6 @@ packages: '@bundled-es-modules/statuses@1.0.1': resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} - '@caed0/webp-conv@1.1.0': - resolution: {integrity: sha512-R+zJgRB9UUVQODBF/NQsjjEZZDa7ZoMv4Z042xNarXPod0zAaS2kl95i3kL6tO68MXCvAhscxAxK+MFRY5raIg==} - '@canvas/image-data@1.0.0': resolution: {integrity: sha512-BxOqI5LgsIQP1odU5KMwV9yoijleOPzHL18/YvNqF9KFSGF2K/DLlYAbDQsWqd/1nbaFuSkYD/191dpMtNh4vw==} @@ -4588,6 +4567,9 @@ packages: '@types/vary@1.1.3': resolution: {integrity: sha512-XJT8/ZQCL7NUut9QDLf6l24JfAEl7bnNdgxfj50cHIpEPRJLHHDDFOAq6i+GsEmeFfH7NamhBE4c4Thtb2egWg==} + '@types/w3c-xmlserializer@2.0.4': + resolution: {integrity: sha512-A37kb4IAiBxTnsQ+guO2p4WMAJx5+n6kmvmosQQyVn8OwmyNwcxlP72PQVFP9yumQIfqgvLNH4vzp2vKLkGcJA==} + '@types/web-push@3.6.3': resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==} @@ -4952,11 +4934,6 @@ packages: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} - acorn@3.3.0: - resolution: {integrity: sha512-OLUyIIZ7mF5oaAUT1w0TFqQS81q3saT46x8t7ukpPjMNk+nbs4ZHhs7ToV8EWnLYLepjETXd4XaCE4uxkMeqUw==} - engines: {node: '>=0.4.0'} - hasBin: true - acorn@7.4.1: resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} engines: {node: '>=0.4.0'} @@ -5022,21 +4999,6 @@ packages: ajv@8.13.0: resolution: {integrity: sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==} - align-text@0.1.4: - resolution: {integrity: sha512-GrTZLRpmp6wIC2ztrWW9MjjTgSKccffgFagbNDOX95/dcjEcYZibYTeaOntySQLcdw1ztBoFkviiUvTMbb9MYg==} - engines: {node: '>=0.10.0'} - - almond@0.2.5: - resolution: {integrity: sha512-Y37372p4xHRrlqPIERtDWE+W3gjDnLvMCTZwo2R5q/brF/QIvfC48ABE0uUrbxn6LPeY2FnlnjFIsWZVFwA6gA==} - engines: {node: '>=0.4.0'} - - alphanum-sort@1.0.2: - resolution: {integrity: sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ==} - - amdefine@1.0.1: - resolution: {integrity: sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==} - engines: {node: '>=0.4.2'} - ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} @@ -5045,10 +5007,6 @@ packages: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} - ansi-regex@2.1.1: - resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} - engines: {node: '>=0.10.0'} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -5057,10 +5015,6 @@ packages: resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} engines: {node: '>=12'} - ansi-styles@2.2.1: - resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} - engines: {node: '>=0.10.0'} - ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} @@ -5084,9 +5038,6 @@ packages: resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} engines: {node: '>= 8'} - apng-js@1.1.1: - resolution: {integrity: sha512-UWaloDssWCE8Bj0wipyNxEXPnMadYS0VAjghCLas5nKGqfiBMNdQJhg8Fawq2+jZ50IOM1feKwjiqPAC/bvKgg==} - app-root-dir@1.0.2: resolution: {integrity: sha512-jlpIfsOoNoafl92Sz//64uQHGSyMrD2vYG5d8o2a4qGvyNCvXur7bzIsWtAC/6flI2RYAp3kv8rsfBtaLm7w0g==} @@ -5130,14 +5081,6 @@ packages: aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} - arr-diff@2.0.0: - resolution: {integrity: sha512-dtXTVMkh6VkEEA7OhXnN1Ecb8aAGFdZ1LFxtOCoqj4qkyOJMt7+qs6Ahdy6p/NQCPYsRSXXivhSB/J5E9jmYKA==} - engines: {node: '>=0.10.0'} - - arr-flatten@1.1.0: - resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} - engines: {node: '>=0.10.0'} - array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -5152,10 +5095,6 @@ packages: resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} engines: {node: '>=8'} - array-unique@0.2.1: - resolution: {integrity: sha512-G2n5bG5fSUCpnsXz4+8FUkYsGPkNfLn9YvS66U5qbTIXI2Ynnlo4Bi42bWv+omKUCqz+ejzfClwne0alJWJPhg==} - engines: {node: '>=0.10.0'} - array.prototype.findlastindex@1.2.3: resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} engines: {node: '>= 0.4'} @@ -5196,9 +5135,6 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - assert@1.5.1: - resolution: {integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==} - assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} @@ -5220,18 +5156,6 @@ packages: async-mutex@0.5.0: resolution: {integrity: sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==} - async@0.1.22: - resolution: {integrity: sha512-2tEzliJmf5fHNafNwQLJXUasGzQCVctvsNkXmnlELHwypU0p08/rHohYvkqKIjyXpx+0rkrYv6QbhJ+UF4QkBg==} - - async@0.2.10: - resolution: {integrity: sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==} - - async@0.9.2: - resolution: {integrity: sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==} - - async@1.5.2: - resolution: {integrity: sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==} - async@3.2.4: resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==} @@ -5246,9 +5170,6 @@ packages: resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==} engines: {node: '>=8.0.0'} - autoprefixer@6.7.7: - resolution: {integrity: sha512-WKExI/eSGgGAkWAO+wMVdFObZV7hQen54UpD1kCCTN3tvlL3W1jL4+lPP/M7MwoP7Q4RHzKtO3JQ4HxYEcd+xQ==} - available-typed-arrays@1.0.5: resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} engines: {node: '>= 0.4'} @@ -5274,9 +5195,6 @@ packages: b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} - babel-code-frame@6.26.0: - resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} - babel-core@7.0.0-bridge.0: resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} peerDependencies: @@ -5329,18 +5247,12 @@ packages: bail@2.0.2: resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} - balanced-match@0.4.2: - resolution: {integrity: sha512-STw03mQKnGUYtoNjmowo4F2cRmIIxYEGiMsjjwla/u5P1lxadj/05WkNaFjNiKTgJkj8KiXbgAiRTmcQRwQNtg==} - balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - batch@0.6.1: - resolution: {integrity: sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==} - bcrypt-pbkdf@1.0.2: resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==} @@ -5355,12 +5267,6 @@ packages: resolution: {integrity: sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==} engines: {node: '>=0.6'} - big.js@3.2.0: - resolution: {integrity: sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==} - - big.js@5.2.2: - resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} - bin-check@4.1.0: resolution: {integrity: sha512-b6weQyEUKsDGFlACWSIOfveEnImkJyK/FGW6FAG42loyoquvjdtOIqO6yBFzHyqyVVhNgNkQxxx09SFLK28YnA==} engines: {node: '>=4'} @@ -5383,10 +5289,6 @@ packages: blob-util@2.0.2: resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} - block-stream@0.0.9: - resolution: {integrity: sha512-OorbnJVPII4DuUKbjARAe8u8EfqOmkEEaSFIyoQ7OjTHn6kafxWl0wLgoZ2rXaYd7MyLcDaU4TmhfxtwgcccMQ==} - engines: {node: 0.4 || >=0.5.8} - bluebird@3.7.2: resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} @@ -5420,10 +5322,6 @@ packages: brace-expansion@2.0.1: resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} - braces@1.8.5: - resolution: {integrity: sha512-xU7bpz2ytJl1bH9cgIurjpg/n8Gohy9GTw81heDYLJQ4RU60dlyJsa+atVF2pI0yMMvKxI9HkKwjePCj5XI1hw==} - engines: {node: '>=0.10.0'} - braces@3.0.2: resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} engines: {node: '>=8'} @@ -5434,17 +5332,9 @@ packages: browser-assert@1.2.1: resolution: {integrity: sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==} - browserify-aes@0.4.0: - resolution: {integrity: sha512-hnvbMhZ/Ete34qnoKKyjikiYQfZbl89d5UZ29cz3EG13cv/8VRyM8Zs84luB/O7BRzC3qSng9dVovJ6jghcAvg==} - browserify-zlib@0.1.4: resolution: {integrity: sha512-19OEpq7vWgsH6WkvkBJQDFvJS1uPcbFOQ4v9CU839dO+ZZXUZO6XpE6hNCqvlIIj+4fZvRiJ6DsAQ382GwiyTQ==} - browserslist@1.7.7: - resolution: {integrity: sha512-qHJblDE2bXVRYzuDetv/wAeHOJyO97+9wxC1cdCtyzgNuSozOyRCiiLaCR1f71AN66lQdVVBipWm63V+a7bPOw==} - deprecated: Browserslist 2 could fail on reading Browserslist >3.0 config used in other tools. - hasBin: true - browserslist@4.22.2: resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -5471,9 +5361,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@4.9.2: - resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} - buffer@5.6.0: resolution: {integrity: sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==} @@ -5487,9 +5374,6 @@ packages: resolution: {integrity: sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==} engines: {node: '>=6.14.2'} - builtin-status-codes@3.0.0: - resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} - bullmq@5.7.8: resolution: {integrity: sha512-F/Haeu6AVHkFrfeaU/kLOjhfrH6x3CaKAZlQQ+76fa8l3kfI9oaUHeFMW+1mYVz0NtYPF7PNTWFq4ylAHYcCgA==} @@ -5539,10 +5423,6 @@ packages: call-bind@1.0.2: resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} - call-bind@1.0.7: - resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} - engines: {node: '>= 0.4'} - call-me-maybe@1.0.2: resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==} @@ -5554,10 +5434,6 @@ packages: resolution: {integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==} engines: {node: '>=8'} - camelcase@1.2.1: - resolution: {integrity: sha512-wzLkDa4K/mzI1OSITC+DUyjgIl/ETNHE9QvYgy6J6Jvqyyz4C0Xfd+lQhb19sX2jMpZV4IssUn0VDVmglV+s4g==} - engines: {node: '>=0.10.0'} - camelcase@5.3.1: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} @@ -5566,15 +5442,9 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-api@1.6.1: - resolution: {integrity: sha512-SBTl70K0PkDUIebbkXrxWqZlHNs0wRgRD6QZ8guctShjbh63gEPfF+Wj0Yw+75f5Y8tSzqAI/NcisYv/cCah2Q==} - caniuse-api@3.0.0: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} - caniuse-db@1.0.30001620: - resolution: {integrity: sha512-dYQIgCcUpy2l/IfiEA6xgNHCgr5jmDWF4i89MRv6DqCiEt4MNJguYsVeVZSxyqWfb8GfhWEZEMkjI3vhIYRrvw==} - caniuse-lite@1.0.30001566: resolution: {integrity: sha512-ggIhCsTxmITBAMmK8yZjEhCO5/47jKXPu6Dha/wuCS4JePVL+3uiDEBuhu2aIoT+bqTOR8L76Ip1ARL9xYsEJA==} @@ -5601,10 +5471,6 @@ packages: ccount@2.0.1: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} - center-align@0.1.3: - resolution: {integrity: sha512-Baz3aNe2gd2LP2qk5U+sDk/m4oSuwSDcBfayTCTBoWpfIGO5XFxPmjILQII4NGiZjD6DoDI6kf7gKaxkf7s3VQ==} - engines: {node: '>=0.10.0'} - chai@4.3.10: resolution: {integrity: sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==} engines: {node: '>=4'} @@ -5613,10 +5479,6 @@ packages: resolution: {integrity: sha512-T2VJbcDuZQ0Tb2EWwSotMPJjgpy1/tGee1BTpUNsGZ/qgNjV2t7Mvu+d4600U564nbLesN1x2dPL+xii174Ekg==} engines: {node: '>=14.16'} - chalk@1.1.3: - resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} - engines: {node: '>=0.10.0'} - chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -5712,10 +5574,6 @@ packages: cjs-module-lexer@1.2.2: resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==} - clap@1.2.3: - resolution: {integrity: sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==} - engines: {node: '>=0.10.0'} - clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} @@ -5753,9 +5611,6 @@ packages: resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} engines: {node: '>= 12'} - cliui@2.1.0: - resolution: {integrity: sha512-GIOYRizG+TGoc7Wgc1LiOTLare95R3mzKgoln+Q/lE4ceiYH19gUpl0l0Ffq4lJDEf3FxujMe6IBfOCs7pfqNA==} - cliui@6.0.0: resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} @@ -5785,10 +5640,6 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - coa@1.0.4: - resolution: {integrity: sha512-KAGck/eNAmCL0dcT3BiuYwLbExK6lduR8DxM3C1TyDzaXhZHyZ8ooX5I5+na2e3dPFuibfxrGdorr0/Lr7RYCQ==} - engines: {node: '>= 0.8.0'} - code-error-fragment@0.0.230: resolution: {integrity: sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==} engines: {node: '>= 4'} @@ -5809,9 +5660,6 @@ packages: color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - color-string@0.3.0: - resolution: {integrity: sha512-sz29j1bmSDfoAxKIEU6zwoIZXN6BrFbAMIhfYCNyiZXBDuU/aiHlN84lp/xDzL2ubyFhLDobHIlU1X70XRrMDA==} - color-string@1.9.1: resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} @@ -5819,9 +5667,6 @@ packages: resolution: {integrity: sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==} hasBin: true - color@0.11.4: - resolution: {integrity: sha512-Ajpjd8asqZ6EdxQeqGzU5WBhhTfJ/0cA4Wlbre7e5vXfmDSmda7Ov6jeKoru+b0vHcb1CqvuroTHp5zIWzhVMA==} - color@4.2.3: resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} engines: {node: '>=12.5.0'} @@ -5832,17 +5677,6 @@ packages: colorette@2.0.19: resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==} - colormin@1.1.2: - resolution: {integrity: sha512-XSEQUUQUR/lXqGyddiNH3XYFUPYlYr1vXy9rTFMsSOw+J7Q6EQkdlQIrTlYn4TccpsOaUE1PYQNjBn20gwCdgQ==} - - colors@0.6.2: - resolution: {integrity: sha512-OsSVtHK8Ir8r3+Fxw/b4jS1ZLPXkV6ZxDRJQzeD7qo0SqMXWrHDM71DgYzPMHY8SFJ0Ao+nNU2p1MmwdzKqPrw==} - engines: {node: '>=0.1.90'} - - colors@1.1.2: - resolution: {integrity: sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w==} - engines: {node: '>=0.1.90'} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -5905,25 +5739,15 @@ packages: config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} - connect-history-api-fallback@1.6.0: - resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} - engines: {node: '>=0.8'} - consola@2.15.3: resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} - console-browserify@1.2.0: - resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} - console-control-strings@1.1.0: resolution: {integrity: sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==} constantinople@4.0.1: resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} - constants-browserify@1.0.0: - resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} - content-disposition@0.5.4: resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} engines: {node: '>= 0.6'} @@ -5932,10 +5756,6 @@ packages: resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} engines: {node: '>= 0.6'} - contentstream@1.0.0: - resolution: {integrity: sha512-jqWbfFZFG9tZbdej7+TzXI4kanABh3BLtTWY6NxqTK5zo6iTIeo5aq4iRVfYsLQ0y8ccQqmJR/J4NeMmEdnR2w==} - engines: {node: '>= 0.8.0'} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -6009,32 +5829,19 @@ packages: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - crypto-browserify@3.3.0: - resolution: {integrity: sha512-9n5nGl6D8zb29Ui8Ji8pVdUIE3RUe6A9zQf2iLPjFKftnkkkJBCGb7TkYAFNjt9nfsvZTLL52CwxzS9Tw7Bujw==} - crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} - css-color-names@0.0.4: - resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} - css-declaration-sorter@7.2.0: resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.0.9 - css-loader@0.26.4: - resolution: {integrity: sha512-BpErUP1CcAdW8IQ0834qG+5yGMzCRKEXH+HmJ/rXNAfOeOY4GwNDOG16Oil7G7MpeP+1QQ9NGCjzI6MebqO+rA==} - engines: {node: '>=0.12.0 || >=4.3.0 <5.0.0 || >=5.10'} - css-select@5.1.0: resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} - css-selector-tokenizer@0.7.3: - resolution: {integrity: sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg==} - css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -6067,20 +5874,12 @@ packages: peerDependencies: postcss: ^8.4.31 - cssnano@3.10.0: - resolution: {integrity: sha512-0o0IMQE0Ezo4b41Yrm8U6Rp9/Ag81vNXY1gZMnT1XhO4DpjEf2utKERqWJbOoz3g1Wdc1d3QSta/cIuJ1wSTEg==} - cssnano@6.1.2: resolution: {integrity: sha512-rYk5UeX7VAM/u0lNqewCdasdtPK81CgX8wJFLEIXHbV2oldWRgJAsZrdhRXkV1NJzA2g850KiFm9mMU2HxNxMA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - csso@2.3.2: - resolution: {integrity: sha512-FmCI/hmqDeHHLaIQckMhMZneS84yzUZdrWDAvJVVxOwcKE1P1LF9FGmzr1ktIQSxOw6fl3PaQsmfg+GN+VvR3w==} - engines: {node: '>=0.10.0'} - hasBin: true - csso@5.0.5: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} @@ -6095,10 +5894,6 @@ packages: cwise-compiler@1.1.3: resolution: {integrity: sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==} - cycle@1.0.3: - resolution: {integrity: sha512-TVF6svNzeQCOpjCqsy0/CSy8VgObG3wXusJ73xW2GbG5rGx7lC8zxDSURicsXI2UsGdi2L0QNRCi745/wUDvsA==} - engines: {node: '>=0.4.0'} - cypress@13.7.3: resolution: {integrity: sha512-uoecY6FTCAuIEqLUYkTrxamDBjMHTYak/1O7jtgwboHiTnS1NaMOoR08KcTrbRZFCBvYOiS4tEkQRmsV+xcrag==} engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0} @@ -6204,10 +5999,6 @@ packages: resolution: {integrity: sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==} engines: {node: '>=6'} - deep-equal@1.1.2: - resolution: {integrity: sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==} - engines: {node: '>= 0.4'} - deep-equal@2.2.0: resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==} @@ -6229,10 +6020,6 @@ packages: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - define-lazy-prop@2.0.0: resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==} engines: {node: '>=8'} @@ -6241,13 +6028,6 @@ packages: resolution: {integrity: sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA==} engines: {node: '>= 0.4'} - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - defined@1.0.1: - resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} - defu@6.1.4: resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} @@ -6266,10 +6046,6 @@ packages: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} - depd@1.1.2: - resolution: {integrity: sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==} - engines: {node: '>= 0.6'} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -6350,10 +6126,6 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} - domain-browser@1.2.0: - resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} - engines: {node: '>=0.4', npm: '>=1.2'} - domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -6416,14 +6188,6 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - emojis-list@2.1.0: - resolution: {integrity: sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng==} - engines: {node: '>= 0.10'} - - emojis-list@3.0.0: - resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} - engines: {node: '>= 4'} - encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} @@ -6437,10 +6201,6 @@ packages: end-of-stream@1.4.4: resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - enhanced-resolve@0.9.1: - resolution: {integrity: sha512-kxpoMgrdtkXZ5h0SeraBS1iRntpTpQ3R8ussdb38+UAFnMGX5DDyJXePm+OCHOcoXvHDw7mc2erbJBpDnl7TPw==} - engines: {node: '>=0.6'} - enquirer@2.3.6: resolution: {integrity: sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==} engines: {node: '>=8.6'} @@ -6464,10 +6224,6 @@ packages: err-code@2.0.3: resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - errno@0.1.8: - resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} - hasBin: true - error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -6475,14 +6231,6 @@ packages: resolution: {integrity: sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw==} engines: {node: '>= 0.4'} - es-define-property@1.0.0: - resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - es-get-iterator@1.1.3: resolution: {integrity: sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==} @@ -6629,11 +6377,6 @@ packages: resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - esprima@2.7.3: - resolution: {integrity: sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==} - engines: {node: '>=0.10.0'} - hasBin: true - esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -6681,18 +6424,10 @@ packages: eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} - events@1.1.1: - resolution: {integrity: sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==} - engines: {node: '>=0.4.x'} - events@3.3.0: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - eventsource@2.0.2: - resolution: {integrity: sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA==} - engines: {node: '>=12.0.0'} - execa@0.7.0: resolution: {integrity: sha512-RztN09XglpYI7aBBrJCPW95jEH7YF1UEPOoX9yDhUTPdp7mK+CQvnLTuD10BNXZ3byLTu2uehZ8EcKT/4CGiFw==} engines: {node: '>=4'} @@ -6721,14 +6456,6 @@ packages: resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} engines: {node: '>= 0.8.0'} - expand-brackets@0.1.5: - resolution: {integrity: sha512-hxx03P2dJxss6ceIeri9cmYOT4SRs3Zk3afZwWpOsRqLqprhTR8u++SlC+sFGsQr7WGFPdMF7Gjc1njDLDK6UA==} - engines: {node: '>=0.10.0'} - - expand-range@1.8.2: - resolution: {integrity: sha512-AFASGfIlnIbkKPQwX1yHaDjFvh/1gyKJODme52V6IORh69uEYgZp0o9C+qsIGNVEiuuhQU0CSSl++Rlegg1qvA==} - engines: {node: '>=0.10.0'} - expect@29.7.0: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -6755,10 +6482,6 @@ packages: extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} - extglob@0.3.2: - resolution: {integrity: sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg==} - engines: {node: '>=0.10.0'} - extract-zip@2.0.1: resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} engines: {node: '>= 10.17.0'} @@ -6768,10 +6491,6 @@ packages: resolution: {integrity: sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==} engines: {'0': node >=0.6.0} - eyes@0.1.8: - resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==} - engines: {node: '> 0.1.90'} - fast-content-type-parse@1.1.0: resolution: {integrity: sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==} @@ -6788,10 +6507,6 @@ packages: resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} engines: {node: '>=8.6.0'} - fast-json-patch@1.2.2: - resolution: {integrity: sha512-Fp8ZmUPKCH1CzeHWAjTbYfQKkny8+QODP7NqXGkx0Z/4+6A4ommqus22/J2i1hgo1bbD/uGUgr8P6TUZyFsn2g==} - engines: {node: '>= 0.4.0'} - fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} @@ -6828,19 +6543,12 @@ packages: fastify@4.26.2: resolution: {integrity: sha512-90pjTuPGrfVKtdpLeLzND5nyC4woXZN5VadiNQCicj/iJU4viNHKhsAnb7jmv1vu2IzkLXyBiCzdWuzeXgQ5Ug==} - fastparse@1.1.2: - resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==} - fastq@1.15.0: resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} - faye-websocket@0.11.4: - resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} - engines: {node: '>=0.8.0'} - fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} @@ -6880,10 +6588,6 @@ packages: filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - filename-regex@2.0.1: - resolution: {integrity: sha512-BTCqyBaWBTsauvnHiE8i562+EdJj+oUpkqWp2R1iCoR8f6oo8STRu3of7WJJ0TqWtxN50a5YFpzYK4Jj9esYfQ==} - engines: {node: '>=0.10.0'} - filename-reserved-regex@3.0.0: resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -6892,10 +6596,6 @@ packages: resolution: {integrity: sha512-M45CbrJLGACfrPOkrTp3j2EcO9OBkKUYME0eiqOCa7i2poaklU0jhlIaMlr8ijLorT0uLAzrn3qXOp5684CkfA==} engines: {node: '>=12.20'} - fill-range@2.2.4: - resolution: {integrity: sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==} - engines: {node: '>=0.10.0'} - fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -6946,10 +6646,6 @@ packages: flatted@3.2.7: resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} - flatten@1.0.3: - resolution: {integrity: sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg==} - deprecated: flatten is deprecated in favor of utility frameworks such as lodash. - flow-parser@0.202.0: resolution: {integrity: sha512-ZiXxSIXK3zPmY3zrzCofFonM2T+/3Jz5QZKJyPVtUERQEJUnYkXBQ+0H3FzyqiyJs+VXqb/UNU6/K6sziVYdxw==} engines: {node: '>=0.4.0'} @@ -6970,14 +6666,6 @@ packages: for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - for-in@1.0.2: - resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} - engines: {node: '>=0.10.0'} - - for-own@0.1.5: - resolution: {integrity: sha512-SKmowqGTJoPzLO1T0BBJpkfp3EMacCMOuH40hOUbrbzElVktk4DioXVM99QkLCyKoiuOmyjgcWMpVz2xjE7LZw==} - engines: {node: '>=0.10.0'} - foreground-child@3.1.1: resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==} engines: {node: '>=14'} @@ -7058,13 +6746,6 @@ packages: engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] - fstream-ignore@0.0.10: - resolution: {integrity: sha512-x3RZbvYrL8mMyaJrg6Kip+YkljUFnCiafRR94RQY0vBFOjbWTRehlPg3iTqcPauayQxs9WvUByJ1v48o3QSLsQ==} - - fstream@0.1.31: - resolution: {integrity: sha512-N1pLGEHoDyCoI8uMmPXJXhn238L4nk41iipXCrqs4Ss0ooYSr5sNj2ucMo5AqJVC4OaOa7IztpBhOaaYTGZVuA==} - engines: {node: '>=0.6'} - function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -7093,10 +6774,6 @@ packages: get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} - get-intrinsic@1.2.4: - resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} - engines: {node: '>= 0.4'} - get-npm-tarball-url@2.0.3: resolution: {integrity: sha512-R/PW6RqyaBQNWYaSyfrh54/qtcnOp22FHCCiRhSSZj0FP3KQWCsxxt0DzIdVTbwTqe9CtQfvl/FPD4UIPt4pqw==} engines: {node: '>=12.17'} @@ -7137,22 +6814,10 @@ packages: getpass@0.1.7: resolution: {integrity: sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==} - gif-encoder-2@1.0.5: - resolution: {integrity: sha512-fsRAKbZuUoZ7FYGjpFElmflTkKwsn/CzAmL/xDl4558aTAgysIDCUF6AXWO8dmai/ApfZACbPVAM+vPezJXlFg==} - gif-encoder@0.4.1: resolution: {integrity: sha512-++rNGpDBgWQ9eXj9JfTBLHMUEd7lDOdzIvFyHQM9yL8ffxkcg4G6jWmsgu/r59Uq6nHc3wcVwtgy3geLnIWunQ==} engines: {node: '>= 0.8.0'} - gif-frames@1.0.1: - resolution: {integrity: sha512-9ddxnrEbAjdv0R6Ib8DHhd3TIsaulrm55qWpiH1dsVp4X/QPE8FxtK2YvuYrj+y+YhiBHRHRXo5Gei9GzdMS3g==} - - gifshot@0.4.5: - resolution: {integrity: sha512-oaOTT7patjxFFv7ptR0R0NNhqy3ZAmcLUQCjM/sTsvsQaUAlB2fHirLajcNAKJ6ufoVhdP+ZkXYvmUycHP1FNg==} - - gifuct-js@2.1.2: - resolution: {integrity: sha512-rI2asw77u0mGgwhV3qA+OEgYqaDn5UNqgs+Bx0FGwSpuqfYn+Ir6RQY5ENNQ8SbIiG/m5gVa7CD5RriO4f4Lsg==} - giget@1.1.2: resolution: {integrity: sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A==} hasBin: true @@ -7160,13 +6825,6 @@ packages: github-slugger@2.0.0: resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==} - glob-base@0.3.0: - resolution: {integrity: sha512-ab1S1g1EbO7YzauaJLkgLp7DZVAqj9M/dvKlTt8DkXA2tiOIcSMrlVI2J1RZyB5iJVccEscjGn+kpOG9788MHA==} - engines: {node: '>=0.10.0'} - - glob-parent@2.0.0: - resolution: {integrity: sha512-JDYOvfxio/t42HKdxkAYaCiBN7oYiuxykOxKxdaUW5Qn0zaYN3gRQWolrwdnf0shM9/EP0ebuuTmyoXNr1cC5w==} - glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} @@ -7243,15 +6901,6 @@ packages: resolution: {integrity: sha512-KOaPMremmsvx6l9BLC04LYE6ZFW4x7e4HkTe3LwBmtuYYQwpeS4XKqzhubTIkaQ1Nr+eXxeori0zuwupXMovBQ==} engines: {node: '>=20'} - graceful-fs@1.1.14: - resolution: {integrity: sha512-JUrvoFoQbLZpOZilKTXZX2e1EV0DTnuG5vsRFNFv4mPf/mnYbwNAFw/5x0rxeyaJslIdObGSgTTsMnM/acRaVw==} - engines: {node: '>=0.4.0'} - deprecated: please upgrade to graceful-fs 4 for compatibility with current and future versions of Node.js - - graceful-fs@3.0.12: - resolution: {integrity: sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==} - engines: {node: '>=0.4.0'} - graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -7295,17 +6944,9 @@ packages: resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} engines: {node: '>=6'} - has-ansi@2.0.0: - resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} - engines: {node: '>=0.10.0'} - has-bigints@1.0.2: resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} - has-flag@1.0.0: - resolution: {integrity: sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==} - engines: {node: '>=0.10.0'} - has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -7317,9 +6958,6 @@ packages: has-property-descriptors@1.0.0: resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - has-proto@1.0.1: resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} engines: {node: '>= 0.4'} @@ -7383,9 +7021,6 @@ packages: resolution: {integrity: sha512-A91dYTeIB6NoXG+PxTQpCCDDnfHsW9kc06Lvpu1TEe9gnd6ZFeiBoRO9JvzEv6xK7EX97/dUE8g/vBMTqTS3CA==} engines: {node: '>=14'} - html-comment-regex@1.1.2: - resolution: {integrity: sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==} - html-encoding-sniffer@4.0.0: resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} engines: {node: '>=18'} @@ -7410,10 +7045,6 @@ packages: http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - http-errors@1.6.3: - resolution: {integrity: sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==} - engines: {node: '>= 0.6'} - http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -7422,20 +7053,10 @@ packages: resolution: {integrity: sha512-3cZ0SRL8fb9MUlU3mKM61FcQvPfXx2dBrZW3Vbg5CXa8jFlK8OaEpePenLe1oEXQduhz8b0QjsqfS59QP4AJDQ==} engines: {node: '>=6.0.0'} - http-parser-js@0.5.8: - resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} - http-proxy-agent@7.0.0: resolution: {integrity: sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==} engines: {node: '>= 14'} - http-proxy-middleware@0.17.4: - resolution: {integrity: sha512-JtH3UZju4oXDdca28/kknbm/CFmt35vy0YV0PNOMWWWpn3rT9WV95IXN451xwBGSjy9L0Cah1O9TCMytboLdfw==} - - http-proxy@1.18.1: - resolution: {integrity: sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==} - engines: {node: '>=8.0.0'} - http-signature@1.2.0: resolution: {integrity: sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==} engines: {node: '>=0.8', npm: '>=1.3.7'} @@ -7456,9 +7077,6 @@ packages: resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==} engines: {node: '>=16'} - https-browserify@0.0.1: - resolution: {integrity: sha512-EjDQFbgJr1vDD/175UJeSX3ncQ3+RUnCL5NkthQGHvF4VNHlzTy8ifJfTqz47qiPRqaFH58+CbuG3x51WuB1XQ==} - https-proxy-agent@2.2.4: resolution: {integrity: sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==} engines: {node: '>= 4.5.0'} @@ -7487,10 +7105,6 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} - i@0.3.7: - resolution: {integrity: sha512-FYz4wlXgkQwIPqhzC5TdNMLSE5+GS1IIDJZY/1ZiEPCT2S3COUVZeT5OW4BmW4r5LHLQuOosSwsvnroG9GR59Q==} - engines: {node: '>=0.4'} - iconv-lite@0.4.24: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} @@ -7499,9 +7113,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - icss-replace-symbols@1.1.0: - resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==} - idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} @@ -7523,9 +7134,6 @@ packages: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} - image-conversion@2.1.1: - resolution: {integrity: sha512-hnMOmP7q2jxA+52FZ+wHNhg3fdFRlgfngsQH2JQHEQkafY7tj/8F15e6Rv/RxDegc872jvyaRHwMbkTZK1Cjbg==} - immutable@4.2.2: resolution: {integrity: sha512-fTMKDwtbvO5tldky9QZ2fMX7slR0mYpY5nbnFWYp0fOzDhHqhgIw9KoYgxLWsoNTS9ZHGauHj18DTyEw6BK3Og==} @@ -7554,21 +7162,9 @@ packages: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} - indexes-of@1.0.1: - resolution: {integrity: sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA==} - - indexof@0.0.1: - resolution: {integrity: sha512-i0G7hLJ1z0DE8dsqJa2rycj9dBmNKgXBvotXtZYXakU9oivfB9Uj2ZBC27qqef2U58/ZLwalxa1X/RDCdkHtVg==} - inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - inherits@1.0.2: - resolution: {integrity: sha512-Al67oatbRSo3RV5hRqIoln6Y5yMVbJSIn4jEJNL7VCImzq/kLr7vvb6sFRJXqr8rpHc/2kJOM+y0sPKN47VdzA==} - - inherits@2.0.3: - resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} - inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} @@ -7590,9 +7186,6 @@ packages: resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} engines: {node: '>= 0.4'} - interpret@0.6.6: - resolution: {integrity: sha512-Vg6X07U0AOZb4HF6CWHa+jnJU8j71buKQ9Pc0C75qBXgvCYbxWBkGo4jnTS3O0MIc9FZtt0mB7h+uclojqdw1Q==} - intersection-observer@0.12.2: resolution: {integrity: sha512-7m1vEcPCxXYI8HqnL8CKI6siDyD+eIWSwgB3DZA+ZTogxk9I4CDnj4wilt9x/+/QbHI4YG5YZNmC6458/e9Ktg==} @@ -7630,10 +7223,6 @@ packages: resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} engines: {node: '>=8'} - is-absolute-url@2.1.0: - resolution: {integrity: sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg==} - engines: {node: '>=0.10.0'} - is-absolute-url@4.0.1: resolution: {integrity: sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -7688,25 +7277,9 @@ packages: engines: {node: '>=8'} hasBin: true - is-dotfile@1.0.3: - resolution: {integrity: sha512-9YclgOGtN/f8zx0Pr4FQYMdibBiTaH3sn52vjYip4ZSf6C4/6RfTEZ+MR4GvKhCxdPh21Bg42/WL55f6KSnKpg==} - engines: {node: '>=0.10.0'} - - is-equal-shallow@0.1.3: - resolution: {integrity: sha512-0EygVC5qPvIyb+gSz7zdD5/AAoS6Qrx1e//6N4yv4oNm30kqvdmG66oZFWVlQHUWe5OjP08FuTw2IdT0EOTcYA==} - engines: {node: '>=0.10.0'} - is-expression@4.0.0: resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==} - is-extendable@0.1.1: - resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} - engines: {node: '>=0.10.0'} - - is-extglob@1.0.0: - resolution: {integrity: sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww==} - engines: {node: '>=0.10.0'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -7726,14 +7299,6 @@ packages: resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} engines: {node: '>= 0.4'} - is-glob@2.0.1: - resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} - engines: {node: '>=0.10.0'} - - is-glob@3.1.0: - resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} - engines: {node: '>=0.10.0'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -7775,14 +7340,6 @@ packages: resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} engines: {node: '>= 0.4'} - is-number@2.1.0: - resolution: {integrity: sha512-QUzH43Gfb9+5yckcrSA0VBDwEtDUchrk4F6tfJZQuNzDJbEDB9cZNzSfXGQ1jqmdDY/kl41lUOWM9syA8z8jlg==} - engines: {node: '>=0.10.0'} - - is-number@4.0.0: - resolution: {integrity: sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==} - engines: {node: '>=0.10.0'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -7811,17 +7368,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} - is-posix-bracket@0.1.1: - resolution: {integrity: sha512-Yu68oeXJ7LeWNmZ3Zov/xg/oDBnBK2RNxwYY1ilNJX+tKKZqgPK+qOn/Gs9jEu66KDY9Netf5XLKNGzas/vPfQ==} - engines: {node: '>=0.10.0'} - is-potential-custom-element-name@1.0.1: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} - is-primitive@2.0.0: - resolution: {integrity: sha512-N3w1tFaRfk3UrPfqeRyD+GYDASU3W5VinKhlORy8EWVf/sIdDL9GAcew85XmktCfH+ngG7SRXEVDoO18WMdB/Q==} - engines: {node: '>=0.10.0'} - is-promise@2.2.2: resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} @@ -7851,10 +7400,6 @@ packages: resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} engines: {node: '>= 0.4'} - is-svg@2.1.0: - resolution: {integrity: sha512-Ya1giYJUkcL/94quj0+XGcmts6cETPBW1MiFz1ReJrnDJ680F52qpAEGAEGU0nq96FRGIGPx6Yo1CyPXcOoyGw==} - engines: {node: '>=0.10.0'} - is-svg@5.0.0: resolution: {integrity: sha512-sRl7J0oX9yUNamSdc8cwgzh9KBLnQXNzGmW0RVHwg/jEYjGNYHC6UvnYD8+hAeut9WwxRvhG9biK7g/wDGxcMw==} engines: {node: '>=14.16'} @@ -7903,10 +7448,6 @@ packages: resolution: {integrity: sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==} engines: {node: '>=16'} - isobject@2.1.0: - resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} - engines: {node: '>=0.10.0'} - isobject@3.0.1: resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} engines: {node: '>=0.10.0'} @@ -7951,11 +7492,6 @@ packages: engines: {node: '>=10'} hasBin: true - jamjs@0.2.17: - resolution: {integrity: sha512-bJrZ1NEtuL1u3hjLMbVSOQAHvoP7g7wZtOSya+6/KFIvcgYz0uExlKQbMe3LOzFJp9vGk8ilXpHZWEwyLeg4YQ==} - engines: {node: '>= 0.6.x'} - hasBin: true - jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -8100,23 +7636,14 @@ packages: jpeg-js@0.3.7: resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==} - js-base64@2.6.4: - resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==} - js-beautify@1.14.9: resolution: {integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==} engines: {node: '>=12'} hasBin: true - js-binary-schema-parser@2.0.3: - resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} - js-stringify@1.0.2: resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} - js-tokens@3.0.2: - resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -8124,10 +7651,6 @@ packages: resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} hasBin: true - js-yaml@3.7.0: - resolution: {integrity: sha512-eIlkGty7HGmntbV6P/ZlAsoncFLGsNoM27lkTzS+oneY/EiNhj+geqD9ezg/ip+SW6Var0BJU2JtV0vEUZpWVQ==} - hasBin: true - js-yaml@4.1.0: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true @@ -8172,9 +7695,6 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-merge-patch@0.2.3: - resolution: {integrity: sha512-mjd5eObNGOhWkKCztwVuF25KOzLj2T4TJaWXLBgCQPeoPRJrMxKNgjNBE8sPmXoWRT0WDlo4Itd/gTlFh29TFw==} - json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -8197,10 +7717,6 @@ packages: resolution: {integrity: sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==} engines: {node: '>= 4'} - json5@0.5.1: - resolution: {integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==} - hasBin: true - json5@1.0.2: resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} hasBin: true @@ -8259,10 +7775,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kind-of@3.2.2: - resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} - engines: {node: '>=0.10.0'} - kind-of@6.0.3: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} @@ -8289,10 +7801,6 @@ packages: resolution: {integrity: sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==} engines: {node: '> 0.8'} - lazy-cache@1.0.4: - resolution: {integrity: sha512-RE2g0b5VGZsOCFOCgP7omTRYFqydmZkBwl5oNnQ1lDYC57uyO9KqNnNVxT7COSHTxrRCWVcAVOcbjk+tvh/rgQ==} - engines: {node: '>=0.10.0'} - lazy-universal-dotenv@4.0.0: resolution: {integrity: sha512-aXpZJRnTkpK6gQ/z4nk+ZBLd/Qdp118cvPruLSIQzQNRhKwEcdXCOzXuF55VDqIiuAaY3UGZ10DJtvZzDcvsxg==} engines: {node: '>=14.0.0'} @@ -8309,9 +7817,6 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libwebpjs@0.0.1: - resolution: {integrity: sha512-HSxZmhSsLoL8eKGIxPkSHTp7UeTSNhm4gLdlIQij7uJrhpBMoUiYMJFQeE+Rs+aJ/i963m68hH19Jvq6bJqXYg==} - light-my-request@5.11.0: resolution: {integrity: sha512-qkFCeloXCOMpmEdZ/MV91P8AT4fjwFXWaAFz3lUeStM8RcoM1ks4J/F8r1b3r6y/H4u3ACEJ1T+Gv5bopj7oDA==} @@ -8331,13 +7836,6 @@ packages: enquirer: optional: true - loader-utils@0.2.17: - resolution: {integrity: sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug==} - - loader-utils@1.4.2: - resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} - engines: {node: '>=4.0.0'} - local-pkg@0.4.3: resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} engines: {node: '>=14'} @@ -8354,9 +7852,6 @@ packages: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} - lodash.camelcase@4.3.0: - resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} - lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} @@ -8401,10 +7896,6 @@ packages: longest-streak@3.1.0: resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} - longest@1.0.1: - resolution: {integrity: sha512-k+yt5n3l48JU4k8ftnKG6V7u32wyH2NfKzeMto9F/QRE0amxy/LayxwlvjjkZEIzqR+19IrtFO8p5kB9QaYUFg==} - engines: {node: '>=0.10.0'} - loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -8428,9 +7919,6 @@ packages: resolution: {integrity: sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==} engines: {node: 14 || >=16.14} - lru-cache@2.7.3: - resolution: {integrity: sha512-WpibWJ60c3AgAz8a2iYErDrcT2C7OmKnsWhIcHOjkUHFjkXncJhtLxNSqUmxRxRunpb5I8Vprd7aNSd2NtksJQ==} - lru-cache@4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -8509,12 +7997,6 @@ packages: peerDependencies: react: '>= 0.14.0' - math-expression-evaluator@1.4.0: - resolution: {integrity: sha512-4vRUvPyxdO8cWULGTh9dZWL2tZK6LDBvj+OGHBER7poH9Qdt7kXEoj20wiz4lQUbUXQZFjPbe5mVDo9nutizCw==} - - math-random@1.0.4: - resolution: {integrity: sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==} - matter-js@0.19.0: resolution: {integrity: sha512-v2huwvQGOHTGOkMqtHd2hercCG3f6QAObTisPPHg8TZqq2lz7eIY/5i/5YUV8Ibf3mEioFEmwibcPUF2/fnKKQ==} @@ -8567,15 +8049,6 @@ packages: memoizerific@1.11.3: resolution: {integrity: sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==} - memory-fs@0.2.0: - resolution: {integrity: sha512-+y4mDxU4rvXXu5UDSGCGNiesFmwCHuefGMoPCO1WYucNYj7DsLqrFaa2fXVI0H+NNiPTwwzKwspn9yTZqUGqng==} - - memory-fs@0.3.0: - resolution: {integrity: sha512-QTNXnl79X97kZ9jJk/meJrtDuvgvRakX5LU7HZW1L7MsXHuSTwoMIzN9tOLLH3Xfsj/gbsSqX/ovnsqz246zKQ==} - - memory-fs@0.4.1: - resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==} - meow@9.0.0: resolution: {integrity: sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==} engines: {node: '>=10'} @@ -8685,10 +8158,6 @@ packages: micromark@4.0.0: resolution: {integrity: sha512-o/sd0nMof8kYff+TqcDx3VSrgBTcZpSvYcAHIfHhv5VAuNmisCxjhx6YmxS8PFEpb9z5WKWKPdzf0jM23ro3RQ==} - micromatch@2.3.11: - resolution: {integrity: sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA==} - engines: {node: '>=0.10.0'} - micromatch@4.0.5: resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} engines: {node: '>=8.6'} @@ -8701,9 +8170,6 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - mime@1.2.11: - resolution: {integrity: sha512-Ysa2F/nqTNGHhhm9MV8ure4+Hc+Y8AWiqUdHxsO7xu8zc92ND9f3kpALHjaP026Ft17UfxrMt95c50PLUeynBw==} - mime@1.6.0: resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} engines: {node: '>=4'} @@ -8745,14 +8211,6 @@ packages: minimalistic-assert@1.0.1: resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} - minimatch@0.2.14: - resolution: {integrity: sha512-zZ+Jy8lVWlvqqeM8iZB7w7KmQkoJn8djM585z88rywrEbzoqawVa9FR5p2hwD+y74nfuKOjmNvi9gtWJNLqHvA==} - deprecated: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue - - minimatch@0.3.0: - resolution: {integrity: sha512-WFX1jI1AaxNTZVOHLBVazwTWKaQjoykSzCBNXB72vDTCzopQGtyP91tKdFK5cv1+qMwPyiTu1HqUriqplI8pcA==} - deprecated: Please update to minimatch 3.0.2 or higher to avoid a RegExp DoS issue - minimatch@3.0.8: resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} @@ -8779,9 +8237,6 @@ packages: resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} engines: {node: '>= 6'} - minimist@0.0.10: - resolution: {integrity: sha512-iotkTvxc+TwOm5Ieim8VnSNvCDjCK9S8G3scJ50ZthspSxa7jx50jkhYduuAtAjvfDUwSgOwf8+If99AlOEhyw==} - minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} @@ -8830,10 +8285,6 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mkdirp@0.3.5: - resolution: {integrity: sha512-8OCq0De/h9ZxseqzCH8Kw/Filf5pF/vMI6+BH7Lu0jXz2pqYCjTAQRolSxRIi+Ax+oCCjlxoJMP0YQ4XlrQNHg==} - deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) - mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -8931,17 +8382,9 @@ packages: engines: {node: ^18 || >=20} hasBin: true - natives@1.1.6: - resolution: {integrity: sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==} - deprecated: This module relies on Node.js's internals and will break at some point. Do not use it, and update to graceful-fs@4.x. - natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} - ncp@0.2.7: - resolution: {integrity: sha512-wPUepcV37u3Mw+ktjrUbl3azxwAkcD9RrVLQGlpSapWcEQM5jL0g8zwKo6ukOjVQAAEjqpRdLeojOalqqySpCg==} - hasBin: true - ncp@2.0.0: resolution: {integrity: sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==} hasBin: true @@ -9043,15 +8486,9 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-libs-browser@0.7.0: - resolution: {integrity: sha512-V0EeBff5/nauAta4yGYMdn/CYXpn2KYcE8r6rwU9qJDXG6wMrBhtWVfoKWphSvsnX+mZk6DzaGSO+Yz/MGBAGQ==} - node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} - node-webpmux@3.2.0: - resolution: {integrity: sha512-Rmc7MIS7zxDRPkfC9B15malsd1uStX2N6mYqHZNZeCJAVRDjmnxxTsyDh6POvWdNC7nmAYBW6CcHVY62DlnzRg==} - nodemailer@6.9.13: resolution: {integrity: sha512-7o38Yogx6krdoBf3jCAqnIN4oSQFx+fMa0I7dK1D+me9kBxx12D+/33wSb+fhOCtIxvYJ+4x4IMEhmhCKfAiOA==} engines: {node: '>=6.0.0'} @@ -9096,22 +8533,10 @@ packages: resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} engines: {node: '>=10'} - normalize-path@2.1.1: - resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} - engines: {node: '>=0.10.0'} - normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - - normalize-url@1.9.1: - resolution: {integrity: sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==} - engines: {node: '>=4'} - normalize-url@6.1.0: resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} engines: {node: '>=10'} @@ -9143,9 +8568,6 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - num2fraction@1.2.2: - resolution: {integrity: sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg==} - nwsapi@2.2.9: resolution: {integrity: sha512-2f3F0SEEer8bBu0dsNCFF50N0cTThV1nWFYcEYFZttdW0lDAoybv9cQoK7X7/68Z89S7FoRrVjP1LPX4XRf9vg==} @@ -9169,9 +8591,6 @@ packages: object-inspect@1.12.3: resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} - object-inspect@1.13.1: - resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==} - object-is@1.1.5: resolution: {integrity: sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==} engines: {node: '>= 0.4'} @@ -9191,10 +8610,6 @@ packages: object.groupby@1.0.1: resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} - object.omit@2.0.1: - resolution: {integrity: sha512-UiAM5mhmIuKLsOvrL+B0U2d1hXHF3bFYWIuH1LMpuV2EJEHG1Ntz06PgLEHjm6VFd87NpH8rastvPoyv6UW2fA==} - engines: {node: '>=0.10.0'} - object.values@1.1.7: resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} engines: {node: '>= 0.4'} @@ -9234,10 +8649,6 @@ packages: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} - open@0.0.5: - resolution: {integrity: sha512-+X/dJYLapVO1VbC620DhtNZK9U4/kQVaTQp/Gh7cb6UTLYfGZzzU2ZXkWrOA/wBrf4UqAFwtLqXYTxe4tSnWQQ==} - engines: {node: '>= 0.6.0'} - open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -9249,9 +8660,6 @@ packages: resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==} hasBin: true - optimist@0.6.1: - resolution: {integrity: sha512-snN4O4TkigujZphWLN0E//nQmm7790RYaE53DdL7ZYwee2D8DDo9/EyYiKUfN3rneWUjhJnueija3G9I2i0h3g==} - optionator@0.9.3: resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} engines: {node: '>= 0.8.0'} @@ -9260,9 +8668,6 @@ packages: resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} engines: {node: '>=10'} - os-browserify@0.2.1: - resolution: {integrity: sha512-vHbnbzdqWJWvGOm7aOMDXHVUykPG0GdhfLkn5ZDmvbRI+wPj/XoB0/CRAkP9v28eZ7REIPPHJa+8ZEYixsWKmQ==} - os-filter-obj@2.0.0: resolution: {integrity: sha512-uksVLsqG3pVdzzPvmAHpBK0wKxYItuzZr7SziusRPoz67tGV8rL1szZ6IdeUrbqLjGDwApBtN29eEE3IqGHOjg==} engines: {node: '>=4'} @@ -9345,10 +8750,6 @@ packages: parse-data-uri@0.2.0: resolution: {integrity: sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==} - parse-glob@3.0.4: - resolution: {integrity: sha512-FC5TeK0AwXzq3tUBFtH74naWkPQCEWs4K+xMxWZBlKDWu0bVHXGZa+KKqxKidd7xwhdZ19ZNuF2uO1M/r196HA==} - engines: {node: '>=0.10.0'} - parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} @@ -9375,9 +8776,6 @@ packages: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} - path-browserify@0.0.0: - resolution: {integrity: sha512-WA3pxi1olUQcsl82W576vkqhUSGp0uBtr/381pxx5WXLp3NC+AB99hUG3aGW7H0Kg9PFr1D8wv1iJeICe+9Mhw==} - path-browserify@1.0.1: resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==} @@ -9441,9 +8839,6 @@ packages: pause-stream@0.0.11: resolution: {integrity: sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A==} - pbkdf2-compat@2.0.1: - resolution: {integrity: sha512-JYubxYhymODUUWVq9/Tmo9VQFZ8LyrD/pbXVpwmt1Npr2z29KZwp7+IBT3/PRjr1xpecX4W1EcbjFjp8nE3stQ==} - peek-readable@5.0.0: resolution: {integrity: sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==} engines: {node: '>=14.16'} @@ -9561,14 +8956,6 @@ packages: pkg-types@1.0.3: resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} - pkginfo@0.2.3: - resolution: {integrity: sha512-7W7wTrE/NsY8xv/DTGjwNIyNah81EQH0MWcTzrHL6pOpMocOGZc0Mbdz9aXxSrp+U0mSmkU8jrNCDCfUs3sOBg==} - engines: {node: '>= 0.4.0'} - - pkginfo@0.4.1: - resolution: {integrity: sha512-8xCNE/aT/EXKenuMDZ+xTVwkT8gsoHN2z/Q29l80u0ppGEXVvsKRzNMbtKhg8LS8k1tJLAHHylf6p4VFmP6XUQ==} - engines: {node: '>= 0.4.0'} - plimit-lit@1.5.0: resolution: {integrity: sha512-Eb/MqCb1Iv/ok4m1FqIXqvUKPISufcjZ605hl3KM/n8GaX8zfhtgdLwZU3vKjuHGh2O9Rjog/bHTq8ofIShdng==} @@ -9588,158 +8975,88 @@ packages: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} - pngjs@7.0.0: - resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} - engines: {node: '>=14.19.0'} - polished@4.2.2: resolution: {integrity: sha512-Sz2Lkdxz6F2Pgnpi9U5Ng/WdWAUZxmHrNPoVlm3aAemxoy2Qy7LGjQg4uf8qKelDAUW94F4np3iH2YPf2qefcQ==} engines: {node: '>=10'} - postcss-calc@5.3.1: - resolution: {integrity: sha512-iBcptYFq+QUh9gzP7ta2btw50o40s4uLI4UDVgd5yRAZtUDWc5APdl5yQDd2h/TyiZNbJrv0HiYhT102CMgN7Q==} - postcss-calc@9.0.1: resolution: {integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.2.2 - postcss-colormin@2.2.2: - resolution: {integrity: sha512-XXitQe+jNNPf+vxvQXIQ1+pvdQKWKgkx8zlJNltcMEmLma1ypDRDQwlLt+6cP26fBreihNhZxohh1rcgCH2W5w==} - postcss-colormin@6.1.0: resolution: {integrity: sha512-x9yX7DOxeMAR+BgGVnNSAxmAj98NX/YxEMNFP+SDCEeNLb2r3i6Hh1ksMsnW8Ub5SLCpbescQqn9YEbE9554Sw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-convert-values@2.6.1: - resolution: {integrity: sha512-SE7mf25D3ORUEXpu3WUqQqy0nCbMuM5BEny+ULE/FXdS/0UMA58OdzwvzuHJRpIFlk1uojt16JhaEogtP6W2oA==} - postcss-convert-values@6.1.0: resolution: {integrity: sha512-zx8IwP/ts9WvUM6NkVSkiU902QZL1bwPhaVaLynPtCsOTqp+ZKbNi+s6XJg3rfqpKGA/oc7Oxk5t8pOQJcwl/w==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-discard-comments@2.0.4: - resolution: {integrity: sha512-yGbyBDo5FxsImE90LD8C87vgnNlweQkODMkUZlDVM/CBgLr9C5RasLGJxxh9GjVOBeG8NcCMatoqI1pXg8JNXg==} - postcss-discard-comments@6.0.2: resolution: {integrity: sha512-65w/uIqhSBBfQmYnG92FO1mWZjJ4GL5b8atm5Yw2UgrwD7HiNiSSNwJor1eCFGzUgYnN/iIknhNRVqjrrpuglw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-discard-duplicates@2.1.0: - resolution: {integrity: sha512-+lk5W1uqO8qIUTET+UETgj9GWykLC3LOldr7EehmymV0Wu36kyoHimC4cILrAAYpHQ+fr4ypKcWcVNaGzm0reA==} - postcss-discard-duplicates@6.0.3: resolution: {integrity: sha512-+JA0DCvc5XvFAxwx6f/e68gQu/7Z9ud584VLmcgto28eB8FqSFZwtrLwB5Kcp70eIoWP/HXqz4wpo8rD8gpsTw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-discard-empty@2.1.0: - resolution: {integrity: sha512-IBFoyrwk52dhF+5z/ZAbzq5Jy7Wq0aLUsOn69JNS+7YeuyHaNzJwBIYE0QlUH/p5d3L+OON72Fsexyb7OK/3og==} - postcss-discard-empty@6.0.3: resolution: {integrity: sha512-znyno9cHKQsK6PtxL5D19Fj9uwSzC2mB74cpT66fhgOadEUPyXFkbgwm5tvc3bt3NAy8ltE5MrghxovZRVnOjQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-discard-overridden@0.1.1: - resolution: {integrity: sha512-IyKoDL8QNObOiUc6eBw8kMxBHCfxUaERYTUe2QF8k7j/xiirayDzzkmlR6lMQjrAM1p1DDRTvWrS7Aa8lp6/uA==} - postcss-discard-overridden@6.0.2: resolution: {integrity: sha512-j87xzI4LUggC5zND7KdjsI25APtyMuynXZSujByMaav2roV6OZX+8AaCUcZSWqckZpjAjRyFDdpqybgjFO0HJQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-discard-unused@2.2.3: - resolution: {integrity: sha512-nCbFNfqYAbKCw9J6PSJubpN9asnrwVLkRDFc4KCwyUEdOtM5XDE/eTW3OpqHrYY1L4fZxgan7LLRAAYYBzwzrg==} - - postcss-filter-plugins@2.0.3: - resolution: {integrity: sha512-T53GVFsdinJhgwm7rg1BzbeBRomOg9y5MBVhGcsV0CxurUdVj1UlPdKtn7aqYA/c/QVkzKMjq2bSV5dKG5+AwQ==} - - postcss-merge-idents@2.1.7: - resolution: {integrity: sha512-9DHmfCZ7/hNHhIKnNkz4CU0ejtGen5BbTRJc13Z2uHfCedeCUsK2WEQoAJRBL+phs68iWK6Qf8Jze71anuysWA==} - - postcss-merge-longhand@2.0.2: - resolution: {integrity: sha512-ma7YvxjdLQdifnc1HFsW/AW6fVfubGyR+X4bE3FOSdBVMY9bZjKVdklHT+odknKBB7FSCfKIHC3yHK7RUAqRPg==} - postcss-merge-longhand@6.0.5: resolution: {integrity: sha512-5LOiordeTfi64QhICp07nzzuTDjNSO8g5Ksdibt44d+uvIIAE1oZdRn8y/W5ZtYgRH/lnLDlvi9F8btZcVzu3w==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-merge-rules@2.1.2: - resolution: {integrity: sha512-Wgg2FS6W3AYBl+5L9poL6ZUISi5YzL+sDCJfM7zNw/Q1qsyVQXXZ2cbVui6mu2cYJpt1hOKCGj1xA4mq/obz/Q==} - postcss-merge-rules@6.1.1: resolution: {integrity: sha512-KOdWF0gju31AQPZiD+2Ar9Qjowz1LTChSjFFbS+e2sFgc4uHOp3ZvVX4sNeTlk0w2O31ecFGgrFzhO0RSWbWwQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-message-helpers@2.0.0: - resolution: {integrity: sha512-tPLZzVAiIJp46TBbpXtrUAKqedXSyW5xDEo1sikrfEfnTs+49SBZR/xDdqCiJvSSbtr615xDsaMF3RrxS2jZlA==} - - postcss-minify-font-values@1.0.5: - resolution: {integrity: sha512-vFSPzrJhNe6/8McOLU13XIsERohBJiIFFuC1PolgajOZdRWqRgKITP/A4Z/n4GQhEmtbxmO9NDw3QLaFfE1dFQ==} - postcss-minify-font-values@6.1.0: resolution: {integrity: sha512-gklfI/n+9rTh8nYaSJXlCo3nOKqMNkxuGpTn/Qm0gstL3ywTr9/WRKznE+oy6fvfolH6dF+QM4nCo8yPLdvGJg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-minify-gradients@1.0.5: - resolution: {integrity: sha512-DZhT0OE+RbVqVyGsTIKx84rU/5cury1jmwPa19bViqYPQu499ZU831yMzzsyC8EhiZVd73+h5Z9xb/DdaBpw7Q==} - postcss-minify-gradients@6.0.3: resolution: {integrity: sha512-4KXAHrYlzF0Rr7uc4VrfwDJ2ajrtNEpNEuLxFgwkhFZ56/7gaE4Nr49nLsQDZyUe+ds+kEhf+YAUolJiYXF8+Q==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-minify-params@1.2.2: - resolution: {integrity: sha512-hhJdMVgP8vasrHbkKAk+ab28vEmPYgyuDzRl31V3BEB3QOR3L5TTIVEWLDNnZZ3+fiTi9d6Ker8GM8S1h8p2Ow==} - postcss-minify-params@6.1.0: resolution: {integrity: sha512-bmSKnDtyyE8ujHQK0RQJDIKhQ20Jq1LYiez54WiaOoBtcSuflfK3Nm596LvbtlFcpipMjgClQGyGr7GAs+H1uA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-minify-selectors@2.1.1: - resolution: {integrity: sha512-e13vxPBSo3ZaPne43KVgM+UETkx3Bs4/Qvm6yXI9HQpQp4nyb7HZ0gKpkF+Wn2x+/dbQ+swNpCdZSbMOT7+TIA==} - postcss-minify-selectors@6.0.4: resolution: {integrity: sha512-L8dZSwNLgK7pjTto9PzWRoMbnLq5vsZSTu8+j1P/2GB8qdtGQfn+K1uSvFgYvgh83cbyxT5m43ZZhUMTJDSClQ==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-modules-extract-imports@1.2.1: - resolution: {integrity: sha512-6jt9XZwUhwmRUhb/CkyJY020PYaPJsCyt3UjbaWo6XEbH/94Hmv6MP7fG2C5NDU/BcHzyGYxNtHvM+LTf9HrYw==} - - postcss-modules-local-by-default@1.2.0: - resolution: {integrity: sha512-X4cquUPIaAd86raVrBwO8fwRfkIdbwFu7CTfEOjiZQHVQwlHRSkTgH5NLDmMm5+1hQO8u6dZ+TOOJDbay1hYpA==} - - postcss-modules-scope@1.1.0: - resolution: {integrity: sha512-LTYwnA4C1He1BKZXIx1CYiHixdSe9LWYVKadq9lK5aCCMkoOkFyZ7aigt+srfjlRplJY3gIol6KUNefdMQJdlw==} - - postcss-modules-values@1.3.0: - resolution: {integrity: sha512-i7IFaR9hlQ6/0UgFuqM6YWaCfA1Ej8WMg8A5DggnH1UGKJvTV/ugqq/KaULixzzOi3T/tF6ClBXcHGCzdd5unA==} - - postcss-normalize-charset@1.1.1: - resolution: {integrity: sha512-RKgjEks83l8w4yEhztOwNZ+nLSrJ+NvPNhpS+mVDzoaiRHZQVoG7NF2TP5qjwnaN9YswUhj6m1E0S0Z+WDCgEQ==} - postcss-normalize-charset@6.0.2: resolution: {integrity: sha512-a8N9czmdnrjPHa3DeFlwqst5eaL5W8jYu3EBbTTkI5FHkfMhFZh1EGbku6jhHhIzTA6tquI2P42NtZ59M/H/kQ==} engines: {node: ^14 || ^16 || >=18.0} @@ -9782,9 +9099,6 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-normalize-url@3.0.8: - resolution: {integrity: sha512-WqtWG6GV2nELsQEFES0RzfL2ebVwmGl/M8VmMbshKto/UClBo+mznX8Zi4/hkThdqx7ijwv+O8HWPdpK7nH/Ig==} - postcss-normalize-url@6.0.2: resolution: {integrity: sha512-kVNcWhCeKAzZ8B4pv/DnrU1wNh458zBNp8dh4y5hhxih5RZQ12QWMuQrDgPRw3LRl8mN9vOVfHl7uhvHYMoXsQ==} engines: {node: ^14 || ^16 || >=18.0} @@ -9797,39 +9111,24 @@ packages: peerDependencies: postcss: ^8.4.31 - postcss-ordered-values@2.2.3: - resolution: {integrity: sha512-5RB1IUZhkxDCfa5fx/ogp/A82mtq+r7USqS+7zt0e428HJ7+BHCxyeY39ClmkkUtxdOd3mk8gD6d9bjH2BECMg==} - postcss-ordered-values@6.0.2: resolution: {integrity: sha512-VRZSOB+JU32RsEAQrO94QPkClGPKJEL/Z9PCBImXMhIeK5KAYo6slP/hBYlLgrCjFxyqvn5VC81tycFEDBLG1Q==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-reduce-idents@2.4.0: - resolution: {integrity: sha512-0+Ow9e8JLtffjumJJFPqvN4qAvokVbdQPnijUDSOX8tfTwrILLP4ETvrZcXZxAtpFLh/U0c+q8oRMJLr1Kiu4w==} - - postcss-reduce-initial@1.0.1: - resolution: {integrity: sha512-jJFrV1vWOPCQsIVitawGesRgMgunbclERQ/IRGW7r93uHrVzNQQmHQ7znsOIjJPZ4yWMzs5A8NFhp3AkPHPbDA==} - postcss-reduce-initial@6.1.0: resolution: {integrity: sha512-RarLgBK/CrL1qZags04oKbVbrrVK2wcxhvta3GCxrZO4zveibqbRPmm2VI8sSgCXwoUHEliRSbOfpR0b/VIoiw==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-reduce-transforms@1.0.4: - resolution: {integrity: sha512-lGgRqnSuAR5i5uUg1TA33r9UngfTadWxOyL2qx1KuPoCQzfmtaHjp9PuwX7yVyRxG3BWBzeFUaS5uV9eVgnEgQ==} - postcss-reduce-transforms@6.0.2: resolution: {integrity: sha512-sB+Ya++3Xj1WaT9+5LOOdirAxP7dJZms3GRcYheSPi1PiTMigsxHAdkrbItHxwYHr4kt1zL7mmcHstgMYT+aiA==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-selector-parser@2.2.3: - resolution: {integrity: sha512-3pqyakeGhrO0BQ5+/tGTfvi5IAUAhHRayGK8WFSu06aEv2BmHoXw/Mhb+w7VY5HERIuC+QoUI7wgrCcq2hqCVA==} - postcss-selector-parser@6.0.15: resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==} engines: {node: '>=4'} @@ -9838,41 +9137,21 @@ packages: resolution: {integrity: sha512-A0RVJrX+IUkVZbW3ClroRWurercFhieevHB38sr2+l9eUClMqome3LmEmnhlNy+5Mr2EYN6B2Kaw9wYdd+VHiw==} engines: {node: '>=4'} - postcss-svgo@2.1.6: - resolution: {integrity: sha512-y5AdQdgBoF4rbpdbeWAJuxE953g/ylRfVNp6mvAi61VCN/Y25Tu9p5mh3CyI42WbTRIiwR9a1GdFtmDnNPeskQ==} - postcss-svgo@6.0.3: resolution: {integrity: sha512-dlrahRmxP22bX6iKEjOM+c8/1p+81asjKT+V5lrgOH944ryx/OHpclnIbGsKVd3uWOXFLYJwCVf0eEkJGvO96g==} engines: {node: ^14 || ^16 || >= 18} peerDependencies: postcss: ^8.4.31 - postcss-unique-selectors@2.0.2: - resolution: {integrity: sha512-WZX8r1M0+IyljoJOJleg3kYm10hxNYF9scqAT7v/xeSX1IdehutOM85SNO0gP9K+bgs86XERr7Ud5u3ch4+D8g==} - postcss-unique-selectors@6.0.4: resolution: {integrity: sha512-K38OCaIrO8+PzpArzkLKB42dSARtC2tmG6PvD4b1o1Q2E9Os8jzfWFfSy/rixsHwohtsDdFtAWGjFVFUdwYaMg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - postcss-value-parser@3.3.1: - resolution: {integrity: sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ==} - postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss-zindex@2.2.0: - resolution: {integrity: sha512-uhRZ2hRgj0lorxm9cr62B01YzpUe63h0RXMXQ4gWW3oa2rpJh+FJAiEAytaFCPU/VgaBS+uW2SJ1XKyDNz1h4w==} - - postcss@5.2.18: - resolution: {integrity: sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==} - engines: {node: '>=0.12'} - - postcss@6.0.23: - resolution: {integrity: sha512-soOk1h6J3VMTZtVeVpv15/Hpdl2cBLX3CAw4TAbkpTJiNPk9YP/zWcD1ND+xEtvyuuvKzbxliTOIyvkSeSJ6ag==} - engines: {node: '>=4.0.0'} - postcss@8.4.38: resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} engines: {node: ^10 || ^12 || >=14} @@ -9916,14 +9195,6 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prepend-http@1.0.4: - resolution: {integrity: sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==} - engines: {node: '>=0.10.0'} - - preserve@0.2.0: - resolution: {integrity: sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ==} - engines: {node: '>=0.10.0'} - prettier@3.2.5: resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==} engines: {node: '>=14'} @@ -9989,10 +9260,6 @@ packages: promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} - prompt@0.2.1: - resolution: {integrity: sha512-HTqCMaVQXu7SzcaF1PJJxPMgTR3My8a6ZZTH5wHzi0X+fotszg9A0Ra6ZDCFX1i/0q9QCS2rMmljVOBGksYsaQ==} - engines: {node: '>= 0.4.0'} - prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} @@ -10016,9 +9283,6 @@ packages: proxycheck-ts@0.0.9: resolution: {integrity: sha512-qxdMgLB01kPksBAeAHI2Zt5q/fW0glZKZK9d9eNm0/KyowKuKzVmvirF2ztsGcIzU7r6s6YvPwXJuPTeQ+LBTg==} - prr@1.0.1: - resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} - ps-list@8.1.1: resolution: {integrity: sha512-OPS9kEJYVmiO48u/B9qneqhkMvgCxT+Tm28VCEJpheTpl8cJ0ffZRRNgS5mrQRTrX5yRTpaJ+hRDeefXYmmorQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10082,9 +9346,6 @@ packages: pumpify@1.5.1: resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} - punycode@1.4.1: - resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -10099,10 +9360,6 @@ packages: resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} engines: {node: '>=6.0.0'} - q@1.5.1: - resolution: {integrity: sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==} - engines: {node: '>=0.6.0', teleport: '>=0.2.0'} - qrcode@1.5.3: resolution: {integrity: sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg==} engines: {node: '>=10.13.0'} @@ -10120,22 +9377,10 @@ packages: resolution: {integrity: sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==} engines: {node: '>=0.6'} - qs@6.12.1: - resolution: {integrity: sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==} - engines: {node: '>=0.6'} - qs@6.5.3: resolution: {integrity: sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==} engines: {node: '>=0.6'} - query-string@4.3.4: - resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==} - engines: {node: '>=0.10.0'} - - querystring-es3@0.2.1: - resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} - engines: {node: '>=0.4.x'} - querystringify@2.2.0: resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} @@ -10166,10 +9411,6 @@ packages: resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==} engines: {node: '>= 0.6.0'} - randomatic@3.1.1: - resolution: {integrity: sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==} - engines: {node: '>= 0.10.0'} - range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -10242,9 +9483,6 @@ packages: resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} engines: {node: '>=8'} - readable-stream@1.0.34: - resolution: {integrity: sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==} - readable-stream@1.1.14: resolution: {integrity: sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==} @@ -10304,12 +9542,6 @@ packages: resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==} engines: {node: '>=4'} - reduce-css-calc@1.3.0: - resolution: {integrity: sha512-0dVfwYVOlf/LBA2ec4OwQ6p3X9mYxn/wOl2xTcLwjnPYrkgEfPx3VI4eGCH3rQLlPISG5v9I9bkZosKsNRTRKA==} - - reduce-function-call@1.0.3: - resolution: {integrity: sha512-Hl/tuV2VDgWgCSEeWMLwxLZqX7OK59eU1guxXsRKTAyeYimivsKdtcV4fu3r710tpG5GmDKDhQ0HSZLExnNmyQ==} - reflect-metadata@0.2.2: resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} @@ -10329,18 +9561,10 @@ packages: regenerator-transform@0.15.2: resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} - regex-cache@0.4.4: - resolution: {integrity: sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==} - engines: {node: '>=0.10.0'} - regexp.prototype.flags@1.5.0: resolution: {integrity: sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA==} engines: {node: '>= 0.4'} - regexp.prototype.flags@1.5.2: - resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} - engines: {node: '>= 0.4'} - regexpu-core@5.3.2: resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} engines: {node: '>=4'} @@ -10364,20 +9588,9 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} - remove-trailing-separator@1.1.0: - resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} - rename@1.0.4: resolution: {integrity: sha512-YMM6Fn3lrFOCjhORKjj+z/yizj8WSzv3F3YUlpJA20fteWCb0HbJU19nvuRBPUM5dWgxJcHP+kix3M+5NowJyA==} - repeat-element@1.1.4: - resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} - engines: {node: '>=0.10.0'} - - repeat-string@1.6.1: - resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} - engines: {node: '>=0.10'} - request-progress@3.0.0: resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} @@ -10386,11 +9599,6 @@ packages: engines: {node: '>= 6'} deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 - request@2.9.203: - resolution: {integrity: sha512-OWtna9w7yRI/gcfu3VaURgIwE1FHgbz5+fHGQ9GJTHcJ4+uvHnDjXd+N7mVDOv5+1fp1CRPzUSY2wcM345Z2Fw==} - engines: {'0': node >= 0.3.6} - deprecated: request has been deprecated, see https://github.com/request/request/issues/3142 - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -10402,11 +9610,6 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - requirejs@2.1.4: - resolution: {integrity: sha512-i7EPWQSHAVh+UNGUzenTR7FeZIQWK8Lo5U9G/PRWAnj7MvZaYD4Bo6QmgK0biPaTvjuQCuqf5JzUxTRq833Ebg==} - engines: {node: '>=0.4.0'} - hasBin: true - requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} @@ -10462,23 +9665,9 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - revalidator@0.1.8: - resolution: {integrity: sha512-xcBILK2pA9oh4SiinPEZfhP8HfrB/ha+a2fTMyl7Om2WjlDVrOQy99N2MXXlUHqGJz4qEu2duXxHJjDWuK/0xg==} - engines: {node: '>= 0.4.0'} - rfdc@1.3.0: resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==} - right-align@0.1.3: - resolution: {integrity: sha512-yqINtL/G7vs2v+dFIZmFUDbnVyFUJFKd6gK22Kgo6R4jfJGFtisKyncWDDULgjfqf4ASQuIQyjJ7XZ+3aWpsAg==} - engines: {node: '>=0.10.0'} - - rimraf@1.0.9: - resolution: {integrity: sha512-vMboELGrBSgcKZ1fVCCfLEvRdhIeOhDAK4XhjuKXFFs/TOkvWxjT9RRHw2kDhM139zfU3wUQbJkRanMXLl7zFA==} - - rimraf@2.0.3: - resolution: {integrity: sha512-uR09PSoW2+1hW0hquRqxb+Ae2h6R5ls3OAy2oNekQFtqbSJkltkhKRa+OhZKoxWsN9195Gp1vg7sELDRoJ8a3w==} - rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} hasBin: true @@ -10491,9 +9680,6 @@ packages: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true - ripemd160@0.2.0: - resolution: {integrity: sha512-JJsJ74Mw4sUDDisXGDnNNyN9xWmt5HcH6Kwvb/0m/IvTKjnLAtZfzeoLdpxk44AxQZki54oCCd+Kt0nPQ2AF2g==} - rollup@4.17.2: resolution: {integrity: sha512-/9ClTJPByC0U4zNLowV1tMBe8yMEAxewtR3cUNX5BoEpGH3dQEWpJLr6CLp0fPdYRF/fzVOgvDb1zXuakwF5kQ==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -10542,9 +9728,6 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - save-pixels-jpeg-js-upgrade@2.3.4-jpeg-js-upgrade.0: - resolution: {integrity: sha512-mFeQrydaAVTYQjywMvuNtjHmYZwAXZlo96Xouh3I7wTYDdUhMttoEPQsfk6EP+Wxt+fo/B8SW/A8dfhBImhKIw==} - sax@1.2.4: resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==} @@ -10569,10 +9752,6 @@ packages: resolution: {integrity: sha512-Rh266MLDYNeML5h90ttdMwfXe1+Nc4LAWd9X1KdJe8pPHP4kFmvLZALtsMNHNdvTyQygbEC0D59sIz47DIaq8w==} engines: {node: '>=8'} - semver@1.0.14: - resolution: {integrity: sha512-edb8Hl6pnVrKQauQHTqQkRlpZB5RZ/pEe2ir3C3Ztdst0qIayag31dSLsxexLRe80NiWkCffTF5MB7XrGydhSQ==} - hasBin: true - semver@5.7.1: resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} hasBin: true @@ -10595,10 +9774,6 @@ packages: resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} engines: {node: '>= 0.8.0'} - serve-index@1.9.1: - resolution: {integrity: sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==} - engines: {node: '>= 0.8.0'} - serve-static@1.15.0: resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} engines: {node: '>= 0.8.0'} @@ -10609,27 +9784,12 @@ packages: set-cookie-parser@2.6.0: resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} - set-function-length@1.2.2: - resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} - engines: {node: '>= 0.4'} - - set-function-name@2.0.2: - resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} - engines: {node: '>= 0.4'} - setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} - setprototypeof@1.1.0: - resolution: {integrity: sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==} - setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sha.js@2.2.6: - resolution: {integrity: sha512-GC+qN4sf/O6bDwz6CHaz8HVQfLbbNyIsXpTZLiD5c1badnWA63WVAH1msoCq+fXcV0dZ50jxTqKA8seu40845A==} - hasBin: true - sha.js@2.4.11: resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true @@ -10664,16 +9824,9 @@ packages: side-channel@1.0.4: resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} - side-channel@1.0.6: - resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} - engines: {node: '>= 0.4'} - siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} - sigmund@1.0.1: - resolution: {integrity: sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==} - signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -10800,13 +9953,6 @@ packages: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - sockjs-client@1.6.1: - resolution: {integrity: sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw==} - engines: {node: '>=12'} - - sockjs@0.3.24: - resolution: {integrity: sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==} - socks-proxy-agent@8.0.2: resolution: {integrity: sha512-8zuqoLv1aP/66PHF5TqwJ7Czm3Yv32urJQHrVyhD7mmA6d61Zv8cIXQYPTWwmg6qlupnPvs/QKDmfa4P/qct2g==} engines: {node: '>= 14'} @@ -10829,9 +9975,6 @@ packages: sortablejs@1.14.0: resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} - source-list-map@0.1.8: - resolution: {integrity: sha512-cabwdhnSNf/tTDMh/DXZXlkeQLvdYT5xfGYBohqHG7wb3bBQrQlHQNWM9NWSOboXXK1zgwz6JzS5e4hZq9vxMw==} - source-map-js@1.0.2: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} @@ -10846,14 +9989,6 @@ packages: source-map-support@0.5.21: resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - source-map@0.4.4: - resolution: {integrity: sha512-Y8nIfcb1s/7DcobUz1yOO1GSp7gyL+D9zLHDehT7iRESqGSxjJ448Sg7rvfgsRJCnKLdSl11uGf0s9X80cH0/A==} - engines: {node: '>=0.8.0'} - - source-map@0.5.7: - resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} - engines: {node: '>=0.10.0'} - source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} @@ -10899,9 +10034,6 @@ packages: resolution: {integrity: sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} - stack-trace@0.0.10: - resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -10917,10 +10049,6 @@ packages: engines: {node: '>=16'} hasBin: true - statuses@1.5.0: - resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} - engines: {node: '>= 0.6'} - statuses@2.0.1: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} @@ -10958,21 +10086,12 @@ packages: resolution: {integrity: sha512-/Mvij0Br5bUwJpCvqAUZMEDIWmdRxEyllvVj8Ukw5lIWJePxfpSsz4px5jg9+R6B9tO8sQSqjg4HJvQ/pZk8Tg==} hasBin: true - stream-browserify@2.0.2: - resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==} - stream-browserify@3.0.0: resolution: {integrity: sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==} - stream-cache@0.0.2: - resolution: {integrity: sha512-FsMTiRi4aXOcbL3M2lh7yAOWqM7kfVWQfkJ6kelrhdKNpJJVm0IebICQ2LURsbC5w9XfPSRwd9DkfqDHR9OP3g==} - stream-combiner@0.0.4: resolution: {integrity: sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==} - stream-http@2.8.3: - resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==} - stream-parser@0.3.1: resolution: {integrity: sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==} @@ -10996,10 +10115,6 @@ packages: strict-event-emitter@0.5.1: resolution: {integrity: sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==} - strict-uri-encode@1.1.0: - resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} - engines: {node: '>=0.10.0'} - string-argv@0.3.1: resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==} engines: {node: '>=0.6.19'} @@ -11038,10 +10153,6 @@ packages: stringz@2.1.0: resolution: {integrity: sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==} - strip-ansi@3.0.1: - resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} - engines: {node: '>=0.10.0'} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -11096,27 +10207,12 @@ packages: resolution: {integrity: sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==} engines: {node: '>=14.16'} - style-loader@0.13.2: - resolution: {integrity: sha512-0lN0o7DS1G/HRoYJQMEO3yP+tNCuAnNuX1mt/2Yw4edSok45vebtyJoHUyBREasuPYBtZpC3d8wvgY/WD68ZJg==} - - style@0.0.3: - resolution: {integrity: sha512-HDhQ0NTbt7xR9jj9b5zWTelA9iHbg8PtdarlceLzMMQ2gEZjd/Rpmd8PJzkxey5q/fC0cwFKJ18BmW9KGmRTcA==} - engines: {node: '>=0.2.0'} - stylehacks@6.1.1: resolution: {integrity: sha512-gSTTEQ670cJNoaeIp9KX6lZmm8LJ3jPB5yJmX8Zq/wQxOsAFXV3qjWzHas3YYk1qesuVIyYWWUpZ0vSE/dTSGg==} engines: {node: ^14 || ^16 || >=18.0} peerDependencies: postcss: ^8.4.31 - supports-color@2.0.0: - resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} - engines: {node: '>=0.8.0'} - - supports-color@3.2.3: - resolution: {integrity: sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==} - engines: {node: '>=0.8.0'} - supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} @@ -11141,12 +10237,6 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - svgo@0.7.2: - resolution: {integrity: sha512-jT/g9FFMoe9lu2IT6HtAxTA7RR2XOrmcrmCtGnyB/+GQnV6ZjNn+KOHZbZ35yL81+1F/aB6OeEsJztzBQ2EEwA==} - engines: {node: '>=0.10.0'} - deprecated: This SVGO version is no longer supported. Upgrade to v2.x.x. - hasBin: true - svgo@3.2.0: resolution: {integrity: sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==} engines: {node: '>=14.0.0'} @@ -11161,10 +10251,6 @@ packages: os: [darwin, linux, win32, freebsd, openbsd, netbsd, sunos, android] hasBin: true - tapable@0.1.10: - resolution: {integrity: sha512-jX8Et4hHg57mug1/079yitEKWGB3LCwoxByLsNim89LABq8NqgiX+6iYVOsq0vX8uJHkU+DZ5fnq95f800bEsQ==} - engines: {node: '>=0.6'} - tar-fs@2.1.1: resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} @@ -11175,10 +10261,6 @@ packages: tar-stream@3.1.6: resolution: {integrity: sha512-B/UyjYwPpMBv+PaFSWAmtYjwdrlEaZQEhMIBFNC5oEG8lpiW8XjcSdmEaClj28ArfKScKHs2nshz3k2le6crsg==} - tar@0.1.20: - resolution: {integrity: sha512-RW79YhJI8vSyOcVv0OzOr7DVGkhtFhyz6cufNETPF7dmCJ/kQH+ubpCZkV8Wb7RvQlPuGplQPy0o2ihElzkEZA==} - deprecated: This version of tar is no longer supported, and will not receive security updates. Please upgrade asap. - tar@4.4.19: resolution: {integrity: sha512-a20gEsvHnWe0ygBY8JbxoM4w3SJdhc7ZAuxkLqh+nvNQN2IOt0B5lLgM490X5Hl8FF0dl0tOf2ewFYAlIFgzVA==} engines: {node: '>=4.5'} @@ -11250,14 +10332,6 @@ packages: through@2.3.8: resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} - time-stamp@2.2.0: - resolution: {integrity: sha512-zxke8goJQpBeEgD82CXABeMh0LSJcj7CXEd0OHOg45HgcofF7pxNwZm9+RknpxpDhwN4gFpySkApKfFYfRQnUA==} - engines: {node: '>=0.10.0'} - - timers-browserify@2.0.12: - resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} - engines: {node: '>=0.6.0'} - tiny-invariant@1.3.1: resolution: {integrity: sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==} @@ -11289,9 +10363,6 @@ packages: tmpl@1.0.5: resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} - to-arraybuffer@1.0.1: - resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} - to-data-view@1.1.0: resolution: {integrity: sha512-1eAdufMg6mwgmlojAx3QeMnzB/BTVp7Tbndi3U7ftcT2zCZadjxkkmLmd97zmaxWi+sgGcgWrokmpEoy0Dn0vQ==} @@ -11408,9 +10479,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - tty-browserify@0.0.0: - resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} - tunnel-agent@0.6.0: resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} @@ -11555,19 +10623,11 @@ packages: ufo@1.3.2: resolution: {integrity: sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==} - uglify-js@2.7.5: - resolution: {integrity: sha512-RvbIYn4DIadCg1MV7YP7OrpxnVrtEieZzbK0KSQvwWGAHojqWJxInkQhmtYGRo9PTwwkJkljIgzMyA1VitEc4Q==} - engines: {node: '>=0.8.0'} - hasBin: true - uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} hasBin: true - uglify-to-browserify@1.0.2: - resolution: {integrity: sha512-vb2s1lYx2xBtUgy+ta+b2J/GLVUR+wmpINwHePmPRhOsIVCG2wDzKJ0n14GslH1BifsqVzSOwQhRaCAsZ/nI4Q==} - uid2@0.0.4: resolution: {integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==} @@ -11585,9 +10645,6 @@ packages: undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} - underscore@1.3.3: - resolution: {integrity: sha512-ddgUaY7xyrznJ0tbSUZgvNdv5qbiF6XcUBTrHgdCOVUrxJYWozD5KyiRjtIwds1reZ7O1iPLv5rIyqnVAcS6gg==} - undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} @@ -11617,9 +10674,6 @@ packages: uniq@1.0.1: resolution: {integrity: sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==} - uniqs@2.0.0: - resolution: {integrity: sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ==} - unique-filename@3.0.0: resolution: {integrity: sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -11682,9 +10736,6 @@ packages: url-parse@1.5.10: resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} - url@0.11.3: - resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} - utf-8-validate@6.0.3: resolution: {integrity: sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==} engines: {node: '>=6.14.2'} @@ -11692,16 +10743,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - util@0.10.4: - resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} - util@0.12.5: resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} - utile@0.1.7: - resolution: {integrity: sha512-ODuep9+cqmpklRDjEPS1JtY27+zg3MUfeWf8/NOVIJeMJB2nZBOemPXnIeseVkFcvGq9euiIMkS4HiUAEIFbow==} - engines: {node: '>= 0.6.4'} - utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -11743,9 +10787,6 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vendors@1.0.4: - resolution: {integrity: sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w==} - verror@1.10.0: resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==} engines: {'0': node >=0.6.0} @@ -11829,9 +10870,6 @@ packages: webdriverio: optional: true - vm-browserify@0.0.4: - resolution: {integrity: sha512-NyZNR3WDah+NPkjh/YmhuWSsT4a0mF0BJYgUmvrJ70zxjTXh5Y2Asobxlh0Nfs0PCFB5FVpRJft7NozAWFMwLQ==} - void-elements@3.1.0: resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} engines: {node: '>=0.10.0'} @@ -11941,9 +10979,6 @@ packages: walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} - watchpack@0.2.9: - resolution: {integrity: sha512-hmLWdxNfe0Ou1xaRj+ublbOYUaZJfVz1VuHQfERLVlUrLS21gUaGa2gWRl8L5Ej1aUS3KxFN+1qoWK4kZLMvKw==} - watchpack@2.4.0: resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==} engines: {node: '>=10.13.0'} @@ -11970,32 +11005,6 @@ packages: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} - webm-wasm@0.4.1: - resolution: {integrity: sha512-FOKtpyY8tXcaWEarq2+o9v+DvnVqeuJPXDSfgp99UWMavkSUNsSh1T4By6Q9BBgTFTWxrcJNlwxVvHGQ3/xP5Q==} - - webp-hero@0.0.2: - resolution: {integrity: sha512-XDN8k2DZerXAawblkGKbcRXAz3WjY6Id5fTmrsOvblzFg5jELfoDCOxRDHD3zIGJo3OPEjLRsVS6Kzl36HxjqA==} - - webpack-core@0.6.9: - resolution: {integrity: sha512-P6ZUGXn5buTEZyTStCHHLwtWGKSm/jA629Zgp4pcHSsy60CCsT9MaHDxNIPL+GGJ2KwOgI6ORwQtHcrYHAt2UQ==} - engines: {node: '>=0.6'} - - webpack-dev-middleware@1.12.2: - resolution: {integrity: sha512-FCrqPy1yy/sN6U/SaEZcHKRXGlqU0DUaEBL45jkUYoB8foVb6wCnbIJ1HKIx+qUFTW+3JpVcCJCxZ8VATL4e+A==} - engines: {node: '>=0.6'} - peerDependencies: - webpack: ^1.0.0 || ^2.0.0 || ^3.0.0 - - webpack-dev-server@1.16.5: - resolution: {integrity: sha512-on9j8SBuJXa2lzyIAv0DasJT8SteshUrEjjKc/mc8D68U7RN0mIBZksAcjnPW72RSJa9scWZ+C+Dme76LDH+lA==} - hasBin: true - peerDependencies: - webpack: '>=1.3.0 <3' - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -12003,27 +11012,6 @@ packages: webpack-virtual-modules@0.5.0: resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} - webpack@1.15.0: - resolution: {integrity: sha512-+8bxNSHMZCWBa6hi++2A2pw9GmLUWY6lII+aIXlgUPpB+ClNrUKgP8hx0w+hxjWhX81hclUYPGFg+7NxgLTUYQ==} - engines: {node: '>=0.6'} - hasBin: true - peerDependencies: - webpack-cli: '*' - webpack-command: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - webpack-command: - optional: true - - websocket-driver@0.7.4: - resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} - engines: {node: '>=0.8.0'} - - websocket-extensions@0.1.4: - resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} - engines: {node: '>=0.8.0'} - whatwg-encoding@3.1.1: resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} engines: {node: '>=18'} @@ -12043,10 +11031,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whet.extend@0.9.9: - resolution: {integrity: sha512-mmIPAft2vTgEILgPeZFqE/wWh24SEsR/k+N9fJ3Jxrz44iDFy9aemCxdksfURSHYFCLmvs/d/7Iso5XjPpNfrA==} - engines: {node: '>=0.6.0'} - which-boxed-primitive@1.0.2: resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} @@ -12082,26 +11066,10 @@ packages: wide-align@1.1.5: resolution: {integrity: sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==} - window-size@0.1.0: - resolution: {integrity: sha512-1pTPQDKTdd61ozlKGNCjhNRd+KPmgLSGa3mZTHoOliaGcESD8G1PXhh7c1fgiPjVbNVfgy2Faw4BI8/m0cC8Mg==} - engines: {node: '>= 0.8.0'} - - winston@0.6.2: - resolution: {integrity: sha512-BzHNq8X415XGFkGPT+ACKTj95ghSAbR4eThAtKg4OC4PYVn5FLIdTETYUv76f4QxUcG1ps6yqnbO1K/81hGIzQ==} - engines: {node: '>= 0.4.0'} - with@7.0.2: resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} engines: {node: '>= 10.0.0'} - wordwrap@0.0.2: - resolution: {integrity: sha512-xSBsCeh+g+dinoBv3GAOWM4LcVVO68wLXRanibtBSdUvkGWQRGeE9P7IwU9EmDDi4jA6L44lz15CGMwdw9N5+Q==} - engines: {node: '>=0.4.0'} - - wordwrap@0.0.3: - resolution: {integrity: sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==} - engines: {node: '>=0.4.0'} - wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} @@ -12209,9 +11177,6 @@ packages: resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} engines: {node: '>=12'} - yargs@3.10.0: - resolution: {integrity: sha512-QFzUah88GAGy9lyDKGBqZdkYApt63rCXYBGYnEP4xDJPXNqXXnBDACnbrXnViV6jRSqAePwrATi2i8mfYm4L1A==} - yauzl@2.10.0: resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} @@ -12736,7 +11701,7 @@ snapshots: '@babel/traverse': 7.23.5 '@babel/types': 7.23.5 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -12756,7 +11721,7 @@ snapshots: '@babel/traverse': 7.24.0 '@babel/types': 7.24.0 convert-source-map: 2.0.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -12826,7 +11791,7 @@ snapshots: '@babel/core': 7.24.0 '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) lodash.debounce: 4.0.8 resolve: 1.22.8 transitivePeerDependencies: @@ -13597,7 +12562,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.23.9 '@babel/types': 7.23.5 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -13612,7 +12577,7 @@ snapshots: '@babel/helper-split-export-declaration': 7.22.6 '@babel/parser': 7.24.0 '@babel/types': 7.24.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: - supports-color @@ -13658,17 +12623,6 @@ snapshots: dependencies: statuses: 2.0.1 - '@caed0/webp-conv@1.1.0(encoding@0.1.13)': - dependencies: - canvas: 2.11.2(encoding@0.1.13) - gif-encoder-2: 1.0.5 - image-conversion: 2.1.1 - node-webpmux: 3.2.0 - pngjs: 7.0.0 - transitivePeerDependencies: - - encoding - - supports-color - '@canvas/image-data@1.0.0': {} '@colors/colors@1.5.0': @@ -14015,7 +12969,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 globals: 13.19.0 ignore: 5.2.4 @@ -14063,7 +13017,7 @@ snapshots: '@fastify/express@3.0.0': dependencies: - express: 4.19.2(supports-color@3.2.3) + express: 4.19.2 fastify-plugin: 4.5.0 transitivePeerDependencies: - supports-color @@ -14166,7 +13120,7 @@ snapshots: '@humanwhocodes/config-array@0.11.13': dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -14174,7 +13128,7 @@ snapshots: '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -14544,6 +13498,7 @@ snapshots: transitivePeerDependencies: - encoding - supports-color + optional: true '@mcaptcha/core-glue@0.1.0-alpha-5': {} @@ -14752,9 +13707,9 @@ snapshots: dependencies: '@nestjs/common': 10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.3.8(@nestjs/common@10.3.8(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.8)(encoding@0.1.13)(reflect-metadata@0.2.2)(rxjs@7.8.1) - body-parser: 1.20.2(supports-color@3.2.3) + body-parser: 1.20.2 cors: 2.8.5 - express: 4.19.2(supports-color@3.2.3) + express: 4.19.2 multer: 1.4.4-lts.1 tslib: 2.6.2 transitivePeerDependencies: @@ -15571,7 +14526,7 @@ snapshots: ejs: 3.1.9 esbuild: 0.20.2 esbuild-plugin-alias: 0.2.1 - express: 4.19.2(supports-color@3.2.3) + express: 4.19.2 fs-extra: 11.1.1 process: 0.11.10 util: 0.12.5 @@ -15764,7 +14719,7 @@ snapshots: better-opn: 3.0.2 chalk: 4.1.2 cli-table3: 0.6.3 - compression: 1.7.4(supports-color@3.2.3) + compression: 1.7.4 detect-port: 1.5.1 express: 4.18.2 fs-extra: 11.1.1 @@ -16680,6 +15635,8 @@ snapshots: dependencies: '@types/node': 20.12.7 + '@types/w3c-xmlserializer@2.0.4': {} + '@types/web-push@3.6.3': dependencies: '@types/node': 20.12.7 @@ -16711,7 +15668,7 @@ snapshots: '@typescript-eslint/type-utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -16731,7 +15688,7 @@ snapshots: '@typescript-eslint/type-utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.2.4 @@ -16751,7 +15708,7 @@ snapshots: '@typescript-eslint/type-utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 graphemer: 1.4.0 ignore: 5.3.1 @@ -16769,7 +15726,7 @@ snapshots: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 optionalDependencies: typescript: 5.3.3 @@ -16782,7 +15739,7 @@ snapshots: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 optionalDependencies: typescript: 5.3.3 @@ -16795,7 +15752,7 @@ snapshots: '@typescript-eslint/types': 7.7.1 '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 optionalDependencies: typescript: 5.4.5 @@ -16821,7 +15778,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 6.11.0(typescript@5.3.3) '@typescript-eslint/utils': 6.11.0(eslint@8.53.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.53.0 ts-api-utils: 1.0.1(typescript@5.3.3) optionalDependencies: @@ -16833,7 +15790,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.1.0(typescript@5.3.3) '@typescript-eslint/utils': 7.1.0(eslint@8.57.0)(typescript@5.3.3) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.0.1(typescript@5.3.3) optionalDependencies: @@ -16845,7 +15802,7 @@ snapshots: dependencies: '@typescript-eslint/typescript-estree': 7.7.1(typescript@5.4.5) '@typescript-eslint/utils': 7.7.1(eslint@8.57.0)(typescript@5.4.5) - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 ts-api-utils: 1.3.0(typescript@5.4.5) optionalDependencies: @@ -16863,7 +15820,7 @@ snapshots: dependencies: '@typescript-eslint/types': 6.11.0 '@typescript-eslint/visitor-keys': 6.11.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -16877,7 +15834,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.1.0 '@typescript-eslint/visitor-keys': 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.3 @@ -16892,7 +15849,7 @@ snapshots: dependencies: '@typescript-eslint/types': 7.7.1 '@typescript-eslint/visitor-keys': 7.7.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 minimatch: 9.0.4 @@ -17202,8 +16159,6 @@ snapshots: acorn-walk@8.3.2: {} - acorn@3.3.0: {} - acorn@7.4.1: {} acorn@8.11.3: {} @@ -17220,13 +16175,13 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color agent-base@7.1.0: dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -17267,32 +16222,16 @@ snapshots: require-from-string: 2.0.2 uri-js: 4.4.1 - align-text@0.1.4: - dependencies: - kind-of: 3.2.2 - longest: 1.0.1 - repeat-string: 1.6.1 - - almond@0.2.5: {} - - alphanum-sort@1.0.2: {} - - amdefine@1.0.1: {} - ansi-colors@4.1.3: {} ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - ansi-regex@2.1.1: {} - ansi-regex@5.0.1: {} ansi-regex@6.0.1: {} - ansi-styles@2.2.1: {} - ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 @@ -17312,15 +16251,14 @@ snapshots: normalize-path: 3.0.0 picomatch: 2.3.1 - apng-js@1.1.1: {} - app-root-dir@1.0.2: {} app-root-path@3.1.0: {} append-field@1.0.0: {} - aproba@2.0.0: {} + aproba@2.0.0: + optional: true arch@2.2.0: {} @@ -17350,6 +16288,7 @@ snapshots: dependencies: delegates: 1.0.0 readable-stream: 3.6.0 + optional: true arg@5.0.2: {} @@ -17363,12 +16302,6 @@ snapshots: dependencies: deep-equal: 2.2.0 - arr-diff@2.0.0: - dependencies: - arr-flatten: 1.1.0 - - arr-flatten@1.1.0: {} - array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.2 @@ -17386,8 +16319,6 @@ snapshots: array-union@2.1.0: {} - array-unique@0.2.1: {} - array.prototype.findlastindex@1.2.3: dependencies: call-bind: 1.0.2 @@ -17444,11 +16375,6 @@ snapshots: assert-plus@1.0.0: {} - assert@1.5.1: - dependencies: - object.assign: 4.1.4 - util: 0.10.4 - assert@2.1.0: dependencies: call-bind: 1.0.2 @@ -17471,14 +16397,6 @@ snapshots: dependencies: tslib: 2.6.2 - async@0.1.22: {} - - async@0.2.10: {} - - async@0.9.2: {} - - async@1.5.2: {} - async@3.2.4: {} asynckit@0.4.0: {} @@ -17487,22 +16405,13 @@ snapshots: atomic-sleep@1.0.0: {} - autoprefixer@6.7.7: - dependencies: - browserslist: 1.7.7 - caniuse-db: 1.0.30001620 - normalize-range: 0.1.2 - num2fraction: 1.2.2 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - available-typed-arrays@1.0.5: {} avvio@8.3.0: dependencies: '@fastify/error': 3.4.0 archy: 1.0.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) fastq: 1.17.1 transitivePeerDependencies: - supports-color @@ -17533,12 +16442,6 @@ snapshots: b4a@1.6.4: {} - babel-code-frame@6.26.0: - dependencies: - chalk: 1.1.3 - esutils: 2.0.3 - js-tokens: 3.0.2 - babel-core@7.0.0-bridge.0(@babel/core@7.24.0): dependencies: '@babel/core': 7.24.0 @@ -17625,14 +16528,10 @@ snapshots: bail@2.0.2: {} - balanced-match@0.4.2: {} - balanced-match@1.0.2: {} base64-js@1.5.1: {} - batch@0.6.1: {} - bcrypt-pbkdf@1.0.2: dependencies: tweetnacl: 0.14.5 @@ -17645,10 +16544,6 @@ snapshots: big-integer@1.6.51: {} - big.js@3.2.0: {} - - big.js@5.2.2: {} - bin-check@4.1.0: dependencies: execa: 0.7.0 @@ -17675,10 +16570,6 @@ snapshots: blob-util@2.0.2: {} - block-stream@0.0.9: - dependencies: - inherits: 2.0.4 - bluebird@3.7.2: {} blurhash@2.0.5: {} @@ -17689,7 +16580,7 @@ snapshots: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 http-errors: 2.0.0 @@ -17702,11 +16593,11 @@ snapshots: transitivePeerDependencies: - supports-color - body-parser@1.20.2(supports-color@3.2.3): + body-parser@1.20.2: dependencies: bytes: 3.1.2 content-type: 1.0.5 - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 http-errors: 2.0.0 @@ -17736,12 +16627,6 @@ snapshots: dependencies: balanced-match: 1.0.2 - braces@1.8.5: - dependencies: - expand-range: 1.8.2 - preserve: 0.2.0 - repeat-element: 1.1.4 - braces@3.0.2: dependencies: fill-range: 7.0.1 @@ -17755,19 +16640,10 @@ snapshots: browser-assert@1.2.1: {} - browserify-aes@0.4.0: - dependencies: - inherits: 2.0.4 - browserify-zlib@0.1.4: dependencies: pako: 0.2.9 - browserslist@1.7.7: - dependencies: - caniuse-db: 1.0.30001620 - electron-to-chromium: 1.4.686 - browserslist@4.22.2: dependencies: caniuse-lite: 1.0.30001566 @@ -17794,12 +16670,6 @@ snapshots: buffer-from@1.1.2: {} - buffer@4.9.2: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - isarray: 1.0.0 - buffer@5.6.0: dependencies: base64-js: 1.5.1 @@ -17820,8 +16690,6 @@ snapshots: node-gyp-build: 4.6.0 optional: true - builtin-status-codes@3.0.0: {} - bullmq@5.7.8: dependencies: cron-parser: 4.8.1 @@ -17892,14 +16760,6 @@ snapshots: function-bind: 1.1.2 get-intrinsic: 1.2.1 - call-bind@1.0.7: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - set-function-length: 1.2.2 - call-me-maybe@1.0.2: {} callsites@3.1.0: {} @@ -17910,19 +16770,10 @@ snapshots: map-obj: 4.3.0 quick-lru: 4.0.1 - camelcase@1.2.1: {} - camelcase@5.3.1: {} camelcase@6.3.0: {} - caniuse-api@1.6.1: - dependencies: - browserslist: 1.7.7 - caniuse-db: 1.0.30001620 - lodash.memoize: 4.1.2 - lodash.uniq: 4.5.0 - caniuse-api@3.0.0: dependencies: browserslist: 4.23.0 @@ -17930,8 +16781,6 @@ snapshots: lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - caniuse-db@1.0.30001620: {} - caniuse-lite@1.0.30001566: {} caniuse-lite@1.0.30001591: {} @@ -17948,6 +16797,7 @@ snapshots: transitivePeerDependencies: - encoding - supports-color + optional: true caseless@0.12.0: {} @@ -17957,11 +16807,6 @@ snapshots: ccount@2.0.1: {} - center-align@0.1.3: - dependencies: - align-text: 0.1.4 - lazy-cache: 1.0.4 - chai@4.3.10: dependencies: assertion-error: 1.1.0 @@ -17976,14 +16821,6 @@ snapshots: dependencies: chalk: 5.3.0 - chalk@1.1.3: - dependencies: - ansi-styles: 2.2.1 - escape-string-regexp: 1.0.5 - has-ansi: 2.0.0 - strip-ansi: 3.0.1 - supports-color: 2.0.0 - chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -18079,10 +16916,6 @@ snapshots: cjs-module-lexer@1.2.2: {} - clap@1.2.3: - dependencies: - chalk: 1.1.3 - clean-stack@2.2.0: {} clean-stack@5.2.0: @@ -18119,12 +16952,6 @@ snapshots: cli-width@4.1.0: {} - cliui@2.1.0: - dependencies: - center-align: 0.1.3 - right-align: 0.1.3 - wordwrap: 0.0.2 - cliui@6.0.0: dependencies: string-width: 4.2.3 @@ -18159,10 +16986,6 @@ snapshots: co@4.6.0: {} - coa@1.0.4: - dependencies: - q: 1.5.1 - code-error-fragment@0.0.230: {} collect-v8-coverage@1.0.1: {} @@ -18179,22 +17002,13 @@ snapshots: color-name@1.1.4: {} - color-string@0.3.0: - dependencies: - color-name: 1.1.4 - color-string@1.9.1: dependencies: color-name: 1.1.4 simple-swizzle: 0.2.2 - color-support@1.1.3: {} - - color@0.11.4: - dependencies: - clone: 1.0.4 - color-convert: 1.9.3 - color-string: 0.3.0 + color-support@1.1.3: + optional: true color@4.2.3: dependencies: @@ -18205,16 +17019,6 @@ snapshots: colorette@2.0.19: {} - colormin@1.1.2: - dependencies: - color: 0.11.4 - css-color-names: 0.0.4 - has: 1.0.3 - - colors@0.6.2: {} - - colors@1.1.2: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 @@ -18249,12 +17053,12 @@ snapshots: dependencies: mime-db: 1.52.0 - compression@1.7.4(supports-color@3.2.3): + compression@1.7.4: dependencies: accepts: 1.3.8 bytes: 3.0.0 compressible: 2.0.18 - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 on-headers: 1.0.2 safe-buffer: 5.1.2 vary: 1.1.2 @@ -18277,31 +17081,22 @@ snapshots: ini: 1.3.8 proto-list: 1.2.4 - connect-history-api-fallback@1.6.0: {} - consola@2.15.3: {} - console-browserify@1.2.0: {} - - console-control-strings@1.1.0: {} + console-control-strings@1.1.0: + optional: true constantinople@4.0.1: dependencies: '@babel/parser': 7.23.9 '@babel/types': 7.23.5 - constants-browserify@1.0.0: {} - content-disposition@0.5.4: dependencies: safe-buffer: 5.2.1 content-type@1.0.5: {} - contentstream@1.0.0: - dependencies: - readable-stream: 1.0.34 - convert-source-map@2.0.0: {} cookie-signature@1.0.6: {} @@ -18386,36 +17181,12 @@ snapshots: shebang-command: 2.0.0 which: 2.0.2 - crypto-browserify@3.3.0: - dependencies: - browserify-aes: 0.4.0 - pbkdf2-compat: 2.0.1 - ripemd160: 0.2.0 - sha.js: 2.2.6 - crypto-random-string@2.0.0: {} - css-color-names@0.0.4: {} - css-declaration-sorter@7.2.0(postcss@8.4.38): dependencies: postcss: 8.4.38 - css-loader@0.26.4: - dependencies: - babel-code-frame: 6.26.0 - css-selector-tokenizer: 0.7.3 - cssnano: 3.10.0 - loader-utils: 1.4.2 - lodash.camelcase: 4.3.0 - object-assign: 4.1.1 - postcss: 5.2.18 - postcss-modules-extract-imports: 1.2.1 - postcss-modules-local-by-default: 1.2.0 - postcss-modules-scope: 1.1.0 - postcss-modules-values: 1.3.0 - source-list-map: 0.1.8 - css-select@5.1.0: dependencies: boolbase: 1.0.0 @@ -18424,11 +17195,6 @@ snapshots: domutils: 3.0.1 nth-check: 2.1.1 - css-selector-tokenizer@0.7.3: - dependencies: - cssesc: 3.0.0 - fastparse: 1.1.2 - css-tree@2.2.1: dependencies: mdn-data: 2.0.28 @@ -18483,52 +17249,12 @@ snapshots: dependencies: postcss: 8.4.38 - cssnano@3.10.0: - dependencies: - autoprefixer: 6.7.7 - decamelize: 1.2.0 - defined: 1.0.1 - has: 1.0.3 - object-assign: 4.1.1 - postcss: 5.2.18 - postcss-calc: 5.3.1 - postcss-colormin: 2.2.2 - postcss-convert-values: 2.6.1 - postcss-discard-comments: 2.0.4 - postcss-discard-duplicates: 2.1.0 - postcss-discard-empty: 2.1.0 - postcss-discard-overridden: 0.1.1 - postcss-discard-unused: 2.2.3 - postcss-filter-plugins: 2.0.3 - postcss-merge-idents: 2.1.7 - postcss-merge-longhand: 2.0.2 - postcss-merge-rules: 2.1.2 - postcss-minify-font-values: 1.0.5 - postcss-minify-gradients: 1.0.5 - postcss-minify-params: 1.2.2 - postcss-minify-selectors: 2.1.1 - postcss-normalize-charset: 1.1.1 - postcss-normalize-url: 3.0.8 - postcss-ordered-values: 2.2.3 - postcss-reduce-idents: 2.4.0 - postcss-reduce-initial: 1.0.1 - postcss-reduce-transforms: 1.0.4 - postcss-svgo: 2.1.6 - postcss-unique-selectors: 2.0.2 - postcss-value-parser: 3.3.1 - postcss-zindex: 2.2.0 - cssnano@6.1.2(postcss@8.4.38): dependencies: cssnano-preset-default: 6.1.2(postcss@8.4.38) lilconfig: 3.1.1 postcss: 8.4.38 - csso@2.3.2: - dependencies: - clap: 1.2.3 - source-map: 0.5.7 - csso@5.0.5: dependencies: css-tree: 2.2.1 @@ -18543,8 +17269,6 @@ snapshots: dependencies: uniq: 1.0.1 - cycle@1.0.3: {} - cypress@13.7.3: dependencies: '@cypress/request': 3.0.0 @@ -18656,17 +17380,9 @@ snapshots: de-indent@1.0.2: {} - debug@2.6.9(supports-color@3.2.3): + debug@2.6.9: dependencies: ms: 2.0.0 - optionalDependencies: - supports-color: 3.2.3 - - debug@3.2.7(supports-color@3.2.3): - dependencies: - ms: 2.1.3 - optionalDependencies: - supports-color: 3.2.3 debug@3.2.7(supports-color@8.1.1): dependencies: @@ -18713,6 +17429,7 @@ snapshots: decompress-response@4.2.1: dependencies: mimic-response: 2.1.0 + optional: true decompress-response@6.0.0: dependencies: @@ -18733,15 +17450,6 @@ snapshots: dependencies: type-detect: 4.0.8 - deep-equal@1.1.2: - dependencies: - is-arguments: 1.1.1 - is-date-object: 1.0.5 - is-regex: 1.1.4 - object-is: 1.1.5 - object-keys: 1.1.1 - regexp.prototype.flags: 1.5.2 - deep-equal@2.2.0: dependencies: call-bind: 1.0.2 @@ -18777,12 +17485,6 @@ snapshots: defer-to-connect@2.0.1: {} - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.0 - es-errors: 1.3.0 - gopd: 1.0.1 - define-lazy-prop@2.0.0: {} define-properties@1.2.0: @@ -18790,14 +17492,6 @@ snapshots: has-property-descriptors: 1.0.0 object-keys: 1.1.1 - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.0 - object-keys: 1.1.1 - - defined@1.0.1: {} - defu@6.1.4: {} del@6.1.1: @@ -18813,12 +17507,11 @@ snapshots: delayed-stream@1.0.0: {} - delegates@1.0.0: {} + delegates@1.0.0: + optional: true denque@2.1.0: {} - depd@1.1.2: {} - depd@2.0.0: {} dequal@2.0.3: {} @@ -18827,7 +17520,8 @@ snapshots: detect-indent@6.1.0: {} - detect-libc@2.0.2: {} + detect-libc@2.0.2: + optional: true detect-libc@2.0.3: {} @@ -18840,7 +17534,7 @@ snapshots: detect-port@1.5.1: dependencies: address: 1.2.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -18882,8 +17576,6 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 - domain-browser@1.2.0: {} - domelementtype@2.3.0: {} domhandler@5.0.3: @@ -18943,10 +17635,6 @@ snapshots: emoji-regex@9.2.2: {} - emojis-list@2.1.0: {} - - emojis-list@3.0.0: {} - encode-utf8@1.0.3: {} encodeurl@1.0.2: {} @@ -18960,12 +17648,6 @@ snapshots: dependencies: once: 1.4.0 - enhanced-resolve@0.9.1: - dependencies: - graceful-fs: 4.2.11 - memory-fs: 0.2.0 - tapable: 0.1.10 - enquirer@2.3.6: dependencies: ansi-colors: 4.1.3 @@ -18980,10 +17662,6 @@ snapshots: err-code@2.0.3: {} - errno@0.1.8: - dependencies: - prr: 1.0.1 - error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -19030,12 +17708,6 @@ snapshots: unbox-primitive: 1.0.2 which-typed-array: 1.1.11 - es-define-property@1.0.0: - dependencies: - get-intrinsic: 1.2.4 - - es-errors@1.3.0: {} - es-get-iterator@1.1.3: dependencies: call-bind: 1.0.2 @@ -19078,7 +17750,7 @@ snapshots: esbuild-register@3.5.0(esbuild@0.20.2): dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) esbuild: 0.20.2 transitivePeerDependencies: - supports-color @@ -19195,7 +17867,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -19203,7 +17875,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@6.11.0(eslint@8.53.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.53.0): dependencies: - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 6.11.0(eslint@8.53.0)(typescript@5.3.3) eslint: 8.53.0 @@ -19213,7 +17885,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.0(eslint@8.57.0)(typescript@5.3.3))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 7.1.0(eslint@8.57.0)(typescript@5.3.3) eslint: 8.57.0 @@ -19223,7 +17895,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.7.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 7.7.1(eslint@8.57.0)(typescript@5.4.5) eslint: 8.57.0 @@ -19237,7 +17909,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.53.0 eslint-import-resolver-node: 0.3.9 @@ -19264,7 +17936,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -19291,7 +17963,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -19348,7 +18020,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -19391,7 +18063,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -19427,8 +18099,6 @@ snapshots: acorn-jsx: 5.3.2(acorn@8.11.3) eslint-visitor-keys: 3.4.3 - esprima@2.7.3: {} - esprima@4.0.1: {} esquery@1.4.2: @@ -19469,12 +18139,8 @@ snapshots: eventemitter3@5.0.1: {} - events@1.1.1: {} - events@3.3.0: {} - eventsource@2.0.2: {} - execa@0.7.0: dependencies: cross-spawn: 5.1.0 @@ -19539,14 +18205,6 @@ snapshots: exit@0.1.2: {} - expand-brackets@0.1.5: - dependencies: - is-posix-bracket: 0.1.1 - - expand-range@1.8.2: - dependencies: - fill-range: 2.2.4 - expect@29.7.0: dependencies: '@jest/expect-utils': 29.7.0 @@ -19566,12 +18224,12 @@ snapshots: content-type: 1.0.5 cookie: 0.5.0 cookie-signature: 1.0.6 - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 depd: 2.0.0 encodeurl: 1.0.2 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0(supports-color@3.2.3) + finalhandler: 1.2.0 fresh: 0.5.2 http-errors: 2.0.0 merge-descriptors: 1.0.1 @@ -19583,8 +18241,8 @@ snapshots: qs: 6.11.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0(supports-color@3.2.3) - serve-static: 1.15.0(supports-color@3.2.3) + send: 0.18.0 + serve-static: 1.15.0 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -19593,21 +18251,21 @@ snapshots: transitivePeerDependencies: - supports-color - express@4.19.2(supports-color@3.2.3): + express@4.19.2: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.2(supports-color@3.2.3) + body-parser: 1.20.2 content-disposition: 0.5.4 content-type: 1.0.5 cookie: 0.6.0 cookie-signature: 1.0.6 - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 depd: 2.0.0 encodeurl: 1.0.2 escape-html: 1.0.3 etag: 1.8.1 - finalhandler: 1.2.0(supports-color@3.2.3) + finalhandler: 1.2.0 fresh: 0.5.2 http-errors: 2.0.0 merge-descriptors: 1.0.1 @@ -19619,8 +18277,8 @@ snapshots: qs: 6.11.0 range-parser: 1.2.1 safe-buffer: 5.2.1 - send: 0.18.0(supports-color@3.2.3) - serve-static: 1.15.0(supports-color@3.2.3) + send: 0.18.0 + serve-static: 1.15.0 setprototypeof: 1.2.0 statuses: 2.0.1 type-is: 1.6.18 @@ -19640,10 +18298,6 @@ snapshots: extend@3.0.2: {} - extglob@0.3.2: - dependencies: - is-extglob: 1.0.0 - extract-zip@2.0.1(supports-color@8.1.1): dependencies: debug: 4.3.4(supports-color@8.1.1) @@ -19656,8 +18310,6 @@ snapshots: extsprintf@1.3.0: {} - eyes@0.1.8: {} - fast-content-type-parse@1.1.0: {} fast-decode-uri-component@1.0.1: {} @@ -19674,8 +18326,6 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.5 - fast-json-patch@1.2.2: {} - fast-json-stable-stringify@2.1.0: {} fast-json-stringify@5.8.0: @@ -19732,8 +18382,6 @@ snapshots: transitivePeerDependencies: - supports-color - fastparse@1.1.2: {} - fastq@1.15.0: dependencies: reusify: 1.0.4 @@ -19742,10 +18390,6 @@ snapshots: dependencies: reusify: 1.0.4 - faye-websocket@0.11.4: - dependencies: - websocket-driver: 0.7.4 - fb-watchman@2.0.2: dependencies: bser: 2.1.1 @@ -19794,8 +18438,6 @@ snapshots: dependencies: minimatch: 5.1.2 - filename-regex@2.0.1: {} - filename-reserved-regex@3.0.0: {} filenamify@5.1.1: @@ -19804,21 +18446,13 @@ snapshots: strip-outer: 2.0.0 trim-repeated: 2.0.0 - fill-range@2.2.4: - dependencies: - is-number: 2.1.0 - isobject: 2.1.0 - randomatic: 3.1.1 - repeat-element: 1.1.4 - repeat-string: 1.6.1 - fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 - finalhandler@1.2.0(supports-color@3.2.3): + finalhandler@1.2.0: dependencies: - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 encodeurl: 1.0.2 escape-html: 1.0.3 on-finished: 2.4.1 @@ -19882,8 +18516,6 @@ snapshots: flatted@3.2.7: {} - flatten@1.0.3: {} - flow-parser@0.202.0: {} fluent-ffmpeg@2.1.2: @@ -19893,18 +18525,12 @@ snapshots: follow-redirects@1.15.2(debug@4.3.4): optionalDependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) for-each@0.3.3: dependencies: is-callable: 1.2.7 - for-in@1.0.2: {} - - for-own@0.1.5: - dependencies: - for-in: 1.0.2 - foreground-child@3.1.1: dependencies: cross-spawn: 7.0.3 @@ -19989,19 +18615,6 @@ snapshots: fsevents@2.3.3: optional: true - fstream-ignore@0.0.10: - dependencies: - fstream: 0.1.31 - inherits: 2.0.4 - minimatch: 0.3.0 - - fstream@0.1.31: - dependencies: - graceful-fs: 3.0.12 - inherits: 2.0.4 - mkdirp: 0.5.6 - rimraf: 2.7.1 - function-bind@1.1.2: {} function.prototype.name@1.1.5: @@ -20024,6 +18637,7 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 wide-align: 1.1.5 + optional: true gensync@1.0.0-beta.2: {} @@ -20038,14 +18652,6 @@ snapshots: has-proto: 1.0.1 has-symbols: 1.0.3 - get-intrinsic@1.2.4: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - has-proto: 1.0.1 - has-symbols: 1.0.3 - hasown: 2.0.0 - get-npm-tarball-url@2.0.3: {} get-package-type@0.1.0: {} @@ -20091,24 +18697,10 @@ snapshots: dependencies: assert-plus: 1.0.0 - gif-encoder-2@1.0.5: {} - gif-encoder@0.4.1: dependencies: readable-stream: 1.1.14 - gif-frames@1.0.1: - dependencies: - get-pixels-frame-info-update: 3.3.2 - multi-integer-range: 3.0.0 - save-pixels-jpeg-js-upgrade: 2.3.4-jpeg-js-upgrade.0 - - gifshot@0.4.5: {} - - gifuct-js@2.1.2: - dependencies: - js-binary-schema-parser: 2.0.3 - giget@1.1.2: dependencies: colorette: 2.0.19 @@ -20123,15 +18715,6 @@ snapshots: github-slugger@2.0.0: {} - glob-base@0.3.0: - dependencies: - glob-parent: 2.0.0 - is-glob: 2.0.1 - - glob-parent@2.0.0: - dependencies: - is-glob: 2.0.1 - glob-parent@5.1.2: dependencies: is-glob: 4.0.3 @@ -20256,13 +18839,6 @@ snapshots: p-cancelable: 4.0.1 responselike: 3.0.0 - graceful-fs@1.1.14: - optional: true - - graceful-fs@3.0.12: - dependencies: - natives: 1.1.6 - graceful-fs@4.2.11: {} grapheme-splitter@1.0.4: {} @@ -20306,14 +18882,8 @@ snapshots: hard-rejection@2.1.0: {} - has-ansi@2.0.0: - dependencies: - ansi-regex: 2.1.1 - has-bigints@1.0.2: {} - has-flag@1.0.0: {} - has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -20322,10 +18892,6 @@ snapshots: dependencies: get-intrinsic: 1.2.1 - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.0 - has-proto@1.0.1: {} has-symbols@1.0.3: {} @@ -20334,7 +18900,8 @@ snapshots: dependencies: has-symbols: 1.0.3 - has-unicode@2.0.1: {} + has-unicode@2.0.1: + optional: true has@1.0.3: dependencies: @@ -20376,8 +18943,6 @@ snapshots: hpagent@1.2.0: {} - html-comment-regex@1.1.2: {} - html-encoding-sniffer@4.0.0: dependencies: whatwg-encoding: 3.1.1 @@ -20399,13 +18964,6 @@ snapshots: http-cache-semantics@4.1.1: {} - http-errors@1.6.3: - dependencies: - depd: 1.1.2 - inherits: 2.0.3 - setprototypeof: 1.1.0 - statuses: 1.5.0 - http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -20416,32 +18974,13 @@ snapshots: http-link-header@1.1.3: {} - http-parser-js@0.5.8: {} - http-proxy-agent@7.0.0: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color - http-proxy-middleware@0.17.4: - dependencies: - http-proxy: 1.18.1 - is-glob: 3.1.0 - lodash: 4.17.21 - micromatch: 2.3.11 - transitivePeerDependencies: - - debug - - http-proxy@1.18.1: - dependencies: - eventemitter3: 4.0.7 - follow-redirects: 1.15.2(debug@4.3.4) - requires-port: 1.0.0 - transitivePeerDependencies: - - debug - http-signature@1.2.0: dependencies: assert-plus: 1.0.0 @@ -20466,12 +19005,10 @@ snapshots: http_ece@1.2.0: {} - https-browserify@0.0.1: {} - https-proxy-agent@2.2.4: dependencies: agent-base: 4.3.0 - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color optional: true @@ -20479,14 +19016,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -20498,8 +19035,6 @@ snapshots: human-signals@5.0.0: {} - i@0.3.7: {} - iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 @@ -20508,8 +19043,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - icss-replace-symbols@1.1.0: {} - idb-keyval@6.2.1: {} ieee754@1.2.1: {} @@ -20524,8 +19057,6 @@ snapshots: ignore@5.3.1: {} - image-conversion@2.1.1: {} - immutable@4.2.2: {} import-fresh@3.3.0: @@ -20546,19 +19077,11 @@ snapshots: indent-string@5.0.0: {} - indexes-of@1.0.1: {} - - indexof@0.0.1: {} - inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - inherits@1.0.2: {} - - inherits@2.0.3: {} - inherits@2.0.4: {} ini@1.3.8: {} @@ -20575,15 +19098,13 @@ snapshots: has: 1.0.3 side-channel: 1.0.4 - interpret@0.6.6: {} - intersection-observer@0.12.2: {} ioredis@5.4.1: dependencies: '@ioredis/commands': 1.2.0 cluster-key-slot: 1.1.2 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) denque: 2.1.0 lodash.defaults: 4.2.0 lodash.isarguments: 3.1.0 @@ -20615,8 +19136,6 @@ snapshots: irregular-plurals@3.5.0: {} - is-absolute-url@2.1.0: {} - is-absolute-url@4.0.1: {} is-arguments@1.1.1: @@ -20667,21 +19186,11 @@ snapshots: is-docker@2.2.1: {} - is-dotfile@1.0.3: {} - - is-equal-shallow@0.1.3: - dependencies: - is-primitive: 2.0.0 - is-expression@4.0.0: dependencies: acorn: 7.4.1 object-assign: 4.1.1 - is-extendable@0.1.1: {} - - is-extglob@1.0.0: {} - is-extglob@2.1.1: {} is-file-animated@1.0.2: {} @@ -20694,14 +19203,6 @@ snapshots: dependencies: has-tostringtag: 1.0.0 - is-glob@2.0.1: - dependencies: - is-extglob: 1.0.0 - - is-glob@3.1.0: - dependencies: - is-extglob: 2.1.1 - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -20736,12 +19237,6 @@ snapshots: dependencies: has-tostringtag: 1.0.0 - is-number@2.1.0: - dependencies: - kind-of: 3.2.2 - - is-number@4.0.0: {} - is-number@7.0.0: {} is-path-cwd@2.2.0: {} @@ -20758,12 +19253,8 @@ snapshots: is-plain-object@5.0.0: {} - is-posix-bracket@0.1.1: {} - is-potential-custom-element-name@1.0.1: {} - is-primitive@2.0.0: {} - is-promise@2.2.2: {} is-regex@1.1.4: @@ -20787,10 +19278,6 @@ snapshots: dependencies: has-tostringtag: 1.0.0 - is-svg@2.1.0: - dependencies: - html-comment-regex: 1.1.2 - is-svg@5.0.0: dependencies: fast-xml-parser: 4.2.5 @@ -20836,10 +19323,6 @@ snapshots: isexe@3.1.1: {} - isobject@2.1.0: - dependencies: - isarray: 1.0.0 - isobject@3.0.1: {} isstream@0.1.2: {} @@ -20874,7 +19357,7 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: @@ -20900,25 +19383,6 @@ snapshots: filelist: 1.0.4 minimatch: 3.1.2 - jamjs@0.2.17: - dependencies: - almond: 0.2.5 - async: 0.1.22 - fstream: 0.1.31 - fstream-ignore: 0.0.10 - inherits: 1.0.2 - mime: 1.2.11 - minimatch: 0.2.14 - mkdirp: 0.3.5 - ncp: 0.2.7 - prompt: 0.2.1 - request: 2.9.203 - requirejs: 2.1.4 - rimraf: 2.0.3 - semver: 1.0.14 - tar: 0.1.20 - underscore: 1.3.3 - jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -21251,8 +19715,6 @@ snapshots: jpeg-js@0.3.7: {} - js-base64@2.6.4: {} - js-beautify@1.14.9: dependencies: config-chain: 1.1.13 @@ -21260,12 +19722,8 @@ snapshots: glob: 8.1.0 nopt: 6.0.0 - js-binary-schema-parser@2.0.3: {} - js-stringify@1.0.2: {} - js-tokens@3.0.2: {} - js-tokens@4.0.0: {} js-yaml@3.14.1: @@ -21273,11 +19731,6 @@ snapshots: argparse: 1.0.10 esprima: 4.0.1 - js-yaml@3.7.0: - dependencies: - argparse: 1.0.10 - esprima: 2.7.3 - js-yaml@4.1.0: dependencies: argparse: 2.0.1 @@ -21351,10 +19804,6 @@ snapshots: json-buffer@3.0.1: {} - json-merge-patch@0.2.3: - dependencies: - deep-equal: 1.1.2 - json-parse-even-better-errors@2.3.1: {} json-schema-traverse@0.4.1: {} @@ -21372,8 +19821,6 @@ snapshots: code-error-fragment: 0.0.230 grapheme-splitter: 1.0.4 - json5@0.5.1: {} - json5@1.0.2: dependencies: minimist: 1.2.8 @@ -21449,10 +19896,6 @@ snapshots: dependencies: json-buffer: 3.0.1 - kind-of@3.2.2: - dependencies: - is-buffer: 1.1.6 - kind-of@6.0.3: {} kleur@3.0.3: {} @@ -21469,8 +19912,6 @@ snapshots: lazy-ass@1.6.0: {} - lazy-cache@1.0.4: {} - lazy-universal-dotenv@4.0.0: dependencies: app-root-dir: 1.0.2 @@ -21488,21 +19929,6 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libwebpjs@0.0.1: - dependencies: - css-loader: 0.26.4 - fast-json-patch: 1.2.2 - jamjs: 0.2.17 - json-merge-patch: 0.2.3 - style: 0.0.3 - style-loader: 0.13.2 - webpack: 1.15.0 - webpack-dev-server: 1.16.5(webpack@1.15.0) - transitivePeerDependencies: - - debug - - webpack-cli - - webpack-command - light-my-request@5.11.0: dependencies: cookie: 0.5.0 @@ -21526,19 +19952,6 @@ snapshots: optionalDependencies: enquirer: 2.3.6 - loader-utils@0.2.17: - dependencies: - big.js: 3.2.0 - emojis-list: 2.1.0 - json5: 0.5.1 - object-assign: 4.1.1 - - loader-utils@1.4.2: - dependencies: - big.js: 5.2.2 - emojis-list: 3.0.0 - json5: 1.0.2 - local-pkg@0.4.3: {} locate-path@3.0.0: @@ -21554,8 +19967,6 @@ snapshots: dependencies: p-locate: 5.0.0 - lodash.camelcase@4.3.0: {} - lodash.debounce@4.0.8: {} lodash.defaults@4.2.0: {} @@ -21592,8 +20003,6 @@ snapshots: longest-streak@3.1.0: {} - longest@1.0.1: {} - loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -21612,8 +20021,6 @@ snapshots: lru-cache@10.2.2: {} - lru-cache@2.7.3: {} - lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 @@ -21694,10 +20101,6 @@ snapshots: dependencies: react: 18.3.1 - math-expression-evaluator@1.4.0: {} - - math-random@1.0.4: {} - matter-js@0.19.0: {} mdast-util-find-and-replace@3.0.1: @@ -21817,18 +20220,6 @@ snapshots: dependencies: map-or-similar: 1.5.0 - memory-fs@0.2.0: {} - - memory-fs@0.3.0: - dependencies: - errno: 0.1.8 - readable-stream: 2.3.7 - - memory-fs@0.4.1: - dependencies: - errno: 0.1.8 - readable-stream: 2.3.7 - meow@9.0.0: dependencies: '@types/minimist': 1.2.2 @@ -22032,7 +20423,7 @@ snapshots: micromark@4.0.0: dependencies: '@types/debug': 4.1.12 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) decode-named-character-reference: 1.0.2 devlop: 1.1.0 micromark-core-commonmark: 2.0.0 @@ -22051,22 +20442,6 @@ snapshots: transitivePeerDependencies: - supports-color - micromatch@2.3.11: - dependencies: - arr-diff: 2.0.0 - array-unique: 0.2.1 - braces: 1.8.5 - expand-brackets: 0.1.5 - extglob: 0.3.2 - filename-regex: 2.0.1 - is-extglob: 1.0.0 - is-glob: 2.0.1 - kind-of: 3.2.2 - normalize-path: 2.1.1 - object.omit: 2.0.1 - parse-glob: 3.0.4 - regex-cache: 0.4.4 - micromatch@4.0.5: dependencies: braces: 3.0.2 @@ -22078,8 +20453,6 @@ snapshots: dependencies: mime-db: 1.52.0 - mime@1.2.11: {} - mime@1.6.0: {} mime@3.0.0: {} @@ -22090,7 +20463,8 @@ snapshots: mimic-response@1.0.1: {} - mimic-response@2.1.0: {} + mimic-response@2.1.0: + optional: true mimic-response@3.1.0: {} @@ -22100,16 +20474,6 @@ snapshots: minimalistic-assert@1.0.1: {} - minimatch@0.2.14: - dependencies: - lru-cache: 2.7.3 - sigmund: 1.0.1 - - minimatch@0.3.0: - dependencies: - lru-cache: 2.7.3 - sigmund: 1.0.1 - minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 @@ -22140,8 +20504,6 @@ snapshots: is-plain-obj: 1.1.0 kind-of: 6.0.3 - minimist@0.0.10: {} - minimist@1.2.8: {} minipass-collect@1.0.2: @@ -22194,8 +20556,6 @@ snapshots: mkdirp-classic@0.5.3: {} - mkdirp@0.3.5: {} - mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -22300,12 +20660,8 @@ snapshots: nanoid@5.0.7: {} - natives@1.1.6: {} - natural-compare@1.4.0: {} - ncp@0.2.7: {} - ncp@2.0.0: {} ndarray-ops@1.2.2: @@ -22329,7 +20685,7 @@ snapshots: needle@2.9.1: dependencies: - debug: 3.2.7(supports-color@3.2.3) + debug: 3.2.7(supports-color@8.1.1) iconv-lite: 0.4.24 sax: 1.2.4 transitivePeerDependencies: @@ -22413,36 +20769,8 @@ snapshots: node-int64@0.4.0: {} - node-libs-browser@0.7.0: - dependencies: - assert: 1.5.1 - browserify-zlib: 0.1.4 - buffer: 4.9.2 - console-browserify: 1.2.0 - constants-browserify: 1.0.0 - crypto-browserify: 3.3.0 - domain-browser: 1.2.0 - events: 1.1.1 - https-browserify: 0.0.1 - os-browserify: 0.2.1 - path-browserify: 0.0.0 - process: 0.11.10 - punycode: 1.4.1 - querystring-es3: 0.2.1 - readable-stream: 2.3.7 - stream-browserify: 2.0.2 - stream-http: 2.8.3 - string_decoder: 0.10.31 - timers-browserify: 2.0.12 - tty-browserify: 0.0.0 - url: 0.11.3 - util: 0.10.4 - vm-browserify: 0.0.4 - node-releases@2.0.14: {} - node-webpmux@3.2.0: {} - nodemailer@6.9.13: {} nodemon@3.0.2: @@ -22480,6 +20808,7 @@ snapshots: nopt@5.0.0: dependencies: abbrev: 1.1.1 + optional: true nopt@6.0.0: dependencies: @@ -22503,21 +20832,8 @@ snapshots: semver: 7.6.0 validate-npm-package-license: 3.0.4 - normalize-path@2.1.1: - dependencies: - remove-trailing-separator: 1.1.0 - normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - - normalize-url@1.9.1: - dependencies: - object-assign: 4.1.1 - prepend-http: 1.0.4 - query-string: 4.3.4 - sort-keys: 1.1.2 - normalize-url@6.1.0: {} normalize-url@8.0.0: {} @@ -22540,6 +20856,7 @@ snapshots: console-control-strings: 1.1.0 gauge: 3.0.2 set-blocking: 2.0.0 + optional: true nsfwjs@2.4.2(@tensorflow/tfjs@4.4.0(encoding@0.1.13)(seedrandom@3.0.5)): dependencies: @@ -22550,8 +20867,6 @@ snapshots: dependencies: boolbase: 1.0.0 - num2fraction@1.2.2: {} - nwsapi@2.2.9: {} oauth-sign@0.9.0: {} @@ -22560,7 +20875,7 @@ snapshots: oauth2orize@1.12.0: dependencies: - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 uid2: 0.0.4 utils-merge: 1.0.1 transitivePeerDependencies: @@ -22572,8 +20887,6 @@ snapshots: object-inspect@1.12.3: {} - object-inspect@1.13.1: {} - object-is@1.1.5: dependencies: call-bind: 1.0.2 @@ -22601,11 +20914,6 @@ snapshots: es-abstract: 1.22.1 get-intrinsic: 1.2.1 - object.omit@2.0.1: - dependencies: - for-own: 0.1.5 - is-extendable: 0.1.1 - object.values@1.1.7: dependencies: call-bind: 1.0.2 @@ -22640,8 +20948,6 @@ snapshots: dependencies: mimic-fn: 4.0.0 - open@0.0.5: {} - open@8.4.2: dependencies: define-lazy-prop: 2.0.0 @@ -22659,11 +20965,6 @@ snapshots: undici: 5.28.2 yargs-parser: 21.1.1 - optimist@0.6.1: - dependencies: - minimist: 0.0.10 - wordwrap: 0.0.3 - optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 @@ -22685,8 +20986,6 @@ snapshots: strip-ansi: 6.0.1 wcwidth: 1.0.1 - os-browserify@0.2.1: {} - os-filter-obj@2.0.0: dependencies: arch: 2.2.0 @@ -22758,13 +21057,6 @@ snapshots: dependencies: data-uri-to-buffer: 0.0.3 - parse-glob@3.0.4: - dependencies: - glob-base: 0.3.0 - is-dotfile: 1.0.3 - is-extglob: 1.0.0 - is-glob: 2.0.1 - parse-json@5.2.0: dependencies: '@babel/code-frame': 7.23.5 @@ -22793,8 +21085,6 @@ snapshots: parseurl@1.3.3: {} - path-browserify@0.0.0: {} - path-browserify@1.0.1: {} path-exists@3.0.0: {} @@ -22841,8 +21131,6 @@ snapshots: dependencies: through: 2.3.8 - pbkdf2-compat@2.0.1: {} - peek-readable@5.0.0: {} peek-stream@1.1.3: @@ -22965,10 +21253,6 @@ snapshots: mlly: 1.5.0 pathe: 1.1.2 - pkginfo@0.2.3: {} - - pkginfo@0.4.1: {} - plimit-lit@1.5.0: dependencies: queue-lit: 1.5.0 @@ -22983,30 +21267,16 @@ snapshots: pngjs@5.0.0: {} - pngjs@7.0.0: {} - polished@4.2.2: dependencies: '@babel/runtime': 7.23.4 - postcss-calc@5.3.1: - dependencies: - postcss: 5.2.18 - postcss-message-helpers: 2.0.0 - reduce-css-calc: 1.3.0 - postcss-calc@9.0.1(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-selector-parser: 6.0.15 postcss-value-parser: 4.2.0 - postcss-colormin@2.2.2: - dependencies: - colormin: 1.1.2 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-colormin@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 @@ -23015,82 +21285,34 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-convert-values@2.6.1: - dependencies: - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-convert-values@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-discard-comments@2.0.4: - dependencies: - postcss: 5.2.18 - postcss-discard-comments@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 - postcss-discard-duplicates@2.1.0: - dependencies: - postcss: 5.2.18 - postcss-discard-duplicates@6.0.3(postcss@8.4.38): dependencies: postcss: 8.4.38 - postcss-discard-empty@2.1.0: - dependencies: - postcss: 5.2.18 - postcss-discard-empty@6.0.3(postcss@8.4.38): dependencies: postcss: 8.4.38 - postcss-discard-overridden@0.1.1: - dependencies: - postcss: 5.2.18 - postcss-discard-overridden@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 - postcss-discard-unused@2.2.3: - dependencies: - postcss: 5.2.18 - uniqs: 2.0.0 - - postcss-filter-plugins@2.0.3: - dependencies: - postcss: 5.2.18 - - postcss-merge-idents@2.1.7: - dependencies: - has: 1.0.3 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - - postcss-merge-longhand@2.0.2: - dependencies: - postcss: 5.2.18 - postcss-merge-longhand@6.0.5(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 stylehacks: 6.1.1(postcss@8.4.38) - postcss-merge-rules@2.1.2: - dependencies: - browserslist: 1.7.7 - caniuse-api: 1.6.1 - postcss: 5.2.18 - postcss-selector-parser: 2.2.3 - vendors: 1.0.4 - postcss-merge-rules@6.1.1(postcss@8.4.38): dependencies: browserslist: 4.23.0 @@ -23099,24 +21321,11 @@ snapshots: postcss: 8.4.38 postcss-selector-parser: 6.0.16 - postcss-message-helpers@2.0.0: {} - - postcss-minify-font-values@1.0.5: - dependencies: - object-assign: 4.1.1 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-minify-font-values@6.1.0(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-minify-gradients@1.0.5: - dependencies: - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-minify-gradients@6.0.3(postcss@8.4.38): dependencies: colord: 2.9.3 @@ -23124,13 +21333,6 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-minify-params@1.2.2: - dependencies: - alphanum-sort: 1.0.2 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - uniqs: 2.0.0 - postcss-minify-params@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 @@ -23138,41 +21340,11 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-minify-selectors@2.1.1: - dependencies: - alphanum-sort: 1.0.2 - has: 1.0.3 - postcss: 5.2.18 - postcss-selector-parser: 2.2.3 - postcss-minify-selectors@6.0.4(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-selector-parser: 6.0.16 - postcss-modules-extract-imports@1.2.1: - dependencies: - postcss: 6.0.23 - - postcss-modules-local-by-default@1.2.0: - dependencies: - css-selector-tokenizer: 0.7.3 - postcss: 6.0.23 - - postcss-modules-scope@1.1.0: - dependencies: - css-selector-tokenizer: 0.7.3 - postcss: 6.0.23 - - postcss-modules-values@1.3.0: - dependencies: - icss-replace-symbols: 1.1.0 - postcss: 6.0.23 - - postcss-normalize-charset@1.1.1: - dependencies: - postcss: 5.2.18 - postcss-normalize-charset@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 @@ -23208,13 +21380,6 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-normalize-url@3.0.8: - dependencies: - is-absolute-url: 2.1.0 - normalize-url: 1.9.1 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-normalize-url@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 @@ -23225,49 +21390,23 @@ snapshots: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-ordered-values@2.2.3: - dependencies: - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-ordered-values@6.0.2(postcss@8.4.38): dependencies: cssnano-utils: 4.0.2(postcss@8.4.38) postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-reduce-idents@2.4.0: - dependencies: - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - - postcss-reduce-initial@1.0.1: - dependencies: - postcss: 5.2.18 - postcss-reduce-initial@6.1.0(postcss@8.4.38): dependencies: browserslist: 4.23.0 caniuse-api: 3.0.0 postcss: 8.4.38 - postcss-reduce-transforms@1.0.4: - dependencies: - has: 1.0.3 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - postcss-reduce-transforms@6.0.2(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 - postcss-selector-parser@2.2.3: - dependencies: - flatten: 1.0.3 - indexes-of: 1.0.1 - uniq: 1.0.1 - postcss-selector-parser@6.0.15: dependencies: cssesc: 3.0.0 @@ -23278,53 +21417,19 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss-svgo@2.1.6: - dependencies: - is-svg: 2.1.0 - postcss: 5.2.18 - postcss-value-parser: 3.3.1 - svgo: 0.7.2 - postcss-svgo@6.0.3(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-value-parser: 4.2.0 svgo: 3.2.0 - postcss-unique-selectors@2.0.2: - dependencies: - alphanum-sort: 1.0.2 - postcss: 5.2.18 - uniqs: 2.0.0 - postcss-unique-selectors@6.0.4(postcss@8.4.38): dependencies: postcss: 8.4.38 postcss-selector-parser: 6.0.16 - postcss-value-parser@3.3.1: {} - postcss-value-parser@4.2.0: {} - postcss-zindex@2.2.0: - dependencies: - has: 1.0.3 - postcss: 5.2.18 - uniqs: 2.0.0 - - postcss@5.2.18: - dependencies: - chalk: 1.1.3 - js-base64: 2.6.4 - source-map: 0.5.7 - supports-color: 3.2.3 - - postcss@6.0.23: - dependencies: - chalk: 2.4.2 - source-map: 0.6.1 - supports-color: 5.5.0 - postcss@8.4.38: dependencies: nanoid: 3.3.7 @@ -23355,10 +21460,6 @@ snapshots: prelude-ls@1.2.1: {} - prepend-http@1.0.4: {} - - preserve@0.2.0: {} - prettier@3.2.5: {} pretty-bytes@5.6.0: {} @@ -23422,14 +21523,6 @@ snapshots: dependencies: asap: 2.0.6 - prompt@0.2.1: - dependencies: - colors: 0.6.2 - pkginfo: 0.4.1 - revalidator: 0.1.8 - utile: 0.1.7 - winston: 0.6.2 - prompts@2.4.2: dependencies: kleur: 3.0.3 @@ -23458,8 +21551,6 @@ snapshots: transitivePeerDependencies: - encoding - prr@1.0.1: {} - ps-list@8.1.1: {} ps-tree@1.2.0: @@ -23555,8 +21646,6 @@ snapshots: inherits: 2.0.4 pump: 2.0.1 - punycode@1.4.1: {} - punycode@2.3.1: {} pure-rand@6.0.0: {} @@ -23567,8 +21656,6 @@ snapshots: pvutils@1.1.3: {} - q@1.5.1: {} - qrcode@1.5.3: dependencies: dijkstrajs: 1.0.2 @@ -23588,19 +21675,8 @@ snapshots: dependencies: side-channel: 1.0.4 - qs@6.12.1: - dependencies: - side-channel: 1.0.6 - qs@6.5.3: {} - query-string@4.3.4: - dependencies: - object-assign: 4.1.1 - strict-uri-encode: 1.1.0 - - querystring-es3@0.2.1: {} - querystringify@2.2.0: {} queue-lit@1.5.0: {} @@ -23621,12 +21697,6 @@ snapshots: dependencies: json-stringify-safe: 5.0.1 - randomatic@3.1.1: - dependencies: - is-number: 4.0.0 - kind-of: 6.0.3 - math-random: 1.0.4 - range-parser@1.2.1: {} ratelimiter@3.4.1: {} @@ -23720,13 +21790,6 @@ snapshots: parse-json: 5.2.0 type-fest: 0.6.0 - readable-stream@1.0.34: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 0.0.1 - string_decoder: 0.10.31 - readable-stream@1.1.14: dependencies: core-util-is: 1.0.3 @@ -23806,16 +21869,6 @@ snapshots: dependencies: redis-errors: 1.2.0 - reduce-css-calc@1.3.0: - dependencies: - balanced-match: 0.4.2 - math-expression-evaluator: 1.4.0 - reduce-function-call: 1.0.3 - - reduce-function-call@1.0.3: - dependencies: - balanced-match: 1.0.2 - reflect-metadata@0.2.2: {} regenerate-unicode-properties@10.1.0: @@ -23832,23 +21885,12 @@ snapshots: dependencies: '@babel/runtime': 7.23.4 - regex-cache@0.4.4: - dependencies: - is-equal-shallow: 0.1.3 - regexp.prototype.flags@1.5.0: dependencies: call-bind: 1.0.2 define-properties: 1.2.0 functions-have-names: 1.2.3 - regexp.prototype.flags@1.5.2: - dependencies: - call-bind: 1.0.7 - define-properties: 1.2.1 - es-errors: 1.3.0 - set-function-name: 2.0.2 - regexpu-core@5.3.2: dependencies: '@babel/regjsgen': 0.8.0 @@ -23905,18 +21947,12 @@ snapshots: mdast-util-to-markdown: 2.1.0 unified: 11.0.4 - remove-trailing-separator@1.1.0: {} - rename@1.0.4: dependencies: - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 transitivePeerDependencies: - supports-color - repeat-element@1.1.4: {} - - repeat-string@1.6.1: {} - request-progress@3.0.0: dependencies: throttleit: 1.0.0 @@ -23944,16 +21980,12 @@ snapshots: tunnel-agent: 0.6.0 uuid: 3.4.0 - request@2.9.203: {} - require-directory@2.1.1: {} require-from-string@2.0.2: {} require-main-filename@2.0.0: {} - requirejs@2.1.4: {} - requires-port@1.0.0: {} resolve-alpn@1.2.1: {} @@ -24000,20 +22032,8 @@ snapshots: reusify@1.0.4: {} - revalidator@0.1.8: {} - rfdc@1.3.0: {} - right-align@0.1.3: - dependencies: - align-text: 0.1.4 - - rimraf@1.0.9: {} - - rimraf@2.0.3: - optionalDependencies: - graceful-fs: 1.1.14 - rimraf@2.6.3: dependencies: glob: 7.2.3 @@ -24021,13 +22041,12 @@ snapshots: rimraf@2.7.1: dependencies: glob: 7.2.3 + optional: true rimraf@3.0.2: dependencies: glob: 7.2.3 - ripemd160@0.2.0: {} - rollup@4.17.2: dependencies: '@types/estree': 1.0.5 @@ -24105,16 +22124,6 @@ snapshots: immutable: 4.2.2 source-map-js: 1.2.0 - save-pixels-jpeg-js-upgrade@2.3.4-jpeg-js-upgrade.0: - dependencies: - contentstream: 1.0.0 - gif-encoder: 0.4.1 - jpeg-js: 0.3.7 - ndarray: 1.0.19 - ndarray-ops: 1.2.2 - pngjs-nozlib: 1.0.0 - through: 2.3.8 - sax@1.2.4: {} saxes@6.0.0: @@ -24135,8 +22144,6 @@ snapshots: dependencies: semver: 6.3.1 - semver@1.0.14: {} - semver@5.7.1: {} semver@6.3.1: {} @@ -24149,9 +22156,9 @@ snapshots: dependencies: lru-cache: 6.0.0 - send@0.18.0(supports-color@3.2.3): + send@0.18.0: dependencies: - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 depd: 2.0.0 destroy: 1.2.0 encodeurl: 1.0.2 @@ -24167,24 +22174,12 @@ snapshots: transitivePeerDependencies: - supports-color - serve-index@1.9.1(supports-color@3.2.3): - dependencies: - accepts: 1.3.8 - batch: 0.6.1 - debug: 2.6.9(supports-color@3.2.3) - escape-html: 1.0.3 - http-errors: 1.6.3 - mime-types: 2.1.35 - parseurl: 1.3.3 - transitivePeerDependencies: - - supports-color - - serve-static@1.15.0(supports-color@3.2.3): + serve-static@1.15.0: dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0(supports-color@3.2.3) + send: 0.18.0 transitivePeerDependencies: - supports-color @@ -24192,30 +22187,10 @@ snapshots: set-cookie-parser@2.6.0: {} - set-function-length@1.2.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - function-bind: 1.1.2 - get-intrinsic: 1.2.4 - gopd: 1.0.1 - has-property-descriptors: 1.0.2 - - set-function-name@2.0.2: - dependencies: - define-data-property: 1.1.4 - es-errors: 1.3.0 - functions-have-names: 1.2.3 - has-property-descriptors: 1.0.2 - setimmediate@1.0.5: {} - setprototypeof@1.1.0: {} - setprototypeof@1.2.0: {} - sha.js@2.2.6: {} - sha.js@2.4.11: dependencies: inherits: 2.0.4 @@ -24273,34 +22248,27 @@ snapshots: get-intrinsic: 1.2.1 object-inspect: 1.12.3 - side-channel@1.0.6: - dependencies: - call-bind: 1.0.7 - es-errors: 1.3.0 - get-intrinsic: 1.2.4 - object-inspect: 1.13.1 - siginfo@2.0.0: {} - sigmund@1.0.1: {} - signal-exit@3.0.7: {} signal-exit@4.1.0: {} - simple-concat@1.0.1: {} + simple-concat@1.0.1: + optional: true simple-get@3.1.1: dependencies: decompress-response: 4.2.1 once: 1.4.0 simple-concat: 1.0.1 + optional: true simple-oauth2@5.0.0: dependencies: '@hapi/hoek': 10.0.1 '@hapi/wreck': 18.0.1 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) joi: 17.11.0 transitivePeerDependencies: - supports-color @@ -24395,26 +22363,10 @@ snapshots: smart-buffer@4.2.0: {} - sockjs-client@1.6.1(supports-color@3.2.3): - dependencies: - debug: 3.2.7(supports-color@3.2.3) - eventsource: 2.0.2 - faye-websocket: 0.11.4 - inherits: 2.0.4 - url-parse: 1.5.10 - transitivePeerDependencies: - - supports-color - - sockjs@0.3.24: - dependencies: - faye-websocket: 0.11.4 - uuid: 8.3.2 - websocket-driver: 0.7.4 - socks-proxy-agent@8.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) socks: 2.7.1 transitivePeerDependencies: - supports-color @@ -24438,8 +22390,6 @@ snapshots: sortablejs@1.14.0: {} - source-list-map@0.1.8: {} - source-map-js@1.0.2: {} source-map-js@1.2.0: {} @@ -24454,12 +22404,6 @@ snapshots: buffer-from: 1.1.2 source-map: 0.6.1 - source-map@0.4.4: - dependencies: - amdefine: 1.0.1 - - source-map@0.5.7: {} - source-map@0.6.1: {} source-map@0.7.4: {} @@ -24506,8 +22450,6 @@ snapshots: dependencies: minipass: 5.0.0 - stack-trace@0.0.10: {} - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -24521,7 +22463,7 @@ snapshots: arg: 5.0.2 bluebird: 3.7.2 check-more-types: 2.24.0 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) execa: 5.1.1 lazy-ass: 1.6.0 ps-tree: 1.2.0 @@ -24529,8 +22471,6 @@ snapshots: transitivePeerDependencies: - supports-color - statuses@1.5.0: {} - statuses@2.0.1: {} std-env@3.7.0: {} @@ -24566,33 +22506,18 @@ snapshots: - supports-color - utf-8-validate - stream-browserify@2.0.2: - dependencies: - inherits: 2.0.4 - readable-stream: 2.3.7 - stream-browserify@3.0.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.0 - stream-cache@0.0.2: {} - stream-combiner@0.0.4: dependencies: duplexer: 0.1.2 - stream-http@2.8.3: - dependencies: - builtin-status-codes: 3.0.0 - inherits: 2.0.4 - readable-stream: 2.3.7 - to-arraybuffer: 1.0.1 - xtend: 4.0.2 - stream-parser@0.3.1: dependencies: - debug: 2.6.9(supports-color@3.2.3) + debug: 2.6.9 transitivePeerDependencies: - supports-color @@ -24611,8 +22536,6 @@ snapshots: strict-event-emitter@0.5.1: {} - strict-uri-encode@1.1.0: {} - string-argv@0.3.1: {} string-length@4.0.2: @@ -24664,10 +22587,6 @@ snapshots: dependencies: char-regex: 1.0.2 - strip-ansi@3.0.1: - dependencies: - ansi-regex: 2.1.1 - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -24709,24 +22628,12 @@ snapshots: '@tokenizer/token': 0.3.0 peek-readable: 5.0.0 - style-loader@0.13.2: - dependencies: - loader-utils: 1.4.2 - - style@0.0.3: {} - stylehacks@6.1.1(postcss@8.4.38): dependencies: browserslist: 4.23.0 postcss: 8.4.38 postcss-selector-parser: 6.0.16 - supports-color@2.0.0: {} - - supports-color@3.2.3: - dependencies: - has-flag: 1.0.0 - supports-color@5.5.0: dependencies: has-flag: 3.0.0 @@ -24748,16 +22655,6 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svgo@0.7.2: - dependencies: - coa: 1.0.4 - colors: 1.1.2 - csso: 2.3.2 - js-yaml: 3.7.0 - mkdirp: 0.5.6 - sax: 1.2.4 - whet.extend: 0.9.9 - svgo@3.2.0: dependencies: '@trysound/sax': 0.2.0 @@ -24772,8 +22669,6 @@ snapshots: systeminformation@5.22.7: {} - tapable@0.1.10: {} - tar-fs@2.1.1: dependencies: chownr: 1.1.4 @@ -24795,12 +22690,6 @@ snapshots: fast-fifo: 1.3.0 streamx: 2.15.0 - tar@0.1.20: - dependencies: - block-stream: 0.0.9 - fstream: 0.1.31 - inherits: 2.0.4 - tar@4.4.19: dependencies: chownr: 1.1.4 @@ -24887,12 +22776,6 @@ snapshots: through@2.3.8: {} - time-stamp@2.2.0: {} - - timers-browserify@2.0.12: - dependencies: - setimmediate: 1.0.5 - tiny-invariant@1.3.1: {} tiny-invariant@1.3.3: {} @@ -24911,8 +22794,6 @@ snapshots: tmpl@1.0.5: {} - to-arraybuffer@1.0.1: {} - to-data-view@1.1.0: {} to-fast-properties@2.0.0: {} @@ -25025,8 +22906,6 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - tty-browserify@0.0.0: {} - tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 @@ -25097,7 +22976,7 @@ snapshots: chalk: 4.1.2 cli-highlight: 2.1.11 dayjs: 1.11.10 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) dotenv: 16.0.3 glob: 10.3.10 mkdirp: 2.1.6 @@ -25120,18 +22999,9 @@ snapshots: ufo@1.3.2: {} - uglify-js@2.7.5: - dependencies: - async: 0.2.10 - source-map: 0.5.7 - uglify-to-browserify: 1.0.2 - yargs: 3.10.0 - uglify-js@3.17.4: optional: true - uglify-to-browserify@1.0.2: {} - uid2@0.0.4: {} uid@2.0.2: @@ -25149,8 +23019,6 @@ snapshots: undefsafe@2.0.5: {} - underscore@1.3.3: {} - undici-types@5.26.5: {} undici@5.28.2: @@ -25180,8 +23048,6 @@ snapshots: uniq@1.0.1: {} - uniqs@2.0.0: {} - unique-filename@3.0.0: dependencies: unique-slug: 4.0.0 @@ -25253,11 +23119,6 @@ snapshots: querystringify: 2.2.0 requires-port: 1.0.0 - url@0.11.3: - dependencies: - punycode: 1.4.1 - qs: 6.12.1 - utf-8-validate@6.0.3: dependencies: node-gyp-build: 4.6.0 @@ -25265,10 +23126,6 @@ snapshots: util-deprecate@1.0.2: {} - util@0.10.4: - dependencies: - inherits: 2.0.3 - util@0.12.5: dependencies: inherits: 2.0.4 @@ -25277,15 +23134,6 @@ snapshots: is-typed-array: 1.1.10 which-typed-array: 1.1.11 - utile@0.1.7: - dependencies: - async: 0.1.22 - deep-equal: 2.2.0 - i: 0.3.7 - mkdirp: 0.5.6 - ncp: 0.2.7 - rimraf: 1.0.9 - utils-merge@1.0.1: {} uuid@3.4.0: {} @@ -25318,8 +23166,6 @@ snapshots: vary@1.1.2: {} - vendors@1.0.4: {} - verror@1.10.0: dependencies: assert-plus: 1.0.0 @@ -25340,7 +23186,7 @@ snapshots: vite-node@0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3): dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) mlly: 1.5.0 pathe: 1.1.2 picocolors: 1.0.0 @@ -25389,7 +23235,7 @@ snapshots: acorn-walk: 8.3.2 cac: 6.7.14 chai: 4.3.10 - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) local-pkg: 0.4.3 magic-string: 0.30.7 pathe: 1.1.2 @@ -25413,10 +23259,6 @@ snapshots: - supports-color - terser - vm-browserify@0.0.4: - dependencies: - indexof: 0.0.1 - void-elements@3.1.0: {} vscode-jsonrpc@8.2.0: {} @@ -25476,7 +23318,7 @@ snapshots: vue-eslint-parser@9.4.2(eslint@8.57.0): dependencies: - debug: 4.3.4(supports-color@5.5.0) + debug: 4.3.4(supports-color@8.1.1) eslint: 8.57.0 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 @@ -25543,12 +23385,6 @@ snapshots: dependencies: makeerror: 1.0.12 - watchpack@0.2.9: - dependencies: - async: 0.9.2 - chokidar: 3.5.3 - graceful-fs: 4.2.11 - watchpack@2.4.0: dependencies: glob-to-regexp: 0.4.1 @@ -25576,73 +23412,10 @@ snapshots: webidl-conversions@7.0.0: {} - webm-wasm@0.4.1: {} - - webp-hero@0.0.2: {} - - webpack-core@0.6.9: - dependencies: - source-list-map: 0.1.8 - source-map: 0.4.4 - - webpack-dev-middleware@1.12.2(webpack@1.15.0): - dependencies: - memory-fs: 0.4.1 - mime: 1.6.0 - path-is-absolute: 1.0.1 - range-parser: 1.2.1 - time-stamp: 2.2.0 - webpack: 1.15.0 - - webpack-dev-server@1.16.5(webpack@1.15.0): - dependencies: - compression: 1.7.4(supports-color@3.2.3) - connect-history-api-fallback: 1.6.0 - express: 4.19.2(supports-color@3.2.3) - http-proxy-middleware: 0.17.4 - open: 0.0.5 - optimist: 0.6.1 - serve-index: 1.9.1(supports-color@3.2.3) - sockjs: 0.3.24 - sockjs-client: 1.6.1(supports-color@3.2.3) - stream-cache: 0.0.2 - strip-ansi: 3.0.1 - supports-color: 3.2.3 - webpack: 1.15.0 - webpack-dev-middleware: 1.12.2(webpack@1.15.0) - transitivePeerDependencies: - - debug - webpack-sources@3.2.3: {} webpack-virtual-modules@0.5.0: {} - webpack@1.15.0: - dependencies: - acorn: 3.3.0 - async: 1.5.2 - clone: 1.0.4 - enhanced-resolve: 0.9.1 - interpret: 0.6.6 - loader-utils: 0.2.17 - memory-fs: 0.3.0 - mkdirp: 0.5.6 - node-libs-browser: 0.7.0 - optimist: 0.6.1 - supports-color: 3.2.3 - tapable: 0.1.10 - uglify-js: 2.7.5 - watchpack: 0.2.9 - webpack-core: 0.6.9 - - websocket-driver@0.7.4: - dependencies: - http-parser-js: 0.5.8 - safe-buffer: 5.2.1 - websocket-extensions: 0.1.4 - - websocket-extensions@0.1.4: {} - whatwg-encoding@3.1.1: dependencies: iconv-lite: 0.6.3 @@ -25661,8 +23434,6 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - whet.extend@0.9.9: {} - which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 @@ -25708,18 +23479,7 @@ snapshots: wide-align@1.1.5: dependencies: string-width: 4.2.3 - - window-size@0.1.0: {} - - winston@0.6.2: - dependencies: - async: 0.1.22 - colors: 0.6.2 - cycle: 1.0.3 - eyes: 0.1.8 - pkginfo: 0.2.3 - request: 2.9.203 - stack-trace: 0.0.10 + optional: true with@7.0.2: dependencies: @@ -25728,10 +23488,6 @@ snapshots: assert-never: 1.2.1 babel-walk: 3.0.0-canary-5 - wordwrap@0.0.2: {} - - wordwrap@0.0.3: {} - wordwrap@1.0.0: {} wrap-ansi@6.2.0: @@ -25844,13 +23600,6 @@ snapshots: y18n: 5.0.8 yargs-parser: 21.1.1 - yargs@3.10.0: - dependencies: - camelcase: 1.2.1 - cliui: 2.1.0 - decamelize: 1.2.0 - window-size: 0.1.0 - yauzl@2.10.0: dependencies: buffer-crc32: 0.2.13 From aa1e0807f4f901adf7741af15584b4408e798f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=81=BE=E3=81=A3=E3=81=A1=E3=82=83=E3=81=A6=E3=81=83?= =?UTF-8?q?=E3=83=BC=E3=80=82?= <56515516+mattyatea@users.noreply.github.com> Date: Sat, 25 May 2024 01:39:09 +0900 Subject: [PATCH 479/501] Update docker.yml --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 23c1bdbc16..342642127a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: env: - REGISTRY_IMAGE: misskey/misskey + REGISTRY_IMAGE: mattyacocacora/prsmsk-msk TAGS: | type=edge type=ref,event=pr From 653f5cfbc205c85905bd82b0ffa1e88028d20b70 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 29 May 2024 01:19:08 +0900 Subject: [PATCH 480/501] update --- .../migration/1716911535226-gapikey.js | 11 + .../src/core/entities/MetaEntityService.ts | 2 + packages/backend/src/models/Meta.ts | 5 + .../server/api/endpoints/admin/update-meta.ts | 5 +- .../src/server/web/ClientServerService.ts | 1 + .../backend/src/server/web/views/base.pug | 8 + packages/frontend/src/boot/main-boot.ts | 2 +- .../src/components/MkDateSeparatedList.vue | 151 +++++++------- packages/frontend/src/components/MkNotes.vue | 3 +- .../frontend/src/components/MkPagination.vue | 80 +++----- .../frontend/src/components/MkTimeline.vue | 191 +++++------------- packages/frontend/src/instance.ts | 2 + .../frontend/src/pages/admin/branding.vue | 7 + 13 files changed, 203 insertions(+), 265 deletions(-) create mode 100644 packages/backend/migration/1716911535226-gapikey.js diff --git a/packages/backend/migration/1716911535226-gapikey.js b/packages/backend/migration/1716911535226-gapikey.js new file mode 100644 index 0000000000..5ec4594aeb --- /dev/null +++ b/packages/backend/migration/1716911535226-gapikey.js @@ -0,0 +1,11 @@ +export class Gapikey1716911535226 { + name = 'Gapikey1716911535226' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" ADD "googleAnalyticsId" character varying(1024)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "googleAnalyticsId"`); + } +} diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts index 50669e1a3c..363f8f0d2d 100644 --- a/packages/backend/src/core/entities/MetaEntityService.ts +++ b/packages/backend/src/core/entities/MetaEntityService.ts @@ -73,6 +73,7 @@ export class MetaEntityService { bannerLight: instance.bannerLight, iconDark: instance.iconDark, iconLight: instance.iconLight, + googleAnalyticsId: instance.googleAnalyticsId, enableHcaptcha: instance.enableHcaptcha, hcaptchaSiteKey: instance.hcaptchaSiteKey, enableMcaptcha: instance.enableMcaptcha, @@ -88,6 +89,7 @@ export class MetaEntityService { bannerUrl: instance.bannerUrl, infoImageUrl: instance.infoImageUrl, serverErrorImageUrl: instance.serverErrorImageUrl, + googleAnalyticsId: instance.googleAnalyticsId, notFoundImageUrl: instance.notFoundImageUrl, iconUrl: instance.iconUrl, backgroundImageUrl: instance.backgroundImageUrl, diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts index 1b3992abe7..1205553436 100644 --- a/packages/backend/src/models/Meta.ts +++ b/packages/backend/src/models/Meta.ts @@ -139,6 +139,11 @@ export class MiMeta { nullable: true, }) public serverErrorImageUrl: string | null; + @Column('varchar', { + length: 1024, + nullable: true, + }) + public googleAnalyticsId: string | null; @Column('varchar', { length: 1024, diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts index dad4ac7287..0849a71ae8 100644 --- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts +++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts @@ -50,6 +50,7 @@ export const paramDef = { mascotImageUrl: { type: 'string', nullable: true }, bannerUrl: { type: 'string', nullable: true }, serverErrorImageUrl: { type: 'string', nullable: true }, + googleAnalyticsId: { type: 'string', nullable: true }, infoImageUrl: { type: 'string', nullable: true }, notFoundImageUrl: { type: 'string', nullable: true }, iconUrl: { type: 'string', nullable: true }, @@ -257,7 +258,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (ps.serverErrorImageUrl !== undefined) { set.serverErrorImageUrl = ps.serverErrorImageUrl; } - + if (ps.googleAnalyticsId !== undefined) { + set.googleAnalyticsId = ps.googleAnalyticsId; + } if (ps.enableProxyCheckio !== undefined) { set.enableProxyCheckio = ps.enableProxyCheckio; } diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 22709dbca7..e7117b9438 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -191,6 +191,7 @@ export class ClientServerService { appleTouchIcon: meta.app512IconUrl, themeColor: meta.themeColor, serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg', + googleAnalyticsId: meta.googleAnalyticsId ?? null, infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg', notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg', instanceUrl: this.config.url, diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index ec1325e4e9..7e519b37ed 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -35,6 +35,14 @@ html link(rel='prefetch' href=serverErrorImageUrl) link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=notFoundImageUrl) + if googleAnalyticsId + script(async src='https://www.googletagmanager.com/gtag/js?id='+ googleAnalyticsId) + script. + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '#{googleAnalyticsId}'); + //- https://github.com/misskey-dev/misskey/issues/9842 link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v3.3.0') link(rel='modulepreload' href=`/vite/${clientEntry.file}`) diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 5cb19f388a..4118a6f81c 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -96,7 +96,7 @@ export async function mainBoot() { }).render(); } } - } + } } catch (error) { // console.error(error); console.error('Failed to initialise the seasonal screen effect canvas context:', error); diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue index 85e131cf9b..3e17368a58 100644 --- a/packages/frontend/src/components/MkDateSeparatedList.vue +++ b/packages/frontend/src/components/MkDateSeparatedList.vue @@ -43,65 +43,73 @@ export default defineComponent({ setup(props, { slots, expose }) { const $style = useCssModule(); // カスタムレンダラなので使っても大丈夫 + const dateTextCache = new Map<string, string>(); + function getDateText(time: string) { + if (dateTextCache.has(time)) { + return dateTextCache.get(time)!; + } const date = new Date(time).getDate(); const month = new Date(time).getMonth() + 1; - return i18n.tsx.monthAndDay({ + const text = i18n.tsx.monthAndDay({ month: month.toString(), day: date.toString(), }); + dateTextCache.set(time, text); + return text; } if (props.items.length === 0) return; - const renderChildrenImpl = () => props.items.map((item, i) => { - if (!slots || !slots.default) return; + const renderChildrenImpl = () => { + const slotContent = slots.default ? slots.default : () => []; + return props.items.map((item, i) => { + const el = slotContent({ + item: item, + })[0]; + if (el.key == null && item.id) el.key = item.id; - const el = slots.default({ - item: item, - })[0]; - if (el.key == null && item.id) el.key = item.id; - - if ( - i !== props.items.length - 1 && - new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate() - ) { - const separator = h('div', { - class: $style['separator'], - key: item.id + ':separator', - }, h('p', { - class: $style['date'], - }, [ - h('span', { - class: $style['date-1'], + if ( + i !== props.items.length - 1 && + new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate() + ) { + const separator = h('div', { + class: $style['separator'], + key: item.id + ':separator', + }, h('p', { + class: $style['date'], }, [ - h('i', { - class: `ti ti-chevron-up ${$style['date-1-icon']}`, - }), - getDateText(item.createdAt), - ]), - h('span', { - class: $style['date-2'], - }, [ - getDateText(props.items[i + 1].createdAt), - h('i', { - class: `ti ti-chevron-down ${$style['date-2-icon']}`, - }), - ]), - ])); + h('span', { + class: $style['date-1'], + }, [ + h('i', { + class: `ti ti-chevron-up ${$style['date-1-icon']}`, + }), + getDateText(item.createdAt), + ]), + h('span', { + class: $style['date-2'], + }, [ + getDateText(props.items[i + 1].createdAt), + h('i', { + class: `ti ti-chevron-down ${$style['date-2-icon']}`, + }), + ]), + ])); - return [el, separator]; - } else { - if (props.ad && item._shouldInsertAd_) { - return [h(MkAd, { - key: item.id + ':ad', - prefer: ['horizontal', 'horizontal-big'], - }), el]; + return [el, separator]; } else { - return el; + if (props.ad && item._shouldInsertAd_) { + return [h(MkAd, { + key: item.id + ':ad', + prefer: ['horizontal', 'horizontal-big'], + }), el]; + } else { + return el; + } } - } - }); + }); + }; const renderChildren = () => { const children = renderChildrenImpl(); @@ -120,14 +128,12 @@ export default defineComponent({ function onBeforeLeave(element: Element) { const el = element as HTMLElement; - el.style.top = `${el.offsetTop}px`; - el.style.left = `${el.offsetLeft}px`; + el.classList.add('before-leave'); } function onLeaveCancelled(element: Element) { const el = element as HTMLElement; - el.style.top = ''; - el.style.left = ''; + el.classList.remove('before-leave'); } // eslint-disable-next-line vue/no-setup-props-destructure @@ -157,21 +163,21 @@ export default defineComponent({ container-type: inline-size; &:global { - > .list-move { - transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1); - } + > .list-move { + transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1); + } - &.deny-move-transition > .list-move { - transition: none !important; - } + &.deny-move-transition > .list-move { + transition: none !important; + } - > .list-enter-active { - transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1); - } + > .list-enter-active { + transition: transform 0.7s cubic-bezier(0.23, 1, 0.32, 1), opacity 0.7s cubic-bezier(0.23, 1, 0.32, 1); + } - > *:empty { - display: none; - } + > *:empty { + display: none; + } } &:not(.date-separated-list-nogap) > *:not(:last-child) { @@ -194,20 +200,20 @@ export default defineComponent({ .direction-up { &:global { - > .list-enter-from, - > .list-leave-to { - opacity: 0; - transform: translateY(64px); - } + > .list-enter-from, + > .list-leave-to { + opacity: 0; + transform: translateY(64px); + } } } .direction-down { &:global { - > .list-enter-from, - > .list-leave-to { - opacity: 0; - transform: translateY(-64px); - } + > .list-enter-from, + > .list-leave-to { + opacity: 0; + transform: translateY(-64px); + } } } @@ -246,5 +252,8 @@ export default defineComponent({ .date-2-icon { margin-left: 8px; } -</style> +.before-leave { + position: absolute !important; +} +</style> diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index ef5ad766b1..11c99a2cd9 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only :ad="true" :class="$style.notes" > - <MkNote v-if="props.withCw && !note.cw || !props.withCw" :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true" /> + <MkNote v-if="props.withCw && !note.cw || !props.withCw" :key="note._featuredId_ || note._prId_ || note.id" :class="$style.note" :note="note" :withHardMute="true"/> </MkDateSeparatedList> </div> </template> @@ -45,7 +45,6 @@ const props = defineProps<{ disableAutoLoad?: boolean; withCw?: boolean; }>(); - const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); defineExpose({ diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index 19e22f78d0..fc1ec0989a 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -234,69 +234,43 @@ const reload = (): Promise<void> => { return init(); }; -const fetchMore = async (): Promise<void> => { +async function fetchMore(): Promise<void> { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; + moreFetching.value = true; - const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; - await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, { - ...params, - limit: SECOND_FETCH_LIMIT, - ...(props.pagination.offsetMode ? { - offset: offset.value, - } : { - untilId: Array.from(items.value.keys()).at(-1), - }), - }).then(res => { - for (let i = 0; i < res.length; i++) { - const item = res[i]; - if (i === 10) item._shouldInsertAd_ = true; - } + try { + const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; + const response = await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, { + ...params, + limit: SECOND_FETCH_LIMIT, + ...(props.pagination.offsetMode ? { offset: offset.value } : { untilId: Array.from(items.value.keys()).pop() }), + }); - const reverseConcat = _res => { - const oldHeight = scrollableElement.value ? scrollableElement.value.scrollHeight : getBodyScrollHeight(); - const oldScroll = scrollableElement.value ? scrollableElement.value.scrollTop : window.scrollY; + const isReversed = props.pagination.reversed; + if (isReversed) { + const oldHeight = scrollableElement.value?.scrollHeight || 0; + const oldScroll = scrollableElement.value?.scrollTop || 0; - items.value = concatMapWithArray(items.value, _res); + items.value = concatMapWithArray(items.value, response); - return nextTick(() => { - if (scrollableElement.value) { - scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' }); - } else { - window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' }); - } - - return nextTick(); - }); - }; - - if (res.length === 0) { - if (props.pagination.reversed) { - reverseConcat(res).then(() => { - more.value = false; - moreFetching.value = false; + await nextTick(); + if (scrollableElement.value) { + scroll(scrollableElement.value, { + top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), + behavior: 'instant', }); - } else { - items.value = concatMapWithArray(items.value, res); - more.value = false; - moreFetching.value = false; } } else { - if (props.pagination.reversed) { - reverseConcat(res).then(() => { - more.value = true; - moreFetching.value = false; - }); - } else { - items.value = concatMapWithArray(items.value, res); - more.value = true; - moreFetching.value = false; - } + items.value = concatMapWithArray(items.value, response); } - offset.value += res.length; - }, err => { + + more.value = response.length > 0; + } catch (error) { + console.error(error); + } finally { moreFetching.value = false; - }); -}; + } +} const fetchMoreAhead = async (): Promise<void> => { if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 3a65406b1e..4ba1ced710 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -1,8 +1,3 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - <template> <MkPullToRefresh ref="prComponent" :refresher="() => reloadTimeline()"> <MkNotes @@ -10,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only ref="tlComponent" :pagination="paginationQuery" :noGap="!defaultStore.state.showGapBetweenNotesInTimeline" - :withCw="props.withCw" + :withCw="props.withCw" @queue="emit('queue', $event)" @status="prComponent?.setDisabled($event)" /> @@ -39,7 +34,7 @@ const props = withDefaults(defineProps<{ withRenotes?: boolean; withReplies?: boolean; onlyFiles?: boolean; - withCw?: boolean; + withCw?: boolean; }>(), { withRenotes: true, withReplies: false, @@ -55,24 +50,13 @@ const emit = defineEmits<{ provide('inTimeline', true); provide('inChannel', computed(() => props.src === 'channel')); -type TimelineQueryType = { - antennaId?: string, - withRenotes?: boolean, - withReplies?: boolean, - withFiles?: boolean, - visibility?: string, - listId?: string, - channelId?: string, - roleId?: string -} - const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>(); const tlComponent = shallowRef<InstanceType<typeof MkNotes>>(); let tlNotesCount = 0; function prepend(note) { - if (tlComponent.value == null) return; + if (!tlComponent.value) return; tlNotesCount++; @@ -96,11 +80,8 @@ let paginationQuery: Paging | null = null; const stream = useStream(); function connectChannel() { - if (props.src === 'antenna') { - if (props.antenna == null) return; - connection = stream.useChannel('antenna', { - antennaId: props.antenna, - }); + if (props.src === 'antenna' && props.antenna) { + connection = stream.useChannel('antenna', { antennaId: props.antenna }); } else if (props.src === 'home') { connection = stream.useChannel('homeTimeline', { withRenotes: props.withRenotes, @@ -119,15 +100,9 @@ function connectChannel() { withRenotes: props.withRenotes, withReplies: props.withReplies, }); - connection.on('note', prepend); - } else if (props.src === 'social') { - connection = stream.useChannel('hybridTimeline', { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - withFiles: props.onlyFiles ? true : undefined, - }); - } else if (props.src === 'global') { - connection = stream.useChannel('globalTimeline', { + } else if (props.src === 'social' || props.src === 'global') { + const channel = props.src === 'social' ? 'hybridTimeline' : 'globalTimeline'; + connection = stream.useChannel(channel, { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, }); @@ -136,34 +111,25 @@ function connectChannel() { connection.on('mention', prepend); } else if (props.src === 'directs') { const onNote = note => { - if (note.visibility === 'specified') { - prepend(note); - } + if (note.visibility === 'specified') prepend(note); }; connection = stream.useChannel('main'); connection.on('mention', onNote); - } else if (props.src === 'list') { - if (props.list == null) return; + } else if (props.src === 'list' && props.list) { connection = stream.useChannel('userList', { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list, }); - } else if (props.src === 'channel') { - if (props.channel == null) return; - connection = stream.useChannel('channel', { - channelId: props.channel, - }); - } else if (props.src === 'role') { - if (props.role == null) return; - connection = stream.useChannel('roleTimeline', { - roleId: props.role, - }); + } else if (props.src === 'channel' && props.channel) { + connection = stream.useChannel('channel', { channelId: props.channel }); + } else if (props.src === 'role' && props.role) { + connection = stream.useChannel('roleTimeline', { roleId: props.role }); } - if (props.src.startsWith('custom-timeline')) { - return; + + if (props.src !== 'directs' && props.src !== 'mentions') { + connection?.on('note', prepend); } - if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend); } function disconnectChannel() { @@ -172,91 +138,47 @@ function disconnectChannel() { } function updatePaginationQuery() { - let endpoint: keyof Misskey.Endpoints | null; - let query: TimelineQueryType | null; + const endpoints = { + antenna: 'antennas/notes', + home: 'notes/timeline', + local: 'notes/local-timeline', + social: 'notes/hybrid-timeline', + global: 'notes/global-timeline', + media: 'notes/hybrid-timeline', + mentions: 'notes/mentions', + directs: 'notes/mentions', + list: 'notes/user-list-timeline', + channel: 'channels/timeline', + role: 'roles/notes', + }; - if (props.src === 'antenna') { - endpoint = 'antennas/notes'; - query = { - antennaId: props.antenna, - }; - } else if (props.src === 'home') { - endpoint = 'notes/timeline'; - query = { - withRenotes: props.withRenotes, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'local') { - endpoint = 'notes/local-timeline'; - query = { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'social') { - endpoint = 'notes/hybrid-timeline'; - query = { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'global') { - endpoint = 'notes/global-timeline'; - query = { - withRenotes: props.withRenotes, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'media') { - endpoint = 'notes/hybrid-timeline'; - query = { - withFiles: true, - withRenotes: props.withRenotes, - withReplies: false, - }; - } else if (props.src === 'mentions') { - endpoint = 'notes/mentions'; - query = null; - } else if (props.src === 'directs') { - endpoint = 'notes/mentions'; - query = { - visibility: 'specified', - }; - } else if (props.src === 'list') { - endpoint = 'notes/user-list-timeline'; - query = { - withRenotes: props.withRenotes, - withFiles: props.onlyFiles ? true : undefined, - listId: props.list, - }; - } else if (props.src === 'channel') { - endpoint = 'channels/timeline'; - query = { - channelId: props.channel, - }; - } else if (props.src === 'role') { - endpoint = 'roles/notes'; - query = { - roleId: props.role, - }; - } else if (props.src.startsWith('custom-timeline')) { - endpoint = 'notes/any-local-timeline'; - query = { - host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split('-')[2]}`], - remoteToken: defaultStore.state[`remoteLocalTimelineToken${props.src.split('-')[2]}`], - }; - } else { - endpoint = null; - query = null; - } + const queries = { + antenna: { antennaId: props.antenna }, + home: { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined }, + local: { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined }, + social: { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined }, + global: { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined }, + media: { withFiles: true, withRenotes: props.withRenotes, withReplies: false }, + mentions: null, + directs: { visibility: 'specified' }, + list: { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list }, + channel: { channelId: props.channel }, + role: { roleId: props.role }, + }; - if (endpoint && query) { + if (props.src.startsWith('custom-timeline')) { paginationQuery = { - endpoint: endpoint, + endpoint: 'notes/any-local-timeline', limit: 10, - params: query, + params: { + host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split('-')[2]}`], + remoteToken: defaultStore.state[`remoteLocalTimelineToken${props.src.split('-')[2]}`], + }, }; } else { - paginationQuery = null; + const endpoint = endpoints[props.src]; + const query = queries[props.src]; + paginationQuery = endpoint && query ? { endpoint, limit: 10, params: query } : null; } } @@ -265,12 +187,9 @@ function refreshEndpointAndChannel() { disconnectChannel(); connectChannel(); } - updatePaginationQuery(); } -// デッキのリストカラムでwithRenotesを変更した場合に自動的に更新されるようにさせる -// IDが切り替わったら切り替え先のTLを表示させたい watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel); // 初回表示用 @@ -282,13 +201,11 @@ onUnmounted(() => { function reloadTimeline() { return new Promise<void>((res) => { - if (tlComponent.value == null) return; + if (!tlComponent.value) return; tlNotesCount = 0; - tlComponent.value.pagingComponent?.reload().then(() => { - res(); - }); + tlComponent.value.pagingComponent?.reload().then(() => res()); }); } diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts index 6847321d6c..1e9e65e39f 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/instance.ts @@ -32,6 +32,8 @@ export const instance: Misskey.entities.MetaDetailed = reactive(cachedMeta ?? {} export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL); +export const googleAnalyticsId = computed(() => instance.googleAnalyticsId ?? null); + export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO_IMAGE_URL); export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL); diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index c785df1b7a..1818a88467 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -77,6 +77,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template #prefix><i class="ti ti-link"></i></template> <template #label>{{ i18n.ts.somethingHappened }}</template> </MkInput> + <MkInput v-model="googleAnalyticsId" type="url"> + <template #prefix><i class="ti ti-link"></i></template> + <template #label>googleAnal </template> + </MkInput> <MkColorInput v-model="themeColor"> <template #label>{{ i18n.ts.themeColor }}</template> @@ -144,6 +148,8 @@ const themeColor = ref<string | null>(null); const defaultLightTheme = ref<string | null>(null); const defaultDarkTheme = ref<string | null>(null); const serverErrorImageUrl = ref<string | null>(null); +const googleAnalyticsId = ref<string | null>(null); + const infoImageUrl = ref<string | null>(null); const notFoundImageUrl = ref<string | null>(null); const repositoryUrl = ref<string | null>(null); @@ -189,6 +195,7 @@ function save() { infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value, notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value, serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value, + googleAnalyticsId: googleAnalyticsId.value === '' ? null : googleAnalyticsId.value, repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value, feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), From 8a819511ceabd31124e3c12027f61688155b133f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 29 May 2024 01:42:51 +0900 Subject: [PATCH 481/501] update --- packages/backend/src/server/web/views/base.pug | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug index 7e519b37ed..bd339a02f6 100644 --- a/packages/backend/src/server/web/views/base.pug +++ b/packages/backend/src/server/web/views/base.pug @@ -35,13 +35,13 @@ html link(rel='prefetch' href=serverErrorImageUrl) link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=notFoundImageUrl) - if googleAnalyticsId - script(async src='https://www.googletagmanager.com/gtag/js?id='+ googleAnalyticsId) - script. - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', '#{googleAnalyticsId}'); + if googleAnalyticsId + script(async src='https://www.googletagmanager.com/gtag/js?id='+ googleAnalyticsId) + script. + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', '#{googleAnalyticsId}'); //- https://github.com/misskey-dev/misskey/issues/9842 link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v3.3.0') From ff355f5d8b20c2f412c426070cd18cd67b39d30f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Sat, 8 Jun 2024 18:25:38 +0900 Subject: [PATCH 482/501] update --- packages/backend/src/server/api/endpoints/i.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/backend/src/server/api/endpoints/i.ts b/packages/backend/src/server/api/endpoints/i.ts index 2b7a1d30fe..cf8f561e55 100644 --- a/packages/backend/src/server/api/endpoints/i.ts +++ b/packages/backend/src/server/api/endpoints/i.ts @@ -82,10 +82,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- this.userProfilesRepository.update({ userId: user.id }, { loggedInDates: [...userProfile.loggedInDates, today], }); - this.usersRepository.update( user.id, { - getPoints: user.getPoints + todayGetPoints, + const user_ = await this.usersRepository.findOne({ + where: { + id: user.id, + }, }); - this.notificationService.createNotification(user. id, 'loginbonus', { + if (user_ == null) { + throw new ApiError(meta.errors.userIsDeleted); + } + this.usersRepository.update( user.id, { + getPoints: user_.getPoints + todayGetPoints, + }); + this.notificationService.createNotification(user.id, 'loginbonus', { loginbonus: todayGetPoints, }); userProfile.loggedInDates = [...userProfile.loggedInDates, today]; From 184890154f75d6101a40974d1b30d4ea2d27d603 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 16:24:32 +0900 Subject: [PATCH 483/501] =?UTF-8?q?feat:=20=E3=83=A1=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AE?= =?UTF-8?q?=E3=82=BF=E3=83=96=E3=82=92=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=9E?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E5=8F=AF=E8=83=BD=E3=81=AB(#8759)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 4 + locales/ja-JP.yml | 1 + .../frontend/src/pages/settings/index.vue | 5 + .../src/pages/settings/timelineHeader.vue | 149 ++++++++++++++++++ packages/frontend/src/pages/timeline.vue | 61 +++---- packages/frontend/src/router/definition.ts | 4 + packages/frontend/src/store.ts | 21 ++- packages/frontend/src/timelineHeader.ts | 117 ++++++++++++++ 8 files changed, 318 insertions(+), 44 deletions(-) create mode 100644 packages/frontend/src/pages/settings/timelineHeader.vue create mode 100644 packages/frontend/src/timelineHeader.ts diff --git a/locales/index.d.ts b/locales/index.d.ts index acdc1fc421..fdce0b9db9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2120,6 +2120,10 @@ export interface Locale extends ILocale { * アカウント設定 */ "accountSettings": string; + /** + * タイムラインのヘッダー + */ + "timelineHeader": string; /** * プロモーション */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 3ac1ce82a3..2187133f41 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -526,6 +526,7 @@ dayOverDayChanges: "前日比" appearance: "アピアランス" clientSettings: "クライアント設定" accountSettings: "アカウント設定" +timelineHeader: "タイムラインのヘッダー" promotion: "プロモーション" promote: "プロモート" numberOfDays: "日数" diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 5fc1fd1bca..d212aaefe6 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -115,6 +115,11 @@ const menuDef = computed(() => [{ text: i18n.ts.navbar, to: '/settings/navbar', active: currentPage.value?.route.name === 'navbar', + }, { + icon: 'ti ti-layout-navbar', + text: i18n.ts.timelineHeader, + to: '/settings/timelineheader', + active: currentPage.value?.route.name === 'timelineHeader', }, { icon: 'ti ti-equal-double', text: i18n.ts.statusbar, diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue new file mode 100644 index 0000000000..9192439bd8 --- /dev/null +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -0,0 +1,149 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="_gaps_m"> + <FormSlot> + <template #label>{{ i18n.ts.timelineHeader }}</template> + <MkContainer :showHeader="false"> + <Sortable + v-model="items" + itemKey="id" + :animation="150" + :handle="'.' + $style.itemHandle" + @start="e => e.item.classList.add('active')" + @end="e => e.item.classList.remove('active')" + > + <template #item="{element,index}"> + <div + v-if="element.type === '-' || timelineHeaderItemDef[element.type]" + :class="$style.item" + > + <button class="_button" :class="$style.itemHandle"><i class="ti ti-menu"></i></button> + + <i class="ti-fw" :class="[$style.itemIcon, timelineHeaderItemDef[element.type]?.icon]"></i><span :class="$style.itemText">{{ timelineHeaderItemDef[element.type]?.title }}</span> + <button class="_button" :class="$style.itemRemove" @click="removeItem(index)"><i class="ti ti-x"></i></button> + </div> + </template> + </Sortable> + </MkContainer> + </FormSlot> + <div class="_buttons"> + <MkButton @click="addItem"><i class="ti ti-plus"></i> {{ i18n.ts.addItem }}</MkButton> + <MkButton danger @click="reset"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> + <MkButton primary class="save" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton> + </div> +</div> +</template> + +<script lang="ts" setup> +import { computed, defineAsyncComponent, ref, watch } from 'vue'; +import MkRadios from '@/components/MkRadios.vue'; +import MkButton from '@/components/MkButton.vue'; +import FormSlot from '@/components/form/slot.vue'; +import MkContainer from '@/components/MkContainer.vue'; +import * as os from '@/os.js'; +import { navbarItemDef } from '@/navbar.js'; +import { defaultStore } from '@/store.js'; +import { unisonReload } from '@/scripts/unison-reload.js'; +import { i18n } from '@/i18n.js'; +import { definePageMetadata } from '@/scripts/page-metadata.js'; +import { timelineHeaderItemDef } from '@/timelineHeader.js'; + +const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); + +const items = ref(defaultStore.state.timelineTopBar.map(x => ({ + id: Math.random().toString(), + type: x, +}))); + +async function reloadAsk() { + const { canceled } = await os.confirm({ + type: 'info', + text: i18n.ts.reloadToApplySetting, + }); + if (canceled) return; + + unisonReload(); +} + +async function addItem() { + const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineTopBar.includes(k)); + console.log(menu); + const { canceled, result: item } = await os.select({ + title: i18n.ts.addItem, + items: [...menu.map(k => ({ + value: k, text: timelineHeaderItemDef[k].title, + }))], + }); + if (canceled) return; + items.value = [...items.value, { + id: Math.random().toString(), + type: item, + }]; +} + +function removeItem(index: number) { + items.value.splice(index, 1); +} + +async function save() { + defaultStore.set('timelineTopBar', items.value.map(x => x.type)); + await reloadAsk(); +} + +function reset() { + items.value = defaultStore.def.timelineTopBar.default.map(x => ({ + id: Math.random().toString(), + type: x, + })); +} + +definePageMetadata(() => ({ + title: i18n.ts.navbar, + icon: 'ti ti-list', +})); +</script> + +<style lang="scss" module> +.item { + position: relative; + display: block; + line-height: 2.85rem; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + color: var(--navFg); +} + +.itemIcon { + position: relative; + width: 32px; + margin-right: 8px; +} + +.itemText { + position: relative; + font-size: 0.9em; +} + +.itemRemove { + position: absolute; + z-index: 10000; + width: 32px; + height: 32px; + color: #ff2a2a; + right: 8px; + opacity: 0.8; +} + +.itemHandle { + cursor: move; + width: 32px; + height: 32px; + margin: 0 8px; + opacity: 0.5; +} +</style> diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 98744c6318..2d88cda853 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -53,6 +53,7 @@ import { deviceKind } from '@/scripts/device-kind.js'; import { deepMerge } from '@/scripts/merge.js'; import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; +import { timelineHeaderItemDef } from '@/timelineHeader.js'; provide('shouldOmitHeaderTitle', true); @@ -277,49 +278,23 @@ const headerActions = computed(() => { } return tmp; }); - -const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserLists.value.map(l => ({ - key: 'list:' + l.id, - title: l.name, - icon: 'ti ti-star', - iconOnly: true, -}))), { - key: 'home', - title: i18n.ts._timelines.home, - icon: 'ti ti-home', - iconOnly: true, -}, ...(isLocalTimelineAvailable ? [{ - key: 'local', - title: i18n.ts._timelines.local, - icon: 'ti ti-planet', - iconOnly: true, -}, { - key: 'social', - title: i18n.ts._timelines.social, - icon: 'ti ti-universe', - iconOnly: true, -}] : []), ...(isGlobalTimelineAvailable ? [{ - key: 'global', - title: i18n.ts._timelines.global, - icon: 'ti ti-whirl', - iconOnly: true, -}] : []), { - icon: 'ti ti-list', - title: i18n.ts.lists, - iconOnly: true, - onClick: chooseList, -}, { - icon: 'ti ti-antenna', - title: i18n.ts.antennas, - iconOnly: true, - onClick: chooseAntenna, -}, { - icon: 'ti ti-device-tv', - title: i18n.ts.channel, - iconOnly: true, - onClick: chooseChannel, -}] as Tab[]); - +let headerTabs = computed(() => defaultStore.reactiveState.timelineTopBar.value.map(tab => ({ + ...(tab !== 'lists' && tab !== 'antennas' && tab !== 'channels' ? { + key: tab, + } : {}), + title: timelineHeaderItemDef[tab].title, + icon: timelineHeaderItemDef[tab].icon, + iconOnly: timelineHeaderItemDef[tab].iconOnly, + ...(tab === 'lists' ? { + onClick: (ev) => chooseList(ev), + } : {}), + ...(tab === 'antennas' ? { + onClick: (ev) => chooseAntenna(ev), + } : {}), + ...(tab === 'channels' ? { + onClick: (ev) => chooseChannel(ev), + } : {}), +})) as Tab[]); const headerTabsWhenNotLogin = computed(() => [ ...(isLocalTimelineAvailable ? [{ key: 'local', diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index 8a443f627b..b63a9e7509 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -112,6 +112,10 @@ const routes: RouteDef[] = [{ path: '/navbar', name: 'navbar', component: page(() => import('@/pages/settings/navbar.vue')), + }, { + path: '/timelineheader', + name: 'timelineHeader', + component: page(() => import('@/pages/settings/timelineHeader.vue')), }, { path: '/statusbar', name: 'statusbar', diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index e8eb5a1ed7..09a1f13f72 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -52,7 +52,8 @@ export type SoundStore = { volume: number; } - +export const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); +export const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); export const postFormActions: PostFormAction[] = []; export const userActions: UserAction[] = []; export const noteActions: NoteAction[] = []; @@ -148,6 +149,22 @@ export const defaultStore = markRaw(new Storage('base', { 'ui', ], }, + timelineTopBar: { + where: 'deviceAccount', + default: [ + 'home', + ...(isLocalTimelineAvailable ? [ + 'local', + 'social', + ] : []), + ...(isGlobalTimelineAvailable ? [ + 'global', + ] : []), + 'lists', + 'antennas', + 'channels', + ], + }, visibility: { where: 'deviceAccount', default: 'public' as (typeof Misskey.noteVisibilities)[number], @@ -522,6 +539,8 @@ interface Watcher { */ import lightTheme from '@/themes/l-light.json5'; import darkTheme from '@/themes/d-green-lime.json5'; +import { $i } from '@/account.js'; +import { instance } from '@/instance.js'; export class ColdDeviceStorage { public static default = { diff --git a/packages/frontend/src/timelineHeader.ts b/packages/frontend/src/timelineHeader.ts new file mode 100644 index 0000000000..490a9442c8 --- /dev/null +++ b/packages/frontend/src/timelineHeader.ts @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { reactive } from 'vue'; +import { i18n } from '@/i18n.js'; +import { antennasCache, favoritedChannelsCache, userListsCache } from '@/cache.js'; +import { MenuItem } from '@/types/menu.js'; +import * as os from '@/os.js'; +import { miLocalStorage } from '@/local-storage.js'; +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/store.js'; + +export const timelineHeaderItemDef = reactive({ + home: { + title: i18n.ts._timelines.home, + icon: 'ti ti-home', + iconOnly: false, + }, + ...(isLocalTimelineAvailable ? { + local: { + key: 'local', + title: i18n.ts._timelines.local, + icon: 'ti ti-planet', + iconOnly: true, + }, + social: { + title: i18n.ts._timelines.social, + icon: 'ti ti-universe', + iconOnly: true, + } } : {}), + ...(isGlobalTimelineAvailable ? { global: { + key: 'global', + title: i18n.ts._timelines.global, + icon: 'ti ti-whirl', + iconOnly: true, + } } : {}), + lists: { + icon: 'ti ti-list', + title: i18n.ts.lists, + iconOnly: true, + }, + antennas: { + icon: 'ti ti-antenna', + title: i18n.ts.antennas, + iconOnly: true, + }, + channels: { + icon: 'ti ti-device-tv', + title: i18n.ts.channel, + iconOnly: true, + }, +}); + +async function chooseList(ev: MouseEvent): Promise<void> { + const lists = await userListsCache.fetch(); + const items: MenuItem[] = [ + ...lists.map(list => ({ + type: 'link' as const, + text: list.name, + to: `/timeline/list/${list.id}`, + })), + (lists.length === 0 ? undefined : { type: 'divider' }), + { + type: 'link' as const, + icon: 'ti ti-plus', + text: i18n.ts.createNew, + to: '/my/lists', + }, + ]; + os.popupMenu(items, ev.currentTarget ?? ev.target); +} + +async function chooseAntenna(ev: MouseEvent): Promise<void> { + const antennas = await antennasCache.fetch(); + const items: MenuItem[] = [ + ...antennas.map(antenna => ({ + type: 'link' as const, + text: antenna.name, + indicate: antenna.hasUnreadNote, + to: `/timeline/antenna/${antenna.id}`, + })), + (antennas.length === 0 ? undefined : { type: 'divider' }), + { + type: 'link' as const, + icon: 'ti ti-plus', + text: i18n.ts.createNew, + to: '/my/antennas', + }, + ]; + os.popupMenu(items, ev.currentTarget ?? ev.target); +} + +async function chooseChannel(ev: MouseEvent): Promise<void> { + const channels = await favoritedChannelsCache.fetch(); + const items: MenuItem[] = [ + ...channels.map(channel => { + const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null; + const hasUnreadNote = (lastReadedAt && channel.lastNotedAt) ? Date.parse(channel.lastNotedAt) > lastReadedAt : !!(!lastReadedAt && channel.lastNotedAt); + + return { + type: 'link' as const, + text: channel.name, + indicate: hasUnreadNote, + to: `/channels/${channel.id}`, + }; + }), + (channels.length === 0 ? undefined : { type: 'divider' }), + { + type: 'link' as const, + icon: 'ti ti-plus', + text: i18n.ts.createNew, + to: '/channels', + }, + ]; + os.popupMenu(items, ev.currentTarget ?? ev.target); +} From 8bfc9f1165a3aa4fee164e9afbb64a16cf3efdb2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 17:38:38 +0900 Subject: [PATCH 484/501] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=82=92=E3=83=94=E3=83=B3=E7=95=99=E3=82=81=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/settings/timelineHeader.vue | 51 ++++++------ packages/frontend/src/timelineHeader.ts | 81 +++---------------- 2 files changed, 36 insertions(+), 96 deletions(-) diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue index 9192439bd8..82e407a688 100644 --- a/packages/frontend/src/pages/settings/timelineHeader.vue +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -1,33 +1,29 @@ -<!-- -SPDX-FileCopyrightText: syuilo and misskey-project -SPDX-License-Identifier: AGPL-3.0-only ---> - <template> <div class="_gaps_m"> <FormSlot> <template #label>{{ i18n.ts.timelineHeader }}</template> <MkContainer :showHeader="false"> - <Sortable - v-model="items" - itemKey="id" - :animation="150" - :handle="'.' + $style.itemHandle" - @start="e => e.item.classList.add('active')" - @end="e => e.item.classList.remove('active')" - > - <template #item="{element,index}"> - <div - v-if="element.type === '-' || timelineHeaderItemDef[element.type]" - :class="$style.item" - > - <button class="_button" :class="$style.itemHandle"><i class="ti ti-menu"></i></button> - - <i class="ti-fw" :class="[$style.itemIcon, timelineHeaderItemDef[element.type]?.icon]"></i><span :class="$style.itemText">{{ timelineHeaderItemDef[element.type]?.title }}</span> - <button class="_button" :class="$style.itemRemove" @click="removeItem(index)"><i class="ti ti-x"></i></button> - </div> - </template> - </Sortable> + <div style="overflow-x: auto;"> + <Sortable + v-model="items" + itemKey="id" + :class="$style.container" + :animation="150" + :handle="'.' + $style.itemHandle" + @start="e => e.item.classList.add('active')" + @end="e => e.item.classList.remove('active')" + > + <template #item="{element,index}"> + <div + :class="$style.item" + > + <button class="_button" :class="$style.itemHandle"><i class="ti ti-menu"></i></button> + <i class="ti-fw" :class="[$style.itemIcon, timelineHeaderItemDef[element.type]?.icon]"></i><span :class="$style.itemText">{{ timelineHeaderItemDef[element.type]?.title }}</span> + <button class="_button" :class="$style.itemRemove" @click="removeItem(index)"><i class="ti ti-x"></i></button> + </div> + </template> + </Sortable> + </div> </MkContainer> </FormSlot> <div class="_buttons"> @@ -116,6 +112,7 @@ definePageMetadata(() => ({ overflow: hidden; white-space: nowrap; color: var(--navFg); + min-width: 200px; } .itemIcon { @@ -146,4 +143,8 @@ definePageMetadata(() => ({ margin: 0 8px; opacity: 0.5; } + +.container{ + display: flex; +} </style> diff --git a/packages/frontend/src/timelineHeader.ts b/packages/frontend/src/timelineHeader.ts index 490a9442c8..f603dc4206 100644 --- a/packages/frontend/src/timelineHeader.ts +++ b/packages/frontend/src/timelineHeader.ts @@ -5,12 +5,9 @@ import { reactive } from 'vue'; import { i18n } from '@/i18n.js'; -import { antennasCache, favoritedChannelsCache, userListsCache } from '@/cache.js'; -import { MenuItem } from '@/types/menu.js'; -import * as os from '@/os.js'; -import { miLocalStorage } from '@/local-storage.js'; +import { userListsCache } from '@/cache.js'; import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/store.js'; - +const lists = await userListsCache.fetch(); export const timelineHeaderItemDef = reactive({ home: { title: i18n.ts._timelines.home, @@ -19,7 +16,6 @@ export const timelineHeaderItemDef = reactive({ }, ...(isLocalTimelineAvailable ? { local: { - key: 'local', title: i18n.ts._timelines.local, icon: 'ti ti-planet', iconOnly: true, @@ -30,7 +26,6 @@ export const timelineHeaderItemDef = reactive({ iconOnly: true, } } : {}), ...(isGlobalTimelineAvailable ? { global: { - key: 'global', title: i18n.ts._timelines.global, icon: 'ti ti-whirl', iconOnly: true, @@ -50,68 +45,12 @@ export const timelineHeaderItemDef = reactive({ title: i18n.ts.channel, iconOnly: true, }, + ...lists.reduce((acc, l) => { + acc['list:' + l.id] = { + title: i18n.ts.lists + ':' + l.name, + icon: 'ti ti-star', + iconOnly: true, + }; + return acc; + }, {}), }); - -async function chooseList(ev: MouseEvent): Promise<void> { - const lists = await userListsCache.fetch(); - const items: MenuItem[] = [ - ...lists.map(list => ({ - type: 'link' as const, - text: list.name, - to: `/timeline/list/${list.id}`, - })), - (lists.length === 0 ? undefined : { type: 'divider' }), - { - type: 'link' as const, - icon: 'ti ti-plus', - text: i18n.ts.createNew, - to: '/my/lists', - }, - ]; - os.popupMenu(items, ev.currentTarget ?? ev.target); -} - -async function chooseAntenna(ev: MouseEvent): Promise<void> { - const antennas = await antennasCache.fetch(); - const items: MenuItem[] = [ - ...antennas.map(antenna => ({ - type: 'link' as const, - text: antenna.name, - indicate: antenna.hasUnreadNote, - to: `/timeline/antenna/${antenna.id}`, - })), - (antennas.length === 0 ? undefined : { type: 'divider' }), - { - type: 'link' as const, - icon: 'ti ti-plus', - text: i18n.ts.createNew, - to: '/my/antennas', - }, - ]; - os.popupMenu(items, ev.currentTarget ?? ev.target); -} - -async function chooseChannel(ev: MouseEvent): Promise<void> { - const channels = await favoritedChannelsCache.fetch(); - const items: MenuItem[] = [ - ...channels.map(channel => { - const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null; - const hasUnreadNote = (lastReadedAt && channel.lastNotedAt) ? Date.parse(channel.lastNotedAt) > lastReadedAt : !!(!lastReadedAt && channel.lastNotedAt); - - return { - type: 'link' as const, - text: channel.name, - indicate: hasUnreadNote, - to: `/channels/${channel.id}`, - }; - }), - (channels.length === 0 ? undefined : { type: 'divider' }), - { - type: 'link' as const, - icon: 'ti ti-plus', - text: i18n.ts.createNew, - to: '/channels', - }, - ]; - os.popupMenu(items, ev.currentTarget ?? ev.target); -} From e8d2ca00904c6eea52dd70ae6ae889e587c2e1ec Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 17:44:21 +0900 Subject: [PATCH 485/501] =?UTF-8?q?fix:=20console.log=E6=B6=88=E3=81=97?= =?UTF-8?q?=E5=BF=98=E3=82=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/frontend/src/pages/settings/timelineHeader.vue | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue index 82e407a688..b796f97ca1 100644 --- a/packages/frontend/src/pages/settings/timelineHeader.vue +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -67,7 +67,6 @@ async function reloadAsk() { async function addItem() { const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineTopBar.includes(k)); - console.log(menu); const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, items: [...menu.map(k => ({ From 41287539e969ff2fab77692421ec5f22ecd865f6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 17:46:48 +0900 Subject: [PATCH 486/501] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b70636d82..a41b4d3980 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 ### Client -- +- メインタイムラインのタブをカスタマイズできるように (#8759) ### Server - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正 From f5ef244d0224b97864f56b12cbb1e0c308163a9c Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 17:50:03 +0900 Subject: [PATCH 487/501] fix: spdx-license-id --- packages/frontend/src/pages/settings/timelineHeader.vue | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue index b796f97ca1..a7f8b42857 100644 --- a/packages/frontend/src/pages/settings/timelineHeader.vue +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -1,3 +1,7 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> <template> <div class="_gaps_m"> <FormSlot> From 1ff2117a05038984beacc878ce13599809d9dc8b Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 17:52:53 +0900 Subject: [PATCH 488/501] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a41b4d3980..215154efd7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ - Fix: 配信停止したインスタンス一覧が見れなくなる問題を修正 ### Client -- メインタイムラインのタブをカスタマイズできるように (#8759) +- メインタイムラインのタブをカスタマイズできるように ### Server - チャート生成時にinstance.suspentionStateに置き換えられたinstance.isSuspendedが参照されてしまう問題を修正 From 269791787390f259ae60289a5960f6b2a29f6706 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 11 Jun 2024 19:19:42 +0900 Subject: [PATCH 489/501] =?UTF-8?q?Fix:=20=E5=91=BD=E5=90=8D=E8=A6=8F?= =?UTF-8?q?=E5=89=87=E3=81=AA=E3=81=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/settings/timelineHeader.vue | 10 ++-- packages/frontend/src/pages/timeline.vue | 53 ++++++++++++------- packages/frontend/src/router/definition.ts | 2 +- .../src/scripts/get-timeline-available.ts | 8 +++ packages/frontend/src/store.ts | 11 ++-- .../{timelineHeader.ts => timeline-header.ts} | 23 ++++++-- 6 files changed, 71 insertions(+), 36 deletions(-) create mode 100644 packages/frontend/src/scripts/get-timeline-available.ts rename packages/frontend/src/{timelineHeader.ts => timeline-header.ts} (74%) diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue index a7f8b42857..16ec626dfe 100644 --- a/packages/frontend/src/pages/settings/timelineHeader.vue +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -50,11 +50,11 @@ import { defaultStore } from '@/store.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { timelineHeaderItemDef } from '@/timelineHeader.js'; +import { timelineHeaderItemDef } from '@/timeline-header.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); -const items = ref(defaultStore.state.timelineTopBar.map(x => ({ +const items = ref(defaultStore.state.timelineHeader.map(x => ({ id: Math.random().toString(), type: x, }))); @@ -70,7 +70,7 @@ async function reloadAsk() { } async function addItem() { - const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineTopBar.includes(k)); + const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineHeader.includes(k)); const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, items: [...menu.map(k => ({ @@ -89,12 +89,12 @@ function removeItem(index: number) { } async function save() { - defaultStore.set('timelineTopBar', items.value.map(x => x.type)); + defaultStore.set('timelineHeader', items.value.map(x => x.type)); await reloadAsk(); } function reset() { - items.value = defaultStore.def.timelineTopBar.default.map(x => ({ + items.value = defaultStore.def.timelineHeader.default.map(x => ({ id: Math.random().toString(), type: x, })); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 2d88cda853..69b0fcbb33 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -53,12 +53,11 @@ import { deviceKind } from '@/scripts/device-kind.js'; import { deepMerge } from '@/scripts/merge.js'; import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; -import { timelineHeaderItemDef } from '@/timelineHeader.js'; +import { timelineHeaderItemDef } from '@/timeline-header.js'; +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; provide('shouldOmitHeaderTitle', true); -const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); -const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); const keymap = { 't': focus, }; @@ -278,23 +277,37 @@ const headerActions = computed(() => { } return tmp; }); -let headerTabs = computed(() => defaultStore.reactiveState.timelineTopBar.value.map(tab => ({ - ...(tab !== 'lists' && tab !== 'antennas' && tab !== 'channels' ? { - key: tab, - } : {}), - title: timelineHeaderItemDef[tab].title, - icon: timelineHeaderItemDef[tab].icon, - iconOnly: timelineHeaderItemDef[tab].iconOnly, - ...(tab === 'lists' ? { - onClick: (ev) => chooseList(ev), - } : {}), - ...(tab === 'antennas' ? { - onClick: (ev) => chooseAntenna(ev), - } : {}), - ...(tab === 'channels' ? { - onClick: (ev) => chooseChannel(ev), - } : {}), -})) as Tab[]); +const headerTabs = computed(() => defaultStore.reactiveState.timelineHeader.value.map(tab => { + if ((tab === 'local' || tab === 'social') && !isLocalTimelineAvailable) { + return {}; + } else if (tab === 'global' && !isGlobalTimelineAvailable) { + return {}; + } + + const tabDef = timelineHeaderItemDef[tab]; + if (!tabDef) { + return {}; + } + + return { + ...(!['channels', 'antennas', 'lists'].includes(tab) ? { + key: tab, + } : {}), + title: tabDef.title, + icon: tabDef.icon, + iconOnly: tabDef.iconOnly, + ...(tab === 'lists' ? { + onClick: (ev) => chooseList(ev), + } : {}), + ...(tab === 'antennas' ? { + onClick: (ev) => chooseAntenna(ev), + } : {}), + ...(tab === 'channels' ? { + onClick: (ev) => chooseChannel(ev), + } : {}), + }; +}) as Tab[]); + const headerTabsWhenNotLogin = computed(() => [ ...(isLocalTimelineAvailable ? [{ key: 'local', diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts index b63a9e7509..360930b363 100644 --- a/packages/frontend/src/router/definition.ts +++ b/packages/frontend/src/router/definition.ts @@ -113,7 +113,7 @@ const routes: RouteDef[] = [{ name: 'navbar', component: page(() => import('@/pages/settings/navbar.vue')), }, { - path: '/timelineheader', + path: '/timeline-header', name: 'timelineHeader', component: page(() => import('@/pages/settings/timelineHeader.vue')), }, { diff --git a/packages/frontend/src/scripts/get-timeline-available.ts b/packages/frontend/src/scripts/get-timeline-available.ts new file mode 100644 index 0000000000..30197441ea --- /dev/null +++ b/packages/frontend/src/scripts/get-timeline-available.ts @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { $i } from '@/account.js'; +import { instance } from '@/instance.js'; +export const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); +export const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index 09a1f13f72..961cbee9ff 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -9,7 +9,7 @@ import { miLocalStorage } from './local-storage.js'; import type { SoundType } from '@/scripts/sound.js'; import { Storage } from '@/pizzax.js'; import { hemisphere } from '@/scripts/intl-const.js'; - +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; interface PostFormAction { title: string, handler: <T>(form: T, update: (key: unknown, value: unknown) => void) => void; @@ -52,8 +52,6 @@ export type SoundStore = { volume: number; } -export const isLocalTimelineAvailable = ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); -export const isGlobalTimelineAvailable = ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); export const postFormActions: PostFormAction[] = []; export const userActions: UserAction[] = []; export const noteActions: NoteAction[] = []; @@ -149,7 +147,7 @@ export const defaultStore = markRaw(new Storage('base', { 'ui', ], }, - timelineTopBar: { + timelineHeader: { where: 'deviceAccount', default: [ 'home', @@ -163,7 +161,7 @@ export const defaultStore = markRaw(new Storage('base', { 'lists', 'antennas', 'channels', - ], + ] as TimelineHeaderItem[], }, visibility: { where: 'deviceAccount', @@ -539,8 +537,7 @@ interface Watcher { */ import lightTheme from '@/themes/l-light.json5'; import darkTheme from '@/themes/d-green-lime.json5'; -import { $i } from '@/account.js'; -import { instance } from '@/instance.js'; +import { TimelineHeaderItem } from '@/timeline-header.js'; export class ColdDeviceStorage { public static default = { diff --git a/packages/frontend/src/timelineHeader.ts b/packages/frontend/src/timeline-header.ts similarity index 74% rename from packages/frontend/src/timelineHeader.ts rename to packages/frontend/src/timeline-header.ts index f603dc4206..5cf27c0c7b 100644 --- a/packages/frontend/src/timelineHeader.ts +++ b/packages/frontend/src/timeline-header.ts @@ -6,13 +6,30 @@ import { reactive } from 'vue'; import { i18n } from '@/i18n.js'; import { userListsCache } from '@/cache.js'; -import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/store.js'; +import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; + +export type TimelineHeaderItem = + 'home' | + 'local' | + 'social' | + 'global' | + 'lists' | + 'antennas' | + 'channels' | + `list:${string}` + +type TimelineHeaderItemsDef = { + title: string; + icon: string; + iconOnly?: boolean; // わからん +} + const lists = await userListsCache.fetch(); -export const timelineHeaderItemDef = reactive({ +export const timelineHeaderItemDef = reactive<Partial<Record<TimelineHeaderItem, TimelineHeaderItemsDef>>>({ home: { title: i18n.ts._timelines.home, icon: 'ti ti-home', - iconOnly: false, + iconOnly: true, }, ...(isLocalTimelineAvailable ? { local: { From e90fda338647d766ad2047d8cd88864120ea88e6 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 12 Jun 2024 16:02:42 +0900 Subject: [PATCH 490/501] Fix --- packages/backend/src/models/RepositoryModule.ts | 7 ++----- .../backend/src/server/api/endpoints/users/report-abuse.ts | 7 +++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 4117394daf..2e0135b2e7 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -3,7 +3,6 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import type { Provider } from '@nestjs/common'; import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; import { @@ -29,8 +28,6 @@ import { MiDriveFolder, MiEmoji, MiEmojiRequest, - MiBubbleGameRecord, - MiFollowRequest, MiFlash, MiFlashLike, MiFollowRequest, @@ -82,8 +79,8 @@ import { MiUserSecurityKey, MiWebhook, MiScheduledNote, - MiBubbleGameRecord -, MiReversiGame } from './_.js'; + MiBubbleGameRecord } from './_.js'; +import type { Provider } from '@nestjs/common'; import type { DataSource } from 'typeorm'; const $usersRepository: Provider = { diff --git a/packages/backend/src/server/api/endpoints/users/report-abuse.ts b/packages/backend/src/server/api/endpoints/users/report-abuse.ts index 87631b14f3..dfe27ab21d 100644 --- a/packages/backend/src/server/api/endpoints/users/report-abuse.ts +++ b/packages/backend/src/server/api/endpoints/users/report-abuse.ts @@ -2,7 +2,6 @@ * SPDX-FileCopyrightText: syuilo and misskey-project * SPDX-License-Identifier: AGPL-3.0-only */ -import { Injectable } from '@nestjs/common'; import { setImmediate } from 'node:timers/promises'; import sanitizeHtml from 'sanitize-html'; import { Inject, Injectable } from '@nestjs/common'; @@ -93,13 +92,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- } const notes = ps.noteIds ? await this.notesRepository.find({ - where: { id: In(ps.noteIds), userId: user.id }, + where: { id: In(ps.noteIds), userId: targetUser.id }, }) : []; const report = await this.abuseUserReportsRepository.insert({ id: this.idService.gen(), - targetUserId: user.id, - targetUserHost: user.host, + targetUserId: targetUser.id, + targetUserHost: targetUser.host, reporterId: me.id, reporterHost: null, comment: ps.comment, From bba25036fa532a38dfb563d67e3f1369c3546721 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Wed, 12 Jun 2024 16:04:20 +0900 Subject: [PATCH 491/501] 2024.5.0-mattyatea2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65cfad1572..f6f00ef712 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-mattyatea1", + "version": "2024.5.0-mattyatea2", "codename": "nasubi", "repository": { "type": "git", From a64c6075848859b9e38fcde8916fc512e9b1bfc2 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 13:48:44 +0900 Subject: [PATCH 492/501] emoji more --- packages/backend/src/core/ReactionService.ts | 59 ++++--- .../src/core/entities/NoteEntityService.ts | 25 +++ packages/backend/src/models/NoteReaction.ts | 3 +- .../api/endpoints/notes/reactions/delete.ts | 3 +- packages/frontend/src/components/MkNote.vue | 151 ++++++++---------- .../src/components/MkNoteDetailed.vue | 12 +- .../components/MkReactionsViewer.reaction.vue | 16 +- .../src/components/MkReactionsViewer.vue | 7 +- .../src/components/global/MkLoading.vue | 4 +- .../frontend/src/scripts/use-note-capture.ts | 8 + 10 files changed, 158 insertions(+), 130 deletions(-) diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index cb0b079df0..96e0b80987 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -162,26 +162,30 @@ export class ReactionService { }; // Create reaction - try { - await this.noteReactionsRepository.insert(record); - } catch (e) { - if (isDuplicateKeyValueError(e)) { - const exists = await this.noteReactionsRepository.findOneByOrFail({ - noteId: note.id, - userId: user.id, - }); + const exists = await this.noteReactionsRepository.findOneBy({ + noteId: note.id, + userId: user.id, + reaction: record.reaction, + }); - if (exists.reaction !== reaction) { - // 別のリアクションがすでにされていたら置き換える - await this.delete(user, note); - await this.noteReactionsRepository.insert(record); - } else { - // 同じリアクションがすでにされていたらエラー - throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); - } + const count = await this.noteReactionsRepository.countBy({ + noteId: note.id, + userId: user.id, + }); + + if (count > 3) { + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); + } + + if (exists == null) { + if (user.host == null) { + await this.noteReactionsRepository.insert(record); } else { - throw e; + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); } + } else { + // 同じリアクションがすでにされていたらエラー + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); } // Increment reactions count @@ -275,17 +279,24 @@ export class ReactionService { } @bindThis - public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote) { + public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, reaction?: string) { // if already unreacted - const exist = await this.noteReactionsRepository.findOneBy({ - noteId: note.id, - userId: user.id, - }); - + let exist; + if (reaction == null) { + exist = await this.noteReactionsRepository.findOneBy({ + noteId: note.id, + userId: user.id, + }); + } else { + exist = await this.noteReactionsRepository.findOneBy({ + noteId: note.id, + userId: user.id, + reaction: reaction.replace(/@./, ''), + }); + } if (exist == null) { throw new IdentifiableError('60527ec9-b4cb-4a88-a6bd-32d3ad26817d', 'not reacted'); } - // Delete reaction const result = await this.noteReactionsRepository.delete(exist.id); diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 49f643a864..675772a608 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -209,6 +209,30 @@ export class NoteEntityService implements OnModuleInit { return undefined; } + @bindThis + public async populateMyReactions(note: { id: MiNote['id']; reactions: MiNote['reactions']; reactionAndUserPairCache?: MiNote['reactionAndUserPairCache']; }, meId: MiUser['id'], _hint_?: { + myReactions: Map<MiNote['id'], string | null>; + }) { + const reactionsCount = Object.values(note.reactions).reduce((a, b) => a + b, 0); + + if (reactionsCount === 0) return undefined; + + // パフォーマンスのためノートが作成されてから2秒以上経っていない場合はリアクションを取得しない + if (this.idService.parse(note.id).date.getTime() + 2000 > Date.now()) { + return undefined; + } + + const reactions = await this.noteReactionsRepository.findBy({ + userId: meId, + noteId: note.id, + }); + + if (reactions.length > 0) { + return reactions.map(reaction => this.reactionService.convertLegacyReaction(reaction.reaction)); + } + + return undefined; + } @bindThis public async isVisibleForMe(note: MiNote, meId: MiUser['id'] | null): Promise<boolean> { @@ -382,6 +406,7 @@ export class NoteEntityService implements OnModuleInit { ...(meId && Object.keys(note.reactions).length > 0 ? { myReaction: this.populateMyReaction(note, meId, options?._hint_), + myReactions: this.populateMyReactions(note, meId, options?._hint_), } : {}), } : {}), }); diff --git a/packages/backend/src/models/NoteReaction.ts b/packages/backend/src/models/NoteReaction.ts index 42dfcaa9ad..26792f10b9 100644 --- a/packages/backend/src/models/NoteReaction.ts +++ b/packages/backend/src/models/NoteReaction.ts @@ -9,7 +9,8 @@ import { MiUser } from './User.js'; import { MiNote } from './Note.js'; @Entity('note_reaction') -@Index(['userId', 'noteId'], { unique: true }) +@Index(['userId', 'noteId', 'reaction'], { unique: true }) +@Index(['userId', 'noteId']) export class MiNoteReaction { @PrimaryColumn(id()) public id: string; diff --git a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts index e6c3bbbcf5..cdc8b503e7 100644 --- a/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts +++ b/packages/backend/src/server/api/endpoints/notes/reactions/delete.ts @@ -42,6 +42,7 @@ export const paramDef = { type: 'object', properties: { noteId: { type: 'string', format: 'misskey:id' }, + reaction: { type: 'string' }, }, required: ['noteId'], } as const; @@ -57,7 +58,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint- if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); throw err; }); - await this.reactionService.delete(me, note).catch(err => { + await this.reactionService.delete(me, note, ps.reaction ?? undefined).catch(err => { if (err.id === '60527ec9-b4cb-4a88-a6bd-32d3ad26817d') throw new ApiError(meta.errors.notReacted); throw err; }); diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 6eeaa15732..976ebd7a8f 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -4,43 +4,45 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> - <div - v-if="!hardMuted && muted === false" - v-show="!isDeleted" - ref="rootEl" - v-hotkey="keymap" - :class="[$style.root, +<div + v-if="!hardMuted && muted === false" + v-show="!isDeleted" + ref="rootEl" + v-hotkey="keymap" + :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover } , {[$style.home] : defaultStore.state.showVisibilityColor && note.visibility === 'home' - ,[$style.followers] : defaultStore.state.showVisibilityColor && note.visibility === 'followers' - ,[$style.specified] : defaultStore.state.showVisibilityColor && note.visibility === 'specified' - },{[$style.localonly] : defaultStore.state.showVisibilityColor && note.localOnly } - ]" + ,[$style.followers] : defaultStore.state.showVisibilityColor && note.visibility === 'followers' + ,[$style.specified] : defaultStore.state.showVisibilityColor && note.visibility === 'specified' + },{[$style.localonly] : defaultStore.state.showVisibilityColor && note.localOnly } + ]" - :tabindex="!isDeleted ? '-1' : undefined" - > - <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> - <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> - <!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> - <!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>--> - <div v-if="isRenote" :class="$style.renote"> - <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> - <MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/> - <i class="ti ti-repeat" style="margin-right: 4px;"></i> - <I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText"> - <template #user> - <MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)"> - <MkUserName :user="note.user"/> - </MkA> - </template> - </I18n> - <div :class="$style.renoteInfo"> - <button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()"> - <i class="ti ti-dots" :class="$style.renoteMenu"></i> - <MkTime :time="note.createdAt"/> - </button> - <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" - :title="i18n.ts._visibility[note.visibility]"> + :tabindex="!isDeleted ? '-1' : undefined" +> + <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> + <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> + <!--<div v-if="appearNote._prId_" class="tip"><i class="ti ti-speakerphone"></i> {{ i18n.ts.promotion }}<button class="_textButton hide" @click="readPromo()">{{ i18n.ts.hideThisNote }} <i class="ti ti-x"></i></button></div>--> + <!--<div v-if="appearNote._featuredId_" class="tip"><i class="ti ti-bolt"></i> {{ i18n.ts.featured }}</div>--> + <div v-if="isRenote" :class="$style.renote"> + <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> + <MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/> + <i class="ti ti-repeat" style="margin-right: 4px;"></i> + <I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText"> + <template #user> + <MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)"> + <MkUserName :user="note.user"/> + </MkA> + </template> + </I18n> + <div :class="$style.renoteInfo"> + <button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()"> + <i class="ti ti-dots" :class="$style.renoteMenu"></i> + <MkTime :time="note.createdAt"/> + </button> + <span + v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" + :title="i18n.ts._visibility[note.visibility]" + > <i v-if="note.visibility === 'home'" class="ti ti-home"></i> <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> @@ -127,8 +129,8 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti ti-ban"></i> </button> <button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> - <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> + <i v-else-if="appearNote.myReactions?.length >= 3 " class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p> @@ -168,7 +170,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue'; +import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import MkNoteSub from '@/components/MkNoteSub.vue'; @@ -182,33 +184,33 @@ import MkPoll from '@/components/MkPoll.vue'; import MkUsersTooltip from '@/components/MkUsersTooltip.vue'; import MkUrlPreview from '@/components/MkUrlPreview.vue'; import MkInstanceTicker from '@/components/MkInstanceTicker.vue'; -import {pleaseLogin} from '@/scripts/please-login.js'; -import {focusPrev, focusNext} from '@/scripts/focus.js'; -import {checkWordMute} from '@/scripts/check-word-mute.js'; -import {userPage} from '@/filters/user.js'; +import { pleaseLogin } from '@/scripts/please-login.js'; +import { focusPrev, focusNext } from '@/scripts/focus.js'; +import { checkWordMute } from '@/scripts/check-word-mute.js'; +import { userPage } from '@/filters/user.js'; import number from '@/filters/number.js'; import * as os from '@/os.js'; import * as sound from '@/scripts/sound.js'; import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; -import {defaultStore, noteViewInterruptors} from '@/store.js'; -import {reactionPicker} from '@/scripts/reaction-picker.js'; -import {extractUrlFromMfm} from '@/scripts/extract-url-from-mfm.js'; -import {$i} from '@/account.js'; -import {i18n} from '@/i18n.js'; -import {getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu} from '@/scripts/get-note-menu.js'; -import {useNoteCapture} from '@/scripts/use-note-capture.js'; -import {deepClone} from '@/scripts/clone.js'; -import {useTooltip} from '@/scripts/use-tooltip.js'; -import {claimAchievement} from '@/scripts/achievements.js'; -import {getNoteSummary} from '@/scripts/get-note-summary.js'; -import {MenuItem} from '@/types/menu.js'; +import { defaultStore, noteViewInterruptors } from '@/store.js'; +import { reactionPicker } from '@/scripts/reaction-picker.js'; +import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; +import { $i } from '@/account.js'; +import { i18n } from '@/i18n.js'; +import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js'; +import { useNoteCapture } from '@/scripts/use-note-capture.js'; +import { deepClone } from '@/scripts/clone.js'; +import { useTooltip } from '@/scripts/use-tooltip.js'; +import { claimAchievement } from '@/scripts/achievements.js'; +import { getNoteSummary } from '@/scripts/get-note-summary.js'; +import { MenuItem } from '@/types/menu.js'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; -import {showMovedDialog} from '@/scripts/show-moved-dialog.js'; -import {shouldCollapsed} from '@/scripts/collapsed.js'; +import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; +import { shouldCollapsed } from '@/scripts/collapsed.js'; import { isEnabledUrlPreview } from '@/instance.js'; const props = withDefaults(defineProps<{ - note: Misskey.entities.Note; + note: Misskey.entities.Note & {myReactions: string[]}; pinned?: boolean; mock?: boolean; withHardMute?: boolean; @@ -416,7 +418,7 @@ function react(viaKeyboard = false): void { const rect = el.getBoundingClientRect(); const x = rect.left + (el.offsetWidth / 2); const y = rect.top + (el.offsetHeight / 2); - os.popup(MkRippleEffect, {x, y}, {}, 'end'); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); } } else { blur(); @@ -441,25 +443,9 @@ function react(viaKeyboard = false): void { } } -function undoReact(targetNote: Misskey.entities.Note): void { - const oldReaction = targetNote.myReaction; - if (!oldReaction) return; - - if (props.mock) { - emit('removeReaction', oldReaction); - return; - } - - misskeyApi('notes/reactions/delete', { - noteId: targetNote.id, - }); -} - function toggleReact() { - if (appearNote.value.myReaction == null) { + if (appearNote.value.myReactions?.length > 3 || !appearNote.value.myReactions ) { react(); - } else { - undoReact(appearNote.value); } } @@ -485,13 +471,13 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const {menu, cleanup} = getNoteMenu({ + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, - currentClip: currentClip?.value + currentClip: currentClip?.value, }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } @@ -502,13 +488,13 @@ function showMenu(viaKeyboard = false): void { return; } - const {menu, cleanup} = getNoteMenu({ + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, - currentClip: currentClip?.value + currentClip: currentClip?.value, }); os.popupMenu(menu, menuButton.value, { viaKeyboard, @@ -523,7 +509,7 @@ async function clip() { os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, - currentClip: currentClip?.value + currentClip: currentClip?.value, }), clipButton.value).then(focus); } @@ -583,13 +569,6 @@ function focusAfter() { focusNext(rootEl.value ?? null); } -function readPromo() { - misskeyApi('promo/read', { - noteId: appearNote.value.id, - }); - isDeleted.value = true; -} - function emitUpdReaction(emoji: string, delta: number) { if (delta < 0) { emit('removeReaction', emoji); diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 11787397c3..3b794bb7ee 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -129,8 +129,8 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti ti-ban"></i> </button> <button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> - <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> + <i v-else-if="appearNote.myReactions?.length >= 3 " class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p> @@ -257,13 +257,11 @@ import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; import { miLocalStorage } from '@/local-storage.js'; -import { infoImageUrl, instance } from '@/instance.js'; +import { infoImageUrl, instance, isEnabledUrlPreview } from '@/instance.js'; import MkPostForm from '@/components/MkPostFormSimple.vue'; import { deviceKind } from '@/scripts/device-kind.js'; const MOBILE_THRESHOLD = 500; -const isMobile = ref(deviceKind === 'smartphone' || window.innerWidth <= MOBILE_THRESHOLD); -import { isEnabledUrlPreview } from '@/instance.js'; const props = withDefaults(defineProps<{ note: Misskey.entities.Note; @@ -504,10 +502,8 @@ function undoReact(targetNote: Misskey.entities.Note): void { } function toggleReact() { - if (appearNote.value.myReaction == null) { + if (appearNote.value.myReactions?.length > 3 || !appearNote.value.myReactions ) { react(); - } else { - undoReact(appearNote.value); } } diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index b7e9355510..1323b896ff 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only ref="buttonEl" v-ripple="canToggle" class="_button" - :class="[$style.root, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' ,[$style.reacted]: note.myReaction == reaction, [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" + :class="[$style.root, { [$style.gamingDark]: gamingType === 'dark',[$style.gamingLight]: gamingType === 'light' ,[$style.reacted]: note.myReactions?.includes(reaction) , [$style.canToggle]: canToggle, [$style.small]: defaultStore.state.reactionsDisplaySize === 'small', [$style.large]: defaultStore.state.reactionsDisplaySize === 'large' }]" @click="toggleReaction()" @contextmenu.prevent.stop="menu" > @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import {computed, inject, onMounted, ref, shallowRef, watch} from 'vue'; +import { computed, inject, onMounted, ref, shallowRef, watch } from 'vue'; import * as Misskey from 'misskey-js'; import MkCustomEmojiDetailedDialog from './MkCustomEmojiDetailedDialog.vue'; import XDetails from '@/components/MkReactionsViewer.details.vue'; @@ -42,7 +42,9 @@ const props = defineProps<{ reaction: string; count: number; isInitial: boolean; - note: Misskey.entities.Note; + note: Misskey.entities.Note & { + myReactions: string[]; + } }>(); const mock = inject<boolean>('mock', false); @@ -64,14 +66,15 @@ const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction async function toggleReaction() { if (!canToggle.value) return; - const oldReaction = props.note.myReaction; + const oldReaction = props.note.myReactions.includes(props.reaction) ? props.reaction : null; + console.log(oldReaction); if (oldReaction) { const confirm = await os.confirm({ type: 'warning', text: oldReaction !== props.reaction ? i18n.ts.changeReactionConfirm : i18n.ts.cancelReactionConfirm, }); if (confirm.canceled) return; - + props.note.myReactions.splice(props.note.myReactions.indexOf(oldReaction), 1); if (oldReaction !== props.reaction) { sound.playMisskeySfx('reaction'); } @@ -83,8 +86,9 @@ async function toggleReaction() { misskeyApi('notes/reactions/delete', { noteId: props.note.id, + reaction: oldReaction, }).then(() => { - if (oldReaction !== props.reaction) { + if (oldReaction !== props.reaction ) { misskeyApi('notes/reactions/create', { noteId: props.note.id, reaction: props.reaction, diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue index 63b202f9f3..5749b521f5 100644 --- a/packages/frontend/src/components/MkReactionsViewer.vue +++ b/packages/frontend/src/components/MkReactionsViewer.vue @@ -24,7 +24,9 @@ import XReaction from '@/components/MkReactionsViewer.reaction.vue'; import { defaultStore } from '@/store.js'; const props = withDefaults(defineProps<{ - note: Misskey.entities.Note; + note: Misskey.entities.Note & { + myReactions: string[]; + } maxNumber?: number; }>(), { maxNumber: Infinity, @@ -57,7 +59,6 @@ function onMockToggleReaction(emoji: string, count: number) { watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumber]) => { let newReactions: [string, number][] = []; hasMoreReactions.value = Object.keys(newSource).length > maxNumber; - for (let i = 0; i < reactions.value.length; i++) { const reaction = reactions.value[i][0]; if (reaction in newSource && newSource[reaction] !== 0) { @@ -76,7 +77,7 @@ watch([() => props.note.reactions, () => props.maxNumber], ([newSource, maxNumbe newReactions = newReactions.slice(0, props.maxNumber); - if (props.note.myReaction && !newReactions.map(([x]) => x).includes(props.note.myReaction)) { + if (props.note. myReaction && !newReactions.map(([x]) => x).includes(props.note.myReaction)) { newReactions.push([props.note.myReaction, newSource[props.note.myReaction]]); } diff --git a/packages/frontend/src/components/global/MkLoading.vue b/packages/frontend/src/components/global/MkLoading.vue index abc214719f..0ac6d4212c 100644 --- a/packages/frontend/src/components/global/MkLoading.vue +++ b/packages/frontend/src/components/global/MkLoading.vue @@ -84,7 +84,9 @@ const props = withDefaults(defineProps<{ height: var(--size); margin: 0 auto; } - +.text{ + color: var(--fg); +} .spinner { position: absolute; top: 0; diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts index 86cff7887c..88a792bf99 100644 --- a/packages/frontend/src/scripts/use-note-capture.ts +++ b/packages/frontend/src/scripts/use-note-capture.ts @@ -39,6 +39,13 @@ export function useNoteCapture(props: { if ($i && (body.userId === $i.id)) { note.value.myReaction = reaction; + console.log(note.value.myReactions); + if (!note.value.myReactions) { + note.value.myReactions = []; + note.value.myReactions.push(reaction); + } else if (!note.value.myReactions.includes(reaction)) { + note.value.myReactions.push(reaction); + } } break; } @@ -55,6 +62,7 @@ export function useNoteCapture(props: { if ($i && (body.userId === $i.id)) { note.value.myReaction = null; + note.value.myReactions = note.value.myReactions.filter(r => r !== reaction); } break; } From 203b824b28610b22cdebf817ce11ae461bc40256 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 13:49:08 +0900 Subject: [PATCH 493/501] emoji more --- .../backend/migration/1718857094676-emojimore.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 packages/backend/migration/1718857094676-emojimore.js diff --git a/packages/backend/migration/1718857094676-emojimore.js b/packages/backend/migration/1718857094676-emojimore.js new file mode 100644 index 0000000000..596b3242b9 --- /dev/null +++ b/packages/backend/migration/1718857094676-emojimore.js @@ -0,0 +1,16 @@ +export class Emojimore1718857094676 { + name = 'Emojimore1718857094676' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_ad0c221b25672daf2df320a817"`); + await queryRunner.query(`CREATE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") `); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_a7751b74317122d11575bff31c" ON "note_reaction" ("userId", "noteId", "reaction") `); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_a7751b74317122d11575bff31c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_ad0c221b25672daf2df320a817"`); + await queryRunner.query(`CREATE UNIQUE INDEX "IDX_ad0c221b25672daf2df320a817" ON "note_reaction" ("userId", "noteId") `); + + } +} From f9a4e7ad3a04a6d296f56af07bfa1b75b9c74b77 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 13:56:54 +0900 Subject: [PATCH 494/501] 2024.5.0-mattyatea3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f6f00ef712..b34a6c1da5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-mattyatea2", + "version": "2024.5.0-mattyatea3", "codename": "nasubi", "repository": { "type": "git", From a72bb3026e114e78413c82db9c0dd14104668067 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 14:01:08 +0900 Subject: [PATCH 495/501] fix --- packages/frontend/src/components/MkNote.vue | 2 +- packages/frontend/src/components/MkNoteDetailed.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 976ebd7a8f..34eebd841d 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -444,7 +444,7 @@ function react(viaKeyboard = false): void { } function toggleReact() { - if (appearNote.value.myReactions?.length > 3 || !appearNote.value.myReactions ) { + if (appearNote.value.myReactions?.length < 3 || !appearNote.value.myReactions ) { react(); } } diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 3b794bb7ee..f6b0f08b37 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -502,7 +502,7 @@ function undoReact(targetNote: Misskey.entities.Note): void { } function toggleReact() { - if (appearNote.value.myReactions?.length > 3 || !appearNote.value.myReactions ) { + if (appearNote.value.myReactions?.length < 3 || !appearNote.value.myReactions ) { react(); } } From e039cba2a3e2d8555f07bd64ec99620a8efde2bf Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 14:08:20 +0900 Subject: [PATCH 496/501] fix --- packages/frontend/src/components/MkNote.vue | 2 +- packages/frontend/src/components/MkNoteDetailed.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 34eebd841d..3e3cd3914e 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -130,7 +130,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> <button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()"> <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> - <i v-else-if="appearNote.myReactions?.length >= 3 " class="ti ti-minus" style="color: var(--accent);"></i> + <i v-else-if="appearNote.myReactions?.length >= 3 || appearNote.myReaction && appearNote.user.host" class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p> diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index f6b0f08b37..910b4c0ca0 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -130,7 +130,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> <button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()"> <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> - <i v-else-if="appearNote.myReactions?.length >= 3 " class="ti ti-minus" style="color: var(--accent);"></i> + <i v-else-if="appearNote.myReactions?.length >= 3 || appearNote.myReaction && appearNote.user.host " class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p> From 0f99ba192d87ec8edc4e2936f21aadd318a2080f Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 14:13:05 +0900 Subject: [PATCH 497/501] fix --- .../frontend/src/components/MkReactionsViewer.reaction.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue index 1323b896ff..ed70654649 100644 --- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue +++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue @@ -66,8 +66,7 @@ const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction async function toggleReaction() { if (!canToggle.value) return; - const oldReaction = props.note.myReactions.includes(props.reaction) ? props.reaction : null; - console.log(oldReaction); + const oldReaction = props.note.myReactions?.includes(props.reaction) ? props.reaction : null; if (oldReaction) { const confirm = await os.confirm({ type: 'warning', From b69ce26a91e191c439d62289bb2edf429db3d066 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 14:21:20 +0900 Subject: [PATCH 498/501] fix --- packages/frontend/src/components/MkNote.vue | 2 +- packages/frontend/src/components/MkNoteDetailed.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 3e3cd3914e..9b5b18efba 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -444,7 +444,7 @@ function react(viaKeyboard = false): void { } function toggleReact() { - if (appearNote.value.myReactions?.length < 3 || !appearNote.value.myReactions ) { + if (appearNote.value.myReactions?.length < 3 || appearNote.value.myReaction && appearNote.value.user.host || !appearNote.value.myReactions ) { react(); } } diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 910b4c0ca0..dc5b224f79 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -502,7 +502,7 @@ function undoReact(targetNote: Misskey.entities.Note): void { } function toggleReact() { - if (appearNote.value.myReactions?.length < 3 || !appearNote.value.myReactions ) { + if (appearNote.value.myReactions?.length < 3 || appearNote.value.myReaction && appearNote.value.user.host || !appearNote.value.myReactions ) { react(); } } From b648e1921baaecac618b8fa9760fb2e31f176e17 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Thu, 20 Jun 2024 14:26:04 +0900 Subject: [PATCH 499/501] fix --- packages/frontend/src/components/MkNote.vue | 4 ++-- packages/frontend/src/components/MkNoteDetailed.vue | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 9b5b18efba..19fd8f6570 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -129,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti ti-ban"></i> </button> <button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 4 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> <i v-else-if="appearNote.myReactions?.length >= 3 || appearNote.myReaction && appearNote.user.host" class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> @@ -444,7 +444,7 @@ function react(viaKeyboard = false): void { } function toggleReact() { - if (appearNote.value.myReactions?.length < 3 || appearNote.value.myReaction && appearNote.value.user.host || !appearNote.value.myReactions ) { + if (appearNote.value.myReactions?.length < 4 || appearNote.value.myReaction && appearNote.value.user.host || !appearNote.value.myReactions ) { react(); } } diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index dc5b224f79..681e58c4b9 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -130,7 +130,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> <button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()"> <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> - <i v-else-if="appearNote.myReactions?.length >= 3 || appearNote.myReaction && appearNote.user.host " class="ti ti-minus" style="color: var(--accent);"></i> + <i v-else-if="appearNote.myReactions?.length >= 4 || appearNote.myReaction && appearNote.user.host " class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.noteFooterButtonCount">{{ number(appearNote.reactionCount) }}</p> @@ -502,7 +502,7 @@ function undoReact(targetNote: Misskey.entities.Note): void { } function toggleReact() { - if (appearNote.value.myReactions?.length < 3 || appearNote.value.myReaction && appearNote.value.user.host || !appearNote.value.myReactions ) { + if (appearNote.value.myReactions?.length < 4 || appearNote.value.myReaction && appearNote.value.user.host || !appearNote.value.myReactions ) { react(); } } From be65fd66a3763ceed063bbddf3096b89004ebfb0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Fri, 21 Jun 2024 18:42:39 +0900 Subject: [PATCH 500/501] fix --- packages/backend/src/core/ReactionService.ts | 61 +++++++++++++------ .../src/components/MkNoteDetailed.vue | 2 +- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 96e0b80987..36a0dd480f 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -160,33 +160,56 @@ export class ReactionService { userId: user.id, reaction, }; + if (user.host == null) { + const exists = await this.noteReactionsRepository.findOneBy({ + noteId: note.id, + userId: user.id, + reaction: record.reaction, + }); - // Create reaction - const exists = await this.noteReactionsRepository.findOneBy({ - noteId: note.id, - userId: user.id, - reaction: record.reaction, - }); + const count = await this.noteReactionsRepository.countBy({ + noteId: note.id, + userId: user.id, + }); - const count = await this.noteReactionsRepository.countBy({ - noteId: note.id, - userId: user.id, - }); + if (count > 3) { + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); + } - if (count > 3) { - throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); - } - - if (exists == null) { - if (user.host == null) { - await this.noteReactionsRepository.insert(record); + if (exists == null) { + if (user.host == null) { + await this.noteReactionsRepository.insert(record); + } else { + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); + } } else { + // 同じリアクションがすでにされていたらエラー throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); } } else { - // 同じリアクションがすでにされていたらエラー - throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); + try { + await this.noteReactionsRepository.insert(record); + } catch (e) { + if (isDuplicateKeyValueError(e)) { + const exists = await this.noteReactionsRepository.findOneByOrFail({ + noteId: note.id, + userId: user.id, + }); + + if (exists.reaction !== reaction) { + // 別のリアクションがすでにされていたら置き換える + await this.delete(user, note); + await this.noteReactionsRepository.insert(record); + } else { + // 同じリアクションがすでにされていたらエラー + throw new IdentifiableError('51c42bb4-931a-456b-bff7-e5a8a70dd298'); + } + } else { + throw e; + } + } } + // Create reaction // Increment reactions count const sql = `jsonb_set("reactions", '{${reaction}}', (COALESCE("reactions"->>'${reaction}', '0')::int + 1)::text::jsonb)`; diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 681e58c4b9..e1b6fb58bd 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -129,7 +129,7 @@ SPDX-License-Identifier: AGPL-3.0-only <i class="ti ti-ban"></i> </button> <button ref="reactButton" :class="$style.noteFooterButton" class="_button" @click="toggleReact()"> - <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 3 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReactions?.length >= 4 " class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> <i v-else-if="appearNote.myReactions?.length >= 4 || appearNote.myReaction && appearNote.user.host " class="ti ti-minus" style="color: var(--accent);"></i> <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> <i v-else class="ti ti-plus"></i> From 564f2e9127ba4130bbb18ca3e347da6772ae43b0 Mon Sep 17 00:00:00 2001 From: mattyatea <mattyatea@pm.me> Date: Tue, 25 Jun 2024 07:09:54 +0900 Subject: [PATCH 501/501] 2024.5.0-mattyatea4 --- package.json | 2 +- .../backend/src/core/NoteCreateService.ts | 3 + .../api/endpoints/notes/any-local-timeline.ts | 211 +-- packages/cherrypick-js/src/entities.ts | 0 packages/frontend/src/boot/main-boot.ts | 1 + .../frontend/src/components/MkTimeline.vue | 6 +- packages/frontend/src/components/XNote.vue | 1075 +++++++++++++ .../frontend/src/components/XNoteHeader.vue | 137 ++ .../frontend/src/components/XPostForm.vue | 1349 +++++++++++++++++ .../src/components/XPostFormDialog.vue | 62 + .../components/global/MkPageHeader.tabs.vue | 15 +- .../src/components/global/MkPageHeader.vue | 3 +- packages/frontend/src/navbar.ts | 7 + packages/frontend/src/os.ts | 31 +- .../frontend/src/pages/settings/index.vue | 2 +- .../src/pages/settings/timelineHeader.vue | 50 +- packages/frontend/src/pages/timeline.vue | 16 +- packages/frontend/src/store.ts | 84 +- packages/frontend/src/timeline-header.ts | 65 +- packages/frontend/src/ui/twilike.sidebar.vue | 260 ++++ packages/frontend/src/ui/twilike.vue | 463 ++++++ 21 files changed, 3579 insertions(+), 263 deletions(-) delete mode 100644 packages/cherrypick-js/src/entities.ts create mode 100644 packages/frontend/src/components/XNote.vue create mode 100644 packages/frontend/src/components/XNoteHeader.vue create mode 100644 packages/frontend/src/components/XPostForm.vue create mode 100644 packages/frontend/src/components/XPostFormDialog.vue create mode 100644 packages/frontend/src/ui/twilike.sidebar.vue create mode 100644 packages/frontend/src/ui/twilike.vue diff --git a/package.json b/package.json index b34a6c1da5..abeb480344 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "misskey", - "version": "2024.5.0-mattyatea3", + "version": "2024.5.0-mattyatea4", "codename": "nasubi", "repository": { "type": "git", diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 987fe78f44..4ab5c23382 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -1010,6 +1010,9 @@ export class NoteCreateService implements OnApplicationShutdown { this.fanoutTimelineService.push('localTimelineWithFiles', note.id, 500, r); } } + if (note.visibility === 'public' && note.userHost !== null) { + this.fanoutTimelineService.push(`remoteLocalTimeline:${note.userHost}`, note.id, 1000, r); + } } if (Math.random() < 0.1) { diff --git a/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts index 3619661be8..077252da43 100644 --- a/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/any-local-timeline.ts @@ -4,7 +4,7 @@ */ import { Inject, Injectable } from '@nestjs/common'; -import { In } from 'typeorm'; +import { Brackets, In } from 'typeorm'; import { Endpoint } from '@/server/api/endpoint-base.js'; import { IdService } from '@/core/IdService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; @@ -24,6 +24,8 @@ import { UtilityService } from '@/core/UtilityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { MetaService } from '@/core/MetaService.js'; import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js'; +import { FanoutTimelineEndpointService } from '@/core/FanoutTimelineEndpointService.js'; +import { QueryService } from '@/core/QueryService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -80,168 +82,81 @@ export const paramDef = { @Injectable() export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export constructor( + private idService: IdService, + private fanoutTimelineEndpointService: FanoutTimelineEndpointService, + private queryService: QueryService, @Inject(DI.notesRepository) private notesRepository: NotesRepository, - private idService: IdService, - private federatedInstanceService: FederatedInstanceService, - private httpRequestService: HttpRequestService, - private utilityService: UtilityService, - private userEntityService: UserEntityService, - private noteEntityService: NoteEntityService, - private metaService: MetaService, - private apResolverService: ApResolverService, - private apDbResolverService: ApDbResolverService, - private apPersonService: ApPersonService, - private apNoteService: ApNoteService, ) { super(meta, paramDef, async (ps, me) => { const untilId = ps.untilId ?? (ps.untilDate ? this.idService.gen(ps.untilDate!) : null); const sinceId = ps.sinceId ?? (ps.sinceDate ? this.idService.gen(ps.sinceDate!) : null); - if (ps.host === undefined) throw new ApiError(meta.errors.hostIsNull); - if (ps.remoteToken === undefined) throw new ApiError(meta.errors.remoteTokenIsNull); - const i = await this.federatedInstanceService.fetch(ps.host); - const noteIds = []; - if (i.softwareName === 'misskey') { - const remoteTimeline: string[] = await (await this.httpRequestService.send('https://' + ps.host + '/api/notes/local-timeline', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - i: ps.remoteToken, - withFiles: ps.withFiles, - withRenotes: ps.withRenotes, - withReplies: ps.withReplies, - limit: 30, - }), - })).json() as string[]; + if (ps.withReplies && ps.withFiles) throw new ApiError(meta.errors.bothWithRepliesAndWithFiles); - if (remoteTimeline.length > 0) { - for (const note of remoteTimeline) { - const uri = `https://${ps.host}/notes/${note.id}`; - const note_ = await this.fetchAny(uri, me); - if (note_ == null) continue; - noteIds.push(note_.id); - } - } + const timeline = await this.fanoutTimelineEndpointService.timeline({ + untilId, + sinceId, + limit: ps.limit, + allowPartial: ps.allowPartial, + me, + useDbFallback: true, + redisTimelines: [`remoteLocalTimeline:${ps.host}`], + alwaysIncludeMyNotes: true, + excludePureRenotes: !ps.withRenotes, + dbFallback: async (untilId, sinceId, limit) => await this.getFromDb({ + untilId, + sinceId, + limit, + withFiles: ps.withFiles, + withReplies: ps.withReplies, + host: ps.host, + }, me), + }); - let notes = await this.notesRepository.findBy({ id: In(noteIds) }); - let packedNote: any[] = await this.noteEntityService.packMany(notes, me, { detail: true }); - if (untilId) { - let lastRemoteId; - const lastUri = packedNote[packedNote.length - 1].uri; - lastRemoteId = lastUri.split('/')[lastUri.split('/').length - 1]; - do { - const remoteTimeline: string[] = await (await this.httpRequestService.send('https://' + ps.host + '/api/notes/local-timeline', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - i: ps.remoteToken, - withFiles: ps.withFiles, - withRenotes: ps.withRenotes, - withReplies: ps.withReplies, - untilId: lastRemoteId, - limit: 30, - }), - })).json() as string[]; + return timeline; + }, - if (remoteTimeline.length > 0) { - for (const note of remoteTimeline) { - const uri = `https://${ps.host}/notes/${note.id}`; - const note_ = await this.fetchAny(uri, me); - if (note_ == null) continue; - //noteIds.push(note_.id); - lastRemoteId = note_.id; - if (lastRemoteId === ps.untilId) { - break; - } - } - } - } while (lastRemoteId !== ps.untilId); - const remoteTimeline: string[] = await (await this.httpRequestService.send('https://' + ps.host + '/api/notes/local-timeline', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - i: ps.remoteToken, - withFiles: ps.withFiles, - withRenotes: ps.withRenotes, - withReplies: ps.withReplies, - untilId: lastRemoteId, - limit: 30, - }), - })).json() as string[]; - - if (remoteTimeline.length > 0) { - for (const note of remoteTimeline) { - const uri = `https://${ps.host}/notes/${note.id}`; - const note_ = await this.fetchAny(uri, me); - if (note_ == null) continue; - noteIds.push(note_.id); - } - } - } - - notes = await this.notesRepository.findBy({ id: In(noteIds) }); - packedNote = await this.noteEntityService.packMany(notes, me, { detail: true }); - return packedNote.reverse(); - } - }); - } - @bindThis - private async fetchAny(uri: string, me: MiLocalUser | null | undefined) { - // ブロックしてたら中断 - const fetchedMeta = await this.metaService.fetch(); - if (this.utilityService.isBlockedHost(fetchedMeta.blockedHosts, this.utilityService.extractDbHost(uri))) return null; - - let local = await this.mergePack(me, ...await Promise.all([ - this.apDbResolverService.getUserFromApId(uri), - this.apDbResolverService.getNoteFromApId(uri), - ])); - if (local != null) return local; - - // リモートから一旦オブジェクトフェッチ - let object; - try { - const resolver = this.apResolverService.createResolver(); - object = await resolver.resolve(uri) as any; - } catch (e) { - return null; - } - if (!object) return null; - // /@user のような正規id以外で取得できるURIが指定されていた場合、ここで初めて正規URIが確定する - // これはDBに存在する可能性があるため再度DB検索 - if (uri !== object.id) { - local = await this.mergePack(me, ...await Promise.all([ - this.apDbResolverService.getUserFromApId(object.id), - this.apDbResolverService.getNoteFromApId(object.id), - ])); - if (local != null) return local; - } - - return await this.mergePack( - me, - isActor(object) ? await this.apPersonService.createPerson(getApId(object)) : null, - isPost(object) ? await this.apNoteService.createNote(getApId(object), undefined, true) : null, ); } + private async getFromDb(ps: { + sinceId: string | null, + untilId: string | null, + limit: number, + withFiles: boolean, + withReplies: boolean, + host: string, + }, me: MiLocalUser | null) { + const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), + ps.sinceId, ps.untilId) + .andWhere(`(note.visibility = \'public\') AND (note.userHost = \'${ps.host}\') AND (note.channelId IS NULL)`) + .innerJoinAndSelect('note.user', 'user') + .leftJoinAndSelect('note.reply', 'reply') + .leftJoinAndSelect('note.renote', 'renote') + .leftJoinAndSelect('reply.user', 'replyUser') + .leftJoinAndSelect('renote.user', 'renoteUser'); - @bindThis - private async mergePack(me: MiLocalUser | null | undefined, user: MiUser | null | undefined, note: MiNote | null | undefined) { - if (note != null) { - try { - const object = await this.noteEntityService.pack(note, me, { detail: true }); + this.queryService.generateVisibilityQuery(query, me); + if (me) this.queryService.generateMutedUserQuery(query, me); + if (me) this.queryService.generateBlockedUserQuery(query, me); + if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me); - return object; - } catch (e) { - return null; - } + if (ps.withFiles) { + query.andWhere('note.fileIds != \'{}\''); } - return null; + if (!ps.withReplies) { + query.andWhere(new Brackets(qb => { + qb + .where('note.replyId IS NULL') // 返信ではない + .orWhere(new Brackets(qb => { + qb // 返信だけど投稿者自身への返信 + .where('note.replyId IS NOT NULL') + .andWhere('note.replyUserId = note.userId'); + })); + })); + } + + return await query.limit(ps.limit).getMany(); } } diff --git a/packages/cherrypick-js/src/entities.ts b/packages/cherrypick-js/src/entities.ts deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 4118a6f81c..7050a8fec8 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -28,6 +28,7 @@ export async function mainBoot() { !$i ? defineAsyncComponent(() => import('@/ui/visitor.vue')) : ui === 'deck' ? defineAsyncComponent(() => import('@/ui/deck.vue')) : ui === 'classic' ? defineAsyncComponent(() => import('@/ui/classic.vue')) : + ui === 'twilike' ? defineAsyncComponent(() => import('@/ui/twilike.vue')) : defineAsyncComponent(() => import('@/ui/universal.vue')), )); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index 4ba1ced710..f33126bfb6 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -165,14 +165,12 @@ function updatePaginationQuery() { channel: { channelId: props.channel }, role: { roleId: props.role }, }; - - if (props.src.startsWith('custom-timeline')) { + if (props.src.startsWith('remoteLocalTimeline')) { paginationQuery = { endpoint: 'notes/any-local-timeline', limit: 10, params: { - host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split('-')[2]}`], - remoteToken: defaultStore.state[`remoteLocalTimelineToken${props.src.split('-')[2]}`], + host: props.list, }, }; } else { diff --git a/packages/frontend/src/components/XNote.vue b/packages/frontend/src/components/XNote.vue new file mode 100644 index 0000000000..16f50870f9 --- /dev/null +++ b/packages/frontend/src/components/XNote.vue @@ -0,0 +1,1075 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div + v-if="!hardMuted && muted === false" + v-show="!isDeleted" + ref="rootEl" + v-hotkey="keymap" + :class="[$style.root, { [$style.showActionsOnlyHover]: defaultStore.state.showNoteActionsOnlyHover }]" + :tabindex="!isDeleted ? '-1' : undefined" +> + <MkNoteSub v-if="appearNote.reply && !renoteCollapsed" :note="appearNote.reply" :class="$style.replyTo"/> + <div v-if="pinned" :class="$style.tip"><i class="ti ti-pin"></i> {{ i18n.ts.pinnedNote }}</div> + <div v-if="isRenote" :class="$style.renote"> + <div v-if="note.channel" :class="$style.colorBar" :style="{ background: note.channel.color }"></div> + <MkAvatar :class="$style.renoteAvatar" :user="note.user" link preview/> + <i class="ti ti-repeat" style="margin-right: 4px;"></i> + <I18n :src="i18n.ts.renotedBy" tag="span" :class="$style.renoteText"> + <template #user> + <MkA v-user-preview="note.userId" :class="$style.renoteUserName" :to="userPage(note.user)"> + <MkUserName :user="note.user"/> + </MkA> + </template> + </I18n> + <div :class="$style.renoteInfo"> + <button ref="renoteTime" :class="$style.renoteTime" class="_button" @click="showRenoteMenu()"> + <i class="ti ti-dots" :class="$style.renoteMenu"></i> + <MkTime :time="note.createdAt"/> + </button> + <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> + <i v-if="note.visibility === 'home'" class="ti ti-home"></i> + <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> + <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> + </span> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> + <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span> + </div> + </div> + <div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget"> + <MkAvatar :class="$style.collapsedRenoteTargetAvatar" :user="appearNote.user" link preview/> + <Mfm :text="getNoteSummary(appearNote)" :plain="true" :nowrap="true" :author="appearNote.user" :nyaize="'respect'" :class="$style.collapsedRenoteTargetText" @click="renoteCollapsed = false"/> + </div> + <article v-else :class="$style.article" @contextmenu.stop="onContextmenu"> + <div v-if="appearNote.channel" :class="$style.colorBar" :style="{ background: appearNote.channel.color }"></div> + <MkAvatar :class="$style.avatar" :user="appearNote.user" :link="!mock" :preview="!mock"/> + <div :class="$style.main"> + + <XNoteHeader :note="appearNote" :mini="true"/> + + <MkInstanceTicker v-if="showTicker" :instance="appearNote.user.instance"/> + <div style="container-type: inline-size;"> + <p v-if="appearNote.cw != null" :class="$style.cw"> + <Mfm v-if="appearNote.cw != ''" style="margin-right: 8px;" :text="appearNote.cw" :author="appearNote.user" :nyaize="'respect'"/> + <MkCwButton v-model="showContent" :text="appearNote.text" :renote="appearNote.renote" :files="appearNote.files" :poll="appearNote.poll" style="margin: 4px 0;"/> + </p> + <div v-show="appearNote.cw == null || showContent" :class="[{ [$style.contentCollapsed]: collapsed }]"> + <div :class="$style.text"> + <span v-if="appearNote.isHidden" style="opacity: 0.5">({{ i18n.ts.private }})</span> + <MkA v-if="appearNote.replyId" :class="$style.replyIcon" :to="`/notes/${appearNote.replyId}`"><i class="ti ti-arrow-back-up"></i></MkA> + <Mfm + v-if="appearNote.text" + :parsedNodes="parsed" + :text="appearNote.text" + :author="appearNote.user" + :nyaize="'respect'" + :emojiUrls="appearNote.emojis" + :enableEmojiMenu="true" + :enableEmojiMenuReaction="true" + /> + <div v-if="translating || translation" :class="$style.translation"> + <MkLoading v-if="translating" mini/> + <div v-else-if="translation"> + <b>{{ i18n.tsx.translatedFrom({ x: translation.sourceLang }) }}: </b> + <Mfm :text="translation.text" :author="appearNote.user" :nyaize="'respect'" :emojiUrls="appearNote.emojis"/> + </div> + </div> + </div> + <div v-if="appearNote.files && appearNote.files.length > 0"> + <MkMediaList :mediaList="appearNote.files"/> + </div> + <MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> + <div v-if="isEnabledUrlPreview"> + <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/> + </div> + <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> + <button v-if="isLong && collapsed" :class="$style.collapsed" class="_button" @click="collapsed = false"> + <span :class="$style.collapsedLabel">{{ i18n.ts.showMore }}</span> + </button> + <button v-else-if="isLong && !collapsed" :class="$style.showLess" class="_button" @click="collapsed = true"> + <span :class="$style.showLessLabel">{{ i18n.ts.showLess }}</span> + </button> + </div> + <MkA v-if="appearNote.channel && !inChannel" :class="$style.channel" :to="`/channels/${appearNote.channel.id}`"><i class="ti ti-device-tv"></i> {{ appearNote.channel.name }}</MkA> + </div> + <MkReactionsViewer v-if="appearNote.reactionAcceptance !== 'likeOnly'" :note="appearNote" :maxNumber="16" @mockUpdateMyReaction="emitUpdReaction"> + <template #more> + <MkA :to="`/notes/${appearNote.id}/reactions`" :class="[$style.reactionOmitted]">{{ i18n.ts.more }}</MkA> + </template> + </MkReactionsViewer> + <footer :class="$style.footer"> + <div :class="$style.footerLeft"> + <button :class="$style.footerButton" class="_button" @click="reply()"> + <i class="ti ti-arrow-back-up"></i> + <p v-if="appearNote.repliesCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.repliesCount) }}</p> + </button> + <button + v-if="canRenote" + ref="renoteButton" + :class="$style.footerButton" + class="_button" + @mousedown="renote()" + > + <i class="ti ti-repeat"></i> + <p v-if="appearNote.renoteCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.renoteCount) }}</p> + </button> + <button v-else :class="$style.footerButton" class="_button" disabled> + <i class="ti ti-ban"></i> + </button> + <button ref="reactButton" :class="$style.footerButton" class="_button" @click="toggleReact()"> + <i v-if="appearNote.reactionAcceptance === 'likeOnly' && appearNote.myReaction != null" class="ti ti-heart-filled" style="color: var(--eventReactionHeart);"></i> + <i v-else-if="appearNote.myReaction != null" class="ti ti-minus" style="color: var(--accent);"></i> + <i v-else-if="appearNote.reactionAcceptance === 'likeOnly'" class="ti ti-heart"></i> + <i v-else class="ti ti-plus"></i> + <p v-if="(appearNote.reactionAcceptance === 'likeOnly' || defaultStore.state.showReactionsCount) && appearNote.reactionCount > 0" :class="$style.footerButtonCount">{{ number(appearNote.reactionCount) }}</p> + </button> + </div> + <div :class="$style.footerRight"> + <button v-if="defaultStore.state.showClipButtonInNoteFooter" ref="clipButton" :class="$style.footerButton" class="_button" @mousedown="clip()"> + <i class="ti ti-paperclip"></i> + </button> + + <button :class="$style.footerButton" class="_button" @mousedown="toggleFavorite()"> + <i v-if="!isFavorite" class="ti ti-star"></i> + <i v-if="isFavorite" class="ti ti-star-off"></i> + </button> + </div> + </footer> + </div> + </article> +</div> +<div v-else-if="!hardMuted" :class="$style.muted" @click="muted = false"> + <I18n v-if="muted === 'sensitiveMute'" :src="i18n.ts.userSaysSomethingSensitive" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> + <I18n v-else :src="i18n.ts.userSaysSomething" tag="small"> + <template #name> + <MkA v-user-preview="appearNote.userId" :to="userPage(appearNote.user)"> + <MkUserName :user="appearNote.user"/> + </MkA> + </template> + </I18n> +</div> +<div v-else> + <!-- + MkDateSeparatedList uses TransitionGroup which requires single element in the child elements + so MkNote create empty div instead of no elements + --> +</div> +</template> + +<script lang="ts" setup> +import { computed, inject, onMounted, ref, shallowRef, Ref, watch, provide } from 'vue'; +import * as mfm from 'mfm-js'; +import * as Misskey from 'misskey-js'; +import MkNoteSub from '@/components/MkNoteSub.vue'; +import XNoteHeader from '@/components/XNoteHeader.vue'; +import MkNoteSimple from '@/components/MkNoteSimple.vue'; +import MkReactionsViewer from '@/components/MkReactionsViewer.vue'; +import MkReactionsViewerDetails from '@/components/MkReactionsViewer.details.vue'; +import MkMediaList from '@/components/MkMediaList.vue'; +import MkCwButton from '@/components/MkCwButton.vue'; +import MkPoll from '@/components/MkPoll.vue'; +import MkUsersTooltip from '@/components/MkUsersTooltip.vue'; +import MkUrlPreview from '@/components/MkUrlPreview.vue'; +import MkInstanceTicker from '@/components/MkInstanceTicker.vue'; +import { pleaseLogin } from '@/scripts/please-login.js'; +import { focusPrev, focusNext } from '@/scripts/focus.js'; +import { checkWordMute } from '@/scripts/check-word-mute.js'; +import { userPage } from '@/filters/user.js'; +import number from '@/filters/number.js'; +import * as os from '@/os.js'; +import * as sound from '@/scripts/sound.js'; +import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; +import { defaultStore, noteViewInterruptors } from '@/store.js'; +import { reactionPicker } from '@/scripts/reaction-picker.js'; +import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; +import { $i } from '@/account.js'; +import { i18n } from '@/i18n.js'; +import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/scripts/get-note-menu.js'; +import { useNoteCapture } from '@/scripts/use-note-capture.js'; +import { deepClone } from '@/scripts/clone.js'; +import { useTooltip } from '@/scripts/use-tooltip.js'; +import { claimAchievement } from '@/scripts/achievements.js'; +import { getNoteSummary } from '@/scripts/get-note-summary.js'; +import { MenuItem } from '@/types/menu.js'; +import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; +import { shouldCollapsed } from '@/scripts/collapsed.js'; +import { isEnabledUrlPreview } from '@/instance.js'; + +const props = withDefaults(defineProps<{ + note: Misskey.entities.Note; + pinned?: boolean; + mock?: boolean; + withHardMute?: boolean; +}>(), { + mock: false, +}); + +provide('mock', props.mock); + +const emit = defineEmits<{ + (ev: 'reaction', emoji: string): void; + (ev: 'removeReaction', emoji: string): void; +}>(); + +const inTimeline = inject<boolean>('inTimeline', false); +const inChannel = inject('inChannel', null); +const currentClip = inject<Ref<Misskey.entities.Clip> | null>('currentClip', null); + +const note = ref(deepClone(props.note)); + +// plugin +if (noteViewInterruptors.length > 0) { + onMounted(async () => { + let result: Misskey.entities.Note | null = deepClone(note.value); + for (const interruptor of noteViewInterruptors) { + try { + result = await interruptor.handler(result!) as Misskey.entities.Note | null; + if (result === null) { + isDeleted.value = true; + return; + } + } catch (err) { + console.error(err); + } + } + note.value = result as Misskey.entities.Note; + }); +} + +const isRenote = ( + note.value.renote != null && + note.value.reply == null && + note.value.text == null && + note.value.cw == null && + note.value.fileIds && note.value.fileIds.length === 0 && + note.value.poll == null +); + +const rootEl = shallowRef<HTMLElement>(); +const renoteButton = shallowRef<HTMLElement>(); +const renoteTime = shallowRef<HTMLElement>(); +const reactButton = shallowRef<HTMLElement>(); +const clipButton = shallowRef<HTMLElement>(); +const 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 parsed = computed(() => appearNote.value.text ? mfm.parse(appearNote.value.text) : null); +const urls = computed(() => parsed.value ? extractUrlFromMfm(parsed.value).filter((url) => appearNote.value.renote?.url !== url && appearNote.value.renote?.uri !== url) : null); +const isLong = shouldCollapsed(appearNote.value, urls.value ?? []); +const collapsed = ref(appearNote.value.cw == null && isLong); +const isDeleted = ref(false); +const muted = ref(checkMute(appearNote.value, $i?.mutedWords)); +const hardMuted = ref(props.withHardMute && checkMute(appearNote.value, $i?.hardMutedWords, true)); +const translation = ref<Misskey.entities.NotesTranslateResponse | null>(null); +const translating = ref(false); +const showTicker = (defaultStore.state.instanceTicker === 'always') || (defaultStore.state.instanceTicker === 'remote' && appearNote.value.user.instance); +const canRenote = computed(() => ['public', 'home'].includes(appearNote.value.visibility) || (appearNote.value.visibility === 'followers' && appearNote.value.userId === $i?.id)); +const isFavorite = ref(false); +let statePromise; +if ($i) { + statePromise = misskeyApi('notes/state', { + noteId: props.note.id, + }); + statePromise.then((state) => { + isFavorite.value = state.isFavorited; + }); + +} +const renoteCollapsed = ref( + defaultStore.state.collapseRenotes && isRenote && ( + ($i && ($i.id === note.value.userId || $i.id === appearNote.value.userId)) || // `||` must be `||`! See https://github.com/misskey-dev/misskey/issues/13131 + (appearNote.value.myReaction != null) + ), +); + +/* Overload FunctionにLintが対応していないのでコメントアウト +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: true): boolean; +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly: false): boolean | 'sensitiveMute'; +*/ +function checkMute(noteToCheck: Misskey.entities.Note, mutedWords: Array<string | string[]> | undefined | null, checkOnly = false): boolean | 'sensitiveMute' { + if (mutedWords == null) return false; + + if (checkWordMute(noteToCheck, $i, mutedWords)) return true; + if (noteToCheck.reply && checkWordMute(noteToCheck.reply, $i, mutedWords)) return true; + if (noteToCheck.renote && checkWordMute(noteToCheck.renote, $i, mutedWords)) return true; + + if (checkOnly) return false; + + if (inTimeline && !defaultStore.state.tl.filter.withSensitive && noteToCheck.files?.some((v) => v.isSensitive)) return 'sensitiveMute'; + return false; +} + +const keymap = { + 'r': () => reply(true), + 'e|a|plus': () => react(true), + 'q': () => renote(true), + 'up|k|shift+tab': focusBefore, + 'down|j|tab': focusAfter, + 'esc': blur, + 's': () => showContent.value !== showContent.value, +}; + +function toggleFavorite(): void { + if ($i == null) { + pleaseLogin(); + return; + } + claimAchievement('noteFavorited1'); + os.apiWithDialog(!isFavorite.value ? 'notes/favorites/create' : 'notes/favorites/delete', { + noteId: props.note.id, + }); + isFavorite.value = !isFavorite.value; +} +provide('react', (reaction: string) => { + misskeyApi('notes/reactions/create', { + noteId: appearNote.value.id, + reaction: reaction, + }); +}); + +if (props.mock) { + watch(() => props.note, (to) => { + note.value = deepClone(to); + }, { deep: true }); +} else { + useNoteCapture({ + rootEl: rootEl, + note: appearNote, + pureNote: note, + isDeletedRef: isDeleted, + }); +} + +if (!props.mock) { + useTooltip(renoteButton, async (showing) => { + const renotes = await misskeyApi('notes/renotes', { + noteId: appearNote.value.id, + limit: 11, + }); + + const users = renotes.map(x => x.user); + + if (users.length < 1) return; + + os.popup(MkUsersTooltip, { + showing, + users, + count: appearNote.value.renoteCount, + targetElement: renoteButton.value, + }, {}, 'closed'); + }); + + if (appearNote.value.reactionAcceptance === 'likeOnly') { + useTooltip(reactButton, async (showing) => { + const reactions = await misskeyApiGet('notes/reactions', { + noteId: appearNote.value.id, + limit: 10, + _cacheKey_: appearNote.value.reactionCount, + }); + + const users = reactions.map(x => x.user); + + if (users.length < 1) return; + + os.popup(MkReactionsViewerDetails, { + showing, + reaction: '❤️', + users, + count: appearNote.value.reactionCount, + targetElement: reactButton.value!, + }, {}, 'closed'); + }); + } +} + +function renote(viaKeyboard = false) { + pleaseLogin(); + showMovedDialog(); + + const { menu } = getRenoteMenu({ note: note.value, renoteButton, mock: props.mock }); + os.popupMenu(menu, renoteButton.value, { + viaKeyboard, + }); +} + +function reply(viaKeyboard = false): void { + pleaseLogin(); + if (props.mock) { + return; + } + os.post({ + reply: appearNote.value, + channel: appearNote.value.channel, + animation: !viaKeyboard, + }).then(() => { + focus(); + }); +} + +function react(viaKeyboard = false): void { + pleaseLogin(); + showMovedDialog(); + if (appearNote.value.reactionAcceptance === 'likeOnly') { + sound.playMisskeySfx('reaction'); + + if (props.mock) { + return; + } + + misskeyApi('notes/reactions/create', { + noteId: appearNote.value.id, + reaction: '❤️', + }); + const el = reactButton.value; + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } + } else { + blur(); + reactionPicker.show(reactButton.value ?? null, note.value, reaction => { + sound.playMisskeySfx('reaction'); + + if (props.mock) { + emit('reaction', reaction); + return; + } + + misskeyApi('notes/reactions/create', { + noteId: appearNote.value.id, + reaction: reaction, + }); + if (appearNote.value.text && appearNote.value.text.length > 100 && (Date.now() - new Date(appearNote.value.createdAt).getTime() < 1000 * 3)) { + claimAchievement('reactWithoutRead'); + } + }, () => { + focus(); + }); + } +} + +function undoReact(targetNote: Misskey.entities.Note): void { + const oldReaction = targetNote.myReaction; + if (!oldReaction) return; + + if (props.mock) { + emit('removeReaction', oldReaction); + return; + } + + misskeyApi('notes/reactions/delete', { + noteId: targetNote.id, + }); +} + +function toggleReact() { + if (appearNote.value.myReaction == null) { + react(); + } else { + undoReact(appearNote.value); + } +} + +function onContextmenu(ev: MouseEvent): void { + if (props.mock) { + return; + } + + const isLink = (el: HTMLElement): boolean => { + if (el.tagName === 'A') return true; + // 再生速度の選択などのために、Audio要素のコンテキストメニューはブラウザデフォルトとする。 + if (el.tagName === 'AUDIO') return true; + if (el.parentElement) { + return isLink(el.parentElement); + } + return false; + }; + + if (ev.target && isLink(ev.target as HTMLElement)) return; + if (window.getSelection()?.toString() !== '') return; + + if (defaultStore.state.useReactionPickerForContextMenu) { + ev.preventDefault(); + react(); + } else { + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + os.contextMenu(menu, ev).then(focus).finally(cleanup); + } +} + +function showMenu(viaKeyboard = false): void { + if (props.mock) { + return; + } + + const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + os.popupMenu(menu, menuButton.value, { + viaKeyboard, + }).then(focus).finally(cleanup); +} + +async function clip() { + if (props.mock) { + return; + } + + os.popupMenu(await getNoteClipMenu({ note: note.value, isDeleted, currentClip: currentClip?.value }), clipButton.value).then(focus); +} + +function showRenoteMenu(viaKeyboard = false): void { + if (props.mock) { + return; + } + + function getUnrenote(): MenuItem { + return { + text: i18n.ts.unrenote, + icon: 'ti ti-trash', + danger: true, + action: () => { + misskeyApi('notes/delete', { + noteId: note.value.id, + }); + isDeleted.value = true; + }, + }; + } + + if (isMyRenote) { + pleaseLogin(); + os.popupMenu([ + getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), + { type: 'divider' }, + getUnrenote(), + ], renoteTime.value, { + viaKeyboard: viaKeyboard, + }); + } else { + os.popupMenu([ + getCopyNoteLinkMenu(note.value, i18n.ts.copyLinkRenote), + { type: 'divider' }, + getAbuseNoteMenu(note.value, i18n.ts.reportAbuseRenote), + ($i?.isModerator || $i?.isAdmin) ? getUnrenote() : undefined, + ], renoteTime.value, { + viaKeyboard: viaKeyboard, + }); + } +} + +function focus() { + rootEl.value?.focus(); +} + +function blur() { + rootEl.value?.blur(); +} + +function focusBefore() { + focusPrev(rootEl.value ?? null); +} + +function focusAfter() { + focusNext(rootEl.value ?? null); +} + +function readPromo() { + misskeyApi('promo/read', { + noteId: appearNote.value.id, + }); + isDeleted.value = true; +} + +function emitUpdReaction(emoji: string, delta: number) { + if (delta < 0) { + emit('removeReaction', emoji); + } else if (delta > 0) { + emit('reaction', emoji); + } +} +</script> + +<style lang="scss" module> +.root { + position: relative; + transition: box-shadow 0.1s ease; + font-size: 1.05em; + overflow: clip; + contain: content; + + // これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、 + // 下の方までスクロールすると上のノートの高さがここで決め打ちされたものに変化し、表示しているノートの位置が変わってしまう + // ノートがマウントされたときに自身の高さを取得し contain-intrinsic-size を設定しなおせばほぼ解決できそうだが、 + // 今度はその処理自体がパフォーマンス低下の原因にならないか懸念される。また、被リアクションでも高さは変化するため、やはり多少のズレは生じる + // 一度レンダリングされた要素はブラウザがよしなにサイズを覚えておいてくれるような実装になるまで待った方が良さそう(なるのか?) + //content-visibility: auto; + //contain-intrinsic-size: 0 128px; + + &:focus-visible { + outline: none; + + &:after { + content: ""; + pointer-events: none; + display: block; + position: absolute; + z-index: 10; + top: 0; + left: 0; + right: 0; + bottom: 0; + margin: auto; + width: calc(100% - 8px); + height: calc(100% - 8px); + border: dashed 1px var(--focus); + border-radius: var(--radius); + box-sizing: border-box; + } + } + + .footer { + display: flex; + font-size: 1em; + justify-content: flex-start; + } + + .footer { + display: flex; + font-size: 1em; + justify-content: flex-start; + margin-left: -64px; + } + + .footerLeft { + flex: 1; + display: grid; + grid-auto-flow: column; + grid-template-columns: repeat(auto-fit, minmax(42px, 1fr)); + grid-auto-rows: 32px; + } + + .footerRight { + flex: 0; + display: grid; + grid-auto-flow: column; + grid-template-columns: repeat(auto-fit, minmax(42px, 1fr)); + grid-auto-rows: 32px; + direction: rtl; + } + + &:hover > .article > .main > .footer > .footerButton { + opacity: 1; + } + + &.showActionsOnlyHover { + .footer { + visibility: hidden; + position: absolute; + top: 12px; + right: 12px; + padding: 0 4px; + margin-bottom: 0 !important; + background: var(--popup); + border-radius: 8px; + box-shadow: 0px 4px 32px var(--shadow); + } + + .footerButton { + font-size: 90%; + + &:not(:last-child) { + margin-right: 0; + } + } + } + + &.showActionsOnlyHover:hover { + .footer { + visibility: visible; + } + } +} + +.tip { + display: flex; + align-items: center; + padding: 16px 32px 8px 32px; + line-height: 24px; + font-size: 90%; + white-space: pre; + color: #d28a3f; +} + +.tip + .article { + padding-top: 8px; +} + +.replyTo { + opacity: 0.7; + padding-bottom: 0; +} + +.renote { + position: relative; + display: flex; + align-items: center; + padding: 16px 32px 8px 32px; + line-height: 28px; + white-space: pre; + color: var(--renote); + + & + .article { + padding-top: 8px; + } + + > .colorBar { + height: calc(100% - 6px); + } +} + +.renoteAvatar { + flex-shrink: 0; + display: inline-block; + width: 28px; + height: 28px; + margin: 0 8px 0 0; +} + +.renoteText { + overflow: hidden; + flex-shrink: 1; + text-overflow: ellipsis; + white-space: nowrap; +} + +.renoteUserName { + font-weight: bold; +} + +.renoteInfo { + margin-left: auto; + font-size: 0.9em; +} + +.renoteTime { + flex-shrink: 0; + color: inherit; +} + +.renoteMenu { + margin-right: 4px; +} + +.collapsedRenoteTarget { + display: flex; + align-items: center; + line-height: 28px; + white-space: pre; + padding: 0 32px 18px; +} + +.collapsedRenoteTargetAvatar { + flex-shrink: 0; + display: inline-block; + width: 28px; + height: 28px; + margin: 0 8px 0 0; +} + +.collapsedRenoteTargetText { + overflow: hidden; + flex-shrink: 1; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 90%; + opacity: 0.7; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} + +.article { + position: relative; + display: flex; + padding: 16px 24px; +} + +.colorBar { + position: absolute; + top: 8px; + left: 8px; + width: 5px; + height: calc(100% - 16px); + border-radius: 999px; + pointer-events: none; +} + +.avatar { + flex-shrink: 0; + display: block !important; + margin: 0 14px 0 0; + width: 58px; + height: 58px; + position: sticky !important; + top: calc(22px + var(--stickyTop, 0px)); + left: 0; +} + +.main { + flex: 1; + min-width: 0; +} + +.cw { + cursor: default; + display: block; + margin: 0; + padding: 0; + overflow-wrap: break-word; +} + +.showLess { + width: 100%; + margin-top: 14px; + position: sticky; + bottom: calc(var(--stickyBottom, 0px) + 14px); +} + +.showLessLabel { + display: inline-block; + background: var(--popup); + padding: 6px 10px; + font-size: 0.8em; + border-radius: 999px; + box-shadow: 0 2px 6px rgb(0 0 0 / 20%); +} + +.contentCollapsed { + position: relative; + max-height: 9em; + overflow: clip; +} + +.collapsed { + display: block; + position: absolute; + bottom: 0; + left: 0; + z-index: 2; + width: 100%; + height: 64px; + background: linear-gradient(0deg, var(--panel), var(--X15)); + + &:hover > .collapsedLabel { + background: var(--panelHighlight); + } +} + +.collapsedLabel { + display: inline-block; + background: var(--panel); + padding: 6px 10px; + font-size: 0.8em; + border-radius: 999px; + box-shadow: 0 2px 6px rgb(0 0 0 / 20%); +} + +.text { + overflow-wrap: break-word; +} + +.replyIcon { + color: var(--accent); + margin-right: 0.5em; +} + +.translation { + border: solid 0.5px var(--divider); + border-radius: var(--radius); + padding: 12px; + margin-top: 8px; +} + +.urlPreview { + margin-top: 8px; +} + +.poll { + font-size: 80%; +} + +.quote { + padding: 8px 0; +} + +.quoteNote { + padding: 16px; + border: dashed 1px var(--renote); + border-radius: 8px; + overflow: clip; +} + +.channel { + opacity: 0.7; + font-size: 80%; +} + +.footer { + margin-bottom: -14px; +} + +.footerButton { + margin: 0; + + opacity: 0.7; + + &:hover { + color: var(--fgHighlighted); + } +} + +.footerButtonCount { + display: inline; + margin: 0 0 0 8px; + opacity: 0.7; +} + +@container (max-width: 580px) { + .root { + font-size: 0.95em; + } + + .renote { + padding: 12px 26px 0 26px; + } + + .article { + padding: 24px 26px; + } + + .avatar { + width: 42px; + height: 42px; + } +} + +@container (max-width: 500px) { + .root { + font-size: 0.9em; + } + + .renote { + padding: 10px 22px 0 22px; + } + + .article { + padding: 20px 22px; + } + + .footer { + margin-bottom: -8px; + } +} + +@container (max-width: 480px) { + .renote { + padding: 8px 16px 0 16px; + } + + .tip { + padding: 8px 16px 0 16px; + } + + .collapsedRenoteTarget { + padding: 0 16px 9px; + margin-top: 4px; + } + + .article { + padding: 14px 16px; + } +} + +@container (max-width: 450px) { + .avatar { + margin: 0 10px 0 0; + width: 46px; + height: 46px; + top: calc(14px + var(--stickyTop, 0px)); + } +} + +@container (max-width: 400px) { + .root:not(.showActionsOnlyHover) { + .footerButton { + &:not(:last-child) { + margin-right: 18px; + } + } + } +} + +@container (max-width: 350px) { + .root:not(.showActionsOnlyHover) { + .footerButton { + &:not(:last-child) { + margin-right: 12px; + } + } + } + + .colorBar { + top: 6px; + left: 6px; + width: 4px; + height: calc(100% - 12px); + } +} + +@container (max-width: 300px) { + .avatar { + width: 44px; + height: 44px; + } + + .root:not(.showActionsOnlyHover) { + .footerButton { + &:not(:last-child) { + margin-right: 8px; + } + } + } +} + +@container (max-width: 250px) { + .quoteNote { + padding: 12px; + } +} + +.muted { + padding: 8px; + text-align: center; + opacity: 0.7; +} + +.reactionOmitted { + display: inline-block; + margin-left: 8px; + opacity: .8; + font-size: 95%; +} +</style> diff --git a/packages/frontend/src/components/XNoteHeader.vue b/packages/frontend/src/components/XNoteHeader.vue new file mode 100644 index 0000000000..a38604382b --- /dev/null +++ b/packages/frontend/src/components/XNoteHeader.vue @@ -0,0 +1,137 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<header :class="$style.root"> + <div v-if="mock" :class="$style.name"> + <MkUserName :user="note.user"/> + </div> + <MkA v-else v-user-preview="note.user.id" :class="$style.name" :to="userPage(note.user)"> + <MkUserName :user="note.user"/> + </MkA> + <div v-if="note.user.isBot" :class="$style.isBot">bot</div> + <div :class="$style.username"><MkAcct :user="note.user"/></div> + <div v-if="note.user.badgeRoles" :class="$style.badgeRoles"> + <img v-for="(role, i) in note.user.badgeRoles" :key="i" v-tooltip="role.name" :class="$style.badgeRole" :src="role.iconUrl!"/> + </div> + <div v-if="mock"> + <MkTime :time="note.createdAt" colored/> + </div> + <MkA v-else :class="$style.time" :to="notePage(note)"> + <MkTime :time="note.createdAt" colored/> + </MkA> + <div :class="$style.info"> + <button ref="menuButton" :class="$style.footerButton" class="_button" @mousedown="showMenu()"> + <i class="ti ti-dots"></i> + </button> + <span v-if="note.visibility !== 'public'" style="margin-left: 0.5em;" :title="i18n.ts._visibility[note.visibility]"> + <i v-if="note.visibility === 'home'" class="ti ti-home"></i> + <i v-else-if="note.visibility === 'followers'" class="ti ti-lock"></i> + <i v-else-if="note.visibility === 'specified'" ref="specified" class="ti ti-mail"></i> + </span> + <span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ti ti-rocket-off"></i></span> + <span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ti ti-device-tv"></i></span> + </div> +</header> +</template> + +<script lang="ts" setup> +import { inject, shallowRef } from 'vue'; +import * as Misskey from 'misskey-js'; +import { i18n } from '@/i18n.js'; +import { notePage } from '@/filters/note.js'; +import { userPage } from '@/filters/user.js'; +import { getNoteMenu } from '@/scripts/get-note-menu.js'; +import * as os from '@/os.js'; +const menuButton = shallowRef<HTMLElement>(); + +const props = defineProps<{ + note: Misskey.entities.Note; +}>(); + +function showMenu(viaKeyboard = false): void { + if (mock) { + return; + } + + const { menu, cleanup } = getNoteMenu({ note: props.note }); + os.popupMenu(menu, menuButton.value, { + viaKeyboard, + }).then(focus).finally(cleanup); +} + +const mock = inject<boolean>('mock', false); +</script> + +<style lang="scss" module> +.root { + display: flex; + align-items: baseline; + white-space: nowrap; +} +.footerButton { + margin: -12px 0 0; + opacity: 0.7; + + &:hover { + color: var(--fgHighlighted); + } +} +.name { + flex-shrink: 1; + display: block; + margin: 0 .5em 0 0; + padding: 0; + overflow: hidden; + font-size: 1em; + font-weight: bold; + text-decoration: none; + text-overflow: ellipsis; + + &:hover { + text-decoration: underline; + } +} + +.isBot { + flex-shrink: 0; + align-self: center; + margin: 0 .5em 0 0; + padding: 1px 6px; + font-size: 80%; + border: solid 0.5px var(--divider); + border-radius: 3px; +} + +.username { + flex-shrink: 9999999; + margin: 0 .5em 0 0; + overflow: hidden; + text-overflow: ellipsis; + color: var(--fgTransparentWeak); +} + +.info { + flex-shrink: 0; + margin-left: auto; + +} + +.badgeRoles { + margin: 0 .5em 0 0; +} + +.badgeRole { + height: 1.3em; + vertical-align: -20%; + + & + .badgeRole { + margin-left: 0.2em; + } +} +.time{ + color: var(--fgTransparentWeak); +} +</style> diff --git a/packages/frontend/src/components/XPostForm.vue b/packages/frontend/src/components/XPostForm.vue new file mode 100644 index 0000000000..030ccf6318 --- /dev/null +++ b/packages/frontend/src/components/XPostForm.vue @@ -0,0 +1,1349 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div + :style="!dialog ? { borderBottom: 'solid 1px var(--divider)' } : {}" + :class="[$style.root, { [$style.modal]: modal, _popup: modal }]" + @dragover.stop="onDragover" + @dragenter="onDragenter" + @dragleave="onDragleave" + @drop.stop="onDrop" +> + <header v-if="!fixed" :class="$style.header"> + <button v-if="!fixed" :class="$style.cancel" class="_button" @click="cancel"><i class="ti ti-x"></i></button> + </header> + <MkNoteSimple v-if="reply" :class="$style.targetNote" :note="reply"/> + <MkNoteSimple v-if="renote" :class="$style.targetNote" :note="renote"/> + <div v-if="quoteId" :class="$style.withQuote"><i class="ti ti-quote"></i> {{ i18n.ts.quoteAttached }}<button @click="quoteId = null"><i class="ti ti-x"></i></button></div> + <div v-if="visibility === 'specified'" :class="$style.toSpecified"> + <span style="margin-right: 8px;">{{ i18n.ts.recipient }}</span> + <div :class="$style.visibleUsers"> + <span v-for="u in visibleUsers" :key="u.id" :class="$style.visibleUser"> + <MkAcct :user="u"/> + <button class="_button" style="padding: 4px 8px;" @click="removeVisibleUser(u)"><i class="ti ti-x"></i></button> + </span> + <button class="_buttonPrimary" style="padding: 4px; border-radius: 8px;" @click="addVisibleUser"><i class="ti ti-plus ti-fw"></i></button> + </div> + </div> + <MkInfo v-if="hasNotSpecifiedMentions" warn :class="$style.hasNotSpecifiedMentions">{{ i18n.ts.notSpecifiedMentionWarning }} - <button class="_textButton" @click="addMissingMention()">{{ i18n.ts.add }}</button></MkInfo> + <input v-show="useCw" ref="cwInputEl" v-model="cw" :class="$style.cw" :placeholder="i18n.ts.annotation" @keydown="onKeydown"> + <div :class="[$style.textOuter, { [$style.withCw]: useCw }]"> + <div v-if="channel" :class="$style.colorBar" :style="{ background: channel.color }"></div> + + <div style="display: flex;"> + <button v-click-anime v-tooltip="i18n.ts.switchAccount" :class="$style.account" class="_button" @click="openAccountMenu"> + <MkAvatar :user="postAccount ?? $i" :class="$style.avatar"/> + </button> + + <textarea ref="textareaEl" v-model="text" :class="[$style.text]" :disabled="posting || posted" :readonly="textAreaReadOnly" :placeholder="placeholder" data-cy-post-form-text @keydown="onKeydown" @paste="onPaste" @compositionupdate="onCompositionUpdate" @compositionend="onCompositionEnd"/> + </div> + <div v-if="maxTextLength - textLength < 100" :class="['_acrylic', $style.textCount, { [$style.textOver]: textLength > maxTextLength }]">{{ maxTextLength - textLength }}</div> + </div> + <input v-show="withHashtags" ref="hashtagsInputEl" v-model="hashtags" :class="$style.hashtags" :placeholder="i18n.ts.hashtags" list="hashtags"> + <XPostFormAttaches v-model="files" @detach="detachFile" @changeSensitive="updateFileSensitive" @changeName="updateFileName" @replaceFile="replaceFile"/> + <MkPollEditor v-if="poll" v-model="poll" style="margin: 8px 32px; padding: 8px; border: solid 0.5px var(--divider); border-radius: 16px;" @destroyed="poll = null"/> + <div v-if="showingOptions" style="padding: 8px 16px;"> + </div> + + <footer :style="dialog ? { borderTop: 'solid 1px var(--divider)' , padding: ' 8px'} : {padding: '0 16px 16px 64px'}" :class="$style.footer"> + <div :class="$style.footerLeft"> + <button v-tooltip="i18n.ts.attachFile" class="_button" :class="$style.footerButton" @click="chooseFileFrom"><i class="ti ti-photo-plus"></i></button> + <button v-tooltip="i18n.ts.poll" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: poll }]" @click="togglePoll"><i class="ti ti-chart-arrows"></i></button> + <button v-tooltip="i18n.ts.useCw" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: useCw }]" @click="useCw = !useCw"><i class="ti ti-eye-off"></i></button> + <button v-tooltip="i18n.ts.mention" class="_button" :class="$style.footerButton" @click="insertMention"><i class="ti ti-at"></i></button> + + <button v-tooltip="i18n.ts.hashtags" class="_button" :class="[$style.footerButton, { [$style.footerButtonActive]: withHashtags }]" @click="withHashtags = !withHashtags"><i class="ti ti-hash"></i></button> + <button v-if="postFormActions.length > 0" v-tooltip="i18n.ts.plugins" class="_button" :class="$style.footerButton" @click="showActions"><i class="ti ti-plug"></i></button> + <button v-tooltip="i18n.ts.emoji" :class="['_button', $style.footerButton]" @click="insertEmoji"><i class="ti ti-mood-happy"></i></button> + <button v-if="showAddMfmFunction" v-tooltip="i18n.ts.addMfmFunction" :class="['_button', $style.footerButton]" @click="insertMfmFunction"><i class="ti ti-palette"></i></button> + </div> + <div :class="$style.footerRight"> + <button v-click-anime class="_button" :class="[$style.footerButton, $style.submit]" :disabled="!canPost" data-cy-open-post-form-submit @click="post"> + <div :class="$style.submitInner"> + <template v-if="posted"></template> + <template v-else-if="posting"><MkEllipsis/></template> + <template v-else>{{ submitText }}</template> + <i style="margin-left: 6px;" :class="posted ? 'ti ti-check' : reply ? 'ti ti-arrow-back-up' : renote ? 'ti ti-quote' : 'ti ti-send'"></i> + </div> + </button> + </div> + </footer> + <datalist id="hashtags"> + <option v-for="hashtag in recentHashtags" :key="hashtag" :value="hashtag"/> + </datalist> +</div> +</template> + +<script lang="ts" setup> +import { inject, watch, nextTick, onMounted, defineAsyncComponent, provide, shallowRef, ref, computed } from 'vue'; +import * as mfm from 'mfm-js'; +import * as Misskey from 'misskey-js'; +import insertTextAtCursor from 'insert-text-at-cursor'; +import { toASCII } from 'punycode/'; +import MkNoteSimple from '@/components/MkNoteSimple.vue'; +import MkNotePreview from '@/components/MkNotePreview.vue'; +import XPostFormAttaches from '@/components/MkPostFormAttaches.vue'; +import MkPollEditor, { type PollEditorModelValue } from '@/components/MkPollEditor.vue'; +import { host, url } from '@/config.js'; +import { erase, unique } from '@/scripts/array.js'; +import { extractMentions } from '@/scripts/extract-mentions.js'; +import { formatTimeString } from '@/scripts/format-time-string.js'; +import { Autocomplete } from '@/scripts/autocomplete.js'; +import * as os from '@/os.js'; +import { misskeyApi } from '@/scripts/misskey-api.js'; +import { selectFiles } from '@/scripts/select-file.js'; +import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; +import MkInfo from '@/components/MkInfo.vue'; +import { i18n } from '@/i18n.js'; +import { instance } from '@/instance.js'; +import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; +import { uploadFile } from '@/scripts/upload.js'; +import { deepClone } from '@/scripts/clone.js'; +import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import { miLocalStorage } from '@/local-storage.js'; +import { claimAchievement } from '@/scripts/achievements.js'; +import { emojiPicker } from '@/scripts/emoji-picker.js'; +import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; + +const $i = signinRequired(); + +const modal = inject('modal'); + +const props = withDefaults(defineProps<{ + reply?: Misskey.entities.Note; + renote?: Misskey.entities.Note; + channel?: Misskey.entities.Channel; // TODO + mention?: Misskey.entities.User; + specified?: Misskey.entities.UserDetailed; + initialText?: string; + initialCw?: string; + initialVisibility?: (typeof Misskey.noteVisibilities)[number]; + initialFiles?: Misskey.entities.DriveFile[]; + initialLocalOnly?: boolean; + initialVisibleUsers?: Misskey.entities.UserDetailed[]; + initialNote?: Misskey.entities.Note; + instant?: boolean; + fixed?: boolean; + autofocus?: boolean; + freezeAfterPosted?: boolean; + mock?: boolean; + dialog?: boolean; +}>(), { + initialVisibleUsers: () => [], + autofocus: true, + mock: false, + initialLocalOnly: undefined, +}); + +provide('mock', props.mock); + +const emit = defineEmits<{ + (ev: 'posted'): void; + (ev: 'cancel'): void; + (ev: 'esc'): void; + + // Mock用 + (ev: 'fileChangeSensitive', fileId: string, to: boolean): void; +}>(); + +const textareaEl = shallowRef<HTMLTextAreaElement | null>(null); +const cwInputEl = shallowRef<HTMLInputElement | null>(null); +const hashtagsInputEl = shallowRef<HTMLInputElement | null>(null); +const visibilityButton = shallowRef<HTMLElement>(); + +const posting = ref(false); +const posted = ref(false); +const text = ref(props.initialText ?? ''); +const files = ref(props.initialFiles ?? []); +const poll = ref<PollEditorModelValue | null>(null); +const useCw = ref<boolean>(!!props.initialCw); +const showPreview = ref(defaultStore.state.showPreview); +watch(showPreview, () => defaultStore.set('showPreview', showPreview.value)); +const showAddMfmFunction = ref(defaultStore.state.enableQuickAddMfmFunction); +watch(showAddMfmFunction, () => defaultStore.set('enableQuickAddMfmFunction', showAddMfmFunction.value)); +const cw = ref<string | null>(props.initialCw ?? null); +const localOnly = ref(props.initialLocalOnly ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.localOnly : defaultStore.state.defaultNoteLocalOnly)); +const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility)); +const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]); +if (props.initialVisibleUsers) { + props.initialVisibleUsers.forEach(u => pushVisibleUser(u)); +} +const reactionAcceptance = ref(defaultStore.state.reactionAcceptance); +const autocomplete = ref(null); +const draghover = ref(false); +const quoteId = ref<string | null>(null); +const hasNotSpecifiedMentions = ref(false); +const recentHashtags = ref(JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]')); +const imeText = ref(''); +const showingOptions = ref(false); +const textAreaReadOnly = ref(false); + +const draftKey = computed((): string => { + let key = props.channel ? `channel:${props.channel.id}` : ''; + + if (props.renote) { + key += `renote:${props.renote.id}`; + } else if (props.reply) { + key += `reply:${props.reply.id}`; + } else { + key += `note:${$i.id}`; + } + + return key; +}); + +const placeholder = computed((): string => { + if (props.renote) { + return i18n.ts._postForm.quotePlaceholder; + } else if (props.reply) { + return i18n.ts._postForm.replyPlaceholder; + } else if (props.channel) { + return i18n.ts._postForm.channelPlaceholder; + } else { + const xs = [ + i18n.ts._postForm._placeholders.a, + i18n.ts._postForm._placeholders.b, + i18n.ts._postForm._placeholders.c, + i18n.ts._postForm._placeholders.d, + i18n.ts._postForm._placeholders.e, + i18n.ts._postForm._placeholders.f, + ]; + return xs[Math.floor(Math.random() * xs.length)]; + } +}); + +const submitText = computed((): string => { + return props.renote + ? i18n.ts.quote + : props.reply + ? i18n.ts.reply + : i18n.ts.note; +}); + +const textLength = computed((): number => { + return (text.value + imeText.value).trim().length; +}); + +const maxTextLength = computed((): number => { + return instance ? instance.maxNoteTextLength : 1000; +}); + +const canPost = computed((): boolean => { + return !props.mock && !posting.value && !posted.value && + ( + 1 <= textLength.value || + 1 <= files.value.length || + poll.value != null || + props.renote != null || + (props.reply != null && quoteId.value != null) + ) && + (textLength.value <= maxTextLength.value) && + (!poll.value || poll.value.choices.length >= 2); +}); + +const withHashtags = computed(defaultStore.makeGetterSetter('postFormWithHashtags')); +const hashtags = computed(defaultStore.makeGetterSetter('postFormHashtags')); + +watch(text, () => { + checkMissingMention(); +}, { immediate: true }); + +watch(visibility, () => { + checkMissingMention(); +}, { immediate: true }); + +watch(visibleUsers, () => { + checkMissingMention(); +}, { + deep: true, +}); + +if (props.mention) { + 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.value = `@${props.reply.user.username}${props.reply.user.host != null ? '@' + toASCII(props.reply.user.host) : ''} `; +} + +if (props.reply && props.reply.text != null) { + const ast = mfm.parse(props.reply.text); + const otherHost = props.reply.user.host; + + for (const x of extractMentions(ast)) { + const mention = x.host ? + `@${x.username}@${toASCII(x.host)}` : + (otherHost == null || otherHost === host) ? + `@${x.username}` : + `@${x.username}@${toASCII(otherHost)}`; + + // 自分は除外 + if ($i.username === x.username && (x.host == null || x.host === host)) continue; + + // 重複は除外 + if (text.value.includes(`${mention} `)) continue; + + text.value += `${mention} `; + } +} + +if ($i.isSilenced && visibility.value === 'public') { + visibility.value = 'home'; +} + +if (props.channel) { + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す +} + +// 公開以外へのリプライ時は元の公開範囲を引き継ぐ +if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visibility)) { + if (props.reply.visibility === 'home' && visibility.value === 'followers') { + visibility.value = 'followers'; + } else if (['home', 'followers'].includes(props.reply.visibility) && visibility.value === 'specified') { + visibility.value = 'specified'; + } else { + visibility.value = props.reply.visibility; + } + + if (visibility.value === 'specified') { + if (props.reply.visibleUserIds) { + misskeyApi('users/show', { + userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId), + }).then(users => { + users.forEach(u => pushVisibleUser(u)); + }); + } + + if (props.reply.userId !== $i.id) { + misskeyApi('users/show', { userId: props.reply.userId }).then(user => { + pushVisibleUser(user); + }); + } + } +} + +if (props.specified) { + visibility.value = 'specified'; + pushVisibleUser(props.specified); +} + +// keep cw when reply +if (defaultStore.state.keepCw && props.reply && 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()); +} + +function checkMissingMention() { + if (visibility.value === 'specified') { + const ast = mfm.parse(text.value); + + for (const x of extractMentions(ast)) { + if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { + hasNotSpecifiedMentions.value = true; + return; + } + } + } + hasNotSpecifiedMentions.value = false; +} + +function addMissingMention() { + const ast = mfm.parse(text.value); + + for (const x of extractMentions(ast)) { + if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) { + misskeyApi('users/show', { username: x.username, host: x.host }).then(user => { + pushVisibleUser(user); + }); + } + } +} + +function togglePoll() { + if (poll.value) { + poll.value = null; + } else { + poll.value = { + choices: ['', ''], + multiple: false, + expiresAt: null, + expiredAfter: null, + }; + } +} + +function addTag(tag: string) { + insertTextAtCursor(textareaEl.value, ` #${tag} `); +} + +function focus() { + if (textareaEl.value) { + textareaEl.value.focus(); + textareaEl.value.setSelectionRange(textareaEl.value.value.length, textareaEl.value.value.length); + } +} + +function chooseFileFrom(ev) { + if (props.mock) return; + + selectFiles(ev.currentTarget ?? ev.target, i18n.ts.attachFile).then(files_ => { + for (const file of files_) { + files.value.push(file); + } + }); +} + +function detachFile(id) { + files.value = files.value.filter(x => x.id !== id); +} + +function updateFileSensitive(file, sensitive) { + if (props.mock) { + emit('fileChangeSensitive', file.id, sensitive); + } + files.value[files.value.findIndex(x => x.id === file.id)].isSensitive = sensitive; +} + +function updateFileName(file, name) { + files.value[files.value.findIndex(x => x.id === file.id)].name = name; +} + +function replaceFile(file: Misskey.entities.DriveFile, newFile: Misskey.entities.DriveFile): void { + files.value[files.value.findIndex(x => x.id === file.id)] = newFile; +} + +function upload(file: File, name?: string): void { + if (props.mock) return; + + uploadFile(file, defaultStore.state.uploadFolder, name).then(res => { + files.value.push(res); + }); +} + +function setVisibility() { + if (props.channel) { + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + return; + } + + os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), { + currentVisibility: visibility.value, + isSilenced: $i.isSilenced, + localOnly: localOnly.value, + src: visibilityButton.value, + ...(props.reply ? { isReplyVisibilitySpecified: props.reply.visibility === 'specified' } : {}), + }, { + changeVisibility: v => { + visibility.value = v; + if (defaultStore.state.rememberNoteVisibility) { + defaultStore.set('visibility', visibility.value); + } + }, + }, 'closed'); +} + +async function toggleLocalOnly() { + if (props.channel) { + visibility.value = 'public'; + localOnly.value = true; // TODO: チャンネルが連合するようになった折には消す + return; + } + + const neverShowInfo = miLocalStorage.getItem('neverShowLocalOnlyInfo'); + + if (!localOnly.value && neverShowInfo !== 'true') { + const confirm = await os.actions({ + type: 'question', + title: i18n.ts.disableFederationConfirm, + text: i18n.ts.disableFederationConfirmWarn, + actions: [ + { + value: 'yes' as const, + text: i18n.ts.disableFederationOk, + primary: true, + }, + { + value: 'neverShow' as const, + text: `${i18n.ts.disableFederationOk} (${i18n.ts.neverShow})`, + danger: true, + }, + { + value: 'no' as const, + text: i18n.ts.cancel, + }, + ], + }); + if (confirm.canceled) return; + if (confirm.result === 'no') return; + + if (confirm.result === 'neverShow') { + miLocalStorage.setItem('neverShowLocalOnlyInfo', 'true'); + } + } + + localOnly.value = !localOnly.value; + if (defaultStore.state.rememberNoteVisibility) { + defaultStore.set('localOnly', localOnly.value); + } +} + +async function toggleReactionAcceptance() { + const select = await os.select({ + title: i18n.ts.reactionAcceptance, + items: [ + { value: null, text: i18n.ts.all }, + { value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote }, + { value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly }, + { value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }, + { value: 'likeOnly' as const, text: i18n.ts.likeOnly }, + ], + default: reactionAcceptance.value, + }); + if (select.canceled) return; + reactionAcceptance.value = select.result; +} + +function pushVisibleUser(user: Misskey.entities.UserDetailed) { + if (!visibleUsers.value.some(u => u.username === user.username && u.host === user.host)) { + visibleUsers.value.push(user); + } +} + +function addVisibleUser() { + os.selectUser().then(user => { + pushVisibleUser(user); + + if (!text.value.toLowerCase().includes(`@${user.username.toLowerCase()}`)) { + text.value = `@${Misskey.acct.toString(user)} ${text.value}`; + } + }); +} + +function removeVisibleUser(user) { + visibleUsers.value = erase(user, visibleUsers.value); +} + +function clear() { + text.value = ''; + files.value = []; + poll.value = null; + quoteId.value = null; +} + +function onKeydown(ev: KeyboardEvent) { + if (ev.key === 'Enter' && (ev.ctrlKey || ev.metaKey) && canPost.value) post(); + if (ev.key === 'Escape') emit('esc'); +} + +function onCompositionUpdate(ev: CompositionEvent) { + imeText.value = ev.data; +} + +function onCompositionEnd(ev: CompositionEvent) { + imeText.value = ''; +} + +async function onPaste(ev: ClipboardEvent) { + if (props.mock) return; + if (!ev.clipboardData) return; + + for (const { item, i } of Array.from(ev.clipboardData.items, (data, x) => ({ item: data, i: x }))) { + if (item.kind === 'file') { + const file = item.getAsFile(); + if (!file) continue; + const lio = file.name.lastIndexOf('.'); + const ext = lio >= 0 ? file.name.slice(lio) : ''; + const formatted = `${formatTimeString(new Date(file.lastModified), defaultStore.state.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`; + upload(file, formatted); + } + } + + const paste = ev.clipboardData.getData('text'); + + if (!props.renote && !quoteId.value && paste.startsWith(url + '/notes/')) { + ev.preventDefault(); + + os.confirm({ + type: 'info', + text: i18n.ts.quoteQuestion, + }).then(({ canceled }) => { + if (canceled) { + insertTextAtCursor(textareaEl.value, paste); + return; + } + + quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null; + }); + } + + if (paste.length > 1000) { + ev.preventDefault(); + os.confirm({ + type: 'info', + text: i18n.ts.attachAsFileQuestion, + }).then(({ canceled }) => { + if (canceled) { + insertTextAtCursor(textareaEl.value, paste); + return; + } + + const fileName = formatTimeString(new Date(), defaultStore.state.pastedFileName).replace(/{{number}}/g, '0'); + const file = new File([paste], `${fileName}.txt`, { type: 'text/plain' }); + upload(file, `${fileName}.txt`); + }); + } +} + +function onDragover(ev) { + if (!ev.dataTransfer.items[0]) return; + const isFile = ev.dataTransfer.items[0].kind === 'file'; + const isDriveFile = ev.dataTransfer.types[0] === _DATA_TRANSFER_DRIVE_FILE_; + if (isFile || isDriveFile) { + ev.preventDefault(); + draghover.value = true; + switch (ev.dataTransfer.effectAllowed) { + case 'all': + case 'uninitialized': + case 'copy': + case 'copyLink': + case 'copyMove': + ev.dataTransfer.dropEffect = 'copy'; + break; + case 'linkMove': + case 'move': + ev.dataTransfer.dropEffect = 'move'; + break; + default: + ev.dataTransfer.dropEffect = 'none'; + break; + } + } +} + +function onDragenter() { + draghover.value = true; +} + +function onDragleave() { + draghover.value = false; +} + +function onDrop(ev: DragEvent): void { + draghover.value = false; + + // ファイルだったら + if (ev.dataTransfer && ev.dataTransfer.files.length > 0) { + ev.preventDefault(); + for (const x of Array.from(ev.dataTransfer.files)) upload(x); + return; + } + + //#region ドライブのファイル + const driveFile = ev.dataTransfer?.getData(_DATA_TRANSFER_DRIVE_FILE_); + if (driveFile != null && driveFile !== '') { + const file = JSON.parse(driveFile); + files.value.push(file); + ev.preventDefault(); + } + //#endregion +} + +function saveDraft() { + if (props.instant || props.mock) return; + + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + + draftData[draftKey.value] = { + updatedAt: new Date(), + data: { + text: text.value, + useCw: useCw.value, + cw: cw.value, + visibility: visibility.value, + localOnly: localOnly.value, + files: files.value, + poll: poll.value, + visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(x => x.id) : undefined, + }, + }; + + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); +} + +function deleteDraft() { + const draftData = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}'); + + delete draftData[draftKey.value]; + + miLocalStorage.setItem('drafts', JSON.stringify(draftData)); +} + +async function post(ev?: MouseEvent) { + if (useCw.value && (cw.value == null || cw.value.trim() === '')) { + os.alert({ + type: 'error', + text: i18n.ts.cwNotationRequired, + }); + return; + } + + if (ev) { + const el = (ev.currentTarget ?? ev.target) as HTMLElement | null; + + if (el) { + const rect = el.getBoundingClientRect(); + const x = rect.left + (el.offsetWidth / 2); + const y = rect.top + (el.offsetHeight / 2); + os.popup(MkRippleEffect, { x, y }, {}, 'end'); + } + } + + if (props.mock) return; + + const annoying = + text.value.includes('$[x2') || + text.value.includes('$[x3') || + text.value.includes('$[x4') || + text.value.includes('$[scale') || + text.value.includes('$[position'); + + if (annoying && visibility.value === 'public') { + const { canceled, result } = await os.actions({ + type: 'warning', + text: i18n.ts.thisPostMayBeAnnoying, + actions: [{ + value: 'home', + text: i18n.ts.thisPostMayBeAnnoyingHome, + primary: true, + }, { + value: 'cancel', + text: i18n.ts.thisPostMayBeAnnoyingCancel, + }, { + value: 'ignore', + text: i18n.ts.thisPostMayBeAnnoyingIgnore, + }], + }); + + if (canceled) return; + if (result === 'cancel') return; + if (result === 'home') { + visibility.value = 'home'; + } + } + + let postData = { + text: text.value === '' ? null : 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.value ? quoteId.value : undefined, + channelId: props.channel ? props.channel.id : undefined, + poll: poll.value, + cw: useCw.value ? cw.value ?? '' : null, + localOnly: localOnly.value, + visibility: visibility.value, + visibleUserIds: visibility.value === 'specified' ? visibleUsers.value.map(u => u.id) : undefined, + reactionAcceptance: reactionAcceptance.value, + }; + + if (withHashtags.value && hashtags.value && hashtags.value.trim() !== '') { + const hashtags_ = hashtags.value.trim().split(' ').map(x => x.startsWith('#') ? x : '#' + x).join(' '); + if (!postData.text) { + postData.text = hashtags_; + } else { + const postTextLines = postData.text.split('\n'); + if (postTextLines[postTextLines.length - 1].trim() === '') { + postTextLines[postTextLines.length - 1] += hashtags_; + } else { + postTextLines[postTextLines.length - 1] += ' ' + hashtags_; + } + postData.text = postTextLines.join('\n'); + } + } + + // plugin + if (notePostInterruptors.length > 0) { + for (const interruptor of notePostInterruptors) { + try { + postData = await interruptor.handler(deepClone(postData)) as typeof postData; + } catch (err) { + console.error(err); + } + } + } + + let token: string | undefined = undefined; + + if (postAccount.value) { + const storedAccounts = await getAccounts(); + token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token; + } + + posting.value = true; + misskeyApi('notes/create', postData, token).then(() => { + if (props.freezeAfterPosted) { + posted.value = true; + } else { + clear(); + } + nextTick(() => { + deleteDraft(); + emit('posted'); + if (postData.text && postData.text !== '') { + const hashtags_ = mfm.parse(postData.text).map(x => x.type === 'hashtag' && x.props.hashtag).filter(x => x) as string[]; + const history = JSON.parse(miLocalStorage.getItem('hashtags') ?? '[]') as string[]; + miLocalStorage.setItem('hashtags', JSON.stringify(unique(hashtags_.concat(history)))); + } + posting.value = false; + postAccount.value = null; + + incNotesCount(); + if (notesCount === 1) { + claimAchievement('notes1'); + } + + const text = postData.text ?? ''; + const lowerCase = text.toLowerCase(); + if ((lowerCase.includes('love') || lowerCase.includes('❤')) && lowerCase.includes('misskey')) { + claimAchievement('iLoveMisskey'); + } + if ([ + 'https://youtu.be/Efrlqw8ytg4', + 'https://www.youtube.com/watch?v=Efrlqw8ytg4', + 'https://m.youtube.com/watch?v=Efrlqw8ytg4', + + 'https://youtu.be/XVCwzwxdHuA', + 'https://www.youtube.com/watch?v=XVCwzwxdHuA', + 'https://m.youtube.com/watch?v=XVCwzwxdHuA', + + 'https://open.spotify.com/track/3Cuj0mZrlLoXx9nydNi7RB', + 'https://open.spotify.com/track/7anfcaNPQWlWCwyCHmZqNy', + 'https://open.spotify.com/track/5Odr16TvEN4my22K9nbH7l', + 'https://open.spotify.com/album/5bOlxyl4igOrp2DwVQxBco', + ].some(url => text.includes(url))) { + claimAchievement('brainDiver'); + } + + if (props.renote && (props.renote.userId === $i.id) && text.length > 0) { + claimAchievement('selfQuote'); + } + + const date = new Date(); + const h = date.getHours(); + const m = date.getMinutes(); + const s = date.getSeconds(); + if (h >= 0 && h <= 3) { + claimAchievement('postedAtLateNight'); + } + if (m === 0 && s === 0) { + claimAchievement('postedAt0min0sec'); + } + }); + }).catch(err => { + posting.value = false; + os.alert({ + type: 'error', + text: err.message + '\n' + (err as any).id, + }); + }); +} + +function cancel() { + emit('cancel'); +} + +function insertMention() { + os.selectUser({ localOnly: localOnly.value, includeSelf: true }).then(user => { + insertTextAtCursor(textareaEl.value, '@' + Misskey.acct.toString(user) + ' '); + }); +} + +async function insertEmoji(ev: MouseEvent) { + textAreaReadOnly.value = true; + const target = ev.currentTarget ?? ev.target; + if (target == null) return; + emojiPicker.show( + target as HTMLElement, + emoji => { + insertTextAtCursor(textareaEl.value, emoji); + }, + () => { + textAreaReadOnly.value = false; + nextTick(() => focus()); + }, + ); +} + +async function insertMfmFunction(ev: MouseEvent) { + if (textareaEl.value == null) return; + mfmFunctionPicker( + ev.currentTarget ?? ev.target, + textareaEl.value, + text, + ); +} + +function showActions(ev: MouseEvent) { + os.popupMenu(postFormActions.map(action => ({ + text: action.title, + action: () => { + action.handler({ + text: text.value, + cw: cw.value, + }, (key, value: any) => { + if (typeof key !== 'string') return; + if (key === 'text') { text.value = value; } + if (key === 'cw') { useCw.value = value !== null; cw.value = value; } + }); + }, + })), ev.currentTarget ?? ev.target); +} + +const postAccount = ref<Misskey.entities.UserDetailed | null>(null); + +function openAccountMenu(ev: MouseEvent) { + if (props.mock) return; + + openAccountMenu_({ + withExtraOperation: false, + includeCurrentAccount: true, + active: postAccount.value != null ? postAccount.value.id : $i.id, + onChoose: (account) => { + if (account.id === $i.id) { + postAccount.value = null; + } else { + postAccount.value = account; + } + }, + }, ev); +} + +onMounted(() => { + if (props.autofocus) { + focus(); + + nextTick(() => { + focus(); + }); + } + + // TODO: detach when unmount + if (textareaEl.value) new Autocomplete(textareaEl.value, text); + if (cwInputEl.value) new Autocomplete(cwInputEl.value, cw); + if (hashtagsInputEl.value) new Autocomplete(hashtagsInputEl.value, hashtags); + + nextTick(() => { + // 書きかけの投稿を復元 + if (!props.instant && !props.mention && !props.specified && !props.mock) { + const draft = JSON.parse(miLocalStorage.getItem('drafts') ?? '{}')[draftKey.value]; + if (draft) { + 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.value = draft.data.poll; + } + if (draft.data.visibleUserIds) { + misskeyApi('users/show', { userIds: draft.data.visibleUserIds }).then(users => { + users.forEach(u => pushVisibleUser(u)); + }); + } + } + } + + // 削除して編集 + if (props.initialNote) { + const init = props.initialNote; + text.value = init.text ? init.text : ''; + files.value = init.files ?? []; + cw.value = init.cw ?? null; + useCw.value = init.cw != null; + if (init.poll) { + poll.value = { + choices: init.poll.choices.map(x => x.text), + multiple: init.poll.multiple, + expiresAt: init.poll.expiresAt ? (new Date(init.poll.expiresAt)).getTime() : null, + expiredAfter: null, + }; + } + visibility.value = init.visibility; + localOnly.value = init.localOnly ?? false; + quoteId.value = init.renote ? init.renote.id : null; + } + + nextTick(() => watchForDraft()); + }); +}); + +defineExpose({ + clear, +}); +</script> + +<style lang="scss" module> +.root { + position: relative; + container-type: inline-size; + &.modal { + width: 100%; + max-width: 520px; + } +} + +//#region header +.header { + z-index: 1000; + min-height: 50px; + display: flex; + flex-wrap: wrap; + align-content: center; + gap: 4px; +} + +.headerLeft { + display: flex; + flex: 0 1 100px; +} + +.cancel { + padding: 0; + font-size: 1em; + height: 100%; + flex: 0 1 50px; +} + +.account { + height: 100%; + margin-left: 16px; +} + +.avatar { + width: 36px; + height: 36px; + margin: auto; +} + +.headerRight { + display: flex; + min-height: 24px; + font-size: 0.9em; + flex-wrap: nowrap; + align-items: center; + margin-left: auto; + gap: 4px; + overflow: clip; + padding-left: 4px; +} + +.submit { + margin: 12px 12px 12px 6px; + vertical-align: bottom; + + &:disabled { + opacity: 0.7; + } + + &.posting { + cursor: wait; + } + + &:not(:disabled):hover { + > .inner { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } + } + + &:not(:disabled):active { + > .inner { + background: linear-gradient(90deg, var(--X8), var(--X8)); + } + } +} + +.colorBar { + position: absolute; + top: 0px; + left: 12px; + width: 5px; + height: 100% ; + border-radius: 999px; + pointer-events: none; +} + +.submitInner { + padding: 0 12px; + line-height: 35px; + font-weight: bold; + border-radius: 6px; + min-width: 90px; + color: var(--fgOnAccent); + background: linear-gradient(90deg, var(--buttonGradateA), var(--buttonGradateB)); +} + +.headerRightItem { + margin: 0; + padding: 8px; + border-radius: 6px; + + &:hover { + background: var(--X5); + } + + &:disabled { + background: none; + } + + &.danger { + color: #ff2a2a; + } +} + +.headerRightButtonText { + padding-left: 6px; +} + +.visibility { + overflow: clip; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 210px; + + &:enabled { + > .headerRightButtonText { + opacity: 0.8; + } + } +} +//#endregion + +.preview { + padding: 16px 20px 0 20px; + min-height: 75px; + max-height: 150px; + overflow: auto; +} + +.targetNote { + padding: 0 20px 16px 20px; +} + +.withQuote { + margin: 0 0 8px 0; + color: var(--accent); +} + +.toSpecified { + padding: 6px 24px; + margin-bottom: 8px; + overflow: auto; + white-space: nowrap; +} + +.visibleUsers { + display: inline; + top: -1px; + font-size: 14px; +} + +.visibleUser { + margin-right: 14px; + padding: 8px 0 8px 8px; + border-radius: 8px; + background: var(--X4); +} + +.hasNotSpecifiedMentions { + margin: 0 20px 16px 20px; +} + +.cw, +.hashtags, +.text { + display: block; + box-sizing: border-box; + padding: 0 24px; + margin: 0; + width: 100%; + font-size: 16px; + border: none; + border-radius: 0; + background: transparent; + color: var(--fg); + font-family: inherit; + + &:focus { + outline: none; + } + + &:disabled { + opacity: 0.5; + } +} + +.cw { + z-index: 1; + padding-bottom: 8px; + border-bottom: solid 0.5px var(--divider); +} + +.hashtags { + z-index: 1; + padding-top: 8px; + padding-bottom: 8px; + border-top: solid 0.5px var(--divider); +} + +.textOuter { + width: 100%; + position: relative; + + &.withCw { + padding-top: 8px; + } +} + +.text { + max-width: 92%; + min-width: 92%; + width: 92%; + min-height: 50px; + margin-top: 8px; + height: 100%; +} + +.textCount { + position: absolute; + top: 0; + right: 2px; + padding: 4px 6px; + font-size: .9em; + color: var(--warn); + border-radius: 6px; + min-width: 1.6em; + text-align: center; + + &.textOver { + color: #ff2a2a; + } +} + +.footer { + display: flex; + font-size: 1em; + justify-content: center; + align-items: center; +} + +.footerLeft { + flex: 1; + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fill, minmax(35px, 1fr)); + grid-auto-rows: 20px; +} + +.footerRight { + flex: 0; + margin-left: auto; + display: grid; + grid-auto-flow: row; + grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); + grid-auto-rows: 35px; + place-items: center; + direction: rtl; +} + +.footerButton { + display: inline-block; + padding: 0; + margin: 0; + font-size: 1em; + width: auto; + height: 100%; + border-radius: 6px; + + &:hover { + background: var(--X5); + } + + &.footerButtonActive { + color: var(--accent); + } +} + +.previewButtonActive { + color: var(--accent); +} + +@container (max-width: 500px) { + .headerRight { + font-size: .9em; + } + + .headerRightButtonText { + display: none; + } + + .visibility { + overflow: initial; + } + + .submit { + margin: 8px 8px 8px 4px; + } + + .toSpecified { + padding: 6px 16px; + } + + .preview { + padding: 16px 14px 0 14px; + } + .cw, + .hashtags, + .text { + padding: 0 16px; + } + + .text { + min-height: 80px; + } + + .footer { + padding: 0 8px 8px 8px; + } +} + +@container (max-width: 350px) { + .footer { + font-size: 0.9em; + } + + .footerLeft { + grid-template-columns: repeat(auto-fill, minmax(50px, 1fr)); + } + + .headerRight { + gap: 0; + } + +} +</style> diff --git a/packages/frontend/src/components/XPostFormDialog.vue b/packages/frontend/src/components/XPostFormDialog.vue new file mode 100644 index 0000000000..a6cfa7449d --- /dev/null +++ b/packages/frontend/src/components/XPostFormDialog.vue @@ -0,0 +1,62 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<MkModal ref="modal" :preferType="'dialog'" @click="modal?.close()" @closed="onModalClosed()"> + <XPostForm ref="form" :class="$style.form" :dialog="true" v-bind="props" autofocus freezeAfterPosted @posted="onPosted" @cancel="modal?.close()" @esc="modal?.close()"/> +</MkModal> +</template> + +<script lang="ts" setup> +import { shallowRef } from 'vue'; +import * as Misskey from 'misskey-js'; +import MkModal from '@/components/MkModal.vue'; +import MkPostForm from '@/components/MkPostForm.vue'; +import XPostForm from '@/components/XPostForm.vue'; + +const props = withDefaults(defineProps<{ + reply?: Misskey.entities.Note; + renote?: Misskey.entities.Note; + channel?: any; // TODO + mention?: Misskey.entities.User; + specified?: Misskey.entities.UserDetailed; + initialText?: string; + initialCw?: string; + initialVisibility?: (typeof Misskey.noteVisibilities)[number]; + initialFiles?: Misskey.entities.DriveFile[]; + initialLocalOnly?: boolean; + initialVisibleUsers?: Misskey.entities.UserDetailed[]; + initialNote?: Misskey.entities.Note; + instant?: boolean; + fixed?: boolean; + autofocus?: boolean; +}>(), { + initialLocalOnly: undefined, +}); + +const emit = defineEmits<{ + (ev: 'closed'): void; +}>(); + +const modal = shallowRef<InstanceType<typeof MkModal>>(); +const form = shallowRef<InstanceType<typeof MkPostForm>>(); + +function onPosted() { + modal.value?.close({ + useSendAnimation: true, + }); +} + +function onModalClosed() { + emit('closed'); +} +</script> + +<style lang="scss" module> +.form { + max-height: 100%; + margin: 0 auto auto auto; +} +</style> diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue index 71f7900548..ec3a80aceb 100644 --- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue +++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue @@ -5,10 +5,10 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div ref="el" :class="$style.tabs" @wheel="onTabWheel"> - <div :class="$style.tabsInner"> + <div :class="ui !== 'twilike' ? $style.tabsInner : $style.tabsInnerX"> <button v-for="t in tabs" :ref="(el) => tabRefs[t.key] = (el as HTMLElement)" v-tooltip.noDelay="t.title" - class="_button" :class="[$style.tab, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]" + class="_button" :class="[ui !== 'twilike' ? $style.tab : $style.tabX, { [$style.active]: t.key != null && t.key === props.tab, [$style.animate]: defaultStore.reactiveState.animation.value }]" @mousedown="(ev) => onTabMousedown(t, ev)" @click="(ev) => onTabClick(t, ev)" > <div :class="$style.tabInner"> @@ -55,6 +55,7 @@ export type Tab = { <script lang="ts" setup> import { onMounted, onUnmounted, watch, nextTick, shallowRef, ref, computed } from 'vue'; import { defaultStore } from '@/store.js'; +import { ui } from '@/config.js'; const gamingType = computed(defaultStore.makeGetterSetter('gamingType')); @@ -207,6 +208,15 @@ onUnmounted(() => { white-space: nowrap; } +.tabsInnerX { + display: flex; + height: var(--height); + white-space: nowrap; + justify-content: space-around; +} +.tabX{ + flex: 1; +} .tab { display: inline-block; position: relative; @@ -231,6 +241,7 @@ onUnmounted(() => { .tabInner { display: flex; align-items: center; + justify-content: center; } .tabIcon + .tabTitle { diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue index faa20ba869..2173f7a261 100644 --- a/packages/frontend/src/components/global/MkPageHeader.vue +++ b/packages/frontend/src/components/global/MkPageHeader.vue @@ -150,8 +150,8 @@ onUnmounted(() => { height: var(--height); .tabs:first-child { - margin-left: auto; padding: 0 12px; + width: 100%; } .tabs { margin-right: auto; @@ -168,6 +168,7 @@ onUnmounted(() => { } &.slim { + width: 100%; text-align: center; gap: 0; .buttonsRight { diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts index f3d83ce079..dedb9d1ff9 100644 --- a/packages/frontend/src/navbar.ts +++ b/packages/frontend/src/navbar.ts @@ -147,6 +147,13 @@ export const navbarItemDef = reactive({ miLocalStorage.setItem('ui', 'classic'); unisonReload(); }, + }, { + text: 'twilike', + active: ui === 'twilike', + action: () => { + miLocalStorage.setItem('ui', 'twilike'); + unisonReload(); + }, }], ev.currentTarget ?? ev.target); }, }, diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index 3a6000526a..205ed16aa5 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -13,6 +13,7 @@ import type { Form, GetFormResultType } from '@/scripts/form.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import MkPostFormDialog from '@/components/MkPostFormDialog.vue'; +import XPostFormDialog from '@/components/XPostFormDialog.vue'; import MkWaitingDialog from '@/components/MkWaitingDialog.vue'; import MkPageWindow from '@/components/MkPageWindow.vue'; import MkToast from '@/components/MkToast.vue'; @@ -25,6 +26,7 @@ import { MenuItem } from '@/types/menu.js'; import copyToClipboard from '@/scripts/copy-to-clipboard.js'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import MkSwitch from '@/components/MkSwitch.vue'; +import { ui } from '@/config.js'; export const openingWindowsCount = ref(0); @@ -551,7 +553,7 @@ export async function selectUser(opts: { includeSelf?: boolean; localOnly?: bool popup(defineAsyncComponent(() => import('@/components/MkUserSelectDialog.vue')), { includeSelf: opts.includeSelf, localOnly: opts.localOnly, - multiple: opts.multiple, + multiple: opts.multiple, }, { ok: user => { resolve(user); @@ -682,14 +684,25 @@ export function post(props: Record<string, any> = {}): Promise<void> { // 複数のpost formを開いたときに場合によってはエラーになる // もちろん複数のpost formを開けること自体Misskeyサイドのバグなのだが let dispose; - popup(MkPostFormDialog, props, { - closed: () => { - resolve(); - dispose(); - }, - }).then(res => { - dispose = res.dispose; - }); + if (ui !== 'twilike') { + popup(MkPostFormDialog, props, { + closed: () => { + resolve(); + dispose(); + }, + }).then(res => { + dispose = res.dispose; + }); + } else { + popup(XPostFormDialog, props, { + closed: () => { + resolve(); + dispose(); + }, + }).then(res => { + dispose = res.dispose; + }); + } }); } diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 7261343674..1038f917f1 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -117,7 +117,7 @@ const menuDef = computed(() => [{ }, { icon: 'ti ti-layout-navbar', text: i18n.ts.timelineHeader, - to: '/settings/timelineheader', + to: '/settings/timeline-header', active: currentPage.value?.route.name === 'timelineHeader', }, { icon: 'ti ti-equal-double', diff --git a/packages/frontend/src/pages/settings/timelineHeader.vue b/packages/frontend/src/pages/settings/timelineHeader.vue index 16ec626dfe..dcec07abd7 100644 --- a/packages/frontend/src/pages/settings/timelineHeader.vue +++ b/packages/frontend/src/pages/settings/timelineHeader.vue @@ -30,6 +30,21 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </MkContainer> </FormSlot> + <MkFoldableSection> + <template #header>リモートのローカルタイムライン</template> + + <div v-if="remoteLocalTimeline.length < 3"> + <MkInput v-model="tmpName" placeholder="remoteLocalTimeline 1"/> + <MkInput v-model="tmpServer" placeholder="https://prismisskey.space"/> + <MkButton @click="addRemote"><i class="ti ti-plus"></i> {{ i18n.ts.addItem }}</MkButton> + </div> + + <div v-for="(a,i) in remoteLocalTimeline" :key="i"> + <MkInput v-model="remoteLocalTimeline[i]['name']" :placeholder="a"/> + <MkInput v-model="remoteLocalTimeline[i]['host']" :placeholder="a"/> + <MkButton danger @click="deleteRemote(i)"><i class="ti ti-trash"></i> {{ i18n.ts.delete }}</MkButton> + </div> + </MkFoldableSection> <div class="_buttons"> <MkButton @click="addItem"><i class="ti ti-plus"></i> {{ i18n.ts.addItem }}</MkButton> <MkButton danger @click="reset"><i class="ti ti-reload"></i> {{ i18n.ts.default }}</MkButton> @@ -39,25 +54,30 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, watch } from 'vue'; -import MkRadios from '@/components/MkRadios.vue'; +import { computed, defineAsyncComponent, ref } from 'vue'; import MkButton from '@/components/MkButton.vue'; import FormSlot from '@/components/form/slot.vue'; import MkContainer from '@/components/MkContainer.vue'; import * as os from '@/os.js'; -import { navbarItemDef } from '@/navbar.js'; import { defaultStore } from '@/store.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { timelineHeaderItemDef } from '@/timeline-header.js'; +import MkInput from '@/components/MkInput.vue'; +import MkFoldableSection from '@/components/MkFoldableSection.vue'; +import { $i } from '@/account.js'; const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); +const tmpName = ref(); +const tmpServer = ref(); const items = ref(defaultStore.state.timelineHeader.map(x => ({ id: Math.random().toString(), type: x, }))); +const remoteLocalTimeline = ref(defaultStore.state.remoteLocalTimeline); +const maxLocalTimeline = $i.policies.localTimelineAnyLimit; async function reloadAsk() { const { canceled } = await os.confirm({ @@ -69,11 +89,31 @@ async function reloadAsk() { unisonReload(); } +async function addRemote() { + if (!tmpName.value || !tmpServer.value) return; + if (maxLocalTimeline <= remoteLocalTimeline.value.length) return; + remoteLocalTimeline.value.push({ + id: Math.random().toString(), + name: tmpName.value, + host: tmpServer.value, + }); + tmpName.value = ''; + tmpServer.value = ''; + await defaultStore.set('remoteLocalTimeline', remoteLocalTimeline.value); +} + +const menu = computed(() => { + return Object.keys(timelineHeaderItemDef).filter(k => !items.value.map(item => item.type).includes(k)); +}); + +async function deleteRemote(index: number) { + remoteLocalTimeline.value.splice(index, 1); +} + async function addItem() { - const menu = Object.keys(timelineHeaderItemDef).filter(k => !defaultStore.state.timelineHeader.includes(k)); const { canceled, result: item } = await os.select({ title: i18n.ts.addItem, - items: [...menu.map(k => ({ + items: [...menu.value.map(k => ({ value: k, text: timelineHeaderItemDef[k].title, }))], }); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index 84e82bbbe8..7274a7a6d5 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -12,7 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only <MkInfo v-if="['home', 'local', 'social', 'global'].includes(src) && !defaultStore.reactiveState.timelineTutorials.value[src]" style="margin-bottom: var(--margin);" closable @close="closeTutorial()"> {{ i18n.ts._timelineDescription[src] }} </MkInfo> - <MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostForm.value" :channel="channelInfo" :autofocus="deviceKind === 'desktop'" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <MkPostForm v-if="$i && defaultStore.reactiveState.showFixedPostForm.value && ui !== 'twilike'" :channel="channelInfo" :autofocus="deviceKind === 'desktop'" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> + <XPostForm v-if="$i && ui === 'twilike' " :channel="channelInfo" :autofocus="deviceKind === 'desktop'" :class="$style.postForm" class="post-form _panel" fixed style="margin-bottom: var(--margin);"/> <div v-if="queue > 0" :class="$style.new"><button class="_buttonPrimary" :class="$style.newButton" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div> <div :class="$style.tl"> <MkTimeline @@ -21,6 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only :src="src.split(':')[0]" :list="src.split(':')[1]" :channel="src.split(':')[1]" + :antenna="src.split(':')[1]" :withRenotes="withRenotes" :withReplies="withReplies" :onlyFiles="onlyFiles" @@ -47,7 +49,6 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { $i } from '@/account.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { antennasCache, userFavoriteListsCache, userListsCache, favoritedChannelsCache } from '@/cache.js'; @@ -57,6 +58,8 @@ import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; import { timelineHeaderItemDef } from '@/timeline-header.js'; import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; +import { ui } from '@/config.js'; +import XPostForm from '@/components/XPostForm.vue'; provide('shouldOmitHeaderTitle', true); @@ -121,14 +124,7 @@ const withSensitive = computed<boolean>({ get: () => defaultStore.reactiveState.tl.value.filter.withSensitive, set: (x) => saveTlFilter('withSensitive', x), }); -const isShowMediaTimeline = ref(defaultStore.state.showMediaTimeline); -const remoteLocalTimelineEnable1 = ref(defaultStore.state.remoteLocalTimelineEnable1); -const remoteLocalTimelineEnable2 = ref(defaultStore.state.remoteLocalTimelineEnable2); -const remoteLocalTimelineEnable3 = ref(defaultStore.state.remoteLocalTimelineEnable3); -const remoteLocalTimelineEnable4 = ref(defaultStore.state.remoteLocalTimelineEnable4); -const remoteLocalTimelineEnable5 = ref(defaultStore.state.remoteLocalTimelineEnable5); -const showHomeTimeline = ref(defaultStore.state.showHomeTimeline); -const showSocialTimeline = ref(defaultStore.state.showSocialTimeline); + const channelInfo = ref(); if (src.value.split(':')[0] === 'channel') { const channelId = src.value.split(':')[1]; diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts index a4b29de810..3b8b644345 100644 --- a/packages/frontend/src/store.ts +++ b/packages/frontend/src/store.ts @@ -10,6 +10,8 @@ import type { SoundType } from '@/scripts/sound.js'; import { Storage } from '@/pizzax.js'; import { hemisphere } from '@/scripts/intl-const.js'; import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; +import { instance } from '@/instance.js'; + interface PostFormAction { title: string, handler: <T>(form: T, update: (key: unknown, value: unknown) => void) => void; @@ -534,85 +536,9 @@ export const defaultStore = markRaw(new Storage('base', { where: 'device', default: 44, }, - remoteLocalTimelineDomain1: { - where: 'account', - default: '', - }, - remoteLocalTimelineDomain2: { - where: 'account', - default: '', - }, - remoteLocalTimelineDomain3: { - where: 'account', - default: '', - }, - remoteLocalTimelineDomain4: { - where: 'account', - default: '', - }, - remoteLocalTimelineDomain5: { - where: 'account', - default: '', - }, - remoteLocalTimelineToken1: { - where: 'account', - default: '', - }, - remoteLocalTimelineToken2: { - where: 'account', - default: '', - }, - remoteLocalTimelineToken3: { - where: 'account', - default: '', - }, - remoteLocalTimelineToken4: { - where: 'account', - default: '', - }, - remoteLocalTimelineToken5: { - where: 'account', - default: '', - }, - remoteLocalTimelineEnable1: { - where: 'account', - default: false, - }, - remoteLocalTimelineEnable2: { - where: 'account', - default: false, - }, - remoteLocalTimelineEnable3: { - where: 'account', - default: false, - }, - remoteLocalTimelineEnable4: { - where: 'account', - default: false, - }, - remoteLocalTimelineEnable5: { - where: 'account', - default: false, - }, - remoteLocalTimelineName1: { - where: 'account', - default: 'custom timeline 1', - }, - remoteLocalTimelineName2: { - where: 'account', - default: 'custom timeline 2 ', - }, - remoteLocalTimelineName3: { - where: 'account', - default: 'custom timeline 3', - }, - remoteLocalTimelineName4: { - where: 'account', - default: 'custom timeline 4', - }, - remoteLocalTimelineName5: { - where: 'account', - default: 'custom timeline 5', + remoteLocalTimeline: { + where: 'device', + default: [], }, onlyAndWithSave: { where: 'device', diff --git a/packages/frontend/src/timeline-header.ts b/packages/frontend/src/timeline-header.ts index 5cf27c0c7b..ee978115d5 100644 --- a/packages/frontend/src/timeline-header.ts +++ b/packages/frontend/src/timeline-header.ts @@ -3,10 +3,18 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { reactive } from 'vue'; +import { computed, reactive, ref } from 'vue'; import { i18n } from '@/i18n.js'; -import { userListsCache } from '@/cache.js'; +import { + antennasCache, + userChannelFollowingsCache, + userChannelsCache, + userFavoriteListsCache, + userListsCache, +} from '@/cache.js'; import { isLocalTimelineAvailable, isGlobalTimelineAvailable } from '@/scripts/get-timeline-available.js'; +import { defaultStore } from '@/store.js'; +import { $i } from '@/account.js'; export type TimelineHeaderItem = 'home' | @@ -16,7 +24,11 @@ export type TimelineHeaderItem = 'lists' | 'antennas' | 'channels' | - `list:${string}` + `list:${string}` | + `channel:${string}` | + `antenna:${string}` | + 'media' | + `customTimeline:${string}`; type TimelineHeaderItemsDef = { title: string; @@ -25,6 +37,11 @@ type TimelineHeaderItemsDef = { } const lists = await userListsCache.fetch(); +const userChannels = await userChannelsCache.fetch(); +const userChannelFollowings = await userChannelFollowingsCache.fetch(); +const userFavoriteLists = await userFavoriteListsCache.fetch(); +const antenna = await antennasCache.fetch(); + export const timelineHeaderItemDef = reactive<Partial<Record<TimelineHeaderItem, TimelineHeaderItemsDef>>>({ home: { title: i18n.ts._timelines.home, @@ -70,4 +87,46 @@ export const timelineHeaderItemDef = reactive<Partial<Record<TimelineHeaderItem, }; return acc; }, {}), + ...userChannels.reduce((acc, l) => { + acc['channel:' + l.id] = { + title: i18n.ts.channel + ':' + l.name, + icon: 'ti ti-star', + iconOnly: true, + }; + return acc; + }, {}), + ...userChannelFollowings.reduce((acc, l) => { + acc['channel:' + l.id] = { + title: i18n.ts.channel + ':' + l.name, + icon: 'ti ti-star', + iconOnly: true, + }; + return acc; + }, {}), + ...userFavoriteLists.reduce((acc, l) => { + acc['channel:' + l.id] = { + title: i18n.ts.channel + ':' + l.name, + icon: 'ti ti-star', + iconOnly: true, + }; + return acc; + }, {}), + ...antenna.reduce((acc, l) => { + acc['antenna:' + l.id] = { + title: i18n.ts.antennas + ':' + l.name, + icon: 'ti ti-star', + iconOnly: true, + }; + return acc; + }, {}), + ...defaultStore.reactiveState.remoteLocalTimeline.value.reduce((acc, t) => { + acc['remoteLocalTimeline:' + t.host.replace('https://', '')] = { + title: 'remoteLocaltimeline:' + t.name, + icon: 'ti ti-star', + iconOnly: true, + }; + return acc; + }, {}), + }); + diff --git a/packages/frontend/src/ui/twilike.sidebar.vue b/packages/frontend/src/ui/twilike.sidebar.vue new file mode 100644 index 0000000000..3c75b5f52b --- /dev/null +++ b/packages/frontend/src/ui/twilike.sidebar.vue @@ -0,0 +1,260 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div class="npcljfve" :class="{ iconOnly }"> + <div class="about"> + <button v-click-anime class="item _button" @click="openInstanceMenu"> + <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" class="_ghost"/> + </button> + </div> + <MkA v-click-anime class="item index" activeClass="active" to="/" exact> + <i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span> + </MkA> + <template v-for="item in menu"> + <div v-if="item === '-'" class="divider"></div> + <component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"> + <i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span> + <span v-if="navbarItemDef[item].indicated" class="indicator"> + <span v-if="navbarItemDef[item].indicateValue" class="_indicateCounter itemIndicateValueIcon">{{ navbarItemDef[item].indicateValue }}</span> + <i v-else class="_indicatorCircle"></i> + </span> + </component> + </template> + <MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'window' : null"> + <i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span> + </MkA> + <button v-click-anime class="item _button" @click="more"> + <i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span> + <span v-if="otherNavItemIndicated" class="indicator"><i class="_indicatorCircle"></i></span> + </button> + <MkA v-click-anime class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'window' : null"> + <i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span> + </MkA> + + <div class="post" data-cy-open-post-form @click="os.post"> + <MkButton class="button" gradate full rounded> + <div class="content"> + <i class="ti ti-pencil ti-fw"></i> + <span v-if="!iconOnly" class="text">{{ i18n.ts.note }}</span> + </div> + </MkButton> + </div> + + <button v-click-anime class="item _button account" @click="openAccountMenu"> + <MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> + </button> + + <!--<MisskeyLogo class="misskey"/>--> +</div> +</template> + +<script lang="ts" setup> +import { defineAsyncComponent, computed, watch, ref, shallowRef, onUnmounted, onMounted } from 'vue'; +import { openInstanceMenu } from './_common_/common.js'; +// import { host } from '@/config.js'; +import * as os from '@/os.js'; +import { navbarItemDef } from '@/navbar.js'; +import { openAccountMenu as openAccountMenu_, $i } from '@/account.js'; +import MkButton from '@/components/MkButton.vue'; +// import { StickySidebar } from '@/scripts/sticky-sidebar.js'; +// import { mainRouter } from '@/router.js'; +//import MisskeyLogo from '@assets/client/misskey.svg'; +import { defaultStore } from '@/store.js'; +import { instance } from '@/instance.js'; +import { i18n } from '@/i18n.js'; +import { mainRouter } from '@/router/main.js'; + +const WINDOW_THRESHOLD = 1400; + +const menu = ref(defaultStore.state.menu); +const menuDisplay = computed(defaultStore.makeGetterSetter('menuDisplay')); +const otherNavItemIndicated = computed<boolean>(() => { + for (const def in navbarItemDef) { + if (menu.value.includes(def)) continue; + if (navbarItemDef[def].indicated) return true; + } + return false; +}); +const el = shallowRef<HTMLElement>(); +// let accounts = $ref([]); +// let connection = $ref(null); +const iconOnly = ref(false); +const settingsWindowed = ref(false); + +function calcViewState() { + iconOnly.value = (window.innerWidth <= WINDOW_THRESHOLD) || (menuDisplay.value === 'sideIcon'); + settingsWindowed.value = (window.innerWidth > WINDOW_THRESHOLD); +} + +function more(ev: MouseEvent) { + os.popup(defineAsyncComponent(() => import('@/components/MkLaunchPad.vue')), { + src: ev.currentTarget ?? ev.target, + }, {}, 'closed'); +} + +function openAccountMenu(ev: MouseEvent) { + openAccountMenu_({ + withExtraOperation: true, + }, ev); +} + +let updateIconOnly: () => void; +watch(defaultStore.reactiveState.menuDisplay, () => { + calcViewState(); +}); +onMounted(() => { + updateIconOnly = () => { + iconOnly.value = window.innerWidth < 1300; + }; + + window.addEventListener('resize', updateIconOnly); + + updateIconOnly(); // 初期値を設定 +}); + +onUnmounted(() => { + window.removeEventListener('resize', updateIconOnly); +}); +</script> + +<style lang="scss" scoped> + + .npcljfve { + $ui-font-size: 1em; // TODO: どこかに集約したい + $nav-icon-only-width: 78px; // TODO: どこかに集約したい + $avatar-size: 32px; + $avatar-margin: 8px; + overflow-y: auto; + padding: 0 16px; + box-sizing: border-box; + width: 260px; + height: 100%; + &.iconOnly { + flex: 0 0 $nav-icon-only-width; + width: $nav-icon-only-width !important; + + > .divider { + margin: 8px auto; + width: calc(100% - 32px); + } + + > .post { + > .button { + width: 46px; + height: 46px; + padding: 0; + } + } + + > .item { + padding-left: 0; + width: 100%; + text-align: center; + font-size: $ui-font-size * 1.5; + line-height: 3.7rem; + + > i, + > .avatar { + margin-right: 0; + } + + > i { + left: 10px; + } + + > .text { + display: none; + } + } + } + + > .post { + position: sticky; + top: 0; + z-index: 1; + padding: 16px 0; + background: var(--bg); + + > .button { + min-width: 0; + min-height: 52px; + } + .content { + font-size: larger; + } + } + + > .about { + fill: currentColor; + padding: 8px 0 16px 0; + text-align: center; + + > .item { + display: block; + width: 32px; + margin: 0 auto; + + img { + display: block; + width: 100%; + } + } + } + + > .item { + position: relative; + display: block; + font-size: $ui-font-size * 1.2; + line-height: 3rem; + text-overflow: ellipsis; + overflow: hidden; + height: 58px; + white-space: nowrap; + width: 100%; + text-align: left; + box-sizing: border-box; + padding: 8px 4px 4px; + > i { + width: 32px; + } + + > i, + > .avatar { + margin-right: $avatar-margin; + } + + > .avatar { + width: $avatar-size; + height: $avatar-size; + vertical-align: middle; + } + + > .indicator { + position: absolute; + top: 0; + left: 0; + color: var(--navIndicator); + font-size: 8px; + animation: global-blink 1s infinite; + + &:has(.itemIndicateValueIcon) { + animation: none; + left: auto; + right: 20px; + } + } + + &:hover { + text-decoration: none; + color: var(--navHoverFg); + } + + &.active { + color: var(--navActive); + } + } + } +</style> diff --git a/packages/frontend/src/ui/twilike.vue b/packages/frontend/src/ui/twilike.vue new file mode 100644 index 0000000000..cd1609acba --- /dev/null +++ b/packages/frontend/src/ui/twilike.vue @@ -0,0 +1,463 @@ +<!-- +SPDX-FileCopyrightText: syuilo and misskey-project +SPDX-License-Identifier: AGPL-3.0-only +--> + +<template> +<div :class="[$style.root,{ wallpaper }]" :style="`--globalHeaderHeight:${globalHeaderHeight}px`"> + <XHeaderMenu v-if="showMenuOnTop" v-get-size="(w, h) => globalHeaderHeight = h"/> + + <div :class="[$style.columns,{ [$style.fullView]:fullView, [$style.withGlobalHeader]: showMenuOnTop }]"> + <div v-if="!showMenuOnTop && isDesktop" :class="$style.sidebar"> + <XSidebar/> + </div> + <div v-else-if="!pageMetadata?.needWideArea && isDesktop" ref="widgetsLeft" :class="[$style.widgets,$style.left]"> + <XWidgets place="left" :marginTop="'var(--margin)'"/> + </div> + + <main :class="[$style.main, {[$style.wide]: pageMetadata?.needWideArea} ]" @contextmenu.stop="onContextmenu"> + <RouterView/> + </main> + + <div v-if="isDesktop && !pageMetadata?.needWideArea" ref="widgetsRight" :class="$style.widgets"> + <XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--margin)'"/> + </div> + </div> + + <Transition :name="defaultStore.state.animation ? 'tray-back' : ''"> + <div + v-if="widgetsShowing" + class="tray-back _modalBg" + @click="widgetsShowing = false" + @touchstart.passive="widgetsShowing = false" + ></div> + </Transition> + + <XCommon/> + <div v-if="!isDesktop" ref="navFooter" :class="$style.nav"> + <button :class="$style.navButton" class="_button" @click="drawerMenuShowing = true"><i :class="$style.navButtonIcon" class="ti ti-menu-2"></i><span v-if="menuIndicated" :class="$style.navButtonIndicator"><i class="_indicatorCircle"></i></span></button> + <button :class="$style.navButton" class="_button" @click="isRoot ? top() : mainRouter.push('/')"><i :class="$style.navButtonIcon" class="ti ti-home"></i></button> + <button :class="$style.navButton" class="_button" @click="mainRouter.push('/my/notifications')"> + <i :class="$style.navButtonIcon" class="ti ti-bell"></i> + <span v-if="$i?.hasUnreadNotification" :class="$style.navButtonIndicator"> + <span class="_indicateCounter" :class="$style.itemIndicateValueIcon">{{ $i.unreadNotificationsCount > 99 ? '99+' : $i.unreadNotificationsCount }}</span> + </span> + </button> + <button :class="$style.navButton" class="_button" @click="widgetsShowing = true"><i :class="$style.navButtonIcon" class="ti ti-apps"></i></button> + <button :class="$style.navButton" class="_button" @click="os.post()"><i :class="$style.navButtonIcon" class="ti ti-pencil"></i></button> + </div> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''" + > + <div + v-if="drawerMenuShowing || widgetsShowing" + :class="$style.menuDrawerBg" + class="_modalBg" + @click="drawerMenuShowing = false; widgetsShowing = false " + @touchstart.passive="drawerMenuShowing = false; widgetsShowing = false" + ></div> + </Transition> + + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''" + > + <div v-if="drawerMenuShowing" :class="$style.menuDrawer"> + <XDrawerMenu/> + </div> + </Transition> + <Transition + :enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''" + :leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''" + :enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''" + :leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''" + > + <div v-if="widgetsShowing" :class="$style.widgetsDrawer"> + <button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button> + <XWidgets/> + </div> + </Transition> +</div> +</template> + +<script lang="ts" setup> +import { defineAsyncComponent, onMounted, provide, ref, computed, shallowRef, watch } from 'vue'; +import XSidebar from './twilike.sidebar.vue'; +import XCommon from './_common_/common.vue'; +import { instanceName, ui } from '@/config.js'; +import * as os from '@/os.js'; +import { PageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; +import { defaultStore } from '@/store.js'; +import { i18n } from '@/i18n.js'; +import { miLocalStorage } from '@/local-storage.js'; +import { mainRouter } from '@/router/main.js'; +import { $i } from '@/account.js'; +import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue'; +const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue')); +const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue')); + +const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); + +const DESKTOP_THRESHOLD = 700; + +const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); +const drawerMenuShowing = ref(false); +const pageMetadata = ref<null | PageMetadata>(null); +const widgetsShowing = ref(false); +const fullView = ref(false); +const globalHeaderHeight = ref(0); +const wallpaper = miLocalStorage.getItem('wallpaper') != null; +const showMenuOnTop = computed(() => defaultStore.state.menuDisplay === 'top'); +const widgetsLeft = ref<HTMLElement>(); +const widgetsRight = ref<HTMLElement>(); + +provide('router', mainRouter); +provideMetadataReceiver((metadataGetter) => { + const info = metadataGetter(); + pageMetadata.value = info; + if (mainRouter.currentRoute.value.path.split('/').slice(1)[0] === 'settings') { + pageMetadata.value.needWideArea = true; + } + if (pageMetadata.value) { + if (isRoot.value && pageMetadata.value.title === instanceName) { + document.title = pageMetadata.value.title; + } else { + document.title = `${pageMetadata.value.title} | ${instanceName}`; + } + } +}); +provideReactiveMetadata(pageMetadata); +provide('shouldHeaderThin', showMenuOnTop.value); +provide('forceSpacerMin', true); + +function onContextmenu(ev: MouseEvent) { + const isLink = (el: HTMLElement) => { + if (el.tagName === 'A') return true; + if (el.parentElement) { + return isLink(el.parentElement); + } + }; + if (isLink(ev.target)) return; + if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes(ev.target.tagName) || ev.target.attributes['contenteditable']) return; + if (window.getSelection().toString() !== '') return; + const path = mainRouter.getCurrentPath(); + os.contextMenu([{ + type: 'label', + text: path, + }, { + icon: fullView.value ? 'ti ti-minimize' : 'ti ti-maximize', + text: fullView.value ? i18n.ts.quitFullView : i18n.ts.fullView, + action: () => { + fullView.value = !fullView.value; + }, + }, { + icon: 'ti ti-window-maximize', + text: i18n.ts.openInWindow, + action: () => { + os.pageWindow(path); + }, + }], ev); +} + +document.documentElement.style.overflowY = 'scroll'; + +defaultStore.loaded.then(() => { + if (defaultStore.state.widgets.length === 0) { + defaultStore.set('widgets', [{ + name: 'calendar', + id: 'a', place: null, data: {}, + }, { + name: 'notifications', + id: 'b', place: null, data: {}, + }, { + name: 'trends', + id: 'c', place: null, data: {}, + }]); + } +}); + +onMounted(() => { + window.addEventListener('resize', () => { + isDesktop.value = (window.innerWidth >= DESKTOP_THRESHOLD); + }, { passive: true }); +}); +</script> + +<style lang="scss" module> +$ui-font-size: 1em; +$widgets-hide-threshold: 1200px; +.transition_widgetsDrawer_enterActive, +.transition_widgetsDrawer_leaveActive { + opacity: 1; + transform: translateX(0); + transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); +} +.transition_widgetsDrawer_enterFrom, +.transition_widgetsDrawer_leaveTo { + opacity: 0; + transform: translateX(240px); +} +.tray-enter-active, +.tray-leave-active { + opacity: 1; + transform: translateX(0); + transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); +} +.tray-enter-from, +.tray-leave-active { + opacity: 0; + transform: translateX(240px); +} + +.tray-back-enter-active, +.tray-back-leave-active { + opacity: 1; + transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); +} +.tray-back-enter-from, +.tray-back-leave-active { + opacity: 0; +} + +.transition_menuDrawerBg_enterActive, +.transition_menuDrawerBg_leaveActive { + opacity: 1; + transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); +} +.transition_menuDrawerBg_enterFrom, +.transition_menuDrawerBg_leaveTo { + opacity: 0; +} + +.transition_menuDrawer_enterActive, +.transition_menuDrawer_leaveActive { + opacity: 1; + transform: translateX(0); + transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); +} +.transition_menuDrawer_enterFrom, +.transition_menuDrawer_leaveTo { + opacity: 0; + transform: translateX(-240px); +} +@media (min-width: 700px) { + .root{ + padding-right:48px; + padding-left:24px; + } +} +.wide{ + &.main { + margin: 0; + border-radius: 0; + box-shadow: none; + width: 100% !important; + max-width: 900px !important; + } +} + +.root { + + min-height: 100dvh; + box-sizing: border-box; + + &.wallpaper { + background: var(--wallpaperOverlay); + } + + > .tray-back { + z-index: 1001; + } + + > .tray { + position: fixed; + top: 0; + right: 0; + z-index: 1001; + height: 100dvh; + margin-top: var(--stickyTop); + padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)); + box-sizing: border-box; + overflow: auto; + background: var(--bg); + } + + > .ivnzpscs { + position: fixed; + bottom: 0; + right: 0; + width: 300px; + height: 600px; + border: none; + pointer-events: none; + } +} + +.columns { + display: flex; + justify-content: center; + max-width: 100%; + + &.fullView { + margin: 0; + + > .sidebar { + display: none; + } + + > .widgets { + display: none; + } + + > .main { + margin: 0; + border-radius: 0; + box-shadow: none; + width: 100%; + } + } + + > .main { + flex: 1; + min-width: 0; + width: 750px; + margin: 0 16px 0 0; + border-left: solid 1px var(--divider); + border-right: solid 1px var(--divider); + border-radius: 0; + overflow: clip; + --margin: 12px; + max-width: 600px; + } + + > .widgets { + //--panelBorder: none; + width: 300px; + height: 100vh; + padding-bottom: calc(var(--margin) + env(safe-area-inset-bottom, 0px)); + position: sticky; + overflow-y: auto; + top: 0; + padding-top: 16px; + + @media (max-width: $widgets-hide-threshold) { + display: none; + } + + &.left { + margin-right: 16px; + } + } + + > .sidebar { + width: 275px; + height: 100vh; + position: sticky; + top: 0; + padding: 20px; + } + + &.withGlobalHeader { + > .main { + margin-top: 0; + border: solid 1px var(--divider); + border-radius: var(--radius); + --stickyTop: var(--globalHeaderHeight); + } + + > .widgets { + --stickyTop: var(--globalHeaderHeight); + margin-top: 0; + } + } + @media (max-width: 1300px) { + .sidebar { + width: 80px; + } + .main { + width: 100%; + max-width: 100%; + } + } + @media (max-width: 850px) { + margin: 0; + + > .sidebar { + border-right: solid 0.5px var(--divider); + } + + > .main { + margin: 0; + border-radius: 0; + box-shadow: none; + width: 100%; + } + } +} +.nav { + position: fixed; + z-index: 1000; + bottom: 0; + left: 0; + padding: 12px 12px max(12px, env(safe-area-inset-bottom, 0px)) 12px; + display: grid; + grid-template-columns: 1fr 1fr 1fr 1fr 1fr; + grid-gap: 8px; + width: 100%; + box-sizing: border-box; + -webkit-backdrop-filter: var(--blur, blur(24px)); + backdrop-filter: var(--blur, blur(24px)); + background-color: var(--bg); + border-top: solid 0.5px var(--divider); + height: 52px; +} + +.menuDrawerBg { + z-index: 1001; +} + +.menuDrawer { + position: fixed; + top: 0; + left: 0; + z-index: 1001; + height: 100dvh; + width: 240px; + box-sizing: border-box; + contain: strict; + overflow: auto; + overscroll-behavior: contain; + background: var(--navBg); +} + +.navButton{ + font-size: 1.5em; +} +.widgetsDrawerBg { + z-index: 1001; +} + +.widgetsDrawer { + position: fixed; + top: 0; + right: 0; + z-index: 1001; + width: 310px; + height: 100dvh; + padding: var(--margin) var(--margin) calc(var(--margin) + env(safe-area-inset-bottom, 0px)) !important; + box-sizing: border-box; + overflow: auto; + overscroll-behavior: contain; + background: var(--bg); +} +.widgetsCloseButton { + padding: 8px; + display: block; + margin: 0 auto; +} +</style>