2023-07-27 07:31:52 +02:00
|
|
|
<!--
|
2024-02-13 16:50:11 +01:00
|
|
|
SPDX-FileCopyrightText: syuilo and other misskey contributors
|
2023-07-27 07:31:52 +02:00
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
-->
|
|
|
|
|
2021-05-04 14:15:57 +02:00
|
|
|
<template>
|
2022-06-20 10:38:49 +02:00
|
|
|
<MkStickyContainer>
|
2023-09-29 05:27:05 +02:00
|
|
|
<template #header>
|
2023-10-29 08:09:20 +01:00
|
|
|
<XHeader :actions="headerActions" :tabs="headerTabs"/>
|
2023-09-29 05:27:05 +02:00
|
|
|
</template>
|
2023-05-19 13:52:15 +02:00
|
|
|
<MkSpacer :contentMax="900">
|
2023-11-22 10:46:27 +01:00
|
|
|
<MkSelect v-model="filterType" :class="$style.input" @update:modelValue="filterItems">
|
2023-11-21 11:50:06 +01:00
|
|
|
<template #label>{{ i18n.ts.state }}</template>
|
2023-11-22 10:46:27 +01:00
|
|
|
<option value="all">{{ i18n.ts.all }}</option>
|
|
|
|
<option value="publishing">{{ i18n.ts.publishing }}</option>
|
|
|
|
<option value="expired">{{ i18n.ts.expired }}</option>
|
2023-11-21 11:50:06 +01:00
|
|
|
</MkSelect>
|
2023-05-19 13:52:15 +02:00
|
|
|
<div>
|
|
|
|
<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
|
2023-11-21 11:50:06 +01:00
|
|
|
<MkAd v-if="ad.url" :key="ad.id" :specify="ad"/>
|
2023-01-05 13:04:56 +01:00
|
|
|
<MkInput v-model="ad.url" type="url">
|
2022-06-20 10:38:49 +02:00
|
|
|
<template #label>URL</template>
|
|
|
|
</MkInput>
|
2023-10-30 05:41:40 +01:00
|
|
|
<MkInput v-model="ad.imageUrl" type="url">
|
2022-06-20 10:38:49 +02:00
|
|
|
<template #label>{{ i18n.ts.imageUrl }}</template>
|
|
|
|
</MkInput>
|
2023-01-07 07:09:46 +01:00
|
|
|
<MkRadios v-model="ad.place">
|
2022-06-20 10:38:49 +02:00
|
|
|
<template #label>Form</template>
|
|
|
|
<option value="square">square</option>
|
|
|
|
<option value="horizontal">horizontal</option>
|
|
|
|
<option value="horizontal-big">horizontal-big</option>
|
2023-01-07 07:09:46 +01:00
|
|
|
</MkRadios>
|
2022-06-20 10:38:49 +02:00
|
|
|
<!--
|
2021-10-23 21:03:07 +02:00
|
|
|
<div style="margin: 32px 0;">
|
2022-05-14 14:33:41 +02:00
|
|
|
{{ i18n.ts.priority }}
|
|
|
|
<MkRadio v-model="ad.priority" value="high">{{ i18n.ts.high }}</MkRadio>
|
|
|
|
<MkRadio v-model="ad.priority" value="middle">{{ i18n.ts.middle }}</MkRadio>
|
|
|
|
<MkRadio v-model="ad.priority" value="low">{{ i18n.ts.low }}</MkRadio>
|
2021-10-23 21:03:07 +02:00
|
|
|
</div>
|
|
|
|
-->
|
2022-06-20 10:38:49 +02:00
|
|
|
<FormSplit>
|
|
|
|
<MkInput v-model="ad.ratio" type="number">
|
|
|
|
<template #label>{{ i18n.ts.ratio }}</template>
|
|
|
|
</MkInput>
|
2023-02-15 06:31:59 +01:00
|
|
|
<MkInput v-model="ad.startsAt" type="datetime-local">
|
2023-02-15 06:29:40 +01:00
|
|
|
<template #label>{{ i18n.ts.startingperiod }}</template>
|
|
|
|
</MkInput>
|
2023-02-09 09:54:30 +01:00
|
|
|
<MkInput v-model="ad.expiresAt" type="datetime-local">
|
2022-06-20 10:38:49 +02:00
|
|
|
<template #label>{{ i18n.ts.expiration }}</template>
|
|
|
|
</MkInput>
|
|
|
|
</FormSplit>
|
2023-07-08 01:56:11 +02:00
|
|
|
<MkFolder>
|
|
|
|
<template #label>{{ i18n.ts.advancedSettings }}</template>
|
|
|
|
<span>
|
|
|
|
{{ i18n.ts._ad.timezoneinfo }}
|
|
|
|
<div v-for="(day, index) in daysOfWeek" :key="index">
|
2023-10-29 08:09:20 +01:00
|
|
|
<input
|
|
|
|
:id="`ad${ad.id}-${index}`" type="checkbox" :checked="(ad.dayOfWeek & (1 << index)) !== 0"
|
|
|
|
@change="toggleDayOfWeek(ad, index)"
|
|
|
|
>
|
2023-07-08 01:56:11 +02:00
|
|
|
<label :for="`ad${ad.id}-${index}`">{{ day }}</label>
|
|
|
|
</div>
|
|
|
|
</span>
|
|
|
|
</MkFolder>
|
2023-01-05 13:04:56 +01:00
|
|
|
<MkTextarea v-model="ad.memo">
|
2022-06-20 10:38:49 +02:00
|
|
|
<template #label>{{ i18n.ts.memo }}</template>
|
|
|
|
</MkTextarea>
|
2023-01-05 13:04:56 +01:00
|
|
|
<div class="buttons">
|
2023-10-29 08:09:20 +01:00
|
|
|
<MkButton class="button" inline primary style="margin-right: 12px;" @click="save(ad)">
|
|
|
|
<i
|
|
|
|
class="ti ti-device-floppy"
|
|
|
|
></i> {{ i18n.ts.save }}
|
|
|
|
</MkButton>
|
|
|
|
<MkButton class="button" inline danger @click="remove(ad)">
|
|
|
|
<i class="ti ti-trash"></i> {{ i18n.ts.remove }}
|
2023-09-29 05:27:05 +02:00
|
|
|
</MkButton>
|
2022-06-20 10:38:49 +02:00
|
|
|
</div>
|
2021-10-23 21:03:07 +02:00
|
|
|
</div>
|
2023-02-20 09:57:01 +01:00
|
|
|
<MkButton class="button" @click="more()">
|
|
|
|
<i class="ti ti-reload"></i>{{ i18n.ts.more }}
|
|
|
|
</MkButton>
|
2023-02-21 02:41:09 +01:00
|
|
|
</div>
|
|
|
|
</MkSpacer>
|
|
|
|
</MkStickyContainer>
|
2021-05-04 14:15:57 +02:00
|
|
|
</template>
|
|
|
|
|
2022-05-14 14:33:41 +02:00
|
|
|
<script lang="ts" setup>
|
2023-12-07 06:42:09 +01:00
|
|
|
import { ref, computed } from 'vue';
|
2023-12-26 06:19:35 +01:00
|
|
|
import * as Misskey from 'misskey-js';
|
2022-06-20 10:38:49 +02:00
|
|
|
import XHeader from './_header_.vue';
|
2022-09-06 11:21:49 +02:00
|
|
|
import MkButton from '@/components/MkButton.vue';
|
2023-01-07 07:09:46 +01:00
|
|
|
import MkInput from '@/components/MkInput.vue';
|
|
|
|
import MkTextarea from '@/components/MkTextarea.vue';
|
|
|
|
import MkRadios from '@/components/MkRadios.vue';
|
2023-07-08 01:56:11 +02:00
|
|
|
import MkFolder from '@/components/MkFolder.vue';
|
2023-11-21 11:50:06 +01:00
|
|
|
import MkSelect from '@/components/MkSelect.vue';
|
2021-12-30 13:47:48 +01:00
|
|
|
import FormSplit from '@/components/form/split.vue';
|
2023-09-19 09:37:43 +02:00
|
|
|
import * as os from '@/os.js';
|
2024-01-04 10:32:46 +01:00
|
|
|
import { misskeyApi } from '@/scripts/misskey-api.js';
|
2023-09-19 09:37:43 +02:00
|
|
|
import { i18n } from '@/i18n.js';
|
|
|
|
import { definePageMetadata } from '@/scripts/page-metadata.js';
|
2021-05-04 14:15:57 +02:00
|
|
|
|
2023-12-26 06:19:35 +01:00
|
|
|
const ads = ref<Misskey.entities.Ad[]>([]);
|
2021-05-04 14:15:57 +02:00
|
|
|
|
2023-02-12 00:23:14 +01:00
|
|
|
// ISO形式はTZがUTCになってしまうので、TZ分ずらして時間を初期化
|
|
|
|
const localTime = new Date();
|
|
|
|
const localTimeDiff = localTime.getTimezoneOffset() * 60 * 1000;
|
2023-07-08 01:56:11 +02:00
|
|
|
const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday, i18n.ts._weekday.tuesday, i18n.ts._weekday.wednesday, i18n.ts._weekday.thursday, i18n.ts._weekday.friday, i18n.ts._weekday.saturday];
|
2023-11-22 10:46:27 +01:00
|
|
|
const filterType = ref('all');
|
2023-11-21 11:50:06 +01:00
|
|
|
let publishing: boolean | null = null;
|
2023-02-12 00:23:14 +01:00
|
|
|
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
2023-11-21 11:50:06 +01:00
|
|
|
if (adsResponse != null) {
|
2023-12-07 06:42:09 +01:00
|
|
|
ads.value = adsResponse.map(r => {
|
2023-11-21 11:50:06 +01:00
|
|
|
const exdate = new Date(r.expiresAt);
|
|
|
|
const stdate = new Date(r.startsAt);
|
|
|
|
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
|
|
|
stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
|
|
|
|
return {
|
|
|
|
...r,
|
|
|
|
expiresAt: exdate.toISOString().slice(0, 16),
|
|
|
|
startsAt: stdate.toISOString().slice(0, 16),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
2022-05-14 14:33:41 +02:00
|
|
|
});
|
2021-05-04 14:15:57 +02:00
|
|
|
|
2023-11-22 10:46:27 +01:00
|
|
|
const filterItems = (v) => {
|
|
|
|
if (v === 'publishing') {
|
|
|
|
publishing = true;
|
|
|
|
} else if (v === 'expired') {
|
|
|
|
publishing = false;
|
|
|
|
} else {
|
|
|
|
publishing = null;
|
|
|
|
}
|
|
|
|
|
2023-09-29 05:27:05 +02:00
|
|
|
refresh();
|
|
|
|
};
|
2023-10-29 08:09:20 +01:00
|
|
|
|
2023-07-08 01:56:11 +02:00
|
|
|
// 選択された曜日(index)のビットフラグを操作する
|
|
|
|
function toggleDayOfWeek(ad, index) {
|
|
|
|
ad.dayOfWeek ^= 1 << index;
|
|
|
|
}
|
|
|
|
|
2022-05-14 14:33:41 +02:00
|
|
|
function add() {
|
2023-12-07 06:42:09 +01:00
|
|
|
ads.value.unshift({
|
2022-05-14 14:33:41 +02:00
|
|
|
id: null,
|
|
|
|
memo: '',
|
|
|
|
place: 'square',
|
|
|
|
priority: 'middle',
|
|
|
|
ratio: 1,
|
|
|
|
url: '',
|
|
|
|
imageUrl: null,
|
|
|
|
expiresAt: null,
|
2023-02-15 06:31:59 +01:00
|
|
|
startsAt: null,
|
2023-07-08 01:56:11 +02:00
|
|
|
dayOfWeek: 0,
|
2022-05-14 14:33:41 +02:00
|
|
|
});
|
|
|
|
}
|
2021-05-04 14:15:57 +02:00
|
|
|
|
2022-05-14 14:33:41 +02:00
|
|
|
function remove(ad) {
|
|
|
|
os.confirm({
|
|
|
|
type: 'warning',
|
2024-01-20 00:11:59 +01:00
|
|
|
text: i18n.tsx.removeAreYouSure({ x: ad.url }),
|
2022-05-14 14:33:41 +02:00
|
|
|
}).then(({ canceled }) => {
|
|
|
|
if (canceled) return;
|
2023-12-07 06:42:09 +01:00
|
|
|
ads.value = ads.value.filter(x => x !== ad);
|
2023-07-08 01:56:11 +02:00
|
|
|
if (ad.id == null) return;
|
2022-05-14 14:33:41 +02:00
|
|
|
os.apiWithDialog('admin/ad/delete', {
|
2022-06-20 10:38:49 +02:00
|
|
|
id: ad.id,
|
2023-09-29 05:27:05 +02:00
|
|
|
}).then(() => {
|
|
|
|
refresh();
|
2021-05-04 14:15:57 +02:00
|
|
|
});
|
2022-05-14 14:33:41 +02:00
|
|
|
});
|
|
|
|
}
|
2021-05-04 14:15:57 +02:00
|
|
|
|
2022-05-14 14:33:41 +02:00
|
|
|
function save(ad) {
|
|
|
|
if (ad.id == null) {
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('admin/ad/create', {
|
2022-05-14 14:33:41 +02:00
|
|
|
...ad,
|
2022-06-20 10:38:49 +02:00
|
|
|
expiresAt: new Date(ad.expiresAt).getTime(),
|
2023-02-15 06:31:59 +01:00
|
|
|
startsAt: new Date(ad.startsAt).getTime(),
|
2023-04-08 10:42:08 +02:00
|
|
|
}).then(() => {
|
|
|
|
os.alert({
|
|
|
|
type: 'success',
|
|
|
|
text: i18n.ts.saved,
|
|
|
|
});
|
|
|
|
refresh();
|
|
|
|
}).catch(err => {
|
|
|
|
os.alert({
|
|
|
|
type: 'error',
|
|
|
|
text: err,
|
|
|
|
});
|
2022-05-14 14:33:41 +02:00
|
|
|
});
|
|
|
|
} else {
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('admin/ad/update', {
|
2022-05-14 14:33:41 +02:00
|
|
|
...ad,
|
2022-06-20 10:38:49 +02:00
|
|
|
expiresAt: new Date(ad.expiresAt).getTime(),
|
2023-02-15 06:31:59 +01:00
|
|
|
startsAt: new Date(ad.startsAt).getTime(),
|
2023-04-08 10:42:08 +02:00
|
|
|
}).then(() => {
|
|
|
|
os.alert({
|
|
|
|
type: 'success',
|
|
|
|
text: i18n.ts.saved,
|
|
|
|
});
|
|
|
|
}).catch(err => {
|
|
|
|
os.alert({
|
|
|
|
type: 'error',
|
|
|
|
text: err,
|
|
|
|
});
|
2022-05-14 14:33:41 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
2023-10-29 08:09:20 +01:00
|
|
|
|
2023-02-20 09:57:01 +01:00
|
|
|
function more() {
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
|
2023-11-21 11:50:06 +01:00
|
|
|
if (adsResponse == null) return;
|
2023-12-07 06:42:09 +01:00
|
|
|
ads.value = ads.value.concat(adsResponse.map(r => {
|
2023-02-20 09:57:01 +01:00
|
|
|
const exdate = new Date(r.expiresAt);
|
|
|
|
const stdate = new Date(r.startsAt);
|
|
|
|
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
|
|
|
stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
|
|
|
|
return {
|
|
|
|
...r,
|
|
|
|
expiresAt: exdate.toISOString().slice(0, 16),
|
|
|
|
startsAt: stdate.toISOString().slice(0, 16),
|
|
|
|
};
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
}
|
2023-04-08 10:42:08 +02:00
|
|
|
|
|
|
|
function refresh() {
|
2024-01-04 10:32:46 +01:00
|
|
|
misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
|
2023-11-21 11:50:06 +01:00
|
|
|
if (adsResponse == null) return;
|
2023-12-07 06:42:09 +01:00
|
|
|
ads.value = adsResponse.map(r => {
|
2023-04-08 10:42:08 +02:00
|
|
|
const exdate = new Date(r.expiresAt);
|
|
|
|
const stdate = new Date(r.startsAt);
|
|
|
|
exdate.setMilliseconds(exdate.getMilliseconds() - localTimeDiff);
|
|
|
|
stdate.setMilliseconds(stdate.getMilliseconds() - localTimeDiff);
|
|
|
|
return {
|
|
|
|
...r,
|
|
|
|
expiresAt: exdate.toISOString().slice(0, 16),
|
|
|
|
startsAt: stdate.toISOString().slice(0, 16),
|
|
|
|
};
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
refresh();
|
|
|
|
|
2023-12-07 06:42:09 +01:00
|
|
|
const headerActions = computed(() => [{
|
2022-06-20 10:38:49 +02:00
|
|
|
asFullButton: true,
|
2022-12-19 11:01:30 +01:00
|
|
|
icon: 'ti ti-plus',
|
2022-06-20 10:38:49 +02:00
|
|
|
text: i18n.ts.add,
|
|
|
|
handler: add,
|
|
|
|
}]);
|
|
|
|
|
2023-12-07 06:42:09 +01:00
|
|
|
const headerTabs = computed(() => []);
|
2022-06-20 10:38:49 +02:00
|
|
|
|
|
|
|
definePageMetadata({
|
|
|
|
title: i18n.ts.ads,
|
2022-12-19 11:01:30 +01:00
|
|
|
icon: 'ti ti-ad',
|
2021-05-04 14:15:57 +02:00
|
|
|
});
|
|
|
|
</script>
|
|
|
|
|
2023-05-19 13:52:15 +02:00
|
|
|
<style lang="scss" module>
|
|
|
|
.ad {
|
|
|
|
padding: 32px;
|
2021-12-10 07:33:01 +01:00
|
|
|
|
2023-05-19 13:52:15 +02:00
|
|
|
&:not(:last-child) {
|
|
|
|
margin-bottom: var(--margin);
|
2021-12-10 07:33:01 +01:00
|
|
|
}
|
2021-05-04 14:15:57 +02:00
|
|
|
}
|
2023-11-21 11:50:06 +01:00
|
|
|
.input {
|
|
|
|
margin-bottom: 32px;
|
|
|
|
}
|
2021-05-04 14:15:57 +02:00
|
|
|
</style>
|