Merge branch 'develop' into hazelnoot/579-reject-reports

# Conflicts:
#	packages/frontend/src/pages/instance-info.vue
This commit is contained in:
Hazel K 2024-10-06 12:26:25 -04:00
commit 047563bdde
19 changed files with 121 additions and 41 deletions

View File

@ -143,8 +143,10 @@ markAsSensitive: "Mark as sensitive"
unmarkAsSensitive: "Unmark as sensitive" unmarkAsSensitive: "Unmark as sensitive"
enterFileName: "Enter filename" enterFileName: "Enter filename"
mute: "Mute" mute: "Mute"
muted: "Muted"
unmute: "Unmute" unmute: "Unmute"
renoteMute: "Mute Boosts" renoteMute: "Mute Boosts"
renoteMuted: "Boosts muted"
renoteUnmute: "Unmute Boosts" renoteUnmute: "Unmute Boosts"
block: "Block" block: "Block"
unblock: "Unblock" unblock: "Unblock"
@ -265,6 +267,9 @@ noCustomEmojis: "There are no emoji"
noJobs: "There are no jobs" noJobs: "There are no jobs"
federating: "Federating" federating: "Federating"
blocked: "Blocked" blocked: "Blocked"
blockedByBase: "This host is blocked implicitly because a base domain is blocked. To unblock this host, first unblock the base domain(s)."
silencedByBase: "This host is silenced implicitly because a base domain is silenced. To un-silence this host, first un-silence the base domain(s)."
mediaSilencedByBase: "This host's media is silenced implicitly because a base domain's media is silenced. To un-silence this host, first un-silence the base domain(s)."
suspended: "Suspended" suspended: "Suspended"
all: "All" all: "All"
subscribing: "Subscribing" subscribing: "Subscribing"

28
locales/index.d.ts vendored
View File

@ -588,6 +588,10 @@ export interface Locale extends ILocale {
* *
*/ */
"mute": string; "mute": string;
/**
* Muted
*/
"muted": string;
/** /**
* *
*/ */
@ -596,6 +600,10 @@ export interface Locale extends ILocale {
* *
*/ */
"renoteMute": string; "renoteMute": string;
/**
* Boosts muted
*/
"renoteMuted": string;
/** /**
* *
*/ */
@ -1076,6 +1084,18 @@ export interface Locale extends ILocale {
* *
*/ */
"blocked": string; "blocked": string;
/**
* This host is blocked implicitly because a base domain is blocked. To unblock this host, first unblock the base domain(s).
*/
"blockedByBase": string;
/**
* This host is silenced implicitly because a base domain is silenced. To un-silence this host, first un-silence the base domain(s).
*/
"silencedByBase": string;
/**
* This host's media is silenced implicitly because a base domain's media is silenced. To un-silence this host, first un-silence the base domain(s).
*/
"mediaSilencedByBase": string;
/** /**
* *
*/ */
@ -4441,10 +4461,6 @@ export interface Locale extends ILocale {
* *
*/ */
"disableFederationOk": string; "disableFederationOk": string;
/**
*
*/
"disableCatSpeak": string;
/** /**
* *
*/ */
@ -5789,7 +5805,7 @@ export interface Locale extends ILocale {
*/ */
"social": string; "social": string;
/** /**
* * 稿
*/ */
"bubble": string; "bubble": string;
/** /**
@ -9151,7 +9167,7 @@ export interface Locale extends ILocale {
*/ */
"global": string; "global": string;
/** /**
* *
*/ */
"bubble": string; "bubble": string;
}; };

View File

@ -143,8 +143,10 @@ markAsSensitive: "センシティブとして設定"
unmarkAsSensitive: "センシティブを解除する" unmarkAsSensitive: "センシティブを解除する"
enterFileName: "ファイル名を入力" enterFileName: "ファイル名を入力"
mute: "ミュート" mute: "ミュート"
muted: "Muted"
unmute: "ミュート解除" unmute: "ミュート解除"
renoteMute: "ブーストをミュート" renoteMute: "ブーストをミュート"
renoteMuted: "Boosts muted"
renoteUnmute: "ブーストのミュートを解除" renoteUnmute: "ブーストのミュートを解除"
block: "ブロック" block: "ブロック"
unblock: "ブロック解除" unblock: "ブロック解除"
@ -265,6 +267,9 @@ noCustomEmojis: "絵文字はありません"
noJobs: "ジョブはありません" noJobs: "ジョブはありません"
federating: "連合中" federating: "連合中"
blocked: "ブロック中" blocked: "ブロック中"
blockedByBase: "This host is blocked implicitly because a base domain is blocked. To unblock this host, first unblock the base domain(s)."
silencedByBase: "This host is silenced implicitly because a base domain is silenced. To un-silence this host, first un-silence the base domain(s)."
mediaSilencedByBase: "This host's media is silenced implicitly because a base domain's media is silenced. To un-silence this host, first un-silence the base domain(s)."
suspended: "配信停止" suspended: "配信停止"
all: "全て" all: "全て"
subscribing: "購読中" subscribing: "購読中"

View File

@ -526,6 +526,7 @@ export class ApRendererService {
publicKey: this.renderKey(user, keypair, '#main-key'), publicKey: this.renderKey(user, keypair, '#main-key'),
isCat: user.isCat, isCat: user.isCat,
noindex: user.noindex, noindex: user.noindex,
indexable: !user.noindex,
speakAsCat: user.speakAsCat, speakAsCat: user.speakAsCat,
attachment: attachment.length ? attachment : undefined, attachment: attachment.length ? attachment : undefined,
}; };

View File

@ -545,6 +545,7 @@ const extension_context_definition = {
Emoji: 'toot:Emoji', Emoji: 'toot:Emoji',
featured: 'toot:featured', featured: 'toot:featured',
discoverable: 'toot:discoverable', discoverable: 'toot:discoverable',
indexable: 'toot:indexable',
// schema // schema
schema: 'http://schema.org#', schema: 'http://schema.org#',
PropertyValue: 'schema:PropertyValue', PropertyValue: 'schema:PropertyValue',

View File

@ -587,7 +587,7 @@ export class ApNoteService {
// ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにートが生成されるが // ここでuriの代わりに添付されてきたNote Objectが指定されていると、サーバーフェッチを経ずにートが生成されるが
// 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。 // 添付されてきたNote Objectは偽装されている可能性があるため、常にuriを指定してサーバーフェッチを行う。
const createFrom = options.sentFrom?.origin === new URL(uri).origin ? value : uri; const createFrom = options.sentFrom?.origin === new URL(uri).origin ? value : uri;
return await this.createNote(createFrom, options.resolver, true); return await this.createNote(createFrom, options.resolver, false);
} finally { } finally {
unlock(); unlock();
} }

View File

@ -128,7 +128,7 @@ export const meta = {
}, },
silencedHosts: { silencedHosts: {
type: 'array', type: 'array',
optional: true, optional: false,
nullable: false, nullable: false,
items: { items: {
type: 'string', type: 'string',

View File

@ -193,9 +193,9 @@ export class ClientServerService {
icon: meta.iconUrl, icon: meta.iconUrl,
appleTouchIcon: meta.app512IconUrl, appleTouchIcon: meta.app512IconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://launcher.moe/error.png', serverErrorImageUrl: meta.serverErrorImageUrl ?? '/status/error.png',
infoImageUrl: meta.infoImageUrl ?? 'https://launcher.moe/nothinghere.png', infoImageUrl: meta.infoImageUrl ?? '/status/nothinghere.png',
notFoundImageUrl: meta.notFoundImageUrl ?? 'https://launcher.moe/missingpage.webp', notFoundImageUrl: meta.notFoundImageUrl ?? '/status/missingpage.webp',
instanceUrl: this.config.url, instanceUrl: this.config.url,
randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined, randomMOTD: this.config.customMOTD ? this.config.customMOTD[Math.floor(Math.random() * this.config.customMOTD.length)] : undefined,
metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)), metaJson: htmlSafeJsonStringify(await this.metaEntityService.packDetailed(meta)),

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@ -216,19 +216,25 @@ export async function mainBoot() {
claimAchievement('collectAchievements30'); claimAchievement('collectAchievements30');
} }
window.setInterval(() => { if (!claimedAchievements.includes('justPlainLucky')) {
if (Math.floor(Math.random() * 20000) === 0) { window.setInterval(() => {
claimAchievement('justPlainLucky'); if (Math.floor(Math.random() * 20000) === 0) {
} claimAchievement('justPlainLucky');
}, 1000 * 10); }
}, 1000 * 10);
}
window.setTimeout(() => { if (!claimedAchievements.includes('client30min')) {
claimAchievement('client30min'); window.setTimeout(() => {
}, 1000 * 60 * 30); claimAchievement('client30min');
}, 1000 * 60 * 30);
}
window.setTimeout(() => { if (!claimedAchievements.includes('client60min')) {
claimAchievement('client60min'); window.setTimeout(() => {
}, 1000 * 60 * 60); claimAchievement('client60min');
}, 1000 * 60 * 60);
}
// 邪魔 // 邪魔
//const lastUsed = miLocalStorage.getItem('lastUsed'); //const lastUsed = miLocalStorage.getItem('lastUsed');

View File

@ -630,11 +630,22 @@ async function onPaste(ev: ClipboardEvent) {
if (paste.length > 1000) { if (paste.length > 1000) {
ev.preventDefault(); ev.preventDefault();
os.confirm({ os.actions({
type: 'info', type: 'question',
text: i18n.ts.attachAsFileQuestion, text: i18n.ts.attachAsFileQuestion,
}).then(({ canceled }) => { actions: [
if (canceled) { {
value: 'yes',
text: i18n.ts.yes,
primary: true,
},
{
value: 'no',
text: i18n.ts.no,
},
],
}).then(({ result }) => {
if (result !== 'yes') {
insertTextAtCursor(textareaEl.value, paste); insertTextAtCursor(textareaEl.value, paste);
return; return;
} }

View File

@ -142,6 +142,7 @@ function showMenu(ev: MouseEvent) {
height: 32px; height: 32px;
border-radius: var(--radius-sm); border-radius: var(--radius-sm);
font-size: 18px; font-size: 18px;
z-index: 50;
} }
.mainFg { .mainFg {

View File

@ -160,9 +160,9 @@ export const ROLE_POLICIES = [
export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP';
export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM'; export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM';
export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://launcher.moe/error.png'; export const DEFAULT_SERVER_ERROR_IMAGE_URL = '/status/error.png';
export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://launcher.moe/missingpage.webp'; export const DEFAULT_NOT_FOUND_IMAGE_URL = '/status/missingpage.webp';
export const DEFAULT_INFO_IMAGE_URL = 'https://launcher.moe/nothinghere.png'; export const DEFAULT_INFO_IMAGE_URL = '/status/nothinghere.png';
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse']; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime', 'crop', 'fade', 'followmouse'];
export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = {

View File

@ -20,7 +20,7 @@
worker-src 'self'; worker-src 'self';
script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh; script-src 'self' 'unsafe-eval' https://*.hcaptcha.com https://challenges.cloudflare.com https://esm.sh;
style-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';
img-src 'self' data: blob: www.google.com xn--931a.moe launcher.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 activitypub.software secure.gravatar.com avatars.githubusercontent.com; img-src 'self' data: blob: www.google.com xn--931a.moe localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 activitypub.software secure.gravatar.com avatars.githubusercontent.com;
media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000; media-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000;
connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com https://api.listenbrainz.org; connect-src 'self' localhost:3000 localhost:5173 127.0.0.1:5173 127.0.0.1:3000 https://newassets.hcaptcha.com https://api.listenbrainz.org;
frame-src *;" frame-src *;"

View File

@ -45,11 +45,14 @@ SPDX-License-Identifier: AGPL-3.0-only
</MkKeyValue> </MkKeyValue>
<MkButton v-if="suspensionState === 'none'" :disabled="!instance" danger @click="stopDelivery">{{ i18n.ts._delivery.stop }}</MkButton> <MkButton v-if="suspensionState === 'none'" :disabled="!instance" danger @click="stopDelivery">{{ i18n.ts._delivery.stop }}</MkButton>
<MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton> <MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch> <MkInfo v-if="isBaseBlocked" warn>{{ i18n.ts.blockedByBase }}</MkInfo>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch> <MkSwitch v-model="isBlocked" :disabled="!meta || !instance || isBaseBlocked" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
<MkInfo v-if="isBaseSilenced" warn>{{ i18n.ts.silencedByBase }}</MkInfo>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance || isBaseSilenced" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
<MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">{{ i18n.ts.markInstanceAsNSFW }}</MkSwitch> <MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">{{ i18n.ts.markInstanceAsNSFW }}</MkSwitch>
<MkSwitch v-model="rejectReports" :disabled="!instance" @update:modelValue="toggleRejectReports">{{ i18n.ts.rejectReports }}</MkSwitch> <MkSwitch v-model="rejectReports" :disabled="!instance" @update:modelValue="toggleRejectReports">{{ i18n.ts.rejectReports }}</MkSwitch>
<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch> <MkInfo v-if="isBaseMediaSilenced" warn>{{ i18n.ts.mediaSilencedByBase }}</MkInfo>
<MkSwitch v-model="isMediaSilenced" :disabled="!meta || !instance || isBaseMediaSilenced" @update:modelValue="toggleMediaSilenced">{{ i18n.ts.mediaSilenceThisInstance }}</MkSwitch>
<MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton> <MkButton @click="refreshMetadata"><i class="ti ti-refresh"></i> Refresh metadata</MkButton>
<MkTextarea v-model="moderationNote" manualSave> <MkTextarea v-model="moderationNote" manualSave>
<template #label>{{ i18n.ts.moderationNote }}</template> <template #label>{{ i18n.ts.moderationNote }}</template>
@ -157,6 +160,7 @@ import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
import { dateString } from '@/filters/date.js'; import { dateString } from '@/filters/date.js';
import MkTextarea from '@/components/MkTextarea.vue'; import MkTextarea from '@/components/MkTextarea.vue';
import MkInfo from '@/components/MkInfo.vue';
const props = defineProps<{ const props = defineProps<{
host: string; host: string;
@ -176,6 +180,21 @@ const isMediaSilenced = ref(false);
const faviconUrl = ref<string | null>(null); const faviconUrl = ref<string | null>(null);
const moderationNote = ref(''); const moderationNote = ref('');
const baseDomains = computed(() => {
const domains: string[] = [];
const parts = props.host.toLowerCase().split('.');
for (let s = 1; s < parts.length; s++) {
const domain = parts.slice(s).join('.');
domains.push(domain);
}
return domains;
});
const isBaseBlocked = computed(() => meta.value && baseDomains.value.some(d => meta.value?.blockedHosts.includes(d)));
const isBaseSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.silencedHosts.includes(d)));
const isBaseMediaSilenced = computed(() => meta.value && baseDomains.value.some(d => meta.value?.mediaSilencedHosts.includes(d)));
const usersPagination = { const usersPagination = {
endpoint: iAmModerator ? 'admin/show-users' : 'users' as const, endpoint: iAmModerator ? 'admin/show-users' : 'users' as const,
limit: 10, limit: 10,

View File

@ -30,7 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
</button> </button>
</div> </div>
</div> </div>
<span v-if="$i && $i.id != user.id && user.isFollowed" class="followed">{{ i18n.ts.followsYou }}</span> <div v-if="$i && $i.id != user.id" class="info-badges">
<span v-if="user.isFollowed">{{ i18n.ts.followsYou }}</span>
<span v-if="user.isMuted">{{ i18n.ts.muted }}</span>
<span v-if="user.isRenoteMuted">{{ i18n.ts.renoteMuted }}</span>
<span v-if="user.isBlocking">{{ i18n.ts.blocked }}</span>
</div>
<div class="actions"> <div class="actions">
<button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button> <button class="menu _button" @click="menu"><i class="ti ti-dots"></i></button>
<MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/> <MkFollowButton v-if="$i?.id != user.id" v-model:user="user" :inline="true" :transparent="false" :full="true" class="koudoku"/>
@ -445,15 +450,25 @@ onUnmounted(() => {
background: linear-gradient(transparent, rgba(#000, 0.7)); background: linear-gradient(transparent, rgba(#000, 0.7));
} }
> .followed { > .info-badges {
position: absolute; position: absolute;
top: 12px; top: 12px;
left: 12px; left: 12px;
padding: 4px 8px;
color: #fff; display: flex;
background: rgba(0, 0, 0, 0.7); flex-direction: row;
font-size: 0.7em;
border-radius: var(--radius-sm); > * {
padding: 4px 8px;
color: #fff;
background: rgba(0, 0, 0, 0.7);
font-size: 0.7em;
border-radius: var(--radius-sm);
}
> :not(:first-child) {
margin-left: 8px;
}
} }
> .actions { > .actions {

View File

@ -5199,7 +5199,7 @@ export type operations = {
enableEmail: boolean; enableEmail: boolean;
enableServiceWorker: boolean; enableServiceWorker: boolean;
translatorAvailable: boolean; translatorAvailable: boolean;
silencedHosts?: string[]; silencedHosts: string[];
mediaSilencedHosts: string[]; mediaSilencedHosts: string[];
pinnedUsers: string[]; pinnedUsers: string[];
hiddenTags: string[]; hiddenTags: string[];