This commit is contained in:
mattyatea 2024-05-29 01:19:08 +09:00
parent 07b4338eff
commit 653f5cfbc2
13 changed files with 203 additions and 265 deletions

View File

@ -0,0 +1,11 @@
export class Gapikey1716911535226 {
name = 'Gapikey1716911535226'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "googleAnalyticsId" character varying(1024)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "googleAnalyticsId"`);
}
}

View File

@ -73,6 +73,7 @@ export class MetaEntityService {
bannerLight: instance.bannerLight, bannerLight: instance.bannerLight,
iconDark: instance.iconDark, iconDark: instance.iconDark,
iconLight: instance.iconLight, iconLight: instance.iconLight,
googleAnalyticsId: instance.googleAnalyticsId,
enableHcaptcha: instance.enableHcaptcha, enableHcaptcha: instance.enableHcaptcha,
hcaptchaSiteKey: instance.hcaptchaSiteKey, hcaptchaSiteKey: instance.hcaptchaSiteKey,
enableMcaptcha: instance.enableMcaptcha, enableMcaptcha: instance.enableMcaptcha,
@ -88,6 +89,7 @@ export class MetaEntityService {
bannerUrl: instance.bannerUrl, bannerUrl: instance.bannerUrl,
infoImageUrl: instance.infoImageUrl, infoImageUrl: instance.infoImageUrl,
serverErrorImageUrl: instance.serverErrorImageUrl, serverErrorImageUrl: instance.serverErrorImageUrl,
googleAnalyticsId: instance.googleAnalyticsId,
notFoundImageUrl: instance.notFoundImageUrl, notFoundImageUrl: instance.notFoundImageUrl,
iconUrl: instance.iconUrl, iconUrl: instance.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl, backgroundImageUrl: instance.backgroundImageUrl,

View File

@ -139,6 +139,11 @@ export class MiMeta {
nullable: true, nullable: true,
}) })
public serverErrorImageUrl: string | null; public serverErrorImageUrl: string | null;
@Column('varchar', {
length: 1024,
nullable: true,
})
public googleAnalyticsId: string | null;
@Column('varchar', { @Column('varchar', {
length: 1024, length: 1024,

View File

@ -50,6 +50,7 @@ export const paramDef = {
mascotImageUrl: { type: 'string', nullable: true }, mascotImageUrl: { type: 'string', nullable: true },
bannerUrl: { type: 'string', nullable: true }, bannerUrl: { type: 'string', nullable: true },
serverErrorImageUrl: { type: 'string', nullable: true }, serverErrorImageUrl: { type: 'string', nullable: true },
googleAnalyticsId: { type: 'string', nullable: true },
infoImageUrl: { type: 'string', nullable: true }, infoImageUrl: { type: 'string', nullable: true },
notFoundImageUrl: { type: 'string', nullable: true }, notFoundImageUrl: { type: 'string', nullable: true },
iconUrl: { type: 'string', nullable: true }, iconUrl: { type: 'string', nullable: true },
@ -257,7 +258,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
if (ps.serverErrorImageUrl !== undefined) { if (ps.serverErrorImageUrl !== undefined) {
set.serverErrorImageUrl = ps.serverErrorImageUrl; set.serverErrorImageUrl = ps.serverErrorImageUrl;
} }
if (ps.googleAnalyticsId !== undefined) {
set.googleAnalyticsId = ps.googleAnalyticsId;
}
if (ps.enableProxyCheckio !== undefined) { if (ps.enableProxyCheckio !== undefined) {
set.enableProxyCheckio = ps.enableProxyCheckio; set.enableProxyCheckio = ps.enableProxyCheckio;
} }

View File

@ -191,6 +191,7 @@ export class ClientServerService {
appleTouchIcon: meta.app512IconUrl, appleTouchIcon: meta.app512IconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg', serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
googleAnalyticsId: meta.googleAnalyticsId ?? null,
infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg', infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg', notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
instanceUrl: this.config.url, instanceUrl: this.config.url,

View File

@ -35,6 +35,14 @@ html
link(rel='prefetch' href=serverErrorImageUrl) link(rel='prefetch' href=serverErrorImageUrl)
link(rel='prefetch' href=infoImageUrl) link(rel='prefetch' href=infoImageUrl)
link(rel='prefetch' href=notFoundImageUrl) link(rel='prefetch' href=notFoundImageUrl)
if googleAnalyticsId
script(async src='https://www.googletagmanager.com/gtag/js?id='+ googleAnalyticsId)
script.
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '#{googleAnalyticsId}');
//- https://github.com/misskey-dev/misskey/issues/9842 //- https://github.com/misskey-dev/misskey/issues/9842
link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v3.3.0') link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v3.3.0')
link(rel='modulepreload' href=`/vite/${clientEntry.file}`) link(rel='modulepreload' href=`/vite/${clientEntry.file}`)

View File

@ -43,21 +43,28 @@ export default defineComponent({
setup(props, { slots, expose }) { setup(props, { slots, expose }) {
const $style = useCssModule(); // 使 const $style = useCssModule(); // 使
const dateTextCache = new Map<string, string>();
function getDateText(time: string) { function getDateText(time: string) {
if (dateTextCache.has(time)) {
return dateTextCache.get(time)!;
}
const date = new Date(time).getDate(); const date = new Date(time).getDate();
const month = new Date(time).getMonth() + 1; const month = new Date(time).getMonth() + 1;
return i18n.tsx.monthAndDay({ const text = i18n.tsx.monthAndDay({
month: month.toString(), month: month.toString(),
day: date.toString(), day: date.toString(),
}); });
dateTextCache.set(time, text);
return text;
} }
if (props.items.length === 0) return; if (props.items.length === 0) return;
const renderChildrenImpl = () => props.items.map((item, i) => { const renderChildrenImpl = () => {
if (!slots || !slots.default) return; const slotContent = slots.default ? slots.default : () => [];
return props.items.map((item, i) => {
const el = slots.default({ const el = slotContent({
item: item, item: item,
})[0]; })[0];
if (el.key == null && item.id) el.key = item.id; if (el.key == null && item.id) el.key = item.id;
@ -102,6 +109,7 @@ export default defineComponent({
} }
} }
}); });
};
const renderChildren = () => { const renderChildren = () => {
const children = renderChildrenImpl(); const children = renderChildrenImpl();
@ -120,14 +128,12 @@ export default defineComponent({
function onBeforeLeave(element: Element) { function onBeforeLeave(element: Element) {
const el = element as HTMLElement; const el = element as HTMLElement;
el.style.top = `${el.offsetTop}px`; el.classList.add('before-leave');
el.style.left = `${el.offsetLeft}px`;
} }
function onLeaveCancelled(element: Element) { function onLeaveCancelled(element: Element) {
const el = element as HTMLElement; const el = element as HTMLElement;
el.style.top = ''; el.classList.remove('before-leave');
el.style.left = '';
} }
// eslint-disable-next-line vue/no-setup-props-destructure // eslint-disable-next-line vue/no-setup-props-destructure
@ -246,5 +252,8 @@ export default defineComponent({
.date-2-icon { .date-2-icon {
margin-left: 8px; margin-left: 8px;
} }
</style>
.before-leave {
position: absolute !important;
}
</style>

View File

@ -45,7 +45,6 @@ const props = defineProps<{
disableAutoLoad?: boolean; disableAutoLoad?: boolean;
withCw?: boolean; withCw?: boolean;
}>(); }>();
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
defineExpose({ defineExpose({

View File

@ -234,69 +234,43 @@ const reload = (): Promise<void> => {
return init(); return init();
}; };
const fetchMore = async (): Promise<void> => { async function fetchMore(): Promise<void> {
if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return;
moreFetching.value = true; moreFetching.value = true;
try {
const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {}; const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {};
await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, { const response = await misskeyApi<MisskeyEntity[]>(props.pagination.endpoint, {
...params, ...params,
limit: SECOND_FETCH_LIMIT, limit: SECOND_FETCH_LIMIT,
...(props.pagination.offsetMode ? { ...(props.pagination.offsetMode ? { offset: offset.value } : { untilId: Array.from(items.value.keys()).pop() }),
offset: offset.value, });
} : {
untilId: Array.from(items.value.keys()).at(-1),
}),
}).then(res => {
for (let i = 0; i < res.length; i++) {
const item = res[i];
if (i === 10) item._shouldInsertAd_ = true;
}
const reverseConcat = _res => { const isReversed = props.pagination.reversed;
const oldHeight = scrollableElement.value ? scrollableElement.value.scrollHeight : getBodyScrollHeight(); if (isReversed) {
const oldScroll = scrollableElement.value ? scrollableElement.value.scrollTop : window.scrollY; const oldHeight = scrollableElement.value?.scrollHeight || 0;
const oldScroll = scrollableElement.value?.scrollTop || 0;
items.value = concatMapWithArray(items.value, _res); items.value = concatMapWithArray(items.value, response);
return nextTick(() => { await nextTick();
if (scrollableElement.value) { if (scrollableElement.value) {
scroll(scrollableElement.value, { top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight), behavior: 'instant' }); scroll(scrollableElement.value, {
top: oldScroll + (scrollableElement.value.scrollHeight - oldHeight),
behavior: 'instant',
});
}
} else { } else {
window.scroll({ top: oldScroll + (getBodyScrollHeight() - oldHeight), behavior: 'instant' }); items.value = concatMapWithArray(items.value, response);
} }
return nextTick(); more.value = response.length > 0;
}); } catch (error) {
}; console.error(error);
} finally {
if (res.length === 0) {
if (props.pagination.reversed) {
reverseConcat(res).then(() => {
more.value = false;
moreFetching.value = false;
});
} else {
items.value = concatMapWithArray(items.value, res);
more.value = false;
moreFetching.value = false;
}
} else {
if (props.pagination.reversed) {
reverseConcat(res).then(() => {
more.value = true;
moreFetching.value = false;
});
} else {
items.value = concatMapWithArray(items.value, res);
more.value = true;
moreFetching.value = false; moreFetching.value = false;
} }
} }
offset.value += res.length;
}, err => {
moreFetching.value = false;
});
};
const fetchMoreAhead = async (): Promise<void> => { const fetchMoreAhead = async (): Promise<void> => {
if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return; if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return;

View File

@ -1,8 +1,3 @@
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template> <template>
<MkPullToRefresh ref="prComponent" :refresher="() => reloadTimeline()"> <MkPullToRefresh ref="prComponent" :refresher="() => reloadTimeline()">
<MkNotes <MkNotes
@ -55,24 +50,13 @@ const emit = defineEmits<{
provide('inTimeline', true); provide('inTimeline', true);
provide('inChannel', computed(() => props.src === 'channel')); provide('inChannel', computed(() => props.src === 'channel'));
type TimelineQueryType = {
antennaId?: string,
withRenotes?: boolean,
withReplies?: boolean,
withFiles?: boolean,
visibility?: string,
listId?: string,
channelId?: string,
roleId?: string
}
const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>(); const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>();
const tlComponent = shallowRef<InstanceType<typeof MkNotes>>(); const tlComponent = shallowRef<InstanceType<typeof MkNotes>>();
let tlNotesCount = 0; let tlNotesCount = 0;
function prepend(note) { function prepend(note) {
if (tlComponent.value == null) return; if (!tlComponent.value) return;
tlNotesCount++; tlNotesCount++;
@ -96,11 +80,8 @@ let paginationQuery: Paging | null = null;
const stream = useStream(); const stream = useStream();
function connectChannel() { function connectChannel() {
if (props.src === 'antenna') { if (props.src === 'antenna' && props.antenna) {
if (props.antenna == null) return; connection = stream.useChannel('antenna', { antennaId: props.antenna });
connection = stream.useChannel('antenna', {
antennaId: props.antenna,
});
} else if (props.src === 'home') { } else if (props.src === 'home') {
connection = stream.useChannel('homeTimeline', { connection = stream.useChannel('homeTimeline', {
withRenotes: props.withRenotes, withRenotes: props.withRenotes,
@ -119,15 +100,9 @@ function connectChannel() {
withRenotes: props.withRenotes, withRenotes: props.withRenotes,
withReplies: props.withReplies, withReplies: props.withReplies,
}); });
connection.on('note', prepend); } else if (props.src === 'social' || props.src === 'global') {
} else if (props.src === 'social') { const channel = props.src === 'social' ? 'hybridTimeline' : 'globalTimeline';
connection = stream.useChannel('hybridTimeline', { connection = stream.useChannel(channel, {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
});
} else if (props.src === 'global') {
connection = stream.useChannel('globalTimeline', {
withRenotes: props.withRenotes, withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined, withFiles: props.onlyFiles ? true : undefined,
}); });
@ -136,34 +111,25 @@ function connectChannel() {
connection.on('mention', prepend); connection.on('mention', prepend);
} else if (props.src === 'directs') { } else if (props.src === 'directs') {
const onNote = note => { const onNote = note => {
if (note.visibility === 'specified') { if (note.visibility === 'specified') prepend(note);
prepend(note);
}
}; };
connection = stream.useChannel('main'); connection = stream.useChannel('main');
connection.on('mention', onNote); connection.on('mention', onNote);
} else if (props.src === 'list') { } else if (props.src === 'list' && props.list) {
if (props.list == null) return;
connection = stream.useChannel('userList', { connection = stream.useChannel('userList', {
withRenotes: props.withRenotes, withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined, withFiles: props.onlyFiles ? true : undefined,
listId: props.list, listId: props.list,
}); });
} else if (props.src === 'channel') { } else if (props.src === 'channel' && props.channel) {
if (props.channel == null) return; connection = stream.useChannel('channel', { channelId: props.channel });
connection = stream.useChannel('channel', { } else if (props.src === 'role' && props.role) {
channelId: props.channel, connection = stream.useChannel('roleTimeline', { roleId: props.role });
});
} else if (props.src === 'role') {
if (props.role == null) return;
connection = stream.useChannel('roleTimeline', {
roleId: props.role,
});
} }
if (props.src.startsWith('custom-timeline')) {
return; if (props.src !== 'directs' && props.src !== 'mentions') {
connection?.on('note', prepend);
} }
if (props.src !== 'directs' && props.src !== 'mentions') connection?.on('note', prepend);
} }
function disconnectChannel() { function disconnectChannel() {
@ -172,91 +138,47 @@ function disconnectChannel() {
} }
function updatePaginationQuery() { function updatePaginationQuery() {
let endpoint: keyof Misskey.Endpoints | null; const endpoints = {
let query: TimelineQueryType | null; antenna: 'antennas/notes',
home: 'notes/timeline',
local: 'notes/local-timeline',
social: 'notes/hybrid-timeline',
global: 'notes/global-timeline',
media: 'notes/hybrid-timeline',
mentions: 'notes/mentions',
directs: 'notes/mentions',
list: 'notes/user-list-timeline',
channel: 'channels/timeline',
role: 'roles/notes',
};
if (props.src === 'antenna') { const queries = {
endpoint = 'antennas/notes'; antenna: { antennaId: props.antenna },
query = { home: { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined },
antennaId: props.antenna, local: { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined },
social: { withRenotes: props.withRenotes, withReplies: props.withReplies, withFiles: props.onlyFiles ? true : undefined },
global: { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined },
media: { withFiles: true, withRenotes: props.withRenotes, withReplies: false },
mentions: null,
directs: { visibility: 'specified' },
list: { withRenotes: props.withRenotes, withFiles: props.onlyFiles ? true : undefined, listId: props.list },
channel: { channelId: props.channel },
role: { roleId: props.role },
}; };
} else if (props.src === 'home') {
endpoint = 'notes/timeline'; if (props.src.startsWith('custom-timeline')) {
query = { paginationQuery = {
withRenotes: props.withRenotes, endpoint: 'notes/any-local-timeline',
withFiles: props.onlyFiles ? true : undefined, limit: 10,
}; params: {
} else if (props.src === 'local') {
endpoint = 'notes/local-timeline';
query = {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
};
} else if (props.src === 'social') {
endpoint = 'notes/hybrid-timeline';
query = {
withRenotes: props.withRenotes,
withReplies: props.withReplies,
withFiles: props.onlyFiles ? true : undefined,
};
} else if (props.src === 'global') {
endpoint = 'notes/global-timeline';
query = {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
};
} else if (props.src === 'media') {
endpoint = 'notes/hybrid-timeline';
query = {
withFiles: true,
withRenotes: props.withRenotes,
withReplies: false,
};
} else if (props.src === 'mentions') {
endpoint = 'notes/mentions';
query = null;
} else if (props.src === 'directs') {
endpoint = 'notes/mentions';
query = {
visibility: 'specified',
};
} else if (props.src === 'list') {
endpoint = 'notes/user-list-timeline';
query = {
withRenotes: props.withRenotes,
withFiles: props.onlyFiles ? true : undefined,
listId: props.list,
};
} else if (props.src === 'channel') {
endpoint = 'channels/timeline';
query = {
channelId: props.channel,
};
} else if (props.src === 'role') {
endpoint = 'roles/notes';
query = {
roleId: props.role,
};
} else if (props.src.startsWith('custom-timeline')) {
endpoint = 'notes/any-local-timeline';
query = {
host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split('-')[2]}`], host: defaultStore.state[`remoteLocalTimelineDomain${props.src.split('-')[2]}`],
remoteToken: defaultStore.state[`remoteLocalTimelineToken${props.src.split('-')[2]}`], remoteToken: defaultStore.state[`remoteLocalTimelineToken${props.src.split('-')[2]}`],
},
}; };
} else { } else {
endpoint = null; const endpoint = endpoints[props.src];
query = null; const query = queries[props.src];
} paginationQuery = endpoint && query ? { endpoint, limit: 10, params: query } : null;
if (endpoint && query) {
paginationQuery = {
endpoint: endpoint,
limit: 10,
params: query,
};
} else {
paginationQuery = null;
} }
} }
@ -265,12 +187,9 @@ function refreshEndpointAndChannel() {
disconnectChannel(); disconnectChannel();
connectChannel(); connectChannel();
} }
updatePaginationQuery(); updatePaginationQuery();
} }
// withRenotes
// IDTL
watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel); watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel);
// //
@ -282,13 +201,11 @@ onUnmounted(() => {
function reloadTimeline() { function reloadTimeline() {
return new Promise<void>((res) => { return new Promise<void>((res) => {
if (tlComponent.value == null) return; if (!tlComponent.value) return;
tlNotesCount = 0; tlNotesCount = 0;
tlComponent.value.pagingComponent?.reload().then(() => { tlComponent.value.pagingComponent?.reload().then(() => res());
res();
});
}); });
} }

View File

@ -32,6 +32,8 @@ export const instance: Misskey.entities.MetaDetailed = reactive(cachedMeta ?? {}
export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL); export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL);
export const googleAnalyticsId = computed(() => instance.googleAnalyticsId ?? null);
export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO_IMAGE_URL); export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO_IMAGE_URL);
export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL); export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);

View File

@ -77,6 +77,10 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #prefix><i class="ti ti-link"></i></template> <template #prefix><i class="ti ti-link"></i></template>
<template #label>{{ i18n.ts.somethingHappened }}</template> <template #label>{{ i18n.ts.somethingHappened }}</template>
</MkInput> </MkInput>
<MkInput v-model="googleAnalyticsId" type="url">
<template #prefix><i class="ti ti-link"></i></template>
<template #label>googleAnal </template>
</MkInput>
<MkColorInput v-model="themeColor"> <MkColorInput v-model="themeColor">
<template #label>{{ i18n.ts.themeColor }}</template> <template #label>{{ i18n.ts.themeColor }}</template>
@ -144,6 +148,8 @@ const themeColor = ref<string | null>(null);
const defaultLightTheme = ref<string | null>(null); const defaultLightTheme = ref<string | null>(null);
const defaultDarkTheme = ref<string | null>(null); const defaultDarkTheme = ref<string | null>(null);
const serverErrorImageUrl = ref<string | null>(null); const serverErrorImageUrl = ref<string | null>(null);
const googleAnalyticsId = ref<string | null>(null);
const infoImageUrl = ref<string | null>(null); const infoImageUrl = ref<string | null>(null);
const notFoundImageUrl = ref<string | null>(null); const notFoundImageUrl = ref<string | null>(null);
const repositoryUrl = ref<string | null>(null); const repositoryUrl = ref<string | null>(null);
@ -189,6 +195,7 @@ function save() {
infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value, infoImageUrl: infoImageUrl.value === '' ? null : infoImageUrl.value,
notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value, notFoundImageUrl: notFoundImageUrl.value === '' ? null : notFoundImageUrl.value,
serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value, serverErrorImageUrl: serverErrorImageUrl.value === '' ? null : serverErrorImageUrl.value,
googleAnalyticsId: googleAnalyticsId.value === '' ? null : googleAnalyticsId.value,
repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value, repositoryUrl: repositoryUrl.value === '' ? null : repositoryUrl.value,
feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value,
manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)),