mirror of
https://activitypub.software/TransFem-org/Sharkey.git
synced 2025-04-13 09:44:40 +00:00
support reactions in mastodon API
This commit is contained in:
parent
fbdee815da
commit
3c54680860
8 changed files with 82 additions and 54 deletions
|
@ -180,10 +180,10 @@ export class MastoConverters {
|
|||
note: profile?.description ?? '',
|
||||
url: user.uri ?? acctUrl,
|
||||
uri: user.uri ?? acctUri,
|
||||
avatar: user.avatarUrl ? user.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
avatar_static: user.avatarUrl ? user.avatarUrl : 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
header: user.bannerUrl ? user.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
header_static: user.bannerUrl ? user.bannerUrl : 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
avatar: user.avatarUrl ?? 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
avatar_static: user.avatarUrl ?? 'https://dev.joinsharkey.org/static-assets/avatar.png',
|
||||
header: user.bannerUrl ?? 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
header_static: user.bannerUrl ?? 'https://dev.joinsharkey.org/static-assets/transparent.png',
|
||||
emojis: emoji,
|
||||
moved: null, //FIXME
|
||||
fields: profile?.fields.map(p => this.encodeField(p)) ?? [],
|
||||
|
@ -196,7 +196,7 @@ export class MastoConverters {
|
|||
});
|
||||
}
|
||||
|
||||
public async getEdits(id: string, me?: MiLocalUser | null): Promise<StatusEdit[]> {
|
||||
public async getEdits(id: string, me: MiLocalUser | null): Promise<StatusEdit[]> {
|
||||
const note = await this.mastodonDataService.getNote(id, me);
|
||||
if (!note) {
|
||||
return [];
|
||||
|
@ -213,7 +213,7 @@ export class MastoConverters {
|
|||
account: noteUser,
|
||||
content: this.mfmService.toMastoApiHtml(mfm.parse(edit.newText ?? ''), JSON.parse(note.mentionedRemoteUsers)) ?? '',
|
||||
created_at: lastDate.toISOString(),
|
||||
emojis: [],
|
||||
emojis: [], //FIXME
|
||||
sensitive: edit.cw != null && edit.cw.length > 0,
|
||||
spoiler_text: edit.cw ?? '',
|
||||
media_attachments: files.length > 0 ? files.map((f) => this.encodeFile(f)) : [],
|
||||
|
@ -222,15 +222,15 @@ export class MastoConverters {
|
|||
history.push(item);
|
||||
}
|
||||
|
||||
return await Promise.all(history);
|
||||
return history;
|
||||
}
|
||||
|
||||
private async convertReblog(status: Entity.Status | null, me?: MiLocalUser | null): Promise<MastodonEntity.Status | null> {
|
||||
private async convertReblog(status: Entity.Status | null, me: MiLocalUser | null): Promise<MastodonEntity.Status | null> {
|
||||
if (!status) return null;
|
||||
return await this.convertStatus(status, me);
|
||||
}
|
||||
|
||||
public async convertStatus(status: Entity.Status, me?: MiLocalUser | null): Promise<MastodonEntity.Status> {
|
||||
public async convertStatus(status: Entity.Status, me: MiLocalUser | null): Promise<MastodonEntity.Status> {
|
||||
const convertedAccount = this.convertAccount(status.account);
|
||||
const note = await this.mastodonDataService.requireNote(status.id, me);
|
||||
const noteUser = await this.getUser(status.account.id);
|
||||
|
@ -279,7 +279,6 @@ export class MastoConverters {
|
|||
: '';
|
||||
|
||||
const reblogged = await this.mastodonDataService.hasReblog(note.id, me);
|
||||
const reactions = await Promise.all(status.emoji_reactions.map(r => this.convertReaction(r)));
|
||||
|
||||
// noinspection ES6MissingAwait
|
||||
return await awaitAll({
|
||||
|
@ -289,11 +288,12 @@ export class MastoConverters {
|
|||
account: convertedAccount,
|
||||
in_reply_to_id: note.replyId,
|
||||
in_reply_to_account_id: note.replyUserId,
|
||||
reblog: !isQuote ? await this.convertReblog(status.reblog, me) : null,
|
||||
reblog: !isQuote ? this.convertReblog(status.reblog, me) : null,
|
||||
content: content,
|
||||
content_type: 'text/x.misskeymarkdown',
|
||||
text: note.text,
|
||||
created_at: status.created_at,
|
||||
edited_at: note.updatedAt?.toISOString() ?? null,
|
||||
emojis: emoji,
|
||||
replies_count: note.repliesCount,
|
||||
reblogs_count: note.renoteCount,
|
||||
|
@ -301,7 +301,7 @@ export class MastoConverters {
|
|||
reblogged,
|
||||
favourited: status.favourited,
|
||||
muted: status.muted,
|
||||
sensitive: status.sensitive,
|
||||
sensitive: status.sensitive || !!note.cw,
|
||||
spoiler_text: note.cw ?? '',
|
||||
visibility: status.visibility,
|
||||
media_attachments: status.media_attachments.map(a => convertAttachment(a)),
|
||||
|
@ -312,15 +312,14 @@ export class MastoConverters {
|
|||
application: null, //FIXME
|
||||
language: null, //FIXME
|
||||
pinned: false, //FIXME
|
||||
reactions,
|
||||
emoji_reactions: reactions,
|
||||
bookmarked: false, //FIXME
|
||||
quote: isQuote ? await this.convertReblog(status.reblog, me) : null,
|
||||
edited_at: note.updatedAt?.toISOString() ?? null,
|
||||
quote_id: isQuote ? status.reblog?.id : undefined,
|
||||
quote: isQuote ? this.convertReblog(status.reblog, me) : null,
|
||||
reactions: status.emoji_reactions,
|
||||
});
|
||||
}
|
||||
|
||||
public async convertConversation(conversation: Entity.Conversation, me?: MiLocalUser | null): Promise<MastodonEntity.Conversation> {
|
||||
public async convertConversation(conversation: Entity.Conversation, me: MiLocalUser | null): Promise<MastodonEntity.Conversation> {
|
||||
return {
|
||||
id: conversation.id,
|
||||
accounts: await Promise.all(conversation.accounts.map(a => this.convertAccount(a))),
|
||||
|
@ -329,7 +328,7 @@ export class MastoConverters {
|
|||
};
|
||||
}
|
||||
|
||||
public async convertNotification(notification: Entity.Notification, me?: MiLocalUser | null): Promise<MastodonEntity.Notification> {
|
||||
public async convertNotification(notification: Entity.Notification, me: MiLocalUser | null): Promise<MastodonEntity.Notification> {
|
||||
return {
|
||||
account: await this.convertAccount(notification.account),
|
||||
created_at: notification.created_at,
|
||||
|
@ -339,12 +338,23 @@ export class MastoConverters {
|
|||
};
|
||||
}
|
||||
|
||||
public async convertReaction(reaction: Entity.Reaction): Promise<Entity.Reaction> {
|
||||
if (reaction.accounts) {
|
||||
reaction.accounts = await Promise.all(reaction.accounts.map(a => this.convertAccount(a)));
|
||||
}
|
||||
return reaction;
|
||||
}
|
||||
// public convertEmoji(emoji: string): MastodonEntity.Emoji {
|
||||
// const reaction: MastodonEntity.Reaction = {
|
||||
// name: emoji,
|
||||
// count: 1,
|
||||
// };
|
||||
//
|
||||
// if (emoji.startsWith(':')) {
|
||||
// const [, name] = emoji.match(/^:([^@:]+(?:@[^@:]+)?):$/) ?? [];
|
||||
// if (name) {
|
||||
// const url = `${this.config.url}/emoji/${name}.webp`;
|
||||
// reaction.url = url;
|
||||
// reaction.static_url = url;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return reaction;
|
||||
// }
|
||||
}
|
||||
|
||||
function simpleConvert<T>(data: T): T {
|
||||
|
@ -423,7 +433,3 @@ export function convertRelationship(relationship: Partial<Entity.Relationship> &
|
|||
};
|
||||
}
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
export function convertStatusSource(status: Entity.StatusSource): MastodonEntity.StatusSource {
|
||||
return simpleConvert(status);
|
||||
}
|
||||
|
|
|
@ -29,13 +29,17 @@ export class ApiNotificationsMastodon {
|
|||
fastify.get<ApiNotifyMastodonRoute>('/v1/notifications', async (request, reply) => {
|
||||
const { client, me } = await this.clientService.getAuthClient(request);
|
||||
const data = await client.getNotifications(parseTimelineArgs(request.query));
|
||||
const response = await Promise.all(data.data.map(async n => {
|
||||
const converted = await this.mastoConverters.convertNotification(n, me);
|
||||
if (converted.type === 'reaction') {
|
||||
converted.type = 'favourite';
|
||||
const notifications = await Promise.all(data.data.map(n => this.mastoConverters.convertNotification(n, me)));
|
||||
const response: MastodonEntity.Notification[] = [];
|
||||
for (const notification of notifications) {
|
||||
response.push(notification);
|
||||
if (notification.type === 'reaction') {
|
||||
response.push({
|
||||
...notification,
|
||||
type: 'favourite',
|
||||
});
|
||||
}
|
||||
return converted;
|
||||
}));
|
||||
}
|
||||
|
||||
attachMinMaxPagination(request, reply, response);
|
||||
reply.send(response);
|
||||
|
@ -46,12 +50,9 @@ export class ApiNotificationsMastodon {
|
|||
|
||||
const { client, me } = await this.clientService.getAuthClient(_request);
|
||||
const data = await client.getNotification(_request.params.id);
|
||||
const converted = await this.mastoConverters.convertNotification(data.data, me);
|
||||
if (converted.type === 'reaction') {
|
||||
converted.type = 'favourite';
|
||||
}
|
||||
const response = await this.mastoConverters.convertNotification(data.data, me);
|
||||
|
||||
reply.send(converted);
|
||||
reply.send(response);
|
||||
});
|
||||
|
||||
fastify.post<ApiNotifyMastodonRoute & { Params: { id?: string } }>('/v1/notification/:id/dismiss', { preHandler: upload.single('none') }, async (_request, reply) => {
|
||||
|
|
|
@ -6,5 +6,7 @@ namespace Entity {
|
|||
me: boolean
|
||||
name: string
|
||||
accounts?: Array<Account>
|
||||
url?: string
|
||||
static_url?: string
|
||||
}
|
||||
}
|
||||
|
|
16
packages/megalodon/src/mastodon/entities/reaction.ts
Normal file
16
packages/megalodon/src/mastodon/entities/reaction.ts
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: hazelnoot and other Sharkey contributors
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
/// <reference path="account.ts" />
|
||||
|
||||
namespace MastodonEntity {
|
||||
export type Reaction = {
|
||||
name: string
|
||||
count: number
|
||||
me?: boolean
|
||||
url?: string
|
||||
static_url?: string
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
/// <reference path="emoji.ts" />
|
||||
/// <reference path="card.ts" />
|
||||
/// <reference path="poll.ts" />
|
||||
/// <reference path="reaction.ts" />
|
||||
|
||||
namespace MastodonEntity {
|
||||
export type Status = {
|
||||
|
@ -41,6 +42,8 @@ namespace MastodonEntity {
|
|||
// These parameters are unique parameters in fedibird.com for quote.
|
||||
quote_id?: string
|
||||
quote?: Status | null
|
||||
// These parameters are unique to glitch-soc for emoji reactions.
|
||||
reactions?: Reaction[]
|
||||
}
|
||||
|
||||
export type StatusTag = {
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
/// <reference path="./entities/poll_option.ts" />
|
||||
/// <reference path="./entities/preferences.ts" />
|
||||
/// <reference path="./entities/push_subscription.ts" />
|
||||
/// <reference path="./entities/reaction.ts" />
|
||||
/// <reference path="./entities/relationship.ts" />
|
||||
/// <reference path="./entities/report.ts" />
|
||||
/// <reference path="./entities/results.ts" />
|
||||
|
|
|
@ -2555,6 +2555,7 @@ export default class Misskey implements MegalodonInterface {
|
|||
}))
|
||||
}
|
||||
|
||||
// TODO implement
|
||||
public async getEmojiReaction(_id: string, _emoji: string): Promise<Response<Entity.Reaction>> {
|
||||
return new Promise((_, reject) => {
|
||||
const err = new NoImplementedError('misskey does not support')
|
||||
|
|
|
@ -286,6 +286,7 @@ namespace MisskeyAPI {
|
|||
plain_content: n.text ? n.text : null,
|
||||
created_at: n.createdAt,
|
||||
edited_at: n.updatedAt || null,
|
||||
// TODO this is probably wrong
|
||||
emojis: mapEmojis(n.emojis).concat(mapReactionEmojis(n.reactionEmojis)),
|
||||
replies_count: n.repliesCount,
|
||||
reblogs_count: n.renoteCount,
|
||||
|
@ -304,7 +305,7 @@ namespace MisskeyAPI {
|
|||
application: null,
|
||||
language: null,
|
||||
pinned: null,
|
||||
emoji_reactions: typeof n.reactions === 'object' ? mapReactions(n.reactions, n.myReaction) : [],
|
||||
emoji_reactions: typeof n.reactions === 'object' ? mapReactions(n.reactions, n.reactionEmojis, n.myReaction) : [],
|
||||
bookmarked: false,
|
||||
quote: n.renote && n.text ? note(n.renote, n.user.host ? n.user.host : host ? host : null) : null
|
||||
}
|
||||
|
@ -334,23 +335,20 @@ namespace MisskeyAPI {
|
|||
) : 0;
|
||||
};
|
||||
|
||||
export const mapReactions = (r: { [key: string]: number }, myReaction?: string): Array<MegalodonEntity.Reaction> => {
|
||||
export const mapReactions = (r: { [key: string]: number }, e: Record<string, string | undefined>, myReaction?: string): Array<MegalodonEntity.Reaction> => {
|
||||
return Object.keys(r).map(key => {
|
||||
if (myReaction && key === myReaction) {
|
||||
return {
|
||||
count: r[key],
|
||||
me: true,
|
||||
name: key
|
||||
}
|
||||
}
|
||||
return {
|
||||
count: r[key],
|
||||
me: false,
|
||||
name: key
|
||||
}
|
||||
const me = myReaction != null && key === myReaction;
|
||||
return {
|
||||
count: r[key],
|
||||
me,
|
||||
name: key,
|
||||
url: e[key],
|
||||
static_url: e[key],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TODO implement other properties
|
||||
const mapReactionEmojis = (r: { [key: string]: string }): Array<MegalodonEntity.Emoji> => {
|
||||
return Object.keys(r).map(key => ({
|
||||
shortcode: key,
|
||||
|
@ -371,7 +369,7 @@ namespace MisskeyAPI {
|
|||
result.push({
|
||||
count: 1,
|
||||
me: false,
|
||||
name: e.type
|
||||
name: e.type,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Reference in a new issue