mirror of
https://github.com/kokonect-link/cherrypick
synced 2024-11-27 14:28:53 +09:00
メディアタイムライン追加
This commit is contained in:
parent
25029ac671
commit
f45745af5a
@ -1927,6 +1927,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Startseite"
|
||||
local: "Lokal"
|
||||
media: "Medien"
|
||||
social: "Sozial"
|
||||
global: "Global"
|
||||
_play:
|
||||
|
@ -1979,6 +1979,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Home"
|
||||
local: "Local"
|
||||
media: "Media"
|
||||
social: "Social"
|
||||
global: "Global"
|
||||
_play:
|
||||
|
@ -1831,6 +1831,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Inicio"
|
||||
local: "Local"
|
||||
media: "Media"
|
||||
social: "Social"
|
||||
global: "Global"
|
||||
_play:
|
||||
|
@ -1491,6 +1491,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Principal"
|
||||
local: "Local"
|
||||
media: "Média"
|
||||
social: "Social"
|
||||
global: "Global"
|
||||
_play:
|
||||
|
@ -1750,6 +1750,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Beranda"
|
||||
local: "Lokal"
|
||||
media: "Media"
|
||||
social: "Sosial"
|
||||
global: "Global"
|
||||
_play:
|
||||
|
@ -1854,6 +1854,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Home"
|
||||
local: "Locale"
|
||||
media: "Contenuti multimediali"
|
||||
social: "Sociale"
|
||||
global: "Federata"
|
||||
_play:
|
||||
|
@ -2029,6 +2029,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "ホーム"
|
||||
local: "ローカル"
|
||||
media: "メディア"
|
||||
social: "ソーシャル"
|
||||
global: "グローバル"
|
||||
|
||||
|
@ -1927,6 +1927,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "ホーム"
|
||||
local: "ローカル"
|
||||
media: "メディア"
|
||||
social: "ソーシャル"
|
||||
global: "グローバル"
|
||||
_play:
|
||||
|
@ -1980,6 +1980,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "홈"
|
||||
local: "로컬"
|
||||
media: "미디어"
|
||||
social: "소셜"
|
||||
global: "글로벌"
|
||||
_play:
|
||||
|
@ -1835,6 +1835,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Персональная"
|
||||
local: "Местная"
|
||||
media: "Медиа"
|
||||
social: "Социальная"
|
||||
global: "Всеобщая"
|
||||
_play:
|
||||
|
@ -1415,6 +1415,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Domov"
|
||||
local: "Lokálne"
|
||||
media: "Médiá"
|
||||
social: "Sociálne"
|
||||
global: "Globálne"
|
||||
_play:
|
||||
|
@ -1880,6 +1880,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "หน้าแรก"
|
||||
local: "ในพื้นที่"
|
||||
media: "สื่อ"
|
||||
social: "โซเชี่ยล"
|
||||
global: "ทั่วโลก"
|
||||
_play:
|
||||
|
@ -1578,6 +1578,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Домівка"
|
||||
local: "Локальна"
|
||||
media: "Медіафайли"
|
||||
social: "Соціальна"
|
||||
global: "Глобальна"
|
||||
_play:
|
||||
|
@ -1632,6 +1632,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "Trang chính"
|
||||
local: "Máy chủ này"
|
||||
media: "Phương tiện"
|
||||
social: "Xã hội"
|
||||
global: "Liên hợp"
|
||||
_play:
|
||||
|
@ -1926,6 +1926,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "首页"
|
||||
local: "本地"
|
||||
media: "媒体"
|
||||
social: "社交"
|
||||
global: "全局"
|
||||
_play:
|
||||
|
@ -1927,6 +1927,7 @@ _instanceCharts:
|
||||
_timelines:
|
||||
home: "首頁"
|
||||
local: "本地"
|
||||
media: "媒體"
|
||||
social: "社交"
|
||||
global: "公開"
|
||||
_play:
|
||||
|
@ -30,6 +30,7 @@ import { HashtagChannelService } from './api/stream/channels/hashtag.js';
|
||||
import { HomeTimelineChannelService } from './api/stream/channels/home-timeline.js';
|
||||
import { HybridTimelineChannelService } from './api/stream/channels/hybrid-timeline.js';
|
||||
import { LocalTimelineChannelService } from './api/stream/channels/local-timeline.js';
|
||||
import { MediaTimelineChannelService } from './api/stream/channels/media-timeline.js';
|
||||
import { MessagingIndexChannelService } from './api/stream/channels/messaging-index.js';
|
||||
import { MessagingChannelService } from './api/stream/channels/messaging.js';
|
||||
import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js';
|
||||
@ -76,6 +77,7 @@ import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.
|
||||
HomeTimelineChannelService,
|
||||
HybridTimelineChannelService,
|
||||
LocalTimelineChannelService,
|
||||
MediaTimelineChannelService,
|
||||
MessagingIndexChannelService,
|
||||
MessagingChannelService,
|
||||
QueueStatsChannelService,
|
||||
|
@ -260,6 +260,7 @@ 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_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
|
||||
import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js';
|
||||
import * as ep___notes_mediaTimeline from './endpoints/notes/media-timeline.js';
|
||||
import * as ep___notes_mentions from './endpoints/notes/mentions.js';
|
||||
import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js';
|
||||
import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js';
|
||||
@ -620,6 +621,7 @@ const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep__
|
||||
const $notes_globalTimeline: Provider = { provide: 'ep:notes/global-timeline', useClass: ep___notes_globalTimeline.default };
|
||||
const $notes_hybridTimeline: Provider = { provide: 'ep:notes/hybrid-timeline', useClass: ep___notes_hybridTimeline.default };
|
||||
const $notes_localTimeline: Provider = { provide: 'ep:notes/local-timeline', useClass: ep___notes_localTimeline.default };
|
||||
const $notes_mediaTimeline: Provider = { provide: 'ep:notes/media-timeline', useClass: ep___notes_mediaTimeline.default };
|
||||
const $notes_mentions: Provider = { provide: 'ep:notes/mentions', useClass: ep___notes_mentions.default };
|
||||
const $notes_polls_recommendation: Provider = { provide: 'ep:notes/polls/recommendation', useClass: ep___notes_polls_recommendation.default };
|
||||
const $notes_polls_vote: Provider = { provide: 'ep:notes/polls/vote', useClass: ep___notes_polls_vote.default };
|
||||
@ -984,6 +986,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||
$notes_globalTimeline,
|
||||
$notes_hybridTimeline,
|
||||
$notes_localTimeline,
|
||||
$notes_mediaTimeline,
|
||||
$notes_mentions,
|
||||
$notes_polls_recommendation,
|
||||
$notes_polls_vote,
|
||||
@ -1341,6 +1344,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention
|
||||
$notes_globalTimeline,
|
||||
$notes_hybridTimeline,
|
||||
$notes_localTimeline,
|
||||
$notes_mediaTimeline,
|
||||
$notes_mentions,
|
||||
$notes_polls_recommendation,
|
||||
$notes_polls_vote,
|
||||
|
@ -260,6 +260,7 @@ 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_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
|
||||
import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js';
|
||||
import * as ep___notes_mediaTimeline from './endpoints/notes/media-timeline.js';
|
||||
import * as ep___notes_mentions from './endpoints/notes/mentions.js';
|
||||
import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js';
|
||||
import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js';
|
||||
@ -618,6 +619,7 @@ const eps = [
|
||||
['notes/global-timeline', ep___notes_globalTimeline],
|
||||
['notes/hybrid-timeline', ep___notes_hybridTimeline],
|
||||
['notes/local-timeline', ep___notes_localTimeline],
|
||||
['notes/media-timeline', ep___notes_mediaTimeline],
|
||||
['notes/mentions', ep___notes_mentions],
|
||||
['notes/polls/recommendation', ep___notes_polls_recommendation],
|
||||
['notes/polls/vote', ep___notes_polls_vote],
|
||||
|
@ -0,0 +1,128 @@
|
||||
import { Brackets } from 'typeorm';
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import type { NotesRepository } from '@/models/index.js';
|
||||
import { Endpoint } from '@/server/api/endpoint-base.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import ActiveUsersChart from '@/core/chart/charts/active-users.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { ApiError } from '../../error.js';
|
||||
|
||||
export const meta = {
|
||||
tags: ['notes'],
|
||||
|
||||
res: {
|
||||
type: 'array',
|
||||
optional: false, nullable: false,
|
||||
items: {
|
||||
type: 'object',
|
||||
optional: false, nullable: false,
|
||||
ref: 'Note',
|
||||
},
|
||||
},
|
||||
|
||||
errors: {
|
||||
ltlDisabled: {
|
||||
message: 'Media timeline has been disabled.',
|
||||
code: 'MTL_DISABLED',
|
||||
id: '45a6eb02-7695-4393-b023-dd4be9aaaefd',
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const paramDef = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
withFiles: {
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Only show notes that have attached files.',
|
||||
},
|
||||
fileType: { type: 'array', items: {
|
||||
type: 'string',
|
||||
} },
|
||||
excludeNsfw: { type: 'boolean', default: false },
|
||||
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
|
||||
sinceId: { type: 'string', format: 'misskey:id' },
|
||||
untilId: { type: 'string', format: 'misskey:id' },
|
||||
sinceDate: { type: 'integer' },
|
||||
untilDate: { type: 'integer' },
|
||||
},
|
||||
required: [],
|
||||
} as const;
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
@Injectable()
|
||||
export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||
constructor(
|
||||
@Inject(DI.notesRepository)
|
||||
private notesRepository: NotesRepository,
|
||||
|
||||
private noteEntityService: NoteEntityService,
|
||||
private queryService: QueryService,
|
||||
private metaService: MetaService,
|
||||
private roleService: RoleService,
|
||||
private activeUsersChart: ActiveUsersChart,
|
||||
private idService: IdService,
|
||||
) {
|
||||
super(meta, paramDef, async (ps, me) => {
|
||||
const policies = await this.roleService.getUserPolicies(me ? me.id : null);
|
||||
if (!policies.ltlAvailable) {
|
||||
throw new ApiError(meta.errors.ltlDisabled);
|
||||
}
|
||||
|
||||
//#region Construct query
|
||||
const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'),
|
||||
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
|
||||
.andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10日前まで
|
||||
.andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)')
|
||||
.andWhere('note.fileIds != \'{}\'')
|
||||
.innerJoinAndSelect('note.user', 'user')
|
||||
.leftJoinAndSelect('note.reply', 'reply')
|
||||
.leftJoinAndSelect('note.renote', 'renote')
|
||||
.leftJoinAndSelect('reply.user', 'replyUser')
|
||||
.leftJoinAndSelect('renote.user', 'renoteUser');
|
||||
|
||||
this.queryService.generateChannelQuery(query, me);
|
||||
this.queryService.generateRepliesQuery(query, me);
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
if (me) this.queryService.generateMutedUserQuery(query, me);
|
||||
if (me) this.queryService.generateMutedNoteQuery(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQuery(query, me);
|
||||
if (me) this.queryService.generateMutedUserRenotesQueryForNotes(query, me);
|
||||
|
||||
if (ps.withFiles) {
|
||||
query.andWhere('note.fileIds != \'{}\'');
|
||||
}
|
||||
|
||||
if (ps.fileType != null) {
|
||||
query.andWhere('note.fileIds != \'{}\'');
|
||||
query.andWhere(new Brackets(qb => {
|
||||
for (const type of ps.fileType!) {
|
||||
const i = ps.fileType!.indexOf(type);
|
||||
qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type });
|
||||
}
|
||||
}));
|
||||
|
||||
if (ps.excludeNsfw) {
|
||||
query.andWhere('note.cw IS NULL');
|
||||
query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)');
|
||||
}
|
||||
}
|
||||
//#endregion
|
||||
|
||||
const timeline = await query.take(ps.limit).getMany();
|
||||
|
||||
process.nextTick(() => {
|
||||
if (me) {
|
||||
this.activeUsersChart.read(me);
|
||||
}
|
||||
});
|
||||
|
||||
return await this.noteEntityService.packMany(timeline, me);
|
||||
});
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { HybridTimelineChannelService } from './channels/hybrid-timeline.js';
|
||||
import { LocalTimelineChannelService } from './channels/local-timeline.js';
|
||||
import { MediaTimelineChannelService } from './channels/media-timeline.js';
|
||||
import { HomeTimelineChannelService } from './channels/home-timeline.js';
|
||||
import { GlobalTimelineChannelService } from './channels/global-timeline.js';
|
||||
import { MainChannelService } from './channels/main.js';
|
||||
@ -23,6 +24,7 @@ export class ChannelsService {
|
||||
private mainChannelService: MainChannelService,
|
||||
private homeTimelineChannelService: HomeTimelineChannelService,
|
||||
private localTimelineChannelService: LocalTimelineChannelService,
|
||||
private mediaTimelineChannelService: MediaTimelineChannelService,
|
||||
private hybridTimelineChannelService: HybridTimelineChannelService,
|
||||
private globalTimelineChannelService: GlobalTimelineChannelService,
|
||||
private userListChannelService: UserListChannelService,
|
||||
@ -45,6 +47,7 @@ export class ChannelsService {
|
||||
case 'main': return this.mainChannelService;
|
||||
case 'homeTimeline': return this.homeTimelineChannelService;
|
||||
case 'localTimeline': return this.localTimelineChannelService;
|
||||
case 'mediaTimeline': return this.mediaTimelineChannelService;
|
||||
case 'hybridTimeline': return this.hybridTimelineChannelService;
|
||||
case 'globalTimeline': return this.globalTimelineChannelService;
|
||||
case 'userList': return this.userListChannelService;
|
||||
|
@ -0,0 +1,111 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { checkWordMute } from '@/misc/check-word-mute.js';
|
||||
import { isUserRelated } from '@/misc/is-user-related.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { MetaService } from '@/core/MetaService.js';
|
||||
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import Channel from '../channel.js';
|
||||
|
||||
class MediaTimelineChannel extends Channel {
|
||||
public readonly chName = 'mediaTimeline';
|
||||
public static shouldShare = true;
|
||||
public static requireCredential = false;
|
||||
|
||||
constructor(
|
||||
private metaService: MetaService,
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
|
||||
id: string,
|
||||
connection: Channel['connection'],
|
||||
) {
|
||||
super(id, connection);
|
||||
//this.onNote = this.onNote.bind(this);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async init(params: any) {
|
||||
const policies = await this.roleService.getUserPolicies(this.user ? this.user.id : null);
|
||||
if (!policies.ltlAvailable) return;
|
||||
|
||||
// Subscribe events
|
||||
this.subscriber.on('notesStream', this.onNote);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async onNote(note: Packed<'Note'>) {
|
||||
if (note.user.host !== null) return;
|
||||
if (note.visibility !== 'public') return;
|
||||
if (note.channelId != null && !this.followingChannels.has(note.channelId)) return;
|
||||
|
||||
// リプライなら再pack
|
||||
if (note.replyId != null) {
|
||||
note.reply = await this.noteEntityService.pack(note.replyId, this.user, {
|
||||
detail: true,
|
||||
});
|
||||
}
|
||||
// Renoteなら再pack
|
||||
if (note.renoteId != null) {
|
||||
note.renote = await this.noteEntityService.pack(note.renoteId, this.user, {
|
||||
detail: true,
|
||||
});
|
||||
}
|
||||
|
||||
// 関係ない返信は除外
|
||||
if (note.reply && this.user && !this.user.showTimelineReplies) {
|
||||
const reply = note.reply;
|
||||
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
|
||||
if (reply.userId !== this.user.id && note.userId !== this.user.id && reply.userId !== note.userId) return;
|
||||
}
|
||||
|
||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
||||
if (isUserRelated(note, this.userIdsWhoBlockingMe)) return;
|
||||
|
||||
if (note.renote && !note.text && isUserRelated(note, this.userIdsWhoMeMutingRenotes)) return;
|
||||
|
||||
// 流れてきたNoteがミュートすべきNoteだったら無視する
|
||||
// TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある)
|
||||
// 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、
|
||||
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
|
||||
// そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
|
||||
if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
|
||||
|
||||
this.connection.cacheNote(note);
|
||||
|
||||
this.send('note', note);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public dispose() {
|
||||
// Unsubscribe events
|
||||
this.subscriber.off('notesStream', this.onNote);
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class MediaTimelineChannelService {
|
||||
public readonly shouldShare = MediaTimelineChannel.shouldShare;
|
||||
public readonly requireCredential = MediaTimelineChannel.requireCredential;
|
||||
|
||||
constructor(
|
||||
private metaService: MetaService,
|
||||
private roleService: RoleService,
|
||||
private noteEntityService: NoteEntityService,
|
||||
) {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public create(id: string, connection: Channel['connection']): MediaTimelineChannel {
|
||||
return new MediaTimelineChannel(
|
||||
this.metaService,
|
||||
this.roleService,
|
||||
this.noteEntityService,
|
||||
id,
|
||||
connection,
|
||||
);
|
||||
}
|
||||
}
|
@ -38,6 +38,18 @@ const prepend = note => {
|
||||
}
|
||||
};
|
||||
|
||||
const prependFilterdMedia = note => {
|
||||
if (note.files !== null && note.files.length > 0) {
|
||||
tlComponent.pagingComponent?.prepend(note);
|
||||
}
|
||||
|
||||
emit('note');
|
||||
|
||||
if (props.sound) {
|
||||
sound.play($i && (note.userId === $i.id) ? 'noteMy' : 'note');
|
||||
}
|
||||
};
|
||||
|
||||
const onUserAdded = () => {
|
||||
tlComponent.pagingComponent?.reload();
|
||||
};
|
||||
@ -82,6 +94,10 @@ if (props.src === 'antenna') {
|
||||
withReplies: defaultStore.state.showTimelineReplies,
|
||||
});
|
||||
connection.on('note', prepend);
|
||||
} else if (props.src === 'media') {
|
||||
endpoint = 'notes/media-timeline';
|
||||
connection = stream.useChannel('mediaTimeline');
|
||||
connection.on('note', prependFilterdMedia);
|
||||
} else if (props.src === 'social') {
|
||||
endpoint = 'notes/hybrid-timeline';
|
||||
query = {
|
||||
|
@ -191,6 +191,11 @@ const headerTabs = $computed(() => [{
|
||||
title: i18n.ts._timelines.local,
|
||||
icon: 'ti ti-planet',
|
||||
iconOnly: true,
|
||||
}, {
|
||||
key: 'media',
|
||||
title: i18n.ts._timelines.media,
|
||||
icon: 'ti ti-photo',
|
||||
iconOnly: true,
|
||||
}, {
|
||||
key: 'social',
|
||||
title: i18n.ts._timelines.social,
|
||||
|
@ -24,7 +24,7 @@ export type Column = {
|
||||
channelId?: string;
|
||||
roleId?: string;
|
||||
includingTypes?: typeof notificationTypes[number][];
|
||||
tl?: 'home' | 'local' | 'social' | 'global';
|
||||
tl?: 'home' | 'local' | 'media' | 'social' | 'global';
|
||||
};
|
||||
|
||||
export const deckStore = markRaw(new Storage('deck', {
|
||||
|
@ -4,6 +4,7 @@
|
||||
<i v-if="column.tl === 'home'" class="ti ti-home"></i>
|
||||
<i v-else-if="column.tl === 'local'" class="ti ti-planet"></i>
|
||||
<i v-else-if="column.tl === 'social'" class="ti ti-rocket"></i>
|
||||
<i v-else-if="column.tl === 'media'" class="ti ti-photo"></i>
|
||||
<i v-else-if="column.tl === 'global'" class="ti ti-whirl"></i>
|
||||
<span style="margin-left: 8px;">{{ column.name }}</span>
|
||||
</template>
|
||||
@ -56,6 +57,8 @@ async function setType() {
|
||||
value: 'home' as const, text: i18n.ts._timelines.home,
|
||||
}, {
|
||||
value: 'local' as const, text: i18n.ts._timelines.local,
|
||||
}, {
|
||||
value: 'media' as const, text: i18n.ts._timelines.media,
|
||||
}, {
|
||||
value: 'social' as const, text: i18n.ts._timelines.social,
|
||||
}, {
|
||||
|
@ -117,6 +117,10 @@ const choose = async (ev) => {
|
||||
text: i18n.ts._timelines.local,
|
||||
icon: 'ti ti-planet',
|
||||
action: () => { setSrc('local'); },
|
||||
}, {
|
||||
text: i18n.ts._timelines.media,
|
||||
icon: 'ti ti-photo',
|
||||
action: () => { setSrc('media'); },
|
||||
}, {
|
||||
text: i18n.ts._timelines.social,
|
||||
icon: 'ti ti-rocket',
|
||||
|
@ -56,6 +56,13 @@ export type Channels = {
|
||||
};
|
||||
receives: null;
|
||||
};
|
||||
mediaTimeline: {
|
||||
params: null;
|
||||
events: {
|
||||
note: (payload: Note) => void;
|
||||
};
|
||||
receives: null;
|
||||
};
|
||||
hybridTimeline: {
|
||||
params: null;
|
||||
events: {
|
||||
|
Loading…
Reference in New Issue
Block a user