Compare commits

...

9 Commits

Author SHA1 Message Date
Puniko 16902a26a3 resolve merge conflicts 2023-02-12 08:58:17 +01:00
Johann150 1ffa4b08e0
client: reformat notification component
who the hell is supposed to read this
2023-02-11 19:25:51 +01:00
Johann150 c9d395961e
server: refactor packing User 2023-02-11 19:17:11 +01:00
Johann150 3a7e8cfe50
server: check instance description length limit
Changelog: Fixed
2023-02-11 19:16:28 +01:00
Johann150 b8796cb1fa
activitypub: remove _misskey_votes property
This is a duplication of `replies.totalItems` and seems unnecessary,
it is even only parsed by Misskey if the afforementioned property is
not available.

Changelog: Removed
2023-02-11 17:49:12 +01:00
Johann150 68bc2e314b
activitypub: remove _misskey_reaction property
This property is duplicated by the `content` property so seems unnecessary.

Changelog: Removed
2023-02-11 17:43:44 +01:00
Johann150 fff93c6965
activitypub: remove _misskey_content attribute
As already noted back in https://github.com/misskey-dev/misskey/pull/8787
the intention was to replace the `_misskey_content` attribute with the
ActivityPub-defined `source` property. Misskey and by extension Foundkey
have shipped with the `source` property and the respective parsing for
quite a while so it seems reasonable to remove it now.

Changelog: Removed
2023-02-11 17:25:24 +01:00
Johann150 7c89e99243
fix registry migration
It can happen that registry items were created at exactly the same time for some reason.
2023-02-11 12:52:28 +01:00
Johann150 6ed13ea9a7
fix typo, the 2nd 2023-02-11 10:23:15 +01:00
13 changed files with 44 additions and 37 deletions

View File

@ -5,8 +5,8 @@ export class registryRemoveDomain1675375940759 {
await queryRunner.query(`DROP INDEX "public"."IDX_0a72bdfcdb97c0eca11fe7ecad"`);
await queryRunner.query(`ALTER TABLE "registry_item" DROP COLUMN "domain"`);
await queryRunner.query(`ALTER TABLE "registry_item" ALTER COLUMN "key" TYPE text USING "key"::text`);
// delete existing duplicated entries, keeping the latest updated one
await queryRunner.query(`DELETE FROM "registry_item" AS "a" WHERE "updatedAt" != (SELECT MAX("updatedAt") FROM "registry_item" AS "b" WHERE "a"."userId" = "b"."userId" AND "a"."key" = "b"."key" AND "a"."scope" = "b"."scope" GROUP BY "userId", "key", "scope")`);
// delete existing duplicated entries, keeping the latest created one
await queryRunner.query(`DELETE FROM "registry_item" AS "a" WHERE "id" != (SELECT MAX("id") FROM "registry_item" AS "b" WHERE "a"."userId" = "b"."userId" AND "a"."key" = "b"."key" AND "a"."scope" = "b"."scope" GROUP BY "userId", "key", "scope")`);
await queryRunner.query(`ALTER TABLE "registry_item" ADD CONSTRAINT "UQ_b8d6509f847331273ab99daccc7" UNIQUE ("userId", "key", "scope")`);
}

View File

@ -270,16 +270,14 @@ export const UserRepository = db.getRepository(User).extend({
const followingCount = profile == null ? null :
(profile.ffVisibility === 'public') || isMe ? user.followingCount :
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followingCount :
(profile.ffVisibility === 'followers') && relation?.isFollowing ? user.followingCount :
null;
const followersCount = profile == null ? null :
(profile.ffVisibility === 'public') || isMe ? user.followersCount :
(profile.ffVisibility === 'followers') && (relation && relation.isFollowing) ? user.followersCount :
(profile.ffVisibility === 'followers') && relation?.isFollowing ? user.followersCount :
null;
const falsy = opts.detail ? false : undefined;
const packed = {
id: user.id,
name: user.name,
@ -287,10 +285,10 @@ export const UserRepository = db.getRepository(User).extend({
host: user.host,
avatarUrl: this.getAvatarUrlSync(user),
avatarBlurhash: user.avatar?.blurhash || null,
isAdmin: user.isAdmin || falsy,
isModerator: user.isModerator || falsy,
isBot: user.isBot || falsy,
isCat: user.isCat || falsy,
isAdmin: user.isAdmin,
isModerator: user.isModerator,
isBot: user.isBot,
isCat: user.isCat,
instance: !user.host ? undefined : userInstanceCache.fetch(user.host)
.then(instance => !instance ? undefined : {
name: instance.name,
@ -312,8 +310,8 @@ export const UserRepository = db.getRepository(User).extend({
bannerUrl: user.banner ? DriveFiles.getPublicUrl(user.banner, false) : null,
bannerBlurhash: user.banner?.blurhash || null,
isLocked: user.isLocked,
isSilenced: user.isSilenced || falsy,
isSuspended: user.isSuspended || falsy,
isSilenced: user.isSilenced,
isSuspended: user.isSuspended,
description: profile!.description,
location: profile!.location,
birthday: profile!.birthday,
@ -369,7 +367,7 @@ export const UserRepository = db.getRepository(User).extend({
mutedInstances: profile!.mutedInstances,
mutingNotificationTypes: profile!.mutingNotificationTypes,
emailNotificationTypes: profile!.emailNotificationTypes,
showTimelineReplies: user.showTimelineReplies || falsy,
showTimelineReplies: user.showTimelineReplies,
federateBlocks: user!.federateBlocks,
} : {}),

View File

@ -11,7 +11,7 @@ export default async (actor: CacheableRemoteUser, activity: ILike) => {
await extractEmojis(activity.tag || [], actor.host).catch(() => null);
return await createReaction(actor, note, activity._misskey_reaction || activity.content || activity.name).catch(e => {
return await createReaction(actor, note, activity.content || activity.name).catch(e => {
if (e.id === '51c42bb4-931a-456b-bff7-e5a8a70dd298') {
return 'skip: already reacted';
} else {

View File

@ -207,8 +207,6 @@ export async function createNote(value: string | IObject, resolver: Resolver, si
let text: string | null = null;
if (note.source?.mediaType === 'text/x.misskeymarkdown' && typeof note.source.content === 'string') {
text = note.source.content;
} else if (typeof note._misskey_content !== 'undefined') {
text = note._misskey_content;
} else if (typeof note.content === 'string') {
text = fromHtml(note.content, quote?.uri);
}

View File

@ -23,7 +23,7 @@ export async function extractPollFromQuestion(source: string | IObject, resolver
.map(x => x.name!);
const votes = question[multiple ? 'anyOf' : 'oneOf']!
.map(x => x.replies && x.replies.totalItems || x._misskey_votes || 0);
.map(x => x.replies && x.replies.totalItems || 0);
return {
choices,

View File

@ -35,10 +35,7 @@ export const renderActivity = (x: any): IActivity | null => {
value: 'schema:value',
// Misskey
misskey: 'https://misskey-hub.net/ns#',
'_misskey_content': 'misskey:_misskey_content',
'_misskey_quote': 'misskey:_misskey_quote',
'_misskey_reaction': 'misskey:_misskey_reaction',
'_misskey_votes': 'misskey:_misskey_votes',
'_misskey_talk': 'misskey:_misskey_talk',
'isCat': 'misskey:isCat',
// vcard

View File

@ -13,10 +13,7 @@ export const renderLike = async (noteReaction: NoteReaction, note: Note) => {
id: `${config.url}/likes/${noteReaction.id}`,
actor: `${config.url}/users/${noteReaction.userId}`,
object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`,
... (reaction !== '\u2b50' ? {
content: reaction,
_misskey_reaction: reaction,
} : {}),
content: reaction,
} as any;
if (reaction.startsWith(':')) {

View File

@ -145,7 +145,6 @@ export default async function renderNote(note: Note, dive = true, isTalk = false
attributedTo,
summary,
content,
_misskey_content: text,
source: {
content: text,
mediaType: 'text/x.misskeymarkdown',

View File

@ -11,7 +11,6 @@ export default async function renderQuestion(user: { id: User['id'] }, note: Not
content: note.text || '',
[poll.multiple ? 'anyOf' : 'oneOf']: poll.choices.map((text, i) => ({
name: text,
_misskey_votes: poll.votes[i],
replies: {
type: 'Collection',
totalItems: poll.votes[i],

View File

@ -171,7 +171,6 @@ export const isQuestion = (object: IObject): object is IQuestion =>
interface IQuestionChoice {
name?: string;
replies?: ICollection;
_misskey_votes?: number;
}
export interface ITombstone extends IObject {
type: 'Tombstone';
@ -282,7 +281,7 @@ export interface IRemove extends IActivity {
export interface ILike extends IActivity {
type: 'Like' | 'EmojiReaction' | 'EmojiReact';
_misskey_reaction?: string;
content?: string;
}
export interface IAnnounce extends IActivity {

View File

@ -246,19 +246,20 @@ async function getSiteName(info: NodeInfo | null, doc: DOMWindow['document'] | n
async function getDescription(info: NodeInfo | null, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
if (info && info.metadata) {
if (info.metadata.nodeDescription || info.metadata.description) {
return info.metadata.nodeDescription || info.metadata.description;
const description = info.metadata.nodeDescription || info.metadata.description;
if (description && description.length < 4096) {
return description;
}
}
if (doc) {
const meta = doc.querySelector('meta[name="description"]')?.getAttribute('content');
if (meta) {
if (meta && meta.length < 4096) {
return meta;
}
const og = doc.querySelector('meta[property="og:description"]')?.getAttribute('content');
if (og) {
if (og && og.length < 4096) {
return og;
}
}

View File

@ -35,7 +35,7 @@ const props = withDefaults(defineProps<{
const self = props.url.startsWith(local);
const uri = new URL(props.url);
if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url');
if (!['http:', 'https:'].includes(uri.protocol)) throw new Error('invalid url');
let el: HTMLElement | null = $ref(null);
let schema = $ref(uri.protocol);

View File

@ -63,10 +63,29 @@
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<i class="fas fa-quote-right"></i>
</MkA>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">{{ i18n.ts.receiveFollowRequest }}<div v-if="full && !followRequestDone"><button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button></div></span>
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b><div v-if="full && !groupInviteDone"><button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> | <button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button></div></span>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">
{{ i18n.ts.youGotNewFollower }}
<div v-if="full">
<MkFollowButton :user="notification.user" :full="true"/>
</div>
</span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">
{{ i18n.ts.followRequestAccepted }}
</span>
<span v-if="notification.type === 'receiveFollowRequest'" class="text" style="opacity: 0.6;">
{{ i18n.ts.receiveFollowRequest }}
<div v-if="full && !followRequestDone">
<button class="_textButton" @click="acceptFollowRequest()">{{ i18n.ts.accept }}</button> |
<button class="_textButton" @click="rejectFollowRequest()">{{ i18n.ts.reject }}</button>
</div>
</span>
<span v-if="notification.type === 'groupInvited'" class="text" style="opacity: 0.6;">
{{ i18n.ts.groupInvited }}: <b>{{ notification.invitation.group.name }}</b>
<div v-if="full && !groupInviteDone">
<button class="_textButton" @click="acceptGroupInvitation()">{{ i18n.ts.accept }}</button> |
<button class="_textButton" @click="rejectGroupInvitation()">{{ i18n.ts.reject }}</button>
</div>
</span>
<span v-if="notification.type === 'app'" class="text">
<Mfm :text="notification.body" :nowrap="!full"/>
</span>