Resolve #5963
This commit is contained in:
parent
aa2c8d101e
commit
a54de07260
16 changed files with 247 additions and 5 deletions
|
@ -412,6 +412,9 @@ dayOverDayChanges: "前日比"
|
||||||
accessibility: "アクセシビリティ"
|
accessibility: "アクセシビリティ"
|
||||||
clinetSettings: "クライアント設定"
|
clinetSettings: "クライアント設定"
|
||||||
accountSettings: "アカウント設定"
|
accountSettings: "アカウント設定"
|
||||||
|
promotion: "プロモーション"
|
||||||
|
promote: "プロモート"
|
||||||
|
numberOfDays: "日数"
|
||||||
|
|
||||||
_ago:
|
_ago:
|
||||||
unknown: "謎"
|
unknown: "謎"
|
||||||
|
|
28
migration/1581979837262-promo.ts
Normal file
28
migration/1581979837262-promo.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||||
|
|
||||||
|
export class promo1581979837262 implements MigrationInterface {
|
||||||
|
name = 'promo1581979837262'
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`CREATE TABLE "promo_note" ("noteId" character varying(32) NOT NULL, "expiresAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, CONSTRAINT "REL_e263909ca4fe5d57f8d4230dd5" UNIQUE ("noteId"), CONSTRAINT "PK_e263909ca4fe5d57f8d4230dd5c" PRIMARY KEY ("noteId"))`, undefined);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_83f0862e9bae44af52ced7099e" ON "promo_note" ("userId") `, undefined);
|
||||||
|
await queryRunner.query(`CREATE TABLE "promo_read" ("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_61917c1541002422b703318b7c9" PRIMARY KEY ("id"))`, undefined);
|
||||||
|
await queryRunner.query(`CREATE INDEX "IDX_9657d55550c3d37bfafaf7d4b0" ON "promo_read" ("userId") `, undefined);
|
||||||
|
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_2882b8a1a07c7d281a98b6db16" ON "promo_read" ("userId", "noteId") `, undefined);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" ADD CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_read" ADD CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_read" ADD CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4" FOREIGN KEY ("noteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_read" DROP CONSTRAINT "FK_a46a1a603ecee695d7db26da5f4"`, undefined);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_read" DROP CONSTRAINT "FK_9657d55550c3d37bfafaf7d4b05"`, undefined);
|
||||||
|
await queryRunner.query(`ALTER TABLE "promo_note" DROP CONSTRAINT "FK_e263909ca4fe5d57f8d4230dd5c"`, undefined);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_2882b8a1a07c7d281a98b6db16"`, undefined);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_9657d55550c3d37bfafaf7d4b0"`, undefined);
|
||||||
|
await queryRunner.query(`DROP TABLE "promo_read"`, undefined);
|
||||||
|
await queryRunner.query(`DROP INDEX "IDX_83f0862e9bae44af52ced7099e"`, undefined);
|
||||||
|
await queryRunner.query(`DROP TABLE "promo_note"`, undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
<sequential-entrance class="sqadhkmv" ref="list" :direction="direction" :reversed="reversed">
|
<sequential-entrance class="sqadhkmv" ref="list" :direction="direction" :reversed="reversed">
|
||||||
<template v-for="(item, i) in items">
|
<template v-for="(item, i) in items">
|
||||||
<slot :item="item" :i="i"></slot>
|
<slot :item="item" :i="i"></slot>
|
||||||
<div class="separator" :key="item.id + '_date'" v-if="i != items.length - 1 && new Date(item.createdAt).getDate() != new Date(items[i + 1].createdAt).getDate()">
|
<div class="separator" :key="item.id + '_date'" v-if="i != items.length - 1 && new Date(item.createdAt).getDate() != new Date(items[i + 1].createdAt).getDate() && !item._prInjectionId_ && !items[i + 1]._prInjectionId_">
|
||||||
<p class="date">
|
<p class="date">
|
||||||
<span><fa class="icon" :icon="faAngleUp"/>{{ getDateText(item.createdAt) }}</span>
|
<span><fa class="icon" :icon="faAngleUp"/>{{ getDateText(item.createdAt) }}</span>
|
||||||
<span>{{ getDateText(items[i + 1].createdAt) }}<fa class="icon" :icon="faAngleDown"/></span>
|
<span>{{ getDateText(items[i + 1].createdAt) }}<fa class="icon" :icon="faAngleDown"/></span>
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
|
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>
|
||||||
<x-sub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
|
<x-sub :note="appearNote.reply" class="reply-to" v-if="appearNote.reply"/>
|
||||||
<div class="pinned" v-if="pinned"><fa :icon="faThumbtack"/> {{ $t('pinnedNote') }}</div>
|
<div class="pinned" v-if="pinned"><fa :icon="faThumbtack"/> {{ $t('pinnedNote') }}</div>
|
||||||
|
<div class="pinned" v-if="appearNote._prInjectionId_"><fa :icon="faBullhorn"/> {{ $t('promotion') }}</div>
|
||||||
<div class="renote" v-if="isRenote">
|
<div class="renote" v-if="isRenote">
|
||||||
<mk-avatar class="avatar" :user="note.user"/>
|
<mk-avatar class="avatar" :user="note.user"/>
|
||||||
<fa :icon="faRetweet"/>
|
<fa :icon="faRetweet"/>
|
||||||
|
@ -83,7 +84,7 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import { faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight } from '@fortawesome/free-solid-svg-icons';
|
import { faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
|
import { faCopy, faTrashAlt, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
|
||||||
import { parse } from '../../mfm/parse';
|
import { parse } from '../../mfm/parse';
|
||||||
import { sum, unique } from '../../prelude/array';
|
import { sum, unique } from '../../prelude/array';
|
||||||
|
@ -140,7 +141,7 @@ export default Vue.extend({
|
||||||
replies: [],
|
replies: [],
|
||||||
showContent: false,
|
showContent: false,
|
||||||
hideThisNote: false,
|
hideThisNote: false,
|
||||||
faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan
|
faBullhorn, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -522,6 +523,15 @@ export default Vue.extend({
|
||||||
text: this.$t('pin'),
|
text: this.$t('pin'),
|
||||||
action: () => this.togglePin(true)
|
action: () => this.togglePin(true)
|
||||||
} : undefined,
|
} : undefined,
|
||||||
|
...(this.$store.state.i.isModerator || this.$store.state.i.isAdmin ? [
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
icon: faBullhorn,
|
||||||
|
text: this.$t('promote'),
|
||||||
|
action: this.promote
|
||||||
|
}]
|
||||||
|
: []
|
||||||
|
),
|
||||||
...(this.appearNote.userId == this.$store.state.i.id ? [
|
...(this.appearNote.userId == this.$store.state.i.id ? [
|
||||||
null,
|
null,
|
||||||
{
|
{
|
||||||
|
@ -614,6 +624,30 @@ export default Vue.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async promote() {
|
||||||
|
const { canceled, result: days } = await this.$root.dialog({
|
||||||
|
title: this.$t('numberOfDays'),
|
||||||
|
input: { type: 'number' }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (canceled) return;
|
||||||
|
|
||||||
|
this.$root.api('admin/promo/create', {
|
||||||
|
noteId: this.appearNote.id,
|
||||||
|
expiresAt: Date.now() + (86400000 * days)
|
||||||
|
}).then(() => {
|
||||||
|
this.$root.dialog({
|
||||||
|
type: 'success',
|
||||||
|
iconOnly: true, autoClose: true
|
||||||
|
});
|
||||||
|
}).catch(e => {
|
||||||
|
this.$root.dialog({
|
||||||
|
type: 'error',
|
||||||
|
text: e
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
focus() {
|
focus() {
|
||||||
this.$el.focus();
|
this.$el.focus();
|
||||||
},
|
},
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<x-list ref="notes" class="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
|
<x-list ref="notes" class="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
|
||||||
<x-note :note="note" :detail="detail" :key="note.id"/>
|
<x-note :note="note" :detail="detail" :key="note._prInjectionId_ || note.id"/>
|
||||||
</x-list>
|
</x-list>
|
||||||
|
|
||||||
<div class="more" v-if="more && !reversed" style="margin-top: var(--margin);">
|
<div class="more" v-if="more && !reversed" style="margin-top: var(--margin);">
|
||||||
|
|
|
@ -55,6 +55,8 @@ import { Clip } from '../models/entities/clip';
|
||||||
import { ClipNote } from '../models/entities/clip-note';
|
import { ClipNote } from '../models/entities/clip-note';
|
||||||
import { Antenna } from '../models/entities/antenna';
|
import { Antenna } from '../models/entities/antenna';
|
||||||
import { AntennaNote } from '../models/entities/antenna-note';
|
import { AntennaNote } from '../models/entities/antenna-note';
|
||||||
|
import { PromoNote } from '../models/entities/promo-note';
|
||||||
|
import { PromoRead } from '../models/entities/promo-read';
|
||||||
|
|
||||||
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
const sqlLogger = dbLogger.createSubLogger('sql', 'white', false);
|
||||||
|
|
||||||
|
@ -140,6 +142,8 @@ export const entities = [
|
||||||
ClipNote,
|
ClipNote,
|
||||||
Antenna,
|
Antenna,
|
||||||
AntennaNote,
|
AntennaNote,
|
||||||
|
PromoNote,
|
||||||
|
PromoRead,
|
||||||
ReversiGame,
|
ReversiGame,
|
||||||
ReversiMatching,
|
ReversiMatching,
|
||||||
...charts as any
|
...charts as any
|
||||||
|
|
28
src/models/entities/promo-note.ts
Normal file
28
src/models/entities/promo-note.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, OneToOne } from 'typeorm';
|
||||||
|
import { Note } from './note';
|
||||||
|
import { User } from './user';
|
||||||
|
import { id } from '../id';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class PromoNote {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public noteId: Note['id'];
|
||||||
|
|
||||||
|
@OneToOne(type => Note, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public note: Note | null;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone')
|
||||||
|
public expiresAt: Date;
|
||||||
|
|
||||||
|
//#region Denormalized fields
|
||||||
|
@Index()
|
||||||
|
@Column({
|
||||||
|
...id(),
|
||||||
|
comment: '[Denormalized]'
|
||||||
|
})
|
||||||
|
public userId: User['id'];
|
||||||
|
//#endregion
|
||||||
|
}
|
35
src/models/entities/promo-read.ts
Normal file
35
src/models/entities/promo-read.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm';
|
||||||
|
import { Note } from './note';
|
||||||
|
import { User } from './user';
|
||||||
|
import { id } from '../id';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
@Index(['userId', 'noteId'], { unique: true })
|
||||||
|
export class PromoRead {
|
||||||
|
@PrimaryColumn(id())
|
||||||
|
public id: string;
|
||||||
|
|
||||||
|
@Column('timestamp with time zone', {
|
||||||
|
comment: 'The created date of the PromoRead.'
|
||||||
|
})
|
||||||
|
public createdAt: Date;
|
||||||
|
|
||||||
|
@Index()
|
||||||
|
@Column(id())
|
||||||
|
public userId: User['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => User, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public user: User | null;
|
||||||
|
|
||||||
|
@Column(id())
|
||||||
|
public noteId: Note['id'];
|
||||||
|
|
||||||
|
@ManyToOne(type => Note, {
|
||||||
|
onDelete: 'CASCADE'
|
||||||
|
})
|
||||||
|
@JoinColumn()
|
||||||
|
public note: Note | null;
|
||||||
|
}
|
|
@ -50,6 +50,8 @@ import { ClipRepository } from './repositories/clip';
|
||||||
import { ClipNote } from './entities/clip-note';
|
import { ClipNote } from './entities/clip-note';
|
||||||
import { AntennaRepository } from './repositories/antenna';
|
import { AntennaRepository } from './repositories/antenna';
|
||||||
import { AntennaNote } from './entities/antenna-note';
|
import { AntennaNote } from './entities/antenna-note';
|
||||||
|
import { PromoNote } from './entities/promo-note';
|
||||||
|
import { PromoRead } from './entities/promo-read';
|
||||||
|
|
||||||
export const Announcements = getRepository(Announcement);
|
export const Announcements = getRepository(Announcement);
|
||||||
export const AnnouncementReads = getRepository(AnnouncementRead);
|
export const AnnouncementReads = getRepository(AnnouncementRead);
|
||||||
|
@ -102,3 +104,5 @@ export const Clips = getCustomRepository(ClipRepository);
|
||||||
export const ClipNotes = getRepository(ClipNote);
|
export const ClipNotes = getRepository(ClipNote);
|
||||||
export const Antennas = getCustomRepository(AntennaRepository);
|
export const Antennas = getCustomRepository(AntennaRepository);
|
||||||
export const AntennaNotes = getRepository(AntennaNote);
|
export const AntennaNotes = getRepository(AntennaNote);
|
||||||
|
export const PromoNotes = getRepository(PromoNote);
|
||||||
|
export const PromoReads = getRepository(PromoRead);
|
||||||
|
|
|
@ -196,6 +196,7 @@ export class NoteRepository extends Repository<Note> {
|
||||||
renoteId: note.renoteId,
|
renoteId: note.renoteId,
|
||||||
mentions: note.mentions.length > 0 ? note.mentions : undefined,
|
mentions: note.mentions.length > 0 ? note.mentions : undefined,
|
||||||
uri: note.uri || undefined,
|
uri: note.uri || undefined,
|
||||||
|
_prInjectionId_: (note as any)._prInjectionId_ || undefined,
|
||||||
|
|
||||||
...(opts.detail ? {
|
...(opts.detail ? {
|
||||||
reply: note.replyId ? this.pack(note.replyId, meId, {
|
reply: note.replyId ? this.pack(note.replyId, meId, {
|
||||||
|
|
36
src/server/api/common/inject-promo.ts
Normal file
36
src/server/api/common/inject-promo.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import rndstr from 'rndstr';
|
||||||
|
import { Note } from '../../../models/entities/note';
|
||||||
|
import { User } from '../../../models/entities/user';
|
||||||
|
import { PromoReads, PromoNotes, Notes, Users } from '../../../models';
|
||||||
|
import { ensure } from '../../../prelude/ensure';
|
||||||
|
|
||||||
|
export async function injectPromo(user: User, timeline: Note[]) {
|
||||||
|
if (timeline.length < 5) return;
|
||||||
|
|
||||||
|
// TODO: readやexpireフィルタはクエリ側でやる
|
||||||
|
|
||||||
|
const reads = await PromoReads.find({
|
||||||
|
userId: user.id
|
||||||
|
});
|
||||||
|
|
||||||
|
let promos = await PromoNotes.find();
|
||||||
|
|
||||||
|
promos = promos.filter(n => n.expiresAt.getTime() > Date.now());
|
||||||
|
promos = promos.filter(n => !reads.map(r => r.noteId).includes(n.noteId));
|
||||||
|
|
||||||
|
if (promos.length === 0) return;
|
||||||
|
|
||||||
|
const promo = promos[Math.floor(Math.random() * promos.length)];
|
||||||
|
|
||||||
|
// Pick random promo
|
||||||
|
const note = await Notes.findOne(promo.noteId).then(ensure);
|
||||||
|
|
||||||
|
// Join
|
||||||
|
note.user = await Users.findOne(note.userId).then(ensure);
|
||||||
|
|
||||||
|
(note as any)._prInjectionId_ = rndstr('a-z0-9', 8);
|
||||||
|
|
||||||
|
// Inject promo
|
||||||
|
timeline.splice(3, 0, note);
|
||||||
|
timeline.pop();
|
||||||
|
}
|
58
src/server/api/endpoints/admin/promo/create.ts
Normal file
58
src/server/api/endpoints/admin/promo/create.ts
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import $ from 'cafy';
|
||||||
|
import { ID } from '../../../../../misc/cafy-id';
|
||||||
|
import define from '../../../define';
|
||||||
|
import { ApiError } from '../../../error';
|
||||||
|
import { getNote } from '../../../common/getters';
|
||||||
|
import { PromoNotes } from '../../../../../models';
|
||||||
|
|
||||||
|
export const meta = {
|
||||||
|
requireCredential: true as const,
|
||||||
|
requireModerator: true,
|
||||||
|
|
||||||
|
params: {
|
||||||
|
noteId: {
|
||||||
|
validator: $.type(ID),
|
||||||
|
},
|
||||||
|
|
||||||
|
expiresAt: {
|
||||||
|
validator: $.num.int()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
errors: {
|
||||||
|
noSuchNote: {
|
||||||
|
message: 'No such note.',
|
||||||
|
code: 'NO_SUCH_NOTE',
|
||||||
|
id: 'ee449fbe-af2a-453b-9cae-cf2fe7c895fc'
|
||||||
|
},
|
||||||
|
|
||||||
|
alreadyPromoted: {
|
||||||
|
message: 'The note has already promoted.',
|
||||||
|
code: 'ALREADY_PROMOTED',
|
||||||
|
id: 'ae427aa2-7a41-484f-a18c-2c1104051604'
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default define(meta, async (ps, user) => {
|
||||||
|
// Get favoritee
|
||||||
|
const note = await getNote(ps.noteId).catch(e => {
|
||||||
|
if (e.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
|
||||||
|
throw e;
|
||||||
|
});
|
||||||
|
|
||||||
|
// if already favorited
|
||||||
|
const exist = await PromoNotes.findOne(note.id);
|
||||||
|
|
||||||
|
if (exist != null) {
|
||||||
|
throw new ApiError(meta.errors.alreadyPromoted);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create favorite
|
||||||
|
await PromoNotes.save({
|
||||||
|
noteId: note.id,
|
||||||
|
createdAt: new Date(),
|
||||||
|
expiresAt: new Date(ps.expiresAt),
|
||||||
|
userId: note.userId,
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,8 +7,8 @@ import { makePaginationQuery } from '../../common/make-pagination-query';
|
||||||
import { Notes } from '../../../../models';
|
import { Notes } from '../../../../models';
|
||||||
import { generateMuteQuery } from '../../common/generate-mute-query';
|
import { generateMuteQuery } from '../../common/generate-mute-query';
|
||||||
import { activeUsersChart } from '../../../../services/chart';
|
import { activeUsersChart } from '../../../../services/chart';
|
||||||
import { Brackets } from 'typeorm';
|
|
||||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||||
|
import { injectPromo } from '../../common/inject-promo';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -90,6 +90,8 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
const timeline = await query.take(ps.limit!).getMany();
|
const timeline = await query.take(ps.limit!).getMany();
|
||||||
|
|
||||||
|
await injectPromo(user, timeline);
|
||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
activeUsersChart.update(user);
|
activeUsersChart.update(user);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
|
||||||
import { generateMuteQuery } from '../../common/generate-mute-query';
|
import { generateMuteQuery } from '../../common/generate-mute-query';
|
||||||
import { activeUsersChart } from '../../../../services/chart';
|
import { activeUsersChart } from '../../../../services/chart';
|
||||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||||
|
import { injectPromo } from '../../common/inject-promo';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -169,6 +170,8 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
const timeline = await query.take(ps.limit!).getMany();
|
const timeline = await query.take(ps.limit!).getMany();
|
||||||
|
|
||||||
|
await injectPromo(user, timeline);
|
||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
activeUsersChart.update(user);
|
activeUsersChart.update(user);
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { generateVisibilityQuery } from '../../common/generate-visibility-query'
|
||||||
import { activeUsersChart } from '../../../../services/chart';
|
import { activeUsersChart } from '../../../../services/chart';
|
||||||
import { Brackets } from 'typeorm';
|
import { Brackets } from 'typeorm';
|
||||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||||
|
import { injectPromo } from '../../common/inject-promo';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -122,6 +123,8 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
const timeline = await query.take(ps.limit!).getMany();
|
const timeline = await query.take(ps.limit!).getMany();
|
||||||
|
|
||||||
|
await injectPromo(user, timeline);
|
||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
activeUsersChart.update(user);
|
activeUsersChart.update(user);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { generateMuteQuery } from '../../common/generate-mute-query';
|
||||||
import { activeUsersChart } from '../../../../services/chart';
|
import { activeUsersChart } from '../../../../services/chart';
|
||||||
import { Brackets } from 'typeorm';
|
import { Brackets } from 'typeorm';
|
||||||
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
import { generateRepliesQuery } from '../../common/generate-replies-query';
|
||||||
|
import { injectPromo } from '../../common/inject-promo';
|
||||||
|
|
||||||
export const meta = {
|
export const meta = {
|
||||||
desc: {
|
desc: {
|
||||||
|
@ -155,6 +156,8 @@ export default define(meta, async (ps, user) => {
|
||||||
|
|
||||||
const timeline = await query.take(ps.limit!).getMany();
|
const timeline = await query.take(ps.limit!).getMany();
|
||||||
|
|
||||||
|
await injectPromo(user, timeline);
|
||||||
|
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
activeUsersChart.update(user);
|
activeUsersChart.update(user);
|
||||||
|
|
Loading…
Reference in a new issue