Merge remote-tracking branch 'mi-dev/develop' into emoji-req

This commit is contained in:
mattyatea 2023-11-15 14:24:41 +09:00
commit 36292549c5
No known key found for this signature in database
GPG key ID: 068E54E2C33BEF9A
157 changed files with 4057 additions and 2161 deletions

View file

@ -86,6 +86,7 @@ export const ACHIEVEMENT_TYPES = [
'cookieClicked',
'brainDiver',
'smashTestNotificationButton',
'tutorialCompleted',
] as const;
@Injectable()

View file

@ -47,6 +47,7 @@ export class AnnouncementService {
const q = this.announcementsRepository.createQueryBuilder('announcement')
.where('announcement.isActive = true')
.andWhere('announcement.silence = false')
.andWhere(new Brackets(qb => {
qb.orWhere('announcement.userId = :userId', { userId: user.id });
qb.orWhere('announcement.userId IS NULL');
@ -73,6 +74,7 @@ export class AnnouncementService {
icon: values.icon,
display: values.display,
forExistingUsers: values.forExistingUsers,
silence: values.silence,
needConfirmationToRead: values.needConfirmationToRead,
userId: values.userId,
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
@ -124,6 +126,7 @@ export class AnnouncementService {
display: values.display,
icon: values.icon,
forExistingUsers: values.forExistingUsers,
silence: values.silence,
needConfirmationToRead: values.needConfirmationToRead,
isActive: values.isActive,
});
@ -210,6 +213,7 @@ export class AnnouncementService {
icon: announcement.icon,
display: announcement.display,
needConfirmationToRead: announcement.needConfirmationToRead,
silence: announcement.silence,
forYou: announcement.userId === me?.id,
isRead: reads.some(read => read.announcementId === announcement.id),
}));

View file

@ -64,6 +64,7 @@ import { ClipService } from './ClipService.js';
import { FeaturedService } from './FeaturedService.js';
import { FunoutTimelineService } from './FunoutTimelineService.js';
import { ChannelFollowingService } from './ChannelFollowingService.js';
import { RegistryApiService } from './RegistryApiService.js';
import { ChartLoggerService } from './chart/ChartLoggerService.js';
import FederationChart from './chart/charts/federation.js';
import NotesChart from './chart/charts/notes.js';
@ -196,6 +197,7 @@ const $ClipService: Provider = { provide: 'ClipService', useExisting: ClipServic
const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: FeaturedService };
const $FunoutTimelineService: Provider = { provide: 'FunoutTimelineService', useExisting: FunoutTimelineService };
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
@ -332,6 +334,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
FeaturedService,
FunoutTimelineService,
ChannelFollowingService,
RegistryApiService,
ChartLoggerService,
FederationChart,
NotesChart,
@ -461,6 +464,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$FeaturedService,
$FunoutTimelineService,
$ChannelFollowingService,
$RegistryApiService,
$ChartLoggerService,
$FederationChart,
$NotesChart,
@ -591,6 +595,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
FeaturedService,
FunoutTimelineService,
ChannelFollowingService,
RegistryApiService,
FederationChart,
NotesChart,
UsersChart,
@ -719,6 +724,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$FeaturedService,
$FunoutTimelineService,
$ChannelFollowingService,
$RegistryApiService,
$FederationChart,
$NotesChart,
$UsersChart,

View file

@ -521,11 +521,13 @@ export class NoteCreateService implements OnApplicationShutdown {
followeeId: user.id,
notify: 'normal',
}).then(followings => {
for (const following of followings) {
// TODO: ワードミュート考慮
this.notificationService.createNotification(following.followerId, 'note', {
noteId: note.id,
}, user.id);
if (note.visibility !== 'specified') {
for (const following of followings) {
// TODO: ワードミュート考慮
this.notificationService.createNotification(following.followerId, 'note', {
noteId: note.id,
}, user.id);
}
}
});
}

View file

@ -0,0 +1,147 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { MiRegistryItem, RegistryItemsRepository } from '@/models/_.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { MiUser } from '@/models/User.js';
import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class RegistryApiService {
constructor(
@Inject(DI.registryItemsRepository)
private registryItemsRepository: RegistryItemsRepository,
private idService: IdService,
private globalEventService: GlobalEventService,
) {
}
@bindThis
public async set(userId: MiUser['id'], domain: string | null, scope: string[], key: string, value: any) {
// TODO: 作成できるキーの数を制限する
const query = this.registryItemsRepository.createQueryBuilder('item');
if (domain) {
query.where('item.domain = :domain', { domain: domain });
} else {
query.where('item.domain IS NULL');
}
query.andWhere('item.userId = :userId', { userId: userId });
query.andWhere('item.key = :key', { key: key });
query.andWhere('item.scope = :scope', { scope: scope });
const existingItem = await query.getOne();
if (existingItem) {
await this.registryItemsRepository.update(existingItem.id, {
updatedAt: new Date(),
value: value,
});
} else {
await this.registryItemsRepository.insert({
id: this.idService.gen(),
updatedAt: new Date(),
userId: userId,
domain: domain,
scope: scope,
key: key,
value: value,
});
}
if (domain == null) {
// TODO: サードパーティアプリが傍受出来てしまうのでどうにかする
this.globalEventService.publishMainStream(userId, 'registryUpdated', {
scope: scope,
key: key,
value: value,
});
}
}
@bindThis
public async getItem(userId: MiUser['id'], domain: string | null, scope: string[], key: string): Promise<MiRegistryItem | null> {
const query = this.registryItemsRepository.createQueryBuilder('item')
.where(domain == null ? 'item.domain IS NULL' : 'item.domain = :domain', { domain: domain })
.andWhere('item.userId = :userId', { userId: userId })
.andWhere('item.key = :key', { key: key })
.andWhere('item.scope = :scope', { scope: scope });
const item = await query.getOne();
return item;
}
@bindThis
public async getAllItemsOfScope(userId: MiUser['id'], domain: string | null, scope: string[]): Promise<MiRegistryItem[]> {
const query = this.registryItemsRepository.createQueryBuilder('item');
query.where(domain == null ? 'item.domain IS NULL' : 'item.domain = :domain', { domain: domain });
query.andWhere('item.userId = :userId', { userId: userId });
query.andWhere('item.scope = :scope', { scope: scope });
const items = await query.getMany();
return items;
}
@bindThis
public async getAllKeysOfScope(userId: MiUser['id'], domain: string | null, scope: string[]): Promise<string[]> {
const query = this.registryItemsRepository.createQueryBuilder('item');
query.select('item.key');
query.where(domain == null ? 'item.domain IS NULL' : 'item.domain = :domain', { domain: domain });
query.andWhere('item.userId = :userId', { userId: userId });
query.andWhere('item.scope = :scope', { scope: scope });
const items = await query.getMany();
return items.map(x => x.key);
}
@bindThis
public async getAllScopeAndDomains(userId: MiUser['id']): Promise<{ domain: string | null; scopes: string[][] }[]> {
const query = this.registryItemsRepository.createQueryBuilder('item')
.select(['item.scope', 'item.domain'])
.where('item.userId = :userId', { userId: userId });
const items = await query.getMany();
const res = [] as { domain: string | null; scopes: string[][] }[];
for (const item of items) {
const target = res.find(x => x.domain === item.domain);
if (target) {
if (target.scopes.some(scope => scope.join('.') === item.scope.join('.'))) continue;
target.scopes.push(item.scope);
} else {
res.push({
domain: item.domain,
scopes: [item.scope],
});
}
}
return res;
}
@bindThis
public async remove(userId: MiUser['id'], domain: string | null, scope: string[], key: string) {
const query = this.registryItemsRepository.createQueryBuilder().delete();
if (domain) {
query.where('domain = :domain', { domain: domain });
} else {
query.where('domain IS NULL');
}
query.andWhere('userId = :userId', { userId: userId });
query.andWhere('key = :key', { key: key });
query.andWhere('scope = :scope', { scope: scope });
await query.execute();
}
}

View file

@ -269,7 +269,7 @@ export class ApPersonService implements OnModuleInit {
const tags = extractApHashtags(person.tag).map(normalizeForSearch).splice(0, 32);
const isBot = getApType(object) === 'Service';
const isBot = getApType(object) === 'Service' || getApType(object) === 'Application';
const bday = person['vcard:bday']?.match(/^\d{4}-\d{2}-\d{2}/);
@ -456,7 +456,7 @@ export class ApPersonService implements OnModuleInit {
emojis: emojiNames,
name: truncate(person.name, nameLength),
tags,
isBot: getApType(object) === 'Service',
isBot: getApType(object) === 'Service' || getApType(object) === 'Application',
isCat: (person as any).isCat === true,
isLocked: person.manuallyApprovesFollowers,
movedToUri: person.movedTo ?? null,

View file

@ -85,6 +85,7 @@ export class ChannelEntityService {
usersCount: channel.usersCount,
notesCount: channel.notesCount,
isSensitive: channel.isSensitive,
allowRenoteToExternal: channel.allowRenoteToExternal,
...(me ? {
isFollowing,

View file

@ -47,6 +47,7 @@ export class InstanceEntityService {
faviconUrl: instance.faviconUrl,
themeColor: instance.themeColor,
infoUpdatedAt: instance.infoUpdatedAt ? instance.infoUpdatedAt.toISOString() : null,
latestRequestReceivedAt: instance.latestRequestReceivedAt ? instance.latestRequestReceivedAt.toISOString() : null,
};
}

View file

@ -350,6 +350,7 @@ export class NoteEntityService implements OnModuleInit {
name: channel.name,
color: channel.color,
isSensitive: channel.isSensitive,
allowRenoteToExternal: channel.allowRenoteToExternal,
} : undefined,
mentions: note.mentions.length > 0 ? note.mentions : undefined,
uri: note.uri ?? undefined,