forked from FoundKeyGang/FoundKey
Compare commits
15 commits
f54fa0ad02
...
ce4b552fa7
Author | SHA1 | Date | |
---|---|---|---|
ce4b552fa7 | |||
47f971c8de | |||
f6381e3227 | |||
9859537b02 | |||
aa7171e116 | |||
de81fac334 | |||
6695171b6a | |||
ffdb112867 | |||
a3c7571670 | |||
79acdd7652 | |||
5788c675c7 | |||
5f9ab12596 | |||
2393487bc2 | |||
eb8463b292 | |||
83567995e7 |
34 changed files with 116 additions and 3070 deletions
|
@ -32,9 +32,6 @@ signup: "Sign Up"
|
||||||
save: "Save"
|
save: "Save"
|
||||||
users: "Users"
|
users: "Users"
|
||||||
addUser: "Add a user"
|
addUser: "Add a user"
|
||||||
favorite: "Add to favorites"
|
|
||||||
favorites: "Favorites"
|
|
||||||
unfavorite: "Remove from favorites"
|
|
||||||
pin: "Pin to profile"
|
pin: "Pin to profile"
|
||||||
unpin: "Unpin from profile"
|
unpin: "Unpin from profile"
|
||||||
copyContent: "Copy contents"
|
copyContent: "Copy contents"
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
export class removeFavourites1685126322423 {
|
||||||
|
name = 'removeFavourites1685126322423';
|
||||||
|
|
||||||
|
async up(queryRunner) {
|
||||||
|
await queryRunner.query(`
|
||||||
|
WITH "new_clips" AS (
|
||||||
|
INSERT INTO "clip" ("id", "createdAt", "userId", "name")
|
||||||
|
SELECT
|
||||||
|
RIGHT(GEN_RANDOM_UUID()::text, 10),
|
||||||
|
NOW(),
|
||||||
|
"userId",
|
||||||
|
'⭐'
|
||||||
|
FROM "note_favorite"
|
||||||
|
GROUP BY "userId"
|
||||||
|
RETURNING "id", "userId"
|
||||||
|
)
|
||||||
|
INSERT INTO "clip_note" ("id", "noteId", "clipId")
|
||||||
|
SELECT
|
||||||
|
"note_favorite"."id",
|
||||||
|
"noteId",
|
||||||
|
"new_clips"."id"
|
||||||
|
FROM "note_favorite"
|
||||||
|
JOIN "new_clips" ON "note_favorite"."userId" = "new_clips"."userId"
|
||||||
|
`);
|
||||||
|
await queryRunner.query(`DROP TABLE "note_favorite"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(queryRunner) {
|
||||||
|
// can't revert the migration to clips, can only recreate the database table
|
||||||
|
await queryRunner.query(`CREATE TABLE "note_favorite" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "noteId" character varying(32) NOT NULL, CONSTRAINT "PK_af0da35a60b9fa4463a62082b36" PRIMARY KEY ("id"))`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -67,7 +67,7 @@
|
||||||
"koa-send": "5.0.1",
|
"koa-send": "5.0.1",
|
||||||
"koa-slow": "2.1.0",
|
"koa-slow": "2.1.0",
|
||||||
"koa-views": "7.0.2",
|
"koa-views": "7.0.2",
|
||||||
"mfm-js": "0.22.1",
|
"mfm-js": "0.23.3",
|
||||||
"mime-types": "2.1.35",
|
"mime-types": "2.1.35",
|
||||||
"mocha": "10.2.0",
|
"mocha": "10.2.0",
|
||||||
"multer": "1.4.5-lts.1",
|
"multer": "1.4.5-lts.1",
|
||||||
|
|
|
@ -33,7 +33,6 @@ import { UserGroup } from '@/models/entities/user-group.js';
|
||||||
import { UserGroupJoining } from '@/models/entities/user-group-joining.js';
|
import { UserGroupJoining } from '@/models/entities/user-group-joining.js';
|
||||||
import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js';
|
import { UserGroupInvitation } from '@/models/entities/user-group-invitation.js';
|
||||||
import { Hashtag } from '@/models/entities/hashtag.js';
|
import { Hashtag } from '@/models/entities/hashtag.js';
|
||||||
import { NoteFavorite } from '@/models/entities/note-favorite.js';
|
|
||||||
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
|
import { AbuseUserReport } from '@/models/entities/abuse-user-report.js';
|
||||||
import { RegistrationTicket } from '@/models/entities/registration-tickets.js';
|
import { RegistrationTicket } from '@/models/entities/registration-tickets.js';
|
||||||
import { MessagingMessage } from '@/models/entities/messaging-message.js';
|
import { MessagingMessage } from '@/models/entities/messaging-message.js';
|
||||||
|
@ -134,7 +133,6 @@ export const entities = [
|
||||||
RenoteMuting,
|
RenoteMuting,
|
||||||
Blocking,
|
Blocking,
|
||||||
Note,
|
Note,
|
||||||
NoteFavorite,
|
|
||||||
NoteReaction,
|
NoteReaction,
|
||||||
NoteWatching,
|
NoteWatching,
|
||||||
NoteThreadMuting,
|
NoteThreadMuting,
|
||||||
|
|
|
@ -5,8 +5,6 @@ export const kinds = [
|
||||||
'write:blocks',
|
'write:blocks',
|
||||||
'read:drive',
|
'read:drive',
|
||||||
'write:drive',
|
'write:drive',
|
||||||
'read:favorites',
|
|
||||||
'write:favorites',
|
|
||||||
'read:following',
|
'read:following',
|
||||||
'write:following',
|
'write:following',
|
||||||
'read:messaging',
|
'read:messaging',
|
||||||
|
|
|
@ -23,7 +23,6 @@ import { packedHashtagSchema } from '@/models/schema/hashtag.js';
|
||||||
import { packedPageSchema } from '@/models/schema/page.js';
|
import { packedPageSchema } from '@/models/schema/page.js';
|
||||||
import { packedUserGroupSchema } from '@/models/schema/user-group.js';
|
import { packedUserGroupSchema } from '@/models/schema/user-group.js';
|
||||||
import { packedUserGroupInvitationSchema } from '@/models/schema/user-group-invitation.js';
|
import { packedUserGroupInvitationSchema } from '@/models/schema/user-group-invitation.js';
|
||||||
import { packedNoteFavoriteSchema } from '@/models/schema/note-favorite.js';
|
|
||||||
import { packedChannelSchema } from '@/models/schema/channel.js';
|
import { packedChannelSchema } from '@/models/schema/channel.js';
|
||||||
import { packedAntennaSchema } from '@/models/schema/antenna.js';
|
import { packedAntennaSchema } from '@/models/schema/antenna.js';
|
||||||
import { packedClipSchema } from '@/models/schema/clip.js';
|
import { packedClipSchema } from '@/models/schema/clip.js';
|
||||||
|
@ -47,7 +46,6 @@ export const refs = {
|
||||||
MessagingMessage: packedMessagingMessageSchema,
|
MessagingMessage: packedMessagingMessageSchema,
|
||||||
Note: packedNoteSchema,
|
Note: packedNoteSchema,
|
||||||
NoteReaction: packedNoteReactionSchema,
|
NoteReaction: packedNoteReactionSchema,
|
||||||
NoteFavorite: packedNoteFavoriteSchema,
|
|
||||||
Notification: packedNotificationSchema,
|
Notification: packedNotificationSchema,
|
||||||
DriveFile: packedDriveFileSchema,
|
DriveFile: packedDriveFileSchema,
|
||||||
DriveFolder: packedDriveFolderSchema,
|
DriveFolder: packedDriveFolderSchema,
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
|
||||||
import { id } from '../id.js';
|
|
||||||
import { Note } from './note.js';
|
|
||||||
import { User } from './user.js';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
@Index(['userId', 'noteId'], { unique: true })
|
|
||||||
export class NoteFavorite {
|
|
||||||
@PrimaryColumn(id())
|
|
||||||
public id: string;
|
|
||||||
|
|
||||||
@Column('timestamp with time zone', {
|
|
||||||
comment: 'The created date of the NoteFavorite.',
|
|
||||||
})
|
|
||||||
public createdAt: Date;
|
|
||||||
|
|
||||||
@Index()
|
|
||||||
@Column(id())
|
|
||||||
public userId: User['id'];
|
|
||||||
|
|
||||||
@ManyToOne(() => User, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public user: User | null;
|
|
||||||
|
|
||||||
@Column(id())
|
|
||||||
public noteId: Note['id'];
|
|
||||||
|
|
||||||
@ManyToOne(() => Note, {
|
|
||||||
onDelete: 'CASCADE',
|
|
||||||
})
|
|
||||||
@JoinColumn()
|
|
||||||
public note: Note | null;
|
|
||||||
}
|
|
|
@ -29,7 +29,6 @@ import { RenoteMutingRepository } from './repositories/renote-muting.js';
|
||||||
import { BlockingRepository } from './repositories/blocking.js';
|
import { BlockingRepository } from './repositories/blocking.js';
|
||||||
import { NoteReactionRepository } from './repositories/note-reaction.js';
|
import { NoteReactionRepository } from './repositories/note-reaction.js';
|
||||||
import { NotificationRepository } from './repositories/notification.js';
|
import { NotificationRepository } from './repositories/notification.js';
|
||||||
import { NoteFavoriteRepository } from './repositories/note-favorite.js';
|
|
||||||
import { UserPublickey } from './entities/user-publickey.js';
|
import { UserPublickey } from './entities/user-publickey.js';
|
||||||
import { UserKeypair } from './entities/user-keypair.js';
|
import { UserKeypair } from './entities/user-keypair.js';
|
||||||
import { AppRepository } from './repositories/app.js';
|
import { AppRepository } from './repositories/app.js';
|
||||||
|
@ -64,7 +63,6 @@ export const Announcements = db.getRepository(Announcement);
|
||||||
export const AnnouncementReads = db.getRepository(AnnouncementRead);
|
export const AnnouncementReads = db.getRepository(AnnouncementRead);
|
||||||
export const Apps = (AppRepository);
|
export const Apps = (AppRepository);
|
||||||
export const Notes = (NoteRepository);
|
export const Notes = (NoteRepository);
|
||||||
export const NoteFavorites = (NoteFavoriteRepository);
|
|
||||||
export const NoteWatchings = db.getRepository(NoteWatching);
|
export const NoteWatchings = db.getRepository(NoteWatching);
|
||||||
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
|
export const NoteThreadMutings = db.getRepository(NoteThreadMuting);
|
||||||
export const NoteReactions = (NoteReactionRepository);
|
export const NoteReactions = (NoteReactionRepository);
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
import { db } from '@/db/postgre.js';
|
|
||||||
import { NoteFavorite } from '@/models/entities/note-favorite.js';
|
|
||||||
import { User } from '@/models/entities/user.js';
|
|
||||||
import { Notes } from '../index.js';
|
|
||||||
|
|
||||||
export const NoteFavoriteRepository = db.getRepository(NoteFavorite).extend({
|
|
||||||
async pack(
|
|
||||||
src: NoteFavorite['id'] | NoteFavorite,
|
|
||||||
me?: { id: User['id'] } | null | undefined,
|
|
||||||
) {
|
|
||||||
const favorite = typeof src === 'object' ? src : await this.findOneByOrFail({ id: src });
|
|
||||||
|
|
||||||
return {
|
|
||||||
id: favorite.id,
|
|
||||||
createdAt: favorite.createdAt.toISOString(),
|
|
||||||
noteId: favorite.noteId,
|
|
||||||
// may throw error
|
|
||||||
note: await Notes.pack(favorite.note || favorite.noteId, me),
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
packMany(
|
|
||||||
favorites: any[],
|
|
||||||
me: { id: User['id'] },
|
|
||||||
) {
|
|
||||||
return Promise.allSettled(favorites.map(x => this.pack(x, me)))
|
|
||||||
.then(promises => promises.flatMap(result => result.status === 'fulfilled' ? [result.value] : []));
|
|
||||||
},
|
|
||||||
});
|
|
|
@ -258,7 +258,7 @@ export const UserRepository = db.getRepository(User).extend({
|
||||||
case 'nobody':
|
case 'nobody':
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
|
async pack<ExpectsMe extends boolean | null = null, D extends boolean = false>(
|
||||||
src: User['id'] | User,
|
src: User['id'] | User,
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
export const packedNoteFavoriteSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
id: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
example: 'xxxxxxxxxx',
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'date-time',
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
type: 'object',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
ref: 'Note',
|
|
||||||
},
|
|
||||||
noteId: {
|
|
||||||
type: 'string',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
format: 'id',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
|
@ -162,7 +162,6 @@ import * as ep___i_exportFollowing from './endpoints/i/export-following.js';
|
||||||
import * as ep___i_exportMute from './endpoints/i/export-mute.js';
|
import * as ep___i_exportMute from './endpoints/i/export-mute.js';
|
||||||
import * as ep___i_exportNotes from './endpoints/i/export-notes.js';
|
import * as ep___i_exportNotes from './endpoints/i/export-notes.js';
|
||||||
import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js';
|
import * as ep___i_exportUserLists from './endpoints/i/export-user-lists.js';
|
||||||
import * as ep___i_favorites from './endpoints/i/favorites.js';
|
|
||||||
import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js';
|
import * as ep___i_getWordMutedNotesCount from './endpoints/i/get-word-muted-notes-count.js';
|
||||||
import * as ep___i_importBlocking from './endpoints/i/import-blocking.js';
|
import * as ep___i_importBlocking from './endpoints/i/import-blocking.js';
|
||||||
import * as ep___i_importFollowing from './endpoints/i/import-following.js';
|
import * as ep___i_importFollowing from './endpoints/i/import-following.js';
|
||||||
|
@ -215,8 +214,6 @@ import * as ep___notes_clips from './endpoints/notes/clips.js';
|
||||||
import * as ep___notes_conversation from './endpoints/notes/conversation.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_create from './endpoints/notes/create.js';
|
||||||
import * as ep___notes_delete from './endpoints/notes/delete.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';
|
|
||||||
import * as ep___notes_featured from './endpoints/notes/featured.js';
|
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_globalTimeline from './endpoints/notes/global-timeline.js';
|
||||||
import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
|
import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
|
||||||
|
@ -459,7 +456,6 @@ const eps = [
|
||||||
['i/export-mute', ep___i_exportMute],
|
['i/export-mute', ep___i_exportMute],
|
||||||
['i/export-notes', ep___i_exportNotes],
|
['i/export-notes', ep___i_exportNotes],
|
||||||
['i/export-user-lists', ep___i_exportUserLists],
|
['i/export-user-lists', ep___i_exportUserLists],
|
||||||
['i/favorites', ep___i_favorites],
|
|
||||||
['i/get-word-muted-notes-count', ep___i_getWordMutedNotesCount],
|
['i/get-word-muted-notes-count', ep___i_getWordMutedNotesCount],
|
||||||
['i/import-blocking', ep___i_importBlocking],
|
['i/import-blocking', ep___i_importBlocking],
|
||||||
['i/import-following', ep___i_importFollowing],
|
['i/import-following', ep___i_importFollowing],
|
||||||
|
@ -512,8 +508,6 @@ const eps = [
|
||||||
['notes/conversation', ep___notes_conversation],
|
['notes/conversation', ep___notes_conversation],
|
||||||
['notes/create', ep___notes_create],
|
['notes/create', ep___notes_create],
|
||||||
['notes/delete', ep___notes_delete],
|
['notes/delete', ep___notes_delete],
|
||||||
['notes/favorites/create', ep___notes_favorites_create],
|
|
||||||
['notes/favorites/delete', ep___notes_favorites_delete],
|
|
||||||
['notes/featured', ep___notes_featured],
|
['notes/featured', ep___notes_featured],
|
||||||
['notes/global-timeline', ep___notes_globalTimeline],
|
['notes/global-timeline', ep___notes_globalTimeline],
|
||||||
['notes/hybrid-timeline', ep___notes_hybridTimeline],
|
['notes/hybrid-timeline', ep___notes_hybridTimeline],
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
import { NoteFavorites } from '@/models/index.js';
|
|
||||||
import define from '@/server/api/define.js';
|
|
||||||
import { makePaginationQuery } from '@/server/api/common/make-pagination-query.js';
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ['account', 'notes', 'favorites'],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: 'read:favorites',
|
|
||||||
|
|
||||||
res: {
|
|
||||||
type: 'array',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
items: {
|
|
||||||
type: 'object',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
ref: 'NoteFavorite',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} 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;
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
const query = makePaginationQuery(NoteFavorites.createQueryBuilder('favorite'), ps.sinceId, ps.untilId)
|
|
||||||
.andWhere('favorite.userId = :meId', { meId: user.id })
|
|
||||||
.leftJoinAndSelect('favorite.note', 'note');
|
|
||||||
|
|
||||||
const favorites = await query
|
|
||||||
.take(ps.limit)
|
|
||||||
.getMany();
|
|
||||||
|
|
||||||
return await NoteFavorites.packMany(favorites, user);
|
|
||||||
});
|
|
|
@ -178,7 +178,7 @@ export default define(meta, paramDef, async (ps, _user, token) => {
|
||||||
const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description;
|
const newDescription = profileUpdates.description === undefined ? profile.description : profileUpdates.description;
|
||||||
|
|
||||||
if (newName != null) {
|
if (newName != null) {
|
||||||
const tokens = mfm.parsePlain(newName);
|
const tokens = mfm.parseSimple(newName);
|
||||||
emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!));
|
emojis = emojis.concat(extractCustomEmojisFromMfm(tokens!));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
import { NoteFavorites } from '@/models/index.js';
|
|
||||||
import { genId } from '@/misc/gen-id.js';
|
|
||||||
import define from '@/server/api/define.js';
|
|
||||||
import { ApiError } from '@/server/api/error.js';
|
|
||||||
import { getNote } from '@/server/api/common/getters.js';
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ['notes', 'favorites'],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: 'write:favorites',
|
|
||||||
|
|
||||||
errors: ['NO_SUCH_NOTE', 'ALREADY_FAVORITED'],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
noteId: { type: 'string', format: 'misskey:id' },
|
|
||||||
},
|
|
||||||
required: ['noteId'],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
// Get favoritee
|
|
||||||
const note = await getNote(ps.noteId, user).catch(err => {
|
|
||||||
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError('NO_SUCH_NOTE');
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// if already favorited
|
|
||||||
const exist = await NoteFavorites.countBy({
|
|
||||||
noteId: note.id,
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (exist) throw new ApiError('ALREADY_FAVORITED');
|
|
||||||
|
|
||||||
// Create favorite
|
|
||||||
await NoteFavorites.insert({
|
|
||||||
id: genId(),
|
|
||||||
createdAt: new Date(),
|
|
||||||
noteId: note.id,
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,42 +0,0 @@
|
||||||
import { NoteFavorites } from '@/models/index.js';
|
|
||||||
import define from '@/server/api/define.js';
|
|
||||||
import { ApiError } from '@/server/api/error.js';
|
|
||||||
import { getNote } from '@/server/api/common/getters.js';
|
|
||||||
|
|
||||||
export const meta = {
|
|
||||||
tags: ['notes', 'favorites'],
|
|
||||||
|
|
||||||
requireCredential: true,
|
|
||||||
|
|
||||||
kind: 'write:favorites',
|
|
||||||
|
|
||||||
errors: ['NO_SUCH_NOTE', 'NOT_FAVORITED'],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
export const paramDef = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {
|
|
||||||
noteId: { type: 'string', format: 'misskey:id' },
|
|
||||||
},
|
|
||||||
required: ['noteId'],
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-default-export
|
|
||||||
export default define(meta, paramDef, async (ps, user) => {
|
|
||||||
// Get favoritee
|
|
||||||
const note = await getNote(ps.noteId, user).catch(err => {
|
|
||||||
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError('NO_SUCH_NOTE');
|
|
||||||
throw err;
|
|
||||||
});
|
|
||||||
|
|
||||||
// if already favorited
|
|
||||||
const exist = await NoteFavorites.findOneBy({
|
|
||||||
noteId: note.id,
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (exist == null) throw new ApiError('NOT_FAVORITED');
|
|
||||||
|
|
||||||
// Delete favorite
|
|
||||||
await NoteFavorites.delete(exist.id);
|
|
||||||
});
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { NoteFavorites, NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
import { NoteThreadMutings, NoteWatchings } from '@/models/index.js';
|
||||||
import { ApiError } from '@/server/api/error.js';
|
import { ApiError } from '@/server/api/error.js';
|
||||||
import { getNote } from '@/server/api/common/getters.js';
|
import { getNote } from '@/server/api/common/getters.js';
|
||||||
import define from '@/server/api/define.js';
|
import define from '@/server/api/define.js';
|
||||||
|
@ -12,10 +12,6 @@ export const meta = {
|
||||||
type: 'object',
|
type: 'object',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
properties: {
|
properties: {
|
||||||
isFavorited: {
|
|
||||||
type: 'boolean',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
isWatching: {
|
isWatching: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -51,14 +47,7 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
const [favorite, watching, threadMuting] = await Promise.all([
|
const [watching, threadMuting] = await Promise.all([
|
||||||
NoteFavorites.count({
|
|
||||||
where: {
|
|
||||||
userId: user.id,
|
|
||||||
noteId: note.id,
|
|
||||||
},
|
|
||||||
take: 1,
|
|
||||||
}),
|
|
||||||
NoteWatchings.count({
|
NoteWatchings.count({
|
||||||
where: {
|
where: {
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
|
@ -76,7 +65,6 @@ export default define(meta, paramDef, async (ps, user) => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isFavorited: favorite !== 0,
|
|
||||||
isWatching: watching !== 0,
|
isWatching: watching !== 0,
|
||||||
isMutedThread: threadMuting !== 0,
|
isMutedThread: threadMuting !== 0,
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { DriveFiles, Followings, NoteFavorites, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js';
|
import { DriveFiles, Followings, NoteReactions, Notes, PageLikes, PollVotes, Users } from '@/models/index.js';
|
||||||
import { awaitAll } from '@/prelude/await-all.js';
|
import { awaitAll } from '@/prelude/await-all.js';
|
||||||
import define from '@/server/api/define.js';
|
import define from '@/server/api/define.js';
|
||||||
import { ApiError } from '@/server/api/error.js';
|
import { ApiError } from '@/server/api/error.js';
|
||||||
|
@ -76,10 +76,6 @@ export const meta = {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
},
|
},
|
||||||
noteFavoritesCount: {
|
|
||||||
type: 'integer',
|
|
||||||
optional: false, nullable: false,
|
|
||||||
},
|
|
||||||
pageLikesCount: {
|
pageLikesCount: {
|
||||||
type: 'integer',
|
type: 'integer',
|
||||||
optional: false, nullable: false,
|
optional: false, nullable: false,
|
||||||
|
@ -148,9 +144,6 @@ export default define(meta, paramDef, async (ps, me) => {
|
||||||
.innerJoin('reaction.note', 'note')
|
.innerJoin('reaction.note', 'note')
|
||||||
.where('note.userId = :userId', { userId: user.id })
|
.where('note.userId = :userId', { userId: user.id })
|
||||||
.getCount(),
|
.getCount(),
|
||||||
noteFavoritesCount: NoteFavorites.createQueryBuilder('favorite')
|
|
||||||
.where('favorite.userId = :userId', { userId: user.id })
|
|
||||||
.getCount(),
|
|
||||||
pageLikesCount: PageLikes.createQueryBuilder('like')
|
pageLikesCount: PageLikes.createQueryBuilder('like')
|
||||||
.where('like.userId = :userId', { userId: user.id })
|
.where('like.userId = :userId', { userId: user.id })
|
||||||
.getCount(),
|
.getCount(),
|
||||||
|
|
|
@ -68,10 +68,6 @@ export const errors: Record<string, { message: string, httpStatusCode: number }>
|
||||||
message: 'That note is already added to that clip.',
|
message: 'That note is already added to that clip.',
|
||||||
httpStatusCode: 409,
|
httpStatusCode: 409,
|
||||||
},
|
},
|
||||||
ALREADY_FAVORITED: {
|
|
||||||
message: 'That note is already favorited.',
|
|
||||||
httpStatusCode: 409,
|
|
||||||
},
|
|
||||||
ALREADY_FOLLOWING: {
|
ALREADY_FOLLOWING: {
|
||||||
message: 'You are already following that user.',
|
message: 'You are already following that user.',
|
||||||
httpStatusCode: 409,
|
httpStatusCode: 409,
|
||||||
|
@ -332,10 +328,6 @@ export const errors: Record<string, { message: string, httpStatusCode: number }>
|
||||||
message: 'That note is not added to that clip.',
|
message: 'That note is not added to that clip.',
|
||||||
httpStatusCode: 409,
|
httpStatusCode: 409,
|
||||||
},
|
},
|
||||||
NOT_FAVORITED: {
|
|
||||||
message: 'You have not favorited that note.',
|
|
||||||
httpStatusCode: 409,
|
|
||||||
},
|
|
||||||
NOT_FOLLOWING: {
|
NOT_FOLLOWING: {
|
||||||
message: 'You are not following that user.',
|
message: 'You are not following that user.',
|
||||||
httpStatusCode: 409,
|
httpStatusCode: 409,
|
||||||
|
|
|
@ -28,18 +28,18 @@ export const perUserDriveChart = new PerUserDriveChart();
|
||||||
export const apRequestChart = new ApRequestChart();
|
export const apRequestChart = new ApRequestChart();
|
||||||
|
|
||||||
const charts = [
|
const charts = [
|
||||||
federationChart,
|
// federationChart,
|
||||||
notesChart,
|
// notesChart,
|
||||||
usersChart,
|
usersChart,
|
||||||
activeUsersChart,
|
// activeUsersChart,
|
||||||
instanceChart,
|
// instanceChart,
|
||||||
perUserNotesChart,
|
// perUserNotesChart,
|
||||||
driveChart,
|
// driveChart,
|
||||||
perUserReactionsChart,
|
// perUserReactionsChart,
|
||||||
hashtagChart,
|
// hashtagChart,
|
||||||
perUserFollowingChart,
|
perUserFollowingChart,
|
||||||
perUserDriveChart,
|
// perUserDriveChart,
|
||||||
apRequestChart,
|
// apRequestChart,
|
||||||
];
|
];
|
||||||
|
|
||||||
// Write memory information to DB every 20 minutes
|
// Write memory information to DB every 20 minutes
|
||||||
|
|
|
@ -60,14 +60,14 @@ export async function deleteFileSync(file: DriveFile, isExpired = false): Promis
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
}
|
}
|
||||||
|
|
||||||
postProcess(file, isExpired);
|
await postProcess(file, isExpired);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postProcess(file: DriveFile, isExpired = false): Promise<void> {
|
async function postProcess(file: DriveFile, isExpired = false): Promise<void> {
|
||||||
// Turn into a direct link after expiring a remote file.
|
// Turn into a direct link after expiring a remote file.
|
||||||
if (isExpired && file.userHost != null && file.uri != null) {
|
if (isExpired && file.userHost != null && file.uri != null) {
|
||||||
const id = uuid();
|
const id = uuid();
|
||||||
DriveFiles.update(file.id, {
|
await DriveFiles.update(file.id, {
|
||||||
isLink: true,
|
isLink: true,
|
||||||
url: file.uri,
|
url: file.uri,
|
||||||
thumbnailUrl: null,
|
thumbnailUrl: null,
|
||||||
|
@ -78,14 +78,14 @@ async function postProcess(file: DriveFile, isExpired = false): Promise<void> {
|
||||||
webpublicAccessKey: 'webpublic-' + id,
|
webpublicAccessKey: 'webpublic-' + id,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
DriveFiles.delete(file.id);
|
await DriveFiles.delete(file.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// update statistics
|
// update statistics
|
||||||
driveChart.update(file, false);
|
await driveChart.update(file, false);
|
||||||
perUserDriveChart.update(file, false);
|
await perUserDriveChart.update(file, false);
|
||||||
if (file.userHost != null) {
|
if (file.userHost != null) {
|
||||||
instanceChart.updateDrive(file, false);
|
await instanceChart.updateDrive(file, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
"json5": "2.2.1",
|
"json5": "2.2.1",
|
||||||
"katex": "0.16.0",
|
"katex": "0.16.0",
|
||||||
"matter-js": "0.18.0",
|
"matter-js": "0.18.0",
|
||||||
"mfm-js": "0.22.1",
|
"mfm-js": "0.23.3",
|
||||||
"photoswipe": "5.2.8",
|
"photoswipe": "5.2.8",
|
||||||
"prismjs": "1.28.0",
|
"prismjs": "1.28.0",
|
||||||
"punycode": "2.1.1",
|
"punycode": "2.1.1",
|
||||||
|
|
|
@ -10,7 +10,6 @@ import MkSearch from '@/components/mfm-search.vue';
|
||||||
import MkSparkle from '@/components/sparkle.vue';
|
import MkSparkle from '@/components/sparkle.vue';
|
||||||
import MkA from '@/components/global/a.vue';
|
import MkA from '@/components/global/a.vue';
|
||||||
import { host } from '@/config';
|
import { host } from '@/config';
|
||||||
import { MFM_TAGS } from '@/scripts/mfm-tags';
|
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -37,12 +36,16 @@ export default defineComponent({
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
rootScale: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.text == null || this.text === '') return;
|
if (this.text == null || this.text === '') return;
|
||||||
|
|
||||||
const ast = (this.plain ? mfm.parsePlain : mfm.parse)(this.text, { fnNameList: MFM_TAGS });
|
const ast = (this.plain ? mfm.parseSimple : mfm.parse)(this.text);
|
||||||
|
|
||||||
const validTime = (t: string | true) => {
|
const validTime = (t: string | true) => {
|
||||||
if (typeof t !== 'string') return null;
|
if (typeof t !== 'string') return null;
|
||||||
|
@ -50,7 +53,7 @@ export default defineComponent({
|
||||||
return t.match(/^[0-9.]+s$/) ? t : null;
|
return t.match(/^[0-9.]+s$/) ? t : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const genEl = (ast: mfm.MfmNode[]) => ast.map((token): VNode | VNode[] => {
|
const genEl = (ast: mfm.MfmNode[], scale: Number) => ast.map((token): VNode | VNode[] => {
|
||||||
switch (token.type) {
|
switch (token.type) {
|
||||||
case 'text': {
|
case 'text': {
|
||||||
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||||
|
@ -69,17 +72,17 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'bold': {
|
case 'bold': {
|
||||||
return h('b', genEl(token.children));
|
return h('b', genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'strike': {
|
case 'strike': {
|
||||||
return h('del', genEl(token.children));
|
return h('del', genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'italic': {
|
case 'italic': {
|
||||||
return h('i', {
|
return h('i', {
|
||||||
style: 'font-style: oblique;',
|
style: 'font-style: oblique;',
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'fn': {
|
case 'fn': {
|
||||||
|
@ -139,18 +142,18 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
case 'x2': {
|
case 'x2': {
|
||||||
return h('span', {
|
return h('span', {
|
||||||
class: 'mfm-x2',
|
class: 'mfm-x2'
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale * 2));
|
||||||
}
|
}
|
||||||
case 'x3': {
|
case 'x3': {
|
||||||
return h('span', {
|
return h('span', {
|
||||||
class: 'mfm-x3',
|
class: 'mfm-x3'
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale * 3));
|
||||||
}
|
}
|
||||||
case 'x4': {
|
case 'x4': {
|
||||||
return h('span', {
|
return h('span', {
|
||||||
class: 'mfm-x4',
|
class: 'mfm-x4'
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale * 4));
|
||||||
}
|
}
|
||||||
case 'font': {
|
case 'font': {
|
||||||
const family =
|
const family =
|
||||||
|
@ -167,7 +170,7 @@ export default defineComponent({
|
||||||
case 'blur': {
|
case 'blur': {
|
||||||
return h('span', {
|
return h('span', {
|
||||||
class: '_mfm_blur_',
|
class: '_mfm_blur_',
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
case 'rainbow': {
|
case 'rainbow': {
|
||||||
const speed = validTime(token.props.args.speed) || '1s';
|
const speed = validTime(token.props.args.speed) || '1s';
|
||||||
|
@ -176,35 +179,60 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
case 'sparkle': {
|
case 'sparkle': {
|
||||||
if (!this.$store.state.animatedMfm) {
|
if (!this.$store.state.animatedMfm) {
|
||||||
return genEl(token.children);
|
return genEl(token.children, scale);
|
||||||
}
|
}
|
||||||
return h(MkSparkle, {}, genEl(token.children));
|
return h(MkSparkle, {}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
case 'rotate': {
|
case 'rotate': {
|
||||||
const degrees = (typeof token.props.args.deg === 'string' ? parseInt(token.props.args.deg) : null) || '90';
|
const degrees = (typeof token.props.args.deg === 'string' ? parseInt(token.props.args.deg) : null) || '90';
|
||||||
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
style = `transform: rotate(${degrees}deg); transform-origin: center center;`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case 'position': {
|
||||||
|
const x = parseFloat(token.props.args.x ?? '0');
|
||||||
|
const y = parseFloat(token.props.args.y ?? '0');
|
||||||
|
style = `transform: translateX(${x}em) translateY(${y}em);`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'scale': {
|
||||||
|
const x = Math.min(parseFloat(token.props.args.x ?? '1'), 5);
|
||||||
|
const y = Math.min(parseFloat(token.props.args.y ?? '1'), 5);
|
||||||
|
style = `transform: scale(${x}, ${y});`;
|
||||||
|
scale = scale * Math.max(x, y);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'fg': {
|
||||||
|
let color = token.props.args.color;
|
||||||
|
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||||
|
style = `color: #${color};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'bg': {
|
||||||
|
let color = token.props.args.color;
|
||||||
|
if (!/^[0-9a-f]{3,6}$/i.test(color)) color = 'f00';
|
||||||
|
style = `background-color: #${color};`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (style == null) {
|
if (style == null) {
|
||||||
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children), ']']);
|
return h('span', {}, ['$[', token.props.name, ' ', ...genEl(token.children, scale), ']']);
|
||||||
} else {
|
} else {
|
||||||
return h('span', {
|
return h('span', {
|
||||||
style: 'display: inline-block;' + style,
|
style: 'display: inline-block;' + style,
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'small': {
|
case 'small': {
|
||||||
return h('small', {
|
return h('small', {
|
||||||
class: '_mfm_small_'
|
class: '_mfm_small_'
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'center': {
|
case 'center': {
|
||||||
return h('div', {
|
return h('div', {
|
||||||
style: 'text-align:center;',
|
style: 'text-align:center;',
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'url': {
|
case 'url': {
|
||||||
|
@ -220,7 +248,7 @@ export default defineComponent({
|
||||||
key: Math.random(),
|
key: Math.random(),
|
||||||
url: token.props.url,
|
url: token.props.url,
|
||||||
rel: 'nofollow noopener',
|
rel: 'nofollow noopener',
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'mention': {
|
case 'mention': {
|
||||||
|
@ -258,7 +286,7 @@ export default defineComponent({
|
||||||
case 'quote': {
|
case 'quote': {
|
||||||
return h(this.nowrap ? 'span' : 'div', {
|
return h(this.nowrap ? 'span' : 'div', {
|
||||||
class: 'quote',
|
class: 'quote',
|
||||||
}, genEl(token.children));
|
}, genEl(token.children, scale));
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'emojiCode': {
|
case 'emojiCode': {
|
||||||
|
@ -311,6 +339,6 @@ export default defineComponent({
|
||||||
}).flat();
|
}).flat();
|
||||||
|
|
||||||
// Parse ast to DOM
|
// Parse ast to DOM
|
||||||
return h('span', genEl(ast));
|
return h('span', genEl(ast, this.rootScale ?? 1));
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -120,12 +120,6 @@ export const menuDef = reactive({
|
||||||
indicated: computed(() => $i != null && $i.hasUnreadSpecifiedNotes),
|
indicated: computed(() => $i != null && $i.hasUnreadSpecifiedNotes),
|
||||||
to: '/my/notifications#directNotes',
|
to: '/my/notifications#directNotes',
|
||||||
},
|
},
|
||||||
favorites: {
|
|
||||||
title: 'favorites',
|
|
||||||
icon: 'fas fa-star',
|
|
||||||
show: computed(() => $i != null),
|
|
||||||
to: '/my/favorites',
|
|
||||||
},
|
|
||||||
pages: {
|
pages: {
|
||||||
title: 'pages',
|
title: 'pages',
|
||||||
icon: 'fas fa-file-alt',
|
icon: 'fas fa-file-alt',
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
<template>
|
|
||||||
<MkStickyContainer>
|
|
||||||
<template #header><MkPageHeader/></template>
|
|
||||||
<MkSpacer :content-max="800">
|
|
||||||
<MkPagination ref="pagingComponent" :pagination="pagination">
|
|
||||||
<template #empty>
|
|
||||||
<div class="_fullinfo">
|
|
||||||
<img :src="instance.images.info" class="_ghost"/>
|
|
||||||
<div>{{ i18n.ts.noNotes }}</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template #default="{ items }">
|
|
||||||
<XList v-slot="{ item }" :items="items" :direction="'down'" :no-gap="false">
|
|
||||||
<XNote :key="item.id" :note="item.note" :class="$style.note"/>
|
|
||||||
</XList>
|
|
||||||
</template>
|
|
||||||
</MkPagination>
|
|
||||||
</MkSpacer>
|
|
||||||
</MkStickyContainer>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { ref } from 'vue';
|
|
||||||
import MkPagination from '@/components/ui/pagination.vue';
|
|
||||||
import XNote from '@/components/note.vue';
|
|
||||||
import XList from '@/components/date-separated-list.vue';
|
|
||||||
import { i18n } from '@/i18n';
|
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
|
||||||
import { instance } from '@/instance';
|
|
||||||
|
|
||||||
const pagination = {
|
|
||||||
endpoint: 'i/favorites' as const,
|
|
||||||
limit: 10,
|
|
||||||
};
|
|
||||||
|
|
||||||
const pagingComponent = ref<InstanceType<typeof MkPagination>>();
|
|
||||||
|
|
||||||
definePageMetadata({
|
|
||||||
title: i18n.ts.favorites,
|
|
||||||
icon: 'fas fa-star',
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
|
||||||
.note {
|
|
||||||
background: var(--panel);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -148,10 +148,6 @@ export const routes = [{
|
||||||
component: page(() => import('./pages/notifications.vue')),
|
component: page(() => import('./pages/notifications.vue')),
|
||||||
hash: 'initialTab',
|
hash: 'initialTab',
|
||||||
loginRequired: true,
|
loginRequired: true,
|
||||||
}, {
|
|
||||||
path: '/my/favorites',
|
|
||||||
component: page(() => import('./pages/favorites.vue')),
|
|
||||||
loginRequired: true,
|
|
||||||
}, {
|
}, {
|
||||||
name: 'messaging',
|
name: 'messaging',
|
||||||
path: '/my/messaging',
|
path: '/my/messaging',
|
||||||
|
|
|
@ -48,12 +48,6 @@ export function getNoteMenu(props: {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleFavorite(favorite: boolean): void {
|
|
||||||
os.apiWithDialog(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
|
|
||||||
noteId: appearNote.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleWatch(watch: boolean): void {
|
function toggleWatch(watch: boolean): void {
|
||||||
os.apiWithDialog(watch ? 'notes/watching/create' : 'notes/watching/delete', {
|
os.apiWithDialog(watch ? 'notes/watching/create' : 'notes/watching/delete', {
|
||||||
noteId: appearNote.id,
|
noteId: appearNote.id,
|
||||||
|
@ -244,15 +238,6 @@ export function getNoteMenu(props: {
|
||||||
action: translate,
|
action: translate,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
null,
|
null,
|
||||||
statePromise.then(state => state.isFavorited ? {
|
|
||||||
icon: 'fas fa-star',
|
|
||||||
text: i18n.ts.unfavorite,
|
|
||||||
action: () => toggleFavorite(false),
|
|
||||||
} : {
|
|
||||||
icon: 'fas fa-star',
|
|
||||||
text: i18n.ts.favorite,
|
|
||||||
action: () => toggleFavorite(true),
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
icon: 'fas fa-paperclip',
|
icon: 'fas fa-paperclip',
|
||||||
text: i18n.ts.clip,
|
text: i18n.ts.clip,
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle', 'rotate'];
|
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'position', 'scale', 'fg', 'bg'];
|
||||||
|
|
|
@ -59,7 +59,6 @@ export const defaultStore = markRaw(new Storage('base', {
|
||||||
where: 'deviceAccount',
|
where: 'deviceAccount',
|
||||||
default: [
|
default: [
|
||||||
'notifications',
|
'notifications',
|
||||||
'favorites',
|
|
||||||
'drive',
|
'drive',
|
||||||
'followRequests',
|
'followRequests',
|
||||||
'-',
|
'-',
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, InstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, Instance,
|
Announcement, Antenna, App, AuthSession, Blocking, Channel, Clip, DateString, InstanceMetadata, DriveFile, DriveFolder, Following, FollowingFolloweePopulated, FollowingFollowerPopulated, FollowRequest, Instance,
|
||||||
MeDetailed,
|
MeDetailed,
|
||||||
Note, NoteFavorite, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage,
|
Note, OriginType, Page, ServerInfo, Stats, User, UserDetailed, UserGroup, UserList, UserSorting, Notification, NoteReaction, Signin, MessagingMessage,
|
||||||
} from './entities.js';
|
} from './entities.js';
|
||||||
|
|
||||||
type TODO = Record<string, any> | null;
|
type TODO = Record<string, any> | null;
|
||||||
|
@ -304,7 +304,6 @@ export type Endpoints = {
|
||||||
'i/export-mute': { req: TODO; res: TODO; };
|
'i/export-mute': { req: TODO; res: TODO; };
|
||||||
'i/export-notes': { req: TODO; res: TODO; };
|
'i/export-notes': { req: TODO; res: TODO; };
|
||||||
'i/export-user-lists': { req: TODO; res: TODO; };
|
'i/export-user-lists': { req: TODO; res: TODO; };
|
||||||
'i/favorites': { req: { limit?: number; sinceId?: NoteFavorite['id']; untilId?: NoteFavorite['id']; }; res: NoteFavorite[]; };
|
|
||||||
'i/get-word-muted-notes-count': { req: TODO; res: TODO; };
|
'i/get-word-muted-notes-count': { req: TODO; res: TODO; };
|
||||||
'i/import-blocking': { req: TODO; res: TODO; };
|
'i/import-blocking': { req: TODO; res: TODO; };
|
||||||
'i/import-following': { req: TODO; res: TODO; };
|
'i/import-following': { req: TODO; res: TODO; };
|
||||||
|
@ -411,8 +410,6 @@ export type Endpoints = {
|
||||||
};
|
};
|
||||||
}; res: { createdNote: Note }; };
|
}; res: { createdNote: Note }; };
|
||||||
'notes/delete': { req: { noteId: Note['id']; }; res: null; };
|
'notes/delete': { req: { noteId: Note['id']; }; res: null; };
|
||||||
'notes/favorites/create': { req: { noteId: Note['id']; }; res: null; };
|
|
||||||
'notes/favorites/delete': { req: { noteId: Note['id']; }; res: null; };
|
|
||||||
'notes/featured': { req: TODO; res: Note[]; };
|
'notes/featured': { req: TODO; res: Note[]; };
|
||||||
'notes/global-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
|
'notes/global-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
|
||||||
'notes/hybrid-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
|
'notes/hybrid-timeline': { req: { limit?: number; sinceId?: Note['id']; untilId?: Note['id']; sinceDate?: number; untilDate?: number; }; res: Note[]; };
|
||||||
|
|
|
@ -13,8 +13,6 @@ export const permissions = [
|
||||||
'write:blocks',
|
'write:blocks',
|
||||||
'read:drive',
|
'read:drive',
|
||||||
'write:drive',
|
'write:drive',
|
||||||
'read:favorites',
|
|
||||||
'write:favorites',
|
|
||||||
'read:following',
|
'read:following',
|
||||||
'write:following',
|
'write:following',
|
||||||
'read:messaging',
|
'read:messaging',
|
||||||
|
|
|
@ -385,13 +385,6 @@ export type AuthSession = {
|
||||||
|
|
||||||
export type Clip = TODO;
|
export type Clip = TODO;
|
||||||
|
|
||||||
export type NoteFavorite = {
|
|
||||||
id: ID;
|
|
||||||
createdAt: DateString;
|
|
||||||
noteId: Note['id'];
|
|
||||||
note: Note;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type FollowRequest = {
|
export type FollowRequest = {
|
||||||
id: ID;
|
id: ID;
|
||||||
follower: User;
|
follower: User;
|
||||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -3750,7 +3750,7 @@ __metadata:
|
||||||
koa-send: 5.0.1
|
koa-send: 5.0.1
|
||||||
koa-slow: 2.1.0
|
koa-slow: 2.1.0
|
||||||
koa-views: 7.0.2
|
koa-views: 7.0.2
|
||||||
mfm-js: 0.22.1
|
mfm-js: 0.23.3
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
mocha: 10.2.0
|
mocha: 10.2.0
|
||||||
multer: 1.4.5-lts.1
|
multer: 1.4.5-lts.1
|
||||||
|
@ -4720,7 +4720,7 @@ __metadata:
|
||||||
json5: 2.2.1
|
json5: 2.2.1
|
||||||
katex: 0.16.0
|
katex: 0.16.0
|
||||||
matter-js: 0.18.0
|
matter-js: 0.18.0
|
||||||
mfm-js: 0.22.1
|
mfm-js: 0.23.3
|
||||||
photoswipe: 5.2.8
|
photoswipe: 5.2.8
|
||||||
prismjs: 1.28.0
|
prismjs: 1.28.0
|
||||||
punycode: 2.1.1
|
punycode: 2.1.1
|
||||||
|
@ -11671,12 +11671,12 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"mfm-js@npm:0.22.1":
|
"mfm-js@npm:0.23.3":
|
||||||
version: 0.22.1
|
version: 0.23.3
|
||||||
resolution: "mfm-js@npm:0.22.1"
|
resolution: "mfm-js@npm:0.23.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
twemoji-parser: 14.0.x
|
twemoji-parser: 14.0.0
|
||||||
checksum: 6d9756c7bd8abf6462fb6403de4656f607a83839eb6b66a05b10eddcd201b5f78f5fe3d0df029936546143fd9cbf112e8369287aed32026e50bb03ce89b4c4f8
|
checksum: 7079f80a53a9afc8599333f3256fb18a6bf7c01102a2f8f2be657843726a34835e2af34e26bc5b27e45b217fb2f120c0d3006e9fab2a972c845e9f7361e3cc1b
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
@ -16723,7 +16723,7 @@ __metadata:
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"twemoji-parser@npm:14.0.0, twemoji-parser@npm:14.0.x":
|
"twemoji-parser@npm:14.0.0":
|
||||||
version: 14.0.0
|
version: 14.0.0
|
||||||
resolution: "twemoji-parser@npm:14.0.0"
|
resolution: "twemoji-parser@npm:14.0.0"
|
||||||
checksum: 8eede69cf71f94735de7b6fddf5dfbfe3cb2e01baefc3201360984ccc97cfc659f206c8f73bd1405a2282779af3b79a8c9bed3864c672e15e2dc6f8ce4810452
|
checksum: 8eede69cf71f94735de7b6fddf5dfbfe3cb2e01baefc3201360984ccc97cfc659f206c8f73bd1405a2282779af3b79a8c9bed3864c672e15e2dc6f8ce4810452
|
||||||
|
|
Loading…
Reference in a new issue