From 71c42bef9b652b2f5c3a66e068caa64aa87cf5ec Mon Sep 17 00:00:00 2001
From: nenohi <kimutipartylove@gmail.com>
Date: Wed, 15 Feb 2023 14:29:40 +0900
Subject: [PATCH] =?UTF-8?q?=E5=BA=83=E5=91=8A=E9=96=8B=E5=A7=8B=E6=99=82?=
 =?UTF-8?q?=E6=9C=9F=E3=81=AE=E8=A8=AD=E5=AE=9A=20(#9944)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* 広告開始時期の設定

* 過去のものも表示するように
---
 locales/ja-JP.yml                                 |  1 +
 packages/backend/migration/1676438468213-ad3.js   |  9 +++++++++
 packages/backend/src/models/entities/Ad.ts        |  6 ++++++
 .../src/server/api/endpoints/admin/ad/create.ts   |  4 +++-
 .../src/server/api/endpoints/admin/ad/list.ts     |  2 --
 .../src/server/api/endpoints/admin/ad/update.ts   |  4 +++-
 packages/backend/src/server/api/endpoints/meta.ts |  3 ++-
 packages/frontend/src/pages/admin/ads.vue         | 15 ++++++++++++---
 8 files changed, 36 insertions(+), 8 deletions(-)
 create mode 100644 packages/backend/migration/1676438468213-ad3.js

diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 3c7645ec91..b1f484f6b2 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -777,6 +777,7 @@ popularPosts: "人気の投稿"
 shareWithNote: "ノートで共有"
 ads: "広告"
 expiration: "期限"
+startingperiod: "開始期間"
 memo: "メモ"
 priority: "優先度"
 high: "高"
diff --git a/packages/backend/migration/1676438468213-ad3.js b/packages/backend/migration/1676438468213-ad3.js
new file mode 100644
index 0000000000..082bb31325
--- /dev/null
+++ b/packages/backend/migration/1676438468213-ad3.js
@@ -0,0 +1,9 @@
+export class ad1676438468213 {
+	name = 'ad1676438468213';
+	async up(queryRunner) {
+			await queryRunner.query(`ALTER TABLE "ad" ADD "startAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
+	}
+	async down(queryRunner) {
+		await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "startAt"`);
+	}
+}
diff --git a/packages/backend/src/models/entities/Ad.ts b/packages/backend/src/models/entities/Ad.ts
index 36b758f205..c87932adb6 100644
--- a/packages/backend/src/models/entities/Ad.ts
+++ b/packages/backend/src/models/entities/Ad.ts
@@ -18,6 +18,12 @@ export class Ad {
 	})
 	public expiresAt: Date;
 
+	@Index()
+	@Column('timestamp with time zone', {
+		comment: 'The expired date of the Ad.',
+	})
+	public startAt: Date;
+
 	@Column('varchar', {
 		length: 32, nullable: false,
 	})
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/create.ts b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
index 8fcbde591b..ba128c75a7 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/create.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/create.ts
@@ -20,9 +20,10 @@ export const paramDef = {
 		priority: { type: 'string' },
 		ratio: { type: 'integer' },
 		expiresAt: { type: 'integer' },
+		startAt: { type: 'integer' },
 		imageUrl: { type: 'string', minLength: 1 },
 	},
-	required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'imageUrl'],
+	required: ['url', 'memo', 'place', 'priority', 'ratio', 'expiresAt', 'startAt', 'imageUrl'],
 } as const;
 
 // eslint-disable-next-line import/no-default-export
@@ -39,6 +40,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				id: this.idService.genId(),
 				createdAt: new Date(),
 				expiresAt: new Date(ps.expiresAt),
+				startAt: new Date(ps.startAt),
 				url: ps.url,
 				imageUrl: ps.imageUrl,
 				priority: ps.priority,
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/list.ts b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
index 29e245ab95..905036c40f 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/list.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/list.ts
@@ -32,8 +32,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 	) {
 		super(meta, paramDef, async (ps, me) => {
 			const query = this.queryService.makePaginationQuery(this.adsRepository.createQueryBuilder('ad'), ps.sinceId, ps.untilId)
-				.andWhere('ad.expiresAt > :now', { now: new Date() });
-
 			const ads = await query.take(ps.limit).getMany();
 
 			return ads;
diff --git a/packages/backend/src/server/api/endpoints/admin/ad/update.ts b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
index 08e3c96ca9..75f6f1d5ad 100644
--- a/packages/backend/src/server/api/endpoints/admin/ad/update.ts
+++ b/packages/backend/src/server/api/endpoints/admin/ad/update.ts
@@ -30,8 +30,9 @@ export const paramDef = {
 		priority: { type: 'string' },
 		ratio: { type: 'integer' },
 		expiresAt: { type: 'integer' },
+		startAt: { type: 'integer' },
 	},
-	required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt'],
+	required: ['id', 'memo', 'url', 'imageUrl', 'place', 'priority', 'ratio', 'expiresAt', 'startAt'],
 } as const;
 
 // eslint-disable-next-line import/no-default-export
@@ -54,6 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 				memo: ps.memo,
 				imageUrl: ps.imageUrl,
 				expiresAt: new Date(ps.expiresAt),
+				startAt: new Date(ps.startAt),
 			});
 		});
 	}
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index 2fa7a09d49..823c3f72b0 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -1,4 +1,4 @@
-import { IsNull, MoreThan } from 'typeorm';
+import { IsNull, LessThanOrEqual, MoreThan } from 'typeorm';
 import { Inject, Injectable } from '@nestjs/common';
 import type { AdsRepository, EmojisRepository, UsersRepository } from '@/models/index.js';
 import { MAX_NOTE_TEXT_LENGTH, DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js';
@@ -262,6 +262,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
 			const ads = await this.adsRepository.find({
 				where: {
 					expiresAt: MoreThan(new Date()),
+					startAt: LessThanOrEqual(new Date()),
 				},
 			});
 
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 701ec31b65..88131fce89 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -29,6 +29,9 @@
 					<MkInput v-model="ad.ratio" type="number">
 						<template #label>{{ i18n.ts.ratio }}</template>
 					</MkInput>
+					<MkInput v-model="ad.startAt" type="datetime-local">
+						<template #label>{{ i18n.ts.startingperiod }}</template>
+					</MkInput>
 					<MkInput v-model="ad.expiresAt" type="datetime-local">
 						<template #label>{{ i18n.ts.expiration }}</template>
 					</MkInput>
@@ -66,11 +69,14 @@ const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
 
 os.api('admin/ad/list').then(adsResponse => {
 	ads = adsResponse.map(r => {
-		const date = new Date(r.expiresAt);
-		date.setMilliseconds(date.getMilliseconds() - localTimeDiff);
+		const exdate = new Date(r.expiresAt);
+		const stdate = new Date(r.startAt);
+		exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
+		stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
 		return {
 			...r,
-			expiresAt: date.toISOString().slice(0, 16),
+			expiresAt: exdate.toISOString().slice(0, 16),
+			startAt: stdate.toISOString().slice(0, 16),
 		};
 	});
 });
@@ -85,6 +91,7 @@ function add() {
 		url: '',
 		imageUrl: null,
 		expiresAt: null,
+		startAt: null,
 	});
 }
 
@@ -106,11 +113,13 @@ function save(ad) {
 		os.apiWithDialog('admin/ad/create', {
 			...ad,
 			expiresAt: new Date(ad.expiresAt).getTime(),
+			startAt: new Date(ad.startAt).getTime(),
 		});
 	} else {
 		os.apiWithDialog('admin/ad/update', {
 			...ad,
 			expiresAt: new Date(ad.expiresAt).getTime(),
+			startAt: new Date(ad.startAt).getTime(),
 		});
 	}
 }