mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 15:34:13 +00:00 
			
		
		
		
	
							parent
							
								
									a74c7f60b5
								
							
						
					
					
						commit
						ce83c483c6
					
				
					 12 changed files with 187 additions and 17 deletions
				
			
		| 
						 | 
					@ -278,6 +278,7 @@ import * as ep___notes_reactions_delete from './endpoints/notes/reactions/delete
 | 
				
			||||||
import * as ep___notes_renotes from './endpoints/notes/renotes.js';
 | 
					import * as ep___notes_renotes from './endpoints/notes/renotes.js';
 | 
				
			||||||
import * as ep___notes_replies from './endpoints/notes/replies.js';
 | 
					import * as ep___notes_replies from './endpoints/notes/replies.js';
 | 
				
			||||||
import * as ep___notes_edit from './endpoints/notes/edit.js';
 | 
					import * as ep___notes_edit from './endpoints/notes/edit.js';
 | 
				
			||||||
 | 
					import * as ep___notes_versions from './endpoints/notes/versions.js';
 | 
				
			||||||
import * as ep___notes_searchByTag from './endpoints/notes/search-by-tag.js';
 | 
					import * as ep___notes_searchByTag from './endpoints/notes/search-by-tag.js';
 | 
				
			||||||
import * as ep___notes_search from './endpoints/notes/search.js';
 | 
					import * as ep___notes_search from './endpoints/notes/search.js';
 | 
				
			||||||
import * as ep___notes_show from './endpoints/notes/show.js';
 | 
					import * as ep___notes_show from './endpoints/notes/show.js';
 | 
				
			||||||
| 
						 | 
					@ -646,6 +647,7 @@ const $notes_translate: Provider = { provide: 'ep:notes/translate', useClass: ep
 | 
				
			||||||
const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default };
 | 
					const $notes_unrenote: Provider = { provide: 'ep:notes/unrenote', useClass: ep___notes_unrenote.default };
 | 
				
			||||||
const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default };
 | 
					const $notes_userListTimeline: Provider = { provide: 'ep:notes/user-list-timeline', useClass: ep___notes_userListTimeline.default };
 | 
				
			||||||
const $notes_edit: Provider = { provide: 'ep:notes/edit', useClass: ep___notes_edit.default };
 | 
					const $notes_edit: Provider = { provide: 'ep:notes/edit', useClass: ep___notes_edit.default };
 | 
				
			||||||
 | 
					const $notes_versions: Provider = { provide: 'ep:notes/versions', useClass: ep___notes_versions.default };
 | 
				
			||||||
const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default };
 | 
					const $notifications_create: Provider = { provide: 'ep:notifications/create', useClass: ep___notifications_create.default };
 | 
				
			||||||
const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default };
 | 
					const $notifications_markAllAsRead: Provider = { provide: 'ep:notifications/mark-all-as-read', useClass: ep___notifications_markAllAsRead.default };
 | 
				
			||||||
const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default };
 | 
					const $notifications_testNotification: Provider = { provide: 'ep:notifications/test-notification', useClass: ep___notifications_testNotification.default };
 | 
				
			||||||
| 
						 | 
					@ -1008,6 +1010,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de
 | 
				
			||||||
		$notes_unrenote,
 | 
							$notes_unrenote,
 | 
				
			||||||
		$notes_userListTimeline,
 | 
							$notes_userListTimeline,
 | 
				
			||||||
		$notes_edit,
 | 
							$notes_edit,
 | 
				
			||||||
 | 
							$notes_versions,
 | 
				
			||||||
		$notifications_create,
 | 
							$notifications_create,
 | 
				
			||||||
		$notifications_markAllAsRead,
 | 
							$notifications_markAllAsRead,
 | 
				
			||||||
		$notifications_testNotification,
 | 
							$notifications_testNotification,
 | 
				
			||||||
| 
						 | 
					@ -1364,6 +1367,7 @@ const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.de
 | 
				
			||||||
		$notes_unrenote,
 | 
							$notes_unrenote,
 | 
				
			||||||
		$notes_userListTimeline,
 | 
							$notes_userListTimeline,
 | 
				
			||||||
		$notes_edit,
 | 
							$notes_edit,
 | 
				
			||||||
 | 
							$notes_versions,
 | 
				
			||||||
		$notifications_create,
 | 
							$notifications_create,
 | 
				
			||||||
		$notifications_markAllAsRead,
 | 
							$notifications_markAllAsRead,
 | 
				
			||||||
		$pagePush,
 | 
							$pagePush,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { Inject, Injectable } from '@nestjs/common';
 | 
					import { Inject, Injectable } from '@nestjs/common';
 | 
				
			||||||
import { DI } from '@/di-symbols.js';
 | 
					import { DI } from '@/di-symbols.js';
 | 
				
			||||||
import type { NotesRepository, UsersRepository } from '@/models/_.js';
 | 
					import type { NotesRepository, UsersRepository, NoteEditRepository } from '@/models/_.js';
 | 
				
			||||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
 | 
					import { IdentifiableError } from '@/misc/identifiable-error.js';
 | 
				
			||||||
import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
 | 
					import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/User.js';
 | 
				
			||||||
import type { MiNote } from '@/models/Note.js';
 | 
					import type { MiNote } from '@/models/Note.js';
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,9 @@ export class GetterService {
 | 
				
			||||||
		@Inject(DI.notesRepository)
 | 
							@Inject(DI.notesRepository)
 | 
				
			||||||
		private notesRepository: NotesRepository,
 | 
							private notesRepository: NotesRepository,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Inject(DI.noteEditRepository)
 | 
				
			||||||
 | 
							private noteEditRepository: NoteEditRepository,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		private userEntityService: UserEntityService,
 | 
							private userEntityService: UserEntityService,
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -39,6 +42,18 @@ export class GetterService {
 | 
				
			||||||
		return note;
 | 
							return note;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Get note for API processing
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						@bindThis
 | 
				
			||||||
 | 
						public async getEdits(noteId: MiNote['id']) {
 | 
				
			||||||
 | 
							const edits = await this.noteEditRepository.findBy({ noteId: noteId }).catch(() => {
 | 
				
			||||||
 | 
								throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return edits;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	 * Get user for API processing
 | 
						 * Get user for API processing
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -288,6 +288,7 @@ import * as ep___notes_translate from './endpoints/notes/translate.js';
 | 
				
			||||||
import * as ep___notes_unrenote from './endpoints/notes/unrenote.js';
 | 
					import * as ep___notes_unrenote from './endpoints/notes/unrenote.js';
 | 
				
			||||||
import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js';
 | 
					import * as ep___notes_userListTimeline from './endpoints/notes/user-list-timeline.js';
 | 
				
			||||||
import * as ep___notes_edit from './endpoints/notes/edit.js';
 | 
					import * as ep___notes_edit from './endpoints/notes/edit.js';
 | 
				
			||||||
 | 
					import * as ep___notes_versions from './endpoints/notes/versions.js';
 | 
				
			||||||
import * as ep___notifications_create from './endpoints/notifications/create.js';
 | 
					import * as ep___notifications_create from './endpoints/notifications/create.js';
 | 
				
			||||||
import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js';
 | 
					import * as ep___notifications_markAllAsRead from './endpoints/notifications/mark-all-as-read.js';
 | 
				
			||||||
import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js';
 | 
					import * as ep___notifications_testNotification from './endpoints/notifications/test-notification.js';
 | 
				
			||||||
| 
						 | 
					@ -644,6 +645,7 @@ const eps = [
 | 
				
			||||||
	['notes/unrenote', ep___notes_unrenote],
 | 
						['notes/unrenote', ep___notes_unrenote],
 | 
				
			||||||
	['notes/user-list-timeline', ep___notes_userListTimeline],
 | 
						['notes/user-list-timeline', ep___notes_userListTimeline],
 | 
				
			||||||
	['notes/edit', ep___notes_edit],
 | 
						['notes/edit', ep___notes_edit],
 | 
				
			||||||
 | 
						['notes/versions', ep___notes_versions],
 | 
				
			||||||
	['notifications/create', ep___notifications_create],
 | 
						['notifications/create', ep___notifications_create],
 | 
				
			||||||
	['notifications/mark-all-as-read', ep___notifications_markAllAsRead],
 | 
						['notifications/mark-all-as-read', ep___notifications_markAllAsRead],
 | 
				
			||||||
	['notifications/test-notification', ep___notifications_testNotification],
 | 
						['notifications/test-notification', ep___notifications_testNotification],
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										63
									
								
								packages/backend/src/server/api/endpoints/notes/versions.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								packages/backend/src/server/api/endpoints/notes/versions.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,63 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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 { GetterService } from '@/server/api/GetterService.js';
 | 
				
			||||||
 | 
					import { ApiError } from '../../error.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						tags: ['notes'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res: {
 | 
				
			||||||
 | 
							type: 'object',
 | 
				
			||||||
 | 
							optional: false, nullable: false,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						errors: {
 | 
				
			||||||
 | 
							noSuchNote: {
 | 
				
			||||||
 | 
								message: 'No such note.',
 | 
				
			||||||
 | 
								code: 'NO_SUCH_NOTE',
 | 
				
			||||||
 | 
								id: '24fcbfc6-2e37-42b6-8388-c29b3861a08d',
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					} 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(
 | 
				
			||||||
 | 
							private getterService: GetterService,
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							super(meta, paramDef, async (ps, me) => {
 | 
				
			||||||
 | 
								const edits = await this.getterService.getEdits(ps.noteId).catch(err => {
 | 
				
			||||||
 | 
									if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
 | 
				
			||||||
 | 
									throw err;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								let editArray = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (const edit of edits) {
 | 
				
			||||||
 | 
									editArray.push({
 | 
				
			||||||
 | 
										updatedAt: new Date(edit.updatedAt).toLocaleString('UTC', { hour: 'numeric', minute: 'numeric', second: 'numeric', year: 'numeric', month: 'short', day: 'numeric' }),
 | 
				
			||||||
 | 
										text: edit.text,
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								editArray = editArray.sort((a, b) => { return new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime(); });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return editArray;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import megalodon, { Entity, MegalodonInterface } from 'megalodon';
 | 
				
			||||||
import querystring from 'querystring';
 | 
					import querystring from 'querystring';
 | 
				
			||||||
import { IsNull } from 'typeorm';
 | 
					import { IsNull } from 'typeorm';
 | 
				
			||||||
import multer from 'fastify-multer';
 | 
					import multer from 'fastify-multer';
 | 
				
			||||||
import type { NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
 | 
					import type { NoteEditRepository, NotesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js';
 | 
				
			||||||
import { DI } from '@/di-symbols.js';
 | 
					import { DI } from '@/di-symbols.js';
 | 
				
			||||||
import { bindThis } from '@/decorators.js';
 | 
					import { bindThis } from '@/decorators.js';
 | 
				
			||||||
import type { Config } from '@/config.js';
 | 
					import type { Config } from '@/config.js';
 | 
				
			||||||
| 
						 | 
					@ -31,6 +31,8 @@ export class MastodonApiServerService {
 | 
				
			||||||
        private notesRepository: NotesRepository,
 | 
					        private notesRepository: NotesRepository,
 | 
				
			||||||
		@Inject(DI.userProfilesRepository)
 | 
							@Inject(DI.userProfilesRepository)
 | 
				
			||||||
		private userProfilesRepository: UserProfilesRepository,
 | 
							private userProfilesRepository: UserProfilesRepository,
 | 
				
			||||||
 | 
							@Inject(DI.noteEditRepository)
 | 
				
			||||||
 | 
							private noteEditRepository: NoteEditRepository,
 | 
				
			||||||
        @Inject(DI.config)
 | 
					        @Inject(DI.config)
 | 
				
			||||||
        private config: Config,
 | 
					        private config: Config,
 | 
				
			||||||
        private metaService: MetaService,
 | 
					        private metaService: MetaService,
 | 
				
			||||||
| 
						 | 
					@ -754,7 +756,7 @@ export class MastodonApiServerService {
 | 
				
			||||||
		//#endregion
 | 
							//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//#region Timelines
 | 
							//#region Timelines
 | 
				
			||||||
		const TLEndpoint = new ApiTimelineMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.userEntityService);
 | 
							const TLEndpoint = new ApiTimelineMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// GET Endpoints
 | 
							// GET Endpoints
 | 
				
			||||||
		TLEndpoint.getTL();
 | 
							TLEndpoint.getTL();
 | 
				
			||||||
| 
						 | 
					@ -779,7 +781,7 @@ export class MastodonApiServerService {
 | 
				
			||||||
		//#endregion
 | 
							//#endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//#region Status
 | 
							//#region Status
 | 
				
			||||||
		const NoteEndpoint = new ApiStatusMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.userEntityService);
 | 
							const NoteEndpoint = new ApiStatusMastodon(fastify, this.config, this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// GET Endpoints
 | 
							// GET Endpoints
 | 
				
			||||||
		NoteEndpoint.getStatus();
 | 
							NoteEndpoint.getStatus();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@ import { parse } from 'mfm-js';
 | 
				
			||||||
import { GetterService } from '../GetterService.js';
 | 
					import { GetterService } from '../GetterService.js';
 | 
				
			||||||
import type { IMentionedRemoteUsers } from '@/models/Note.js';
 | 
					import type { IMentionedRemoteUsers } from '@/models/Note.js';
 | 
				
			||||||
import type { MiUser } from '@/models/User.js';
 | 
					import type { MiUser } from '@/models/User.js';
 | 
				
			||||||
import type { NotesRepository, UsersRepository } from '@/models/_.js';
 | 
					import type { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
 | 
				
			||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
					import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const CHAR_COLLECTION = '0123456789abcdefghijklmnopqrstuvwxyz';
 | 
					const CHAR_COLLECTION = '0123456789abcdefghijklmnopqrstuvwxyz';
 | 
				
			||||||
| 
						 | 
					@ -40,10 +40,13 @@ export class MastoConverters {
 | 
				
			||||||
		@Inject(DI.notesRepository)
 | 
							@Inject(DI.notesRepository)
 | 
				
			||||||
		private notesRepository: NotesRepository,
 | 
							private notesRepository: NotesRepository,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							@Inject(DI.noteEditRepository)
 | 
				
			||||||
 | 
							private noteEditRepository: NoteEditRepository,
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		private userEntityService: UserEntityService
 | 
							private userEntityService: UserEntityService
 | 
				
			||||||
	) {
 | 
						) {
 | 
				
			||||||
		this.MfmService = new MfmService(this.config);
 | 
							this.MfmService = new MfmService(this.config);
 | 
				
			||||||
		this.GetterService = new GetterService(this.usersRepository, this.notesRepository, this.userEntityService);
 | 
							this.GetterService = new GetterService(this.usersRepository, this.notesRepository, this.noteEditRepository, this.userEntityService);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private encode(u: MiUser, m: IMentionedRemoteUsers): MastodonEntity.Mention {
 | 
						private encode(u: MiUser, m: IMentionedRemoteUsers): MastodonEntity.Mention {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@ import { convertTimelinesArgsId, limitToInt } from './timeline.js';
 | 
				
			||||||
import type { Entity } from 'megalodon';
 | 
					import type { Entity } from 'megalodon';
 | 
				
			||||||
import type { FastifyInstance } from 'fastify';
 | 
					import type { FastifyInstance } from 'fastify';
 | 
				
			||||||
import type { Config } from '@/config.js';
 | 
					import type { Config } from '@/config.js';
 | 
				
			||||||
import { NotesRepository, UsersRepository } from '@/models/_.js';
 | 
					import { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
 | 
				
			||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
					import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function normalizeQuery(data: any) {
 | 
					function normalizeQuery(data: any) {
 | 
				
			||||||
| 
						 | 
					@ -18,9 +18,9 @@ export class ApiStatusMastodon {
 | 
				
			||||||
	private fastify: FastifyInstance;
 | 
						private fastify: FastifyInstance;
 | 
				
			||||||
	private mastoconverter: MastoConverters;
 | 
						private mastoconverter: MastoConverters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(fastify: FastifyInstance, config: Config, usersrepo: UsersRepository, notesrepo: NotesRepository, userentity: UserEntityService) {
 | 
						constructor(fastify: FastifyInstance, config: Config, usersrepo: UsersRepository, notesrepo: NotesRepository, noteeditrepo: NoteEditRepository, userentity: UserEntityService) {
 | 
				
			||||||
		this.fastify = fastify;
 | 
							this.fastify = fastify;
 | 
				
			||||||
		this.mastoconverter = new MastoConverters(config, usersrepo, notesrepo, userentity);
 | 
							this.mastoconverter = new MastoConverters(config, usersrepo, notesrepo, noteeditrepo, userentity);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getStatus() {
 | 
						public async getStatus() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import { getClient } from '../MastodonApiServerService.js';
 | 
				
			||||||
import type { Entity } from 'megalodon';
 | 
					import type { Entity } from 'megalodon';
 | 
				
			||||||
import type { FastifyInstance } from 'fastify';
 | 
					import type { FastifyInstance } from 'fastify';
 | 
				
			||||||
import type { Config } from '@/config.js';
 | 
					import type { Config } from '@/config.js';
 | 
				
			||||||
import { NotesRepository, UsersRepository } from '@/models/_.js';
 | 
					import { NoteEditRepository, NotesRepository, UsersRepository } from '@/models/_.js';
 | 
				
			||||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
					import { UserEntityService } from '@/core/entities/UserEntityService.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function limitToInt(q: ParsedUrlQuery) {
 | 
					export function limitToInt(q: ParsedUrlQuery) {
 | 
				
			||||||
| 
						 | 
					@ -43,9 +43,9 @@ export class ApiTimelineMastodon {
 | 
				
			||||||
	private fastify: FastifyInstance;
 | 
						private fastify: FastifyInstance;
 | 
				
			||||||
	private mastoconverter: MastoConverters;
 | 
						private mastoconverter: MastoConverters;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(fastify: FastifyInstance, config: Config, usersRepository: UsersRepository, notesRepository: NotesRepository, userEntityService: UserEntityService) {
 | 
						constructor(fastify: FastifyInstance, config: Config, usersRepository: UsersRepository, notesRepository: NotesRepository, noteEditRepository: NoteEditRepository, userEntityService: UserEntityService) {
 | 
				
			||||||
		this.fastify = fastify;
 | 
							this.fastify = fastify;
 | 
				
			||||||
		this.mastoconverter = new MastoConverters(config, usersRepository, notesRepository, userEntityService);
 | 
							this.mastoconverter = new MastoConverters(config, usersRepository, notesRepository, noteEditRepository, userEntityService);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public async getTL() {
 | 
						public async getTL() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
			</span>
 | 
								</span>
 | 
				
			||||||
			<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold pg-lg"></i></span>
 | 
								<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold pg-lg"></i></span>
 | 
				
			||||||
			<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
 | 
								<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
 | 
				
			||||||
			<span v-if="note.updatedAt" style="margin-left: 0.5em;" title="Edited"><i class="ph-pencil ph-bold ph-lg"></i></span>
 | 
								<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
	<div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget">
 | 
						<div v-if="renoteCollapsed" :class="$style.collapsedRenoteTarget">
 | 
				
			||||||
| 
						 | 
					@ -177,6 +177,7 @@ import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
 | 
				
			||||||
import { $i } from '@/account.js';
 | 
					import { $i } from '@/account.js';
 | 
				
			||||||
import { i18n } from '@/i18n.js';
 | 
					import { i18n } from '@/i18n.js';
 | 
				
			||||||
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
 | 
					import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
 | 
				
			||||||
 | 
					import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
 | 
				
			||||||
import { useNoteCapture } from '@/scripts/use-note-capture.js';
 | 
					import { useNoteCapture } from '@/scripts/use-note-capture.js';
 | 
				
			||||||
import { deepClone } from '@/scripts/clone.js';
 | 
					import { deepClone } from '@/scripts/clone.js';
 | 
				
			||||||
import { useTooltip } from '@/scripts/use-tooltip.js';
 | 
					import { useTooltip } from '@/scripts/use-tooltip.js';
 | 
				
			||||||
| 
						 | 
					@ -224,6 +225,7 @@ const isRenote = (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const el = shallowRef<HTMLElement>();
 | 
					const el = shallowRef<HTMLElement>();
 | 
				
			||||||
const menuButton = shallowRef<HTMLElement>();
 | 
					const menuButton = shallowRef<HTMLElement>();
 | 
				
			||||||
 | 
					const menuVersionsButton = shallowRef<HTMLElement>();
 | 
				
			||||||
const renoteButton = shallowRef<HTMLElement>();
 | 
					const renoteButton = shallowRef<HTMLElement>();
 | 
				
			||||||
const renoteTime = shallowRef<HTMLElement>();
 | 
					const renoteTime = shallowRef<HTMLElement>();
 | 
				
			||||||
const reactButton = shallowRef<HTMLElement>();
 | 
					const reactButton = shallowRef<HTMLElement>();
 | 
				
			||||||
| 
						 | 
					@ -563,6 +565,13 @@ function menu(viaKeyboard = false): void {
 | 
				
			||||||
	}).then(focus).finally(cleanup);
 | 
						}).then(focus).finally(cleanup);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function menuVersions(viaKeyboard = false): Promise<void> {
 | 
				
			||||||
 | 
						const { menu, cleanup } = await getNoteVersionsMenu({ note: note, menuVersionsButton });
 | 
				
			||||||
 | 
						os.popupMenu(menu, menuVersionsButton.value, {
 | 
				
			||||||
 | 
							viaKeyboard,
 | 
				
			||||||
 | 
						}).then(focus).finally(cleanup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function clip() {
 | 
					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);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,7 +58,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
							<i v-else-if="appearNote.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
 | 
												<i v-else-if="appearNote.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
 | 
				
			||||||
							<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
 | 
												<i v-else-if="appearNote.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
 | 
				
			||||||
						</span>
 | 
											</span>
 | 
				
			||||||
						<span v-if="appearNote.updatedAt" style="margin-left: 0.5em;" title="Edited"><i class="ph-pencil ph-bold ph-lg"></i></span>
 | 
											<span v-if="appearNote.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
 | 
				
			||||||
						<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold pg-lg"></i></span>
 | 
											<span v-if="appearNote.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold pg-lg"></i></span>
 | 
				
			||||||
					</div>
 | 
										</div>
 | 
				
			||||||
				</div>
 | 
									</div>
 | 
				
			||||||
| 
						 | 
					@ -232,6 +232,7 @@ import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
 | 
				
			||||||
import { $i } from '@/account.js';
 | 
					import { $i } from '@/account.js';
 | 
				
			||||||
import { i18n } from '@/i18n.js';
 | 
					import { i18n } from '@/i18n.js';
 | 
				
			||||||
import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
 | 
					import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
 | 
				
			||||||
 | 
					import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
 | 
				
			||||||
import { useNoteCapture } from '@/scripts/use-note-capture.js';
 | 
					import { useNoteCapture } from '@/scripts/use-note-capture.js';
 | 
				
			||||||
import { deepClone } from '@/scripts/clone.js';
 | 
					import { deepClone } from '@/scripts/clone.js';
 | 
				
			||||||
import { useTooltip } from '@/scripts/use-tooltip.js';
 | 
					import { useTooltip } from '@/scripts/use-tooltip.js';
 | 
				
			||||||
| 
						 | 
					@ -273,6 +274,7 @@ const isRenote = (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const el = shallowRef<HTMLElement>();
 | 
					const el = shallowRef<HTMLElement>();
 | 
				
			||||||
const menuButton = shallowRef<HTMLElement>();
 | 
					const menuButton = shallowRef<HTMLElement>();
 | 
				
			||||||
 | 
					const menuVersionsButton = shallowRef<HTMLElement>();
 | 
				
			||||||
const renoteButton = shallowRef<HTMLElement>();
 | 
					const renoteButton = shallowRef<HTMLElement>();
 | 
				
			||||||
const renoteTime = shallowRef<HTMLElement>();
 | 
					const renoteTime = shallowRef<HTMLElement>();
 | 
				
			||||||
const reactButton = shallowRef<HTMLElement>();
 | 
					const reactButton = shallowRef<HTMLElement>();
 | 
				
			||||||
| 
						 | 
					@ -612,6 +614,13 @@ function menu(viaKeyboard = false): void {
 | 
				
			||||||
	}).then(focus).finally(cleanup);
 | 
						}).then(focus).finally(cleanup);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function menuVersions(viaKeyboard = false): Promise<void> {
 | 
				
			||||||
 | 
						const { menu, cleanup } = await getNoteVersionsMenu({ note: note, menuVersionsButton });
 | 
				
			||||||
 | 
						os.popupMenu(menu, menuVersionsButton.value, {
 | 
				
			||||||
 | 
							viaKeyboard,
 | 
				
			||||||
 | 
						}).then(focus).finally(cleanup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function clip() {
 | 
					async function clip() {
 | 
				
			||||||
	os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus);
 | 
						os.popupMenu(await getNoteClipMenu({ note: note, isDeleted }), clipButton.value).then(focus);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
			<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
 | 
								<i v-else-if="note.visibility === 'followers'" class="ph-lock ph-bold ph-lg"></i>
 | 
				
			||||||
			<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
 | 
								<i v-else-if="note.visibility === 'specified'" ref="specified" class="ph-envelope ph-bold ph-lg"></i>
 | 
				
			||||||
		</span>
 | 
							</span>
 | 
				
			||||||
		<span v-if="note.updatedAt" style="margin-left: 0.5em;" title="Edited"><i class="ph-pencil ph-bold ph-lg"></i></span>
 | 
							<span v-if="note.updatedAt" ref="menuVersionsButton" style="margin-left: 0.5em;" title="Edited" @mousedown="menuVersions()"><i class="ph-pencil ph-bold ph-lg"></i></span>
 | 
				
			||||||
		<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold pg-lg"></i></span>
 | 
							<span v-if="note.localOnly" style="margin-left: 0.5em;" :title="i18n.ts._visibility['disableFederation']"><i class="ph-rocket ph-bold pg-lg"></i></span>
 | 
				
			||||||
		<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
 | 
							<span v-if="note.channel" style="margin-left: 0.5em;" :title="note.channel.name"><i class="ph-television ph-bold ph-lg"></i></span>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
| 
						 | 
					@ -30,15 +30,25 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts" setup>
 | 
					<script lang="ts" setup>
 | 
				
			||||||
import { } from 'vue';
 | 
					import { shallowRef } from 'vue';
 | 
				
			||||||
import * as Misskey from 'misskey-js';
 | 
					import * as Misskey from 'misskey-js';
 | 
				
			||||||
import { i18n } from '@/i18n.js';
 | 
					import { i18n } from '@/i18n.js';
 | 
				
			||||||
import { notePage } from '@/filters/note.js';
 | 
					import { notePage } from '@/filters/note.js';
 | 
				
			||||||
import { userPage } from '@/filters/user.js';
 | 
					import { userPage } from '@/filters/user.js';
 | 
				
			||||||
 | 
					import { getNoteVersionsMenu } from '@/scripts/get-note-versions-menu.js';
 | 
				
			||||||
 | 
					import { popupMenu } from '@/os.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
defineProps<{
 | 
					const props = defineProps<{
 | 
				
			||||||
	note: Misskey.entities.Note;
 | 
						note: Misskey.entities.Note;
 | 
				
			||||||
}>();
 | 
					}>();
 | 
				
			||||||
 | 
					const menuVersionsButton = shallowRef<HTMLElement>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function menuVersions(viaKeyboard = false): Promise<void> {
 | 
				
			||||||
 | 
						const { menu, cleanup } = await getNoteVersionsMenu({ note: props.note, menuVersionsButton });
 | 
				
			||||||
 | 
						popupMenu(menu, menuVersionsButton.value, {
 | 
				
			||||||
 | 
							viaKeyboard,
 | 
				
			||||||
 | 
						}).then(focus).finally(cleanup);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="scss" module>
 | 
					<style lang="scss" module>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										53
									
								
								packages/frontend/src/scripts/get-note-versions-menu.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								packages/frontend/src/scripts/get-note-versions-menu.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,53 @@
 | 
				
			||||||
 | 
					import { Ref } from 'vue';
 | 
				
			||||||
 | 
					import * as Misskey from 'misskey-js';
 | 
				
			||||||
 | 
					import { i18n } from '@/i18n.js';
 | 
				
			||||||
 | 
					import * as os from '@/os.js';
 | 
				
			||||||
 | 
					import { MenuItem } from '@/types/menu.js';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export async function getNoteVersionsMenu(props: {
 | 
				
			||||||
 | 
						note: Misskey.entities.Note;
 | 
				
			||||||
 | 
						menuButton: Ref<HTMLElement>;
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
						const isRenote = (
 | 
				
			||||||
 | 
							props.note.renote != null &&
 | 
				
			||||||
 | 
							props.note.text == null &&
 | 
				
			||||||
 | 
							props.note.fileIds.length === 0 &&
 | 
				
			||||||
 | 
							props.note.poll == null
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const appearNote = isRenote ? props.note.renote as Misskey.entities.Note : props.note;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const cleanups = [] as (() => void)[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						function openVersion(info): void {
 | 
				
			||||||
 | 
							os.alert({ type: 'info', title: `Edits from ${info.updatedAt}`, text: info.text });
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const menu: MenuItem[] = [];
 | 
				
			||||||
 | 
						const statePromise = os.api('notes/versions', {
 | 
				
			||||||
 | 
							noteId: appearNote.id,
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						await statePromise.then((versions) => {
 | 
				
			||||||
 | 
							for (const edit of versions) {
 | 
				
			||||||
 | 
								menu.push({
 | 
				
			||||||
 | 
									icon: 'ph-pencil ph-bold ph-lg',
 | 
				
			||||||
 | 
									text: `${edit.updatedAt}`,
 | 
				
			||||||
 | 
									action: () => openVersion(edit),
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						console.log(menu);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const cleanup = () => {
 | 
				
			||||||
 | 
							if (_DEV_) console.log('note menu cleanup', cleanups);
 | 
				
			||||||
 | 
							for (const cl of cleanups) {
 | 
				
			||||||
 | 
								cl();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return {
 | 
				
			||||||
 | 
							menu,
 | 
				
			||||||
 | 
							cleanup,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue