Merge branch 'misskey-dev:develop' into error-style

This commit is contained in:
Kainoa Kanter 2022-07-05 00:50:15 -07:00 committed by GitHub
commit 2cfb3a1d36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
67 changed files with 281 additions and 157 deletions

View file

@ -35,6 +35,7 @@ You should also include the user name that made the change.
- Server: Supports IPv6 on Redis transport. @mei23
IPv4/IPv6 is used by default. You can tune this behavior via `redis.family`.
- Server: Add possibility to log IP addresses of users @syuilo
- Add additional drive capacity change support @CyberRex0
### Bugfixes
- Server: Fix GenerateVideoThumbnail failed @mei23

View file

@ -203,6 +203,7 @@ done: "完了"
processing: "処理中"
preview: "プレビュー"
default: "デフォルト"
defaultValueIs: "デフォルト: {value}"
noCustomEmojis: "絵文字はありません"
noJobs: "ジョブはありません"
federating: "連合中"
@ -542,7 +543,7 @@ relays: "リレー"
addRelay: "リレーの追加"
inboxUrl: "inboxのURL"
addedRelays: "追加済みのリレー"
serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。"
serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。"
deletedNote: "削除された投稿"
invisibleNote: "非公開の投稿"
enableInfiniteScroll: "自動でもっと見る"
@ -855,6 +856,8 @@ noEmailServerWarning: "メールサーバーの設定がされていません。
thereIsUnresolvedAbuseReportWarning: "未対応の通報があります。"
recommended: "推奨"
check: "チェック"
driveCapOverrideLabel: "このユーザーのドライブ容量上限を変更"
driveCapOverrideCaption: "0以下を指定すると解除されます。"
requireAdminForView: "閲覧するには管理者アカウントでログインしている必要があります。"
isSystemAccount: "システムにより自動で作成・管理されているアカウントです。"
typeToConfirm: "この操作を行うには {x} と入力してください"
@ -1731,6 +1734,7 @@ _deck:
profile: "プロファイル"
introduction: "カラムを組み合わせて自分だけのインターフェイスを作りましょう!"
introduction2: "画面の右にある + を押して、いつでもカラムを追加できます。"
widgetsIntroduction: "カラムのメニューから、「ウィジェットの編集」を選択してウィジェットを追加してください"
_columns:
main: "メイン"

View file

@ -0,0 +1,13 @@
export class driveCapacityOverrideMb1655813815729 {
name = 'driveCapacityOverrideMb1655813815729'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "user" ADD "driveCapacityOverrideMb" integer`);
await queryRunner.query(`COMMENT ON COLUMN "user"."driveCapacityOverrideMb" IS 'Overrides user drive capacity limit'`);
}
async down(queryRunner) {
await queryRunner.query(`COMMENT ON COLUMN "user"."driveCapacityOverrideMb" IS 'Overrides user drive capacity limit'`);
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "driveCapacityOverrideMb"`);
}
}

View file

@ -218,6 +218,12 @@ export class User {
})
public token: string | null;
@Column('integer', {
nullable: true,
comment: 'Overrides user drive capacity limit',
})
public driveCapacityOverrideMb: number | null;
constructor(data: Partial<User>) {
if (data == null) return;

View file

@ -315,6 +315,7 @@ export const UserRepository = db.getRepository(User).extend({
} : undefined) : undefined,
emojis: populateEmojis(user.emojis, user.host),
onlineStatus: this.getOnlineStatus(user),
driveCapacityOverrideMb: user.driveCapacityOverrideMb,
...(opts.detail ? {
url: profile!.url,

View file

@ -314,6 +314,7 @@ import * as ep___users_search from './endpoints/users/search.js';
import * as ep___users_show from './endpoints/users/show.js';
import * as ep___users_stats from './endpoints/users/stats.js';
import * as ep___fetchRss from './endpoints/fetch-rss.js';
import * as ep___admin_driveCapOverride from './endpoints/admin/drive-capacity-override.js';
const eps = [
['admin/meta', ep___admin_meta],
@ -629,6 +630,7 @@ const eps = [
['users/search', ep___users_search],
['users/show', ep___users_show],
['users/stats', ep___users_stats],
['admin/drive-capacity-override', ep___admin_driveCapOverride],
['fetch-rss', ep___fetchRss],
];

View file

@ -0,0 +1,47 @@
import define from '../../define.js';
import { Users } from '@/models/index.js';
import { User } from '@/models/entities/user.js';
import { insertModerationLog } from '@/services/insert-moderation-log.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
} as const;
export const paramDef = {
type: 'object',
properties: {
userId: { type: 'string', format: 'misskey:id' },
overrideMb: { type: 'number', nullable: true },
},
required: ['userId', 'overrideMb'],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, me) => {
const user = await Users.findOneBy({ id: ps.userId });
if (user == null) {
throw new Error('user not found');
}
if (!Users.isLocalUser(user)) {
throw new Error('user is not local user');
}
/*if (user.isAdmin) {
throw new Error('cannot suspend admin');
}
if (user.isModerator) {
throw new Error('cannot suspend moderator');
}*/
await Users.update(user.id, {
driveCapacityOverrideMb: ps.overrideMb,
});
insertModerationLog(me, 'change-drive-capacity-override', {
targetId: user.id,
});
});

View file

@ -39,7 +39,7 @@ export default define(meta, paramDef, async (ps, user) => {
const usage = await DriveFiles.calcDriveUsageOf(user.id);
return {
capacity: 1024 * 1024 * instance.localDriveCapacityMb,
capacity: 1024 * 1024 * (user.driveCapacityOverrideMb || instance.localDriveCapacityMb),
usage: usage,
};
});

View file

@ -307,7 +307,7 @@ async function deleteOldFile(user: IRemoteUser) {
type AddFileArgs = {
/** User who wish to add file */
user: { id: User['id']; host: User['host'] } | null;
user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null;
/** File path */
path: string;
/** Name */
@ -371,9 +371,16 @@ export async function addFile({
//#region Check drive usage
if (user && !isLink) {
const usage = await DriveFiles.calcDriveUsageOf(user);
const u = await Users.findOneBy({ id: user.id });
const instance = await fetchMeta();
const driveCapacity = 1024 * 1024 * (Users.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb);
let driveCapacity = 1024 * 1024 * (Users.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb);
if (Users.isLocalUser(user) && u?.driveCapacityOverrideMb != null) {
driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb;
logger.debug('drive capacity override applied');
logger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
}
logger.debug(`drive usage is ${usage} (max: ${driveCapacity})`);

View file

@ -74,7 +74,7 @@
"uuid": "8.3.2",
"v-debounce": "0.1.2",
"vanilla-tilt": "1.7.2",
"vite": "3.0.0-beta.5",
"vite": "3.0.0-beta.6",
"vue": "3.2.37",
"vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "4.0.1",

View file

@ -1,5 +1,5 @@
<template>
<XWindow ref="window" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')">
<XWindow ref="uiWindow" :initial-width="400" :initial-height="500" :can-resize="true" @closed="emit('closed')">
<template #header>
<i class="fas fa-exclamation-circle" style="margin-right: 0.5em;"></i>
<I18n :src="i18n.ts.reportAbuseOf" tag="span">
@ -40,7 +40,7 @@ const emit = defineEmits<{
(ev: 'closed'): void;
}>();
const window = ref<InstanceType<typeof XWindow>>();
const uiWindow = ref<InstanceType<typeof XWindow>>();
const comment = ref(props.initialComment || '');
function send() {
@ -52,7 +52,7 @@ function send() {
type: 'success',
text: i18n.ts.abuseReported
});
window.value?.close();
uiWindow.value?.close();
emit('closed');
});
}

View file

@ -20,6 +20,7 @@
<span v-if="emoji.isCustomEmoji" class="emoji"><img :src="defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(emoji.url) : emoji.url" :alt="emoji.emoji"/></span>
<span v-else-if="!defaultStore.state.useOsNativeEmojis" class="emoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
<span v-else class="emoji">{{ emoji.emoji }}</span>
<!-- eslint-disable-next-line vue/no-v-html -->
<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
<span v-if="emoji.aliasOf" class="alias">({{ emoji.aliasOf }})</span>
</li>

View file

@ -1,3 +1,4 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<code v-if="inline" :class="`language-${prismLang}`" v-html="html"></code>
<pre v-else :class="`language-${prismLang}`"><code :class="`language-${prismLang}`" v-html="html"></code></pre>
@ -5,7 +6,7 @@
<script lang="ts" setup>
import { computed } from 'vue';
import 'prismjs';
import { Prism } from 'prismjs';
import 'prismjs/themes/prism-okaidia.css';
const props = defineProps<{

View file

@ -9,12 +9,12 @@
<form v-if="instance.enableEmail" class="bafeceda" @submit.prevent="onSubmit">
<div class="main _formRoot">
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required>
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required>
<template #label>{{ i18n.ts.username }}</template>
<template #prefix>@</template>
</MkInput>
<MkInput v-model="email" class="_formBlock" type="email" spellcheck="false" required>
<MkInput v-model="email" class="_formBlock" type="email" :spellcheck="false" required>
<template #label>{{ i18n.ts.emailAddress }}</template>
<template #caption>{{ i18n.ts._forgotPassword.enterEmail }}</template>
</MkInput>

View file

@ -98,7 +98,7 @@ export default defineComponent({
created() {
for (const item in this.form) {
this.values[item] = this.form[item].hasOwnProperty('default') ? this.form[item].default : null;
this.values[item] = this.form[item].default ?? null;
}
},

View file

@ -1,4 +1,4 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<div v-if="block" v-html="compiledFormula"></div>
<span v-else v-html="compiledFormula"></span>

View file

@ -3,7 +3,8 @@
</template>
<script lang="ts">
import { defineComponent, defineAsyncComponent } from 'vue';import * as os from '@/os';
import { defineComponent, defineAsyncComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({
components: {

View file

@ -13,9 +13,6 @@ const props = defineProps<{
router?: Router;
}>();
const emit = defineEmits<{
}>();
const router = props.router ?? inject('router');
if (router == null) {

View file

@ -77,10 +77,16 @@ export default {
<style lang="scss" module>
.wrap {
overflow: hidden; overflow: clip;
animation-play-state: running;
&:hover {
animation-play-state: paused;
}
}
.content {
display: inline-block;
white-space: nowrap;
animation-play-state: inherit;
}
.text {
display: inline-block;
@ -88,6 +94,7 @@ export default {
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-duration: inherit;
animation-play-state: inherit;
}
.paused .text {
animation-play-state: paused;

View file

@ -24,7 +24,6 @@ export default defineComponent({
},
},
setup(props, ctx) {
const hpml = new Hpml(props.page, {
randomSeed: Math.random(),
visitor: $i,

View file

@ -116,8 +116,11 @@ function get() {
let base = parseInt(after.value);
switch (unit.value) {
case 'day': base *= 24;
// fallthrough
case 'hour': base *= 60;
// fallthrough
case 'minute': base *= 60;
// fallthrough
case 'second': return base *= 1000;
default: return null;
}

View file

@ -6,7 +6,7 @@
{{ message }}
</MkInfo>
<div v-if="!totpLogin" class="normal-signin">
<MkInput v-model="username" class="_formBlock" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" spellcheck="false" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
<MkInput v-model="username" class="_formBlock" :placeholder="i18n.ts.username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required data-cy-signin-username @update:modelValue="onUsernameChange">
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
</MkInput>
@ -32,7 +32,7 @@
<template #label>{{ i18n.ts.password }}</template>
<template #prefix><i class="fas fa-lock"></i></template>
</MkInput>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false" required>
<template #label>{{ i18n.ts.token }}</template>
<template #prefix><i class="fas fa-gavel"></i></template>
</MkInput>

View file

@ -1,11 +1,11 @@
<template>
<form class="qlvuhzng _formRoot" autocomplete="new-password" @submit.prevent="onSubmit">
<template v-if="meta">
<MkInput v-if="meta.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" spellcheck="false" required>
<MkInput v-if="meta.disableRegistration" v-model="invitationCode" class="_formBlock" type="text" :spellcheck="false" required>
<template #label>{{ $ts.invitationCode }}</template>
<template #prefix><i class="fas fa-key"></i></template>
</MkInput>
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
<template #label>{{ $ts.username }} <div v-tooltip:dialog="$ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>
@ -19,7 +19,7 @@
<span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.tooLong }}</span>
</template>
</MkInput>
<MkInput v-if="meta.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
<MkInput v-if="meta.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
<template #label>{{ $ts.emailAddress }} <div v-tooltip:dialog="$ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template>
<template #prefix><i class="fas fa-envelope"></i></template>
<template #caption>

View file

@ -13,8 +13,6 @@
import { onMounted, ref, watch, PropType, onBeforeUnmount } from 'vue';
import tinycolor from 'tinycolor2';
const props = defineProps<{}>();
const loaded = !!window.TagCanvas;
const SAFE_FOR_HTML_ID = 'abcdefghijklmnopqrstuvwxyz';
const computedStyle = getComputedStyle(document.documentElement);

View file

@ -3,7 +3,8 @@
</template>
<script lang="ts">
import { defineComponent } from 'vue';import * as os from '@/os';
import { defineComponent } from 'vue';
import * as os from '@/os';
export default defineComponent({});
</script>

View file

@ -99,12 +99,12 @@ export default defineComponent({
buttonsLeft: {
type: Array,
required: false,
default: [],
default: () => [],
},
buttonsRight: {
type: Array,
required: false,
default: [],
default: () => [],
},
},

View file

@ -34,7 +34,6 @@ function calc(src: Element) {
export default {
mounted(src, binding, vn) {
const resize = new ResizeObserver((entries, observer) => {
calc(src);
});

View file

@ -49,6 +49,7 @@ export default {
showing,
text: self.text,
asMfm: binding.modifiers.mfm,
direction: binding.modifiers.left ? 'left' : binding.modifiers.right ? 'right' : binding.modifiers.top ? 'top' : binding.modifiers.bottom ? 'bottom' : 'top',
targetElement: el,
}, {}, 'closed');

View file

@ -75,7 +75,6 @@ const hasTabs = computed(() => {
const showTabsPopup = (ev: MouseEvent) => {
if (!hasTabs.value) return;
if (!narrow.value) return;
ev.preventDefault();
ev.stopPropagation();
const menu = props.tabs.map(tab => ({

View file

@ -27,10 +27,10 @@
</div>
<!-- TODO
<div class="inputs" style="display: flex; padding-top: 1.2em;">
<MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" spellcheck="false">
<MkInput v-model="searchUsername" style="margin: 0; flex: 1;" type="text" :spellcheck="false">
<span>{{ $ts.username }}</span>
</MkInput>
<MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" spellcheck="false" :disabled="pagination.params().origin === 'local'">
<MkInput v-model="searchHost" style="margin: 0; flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params().origin === 'local'">
<span>{{ $ts.host }}</span>
</MkInput>
</div>

View file

@ -61,27 +61,22 @@ let hcaptchaSecretKey: string | null = $ref(null);
let recaptchaSiteKey: string | null = $ref(null);
let recaptchaSecretKey: string | null = $ref(null);
const enableHcaptcha = $computed(() => provider === 'hcaptcha');
const enableRecaptcha = $computed(() => provider === 'recaptcha');
async function init() {
const meta = await os.api('admin/meta');
enableHcaptcha = meta.enableHcaptcha;
hcaptchaSiteKey = meta.hcaptchaSiteKey;
hcaptchaSecretKey = meta.hcaptchaSecretKey;
enableRecaptcha = meta.enableRecaptcha;
recaptchaSiteKey = meta.recaptchaSiteKey;
recaptchaSecretKey = meta.recaptchaSecretKey;
provider = enableHcaptcha ? 'hcaptcha' : enableRecaptcha ? 'recaptcha' : null;
provider = meta.enableHcaptcha ? 'hcaptcha' : meta.enableRecaptcha ? 'recaptcha' : null;
}
function save() {
os.apiWithDialog('admin/update-meta', {
enableHcaptcha,
enableHcaptcha: provider === 'hcaptcha',
hcaptchaSiteKey,
hcaptchaSecretKey,
enableRecaptcha,
enableRecaptcha: provider === 'recaptcha',
recaptchaSiteKey,
recaptchaSecretKey,
}).then(() => {

View file

@ -1,7 +1,7 @@
<template>
<div>
<MkStickyContainer>
<template #header><XHeader :actions="headerActions" :tabs="headerTabs" v-model:tab="tab"/></template>
<template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="900">
<div class="ogwlenmc">
<div v-if="tab === 'local'" class="local">

View file

@ -19,7 +19,7 @@ const props = defineProps<{
user: misskey.entities.User;
}>();
const chart = $ref(null);
let chart = $ref(null);
os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16, span: 'day' }).then(res => {
chart = res;

View file

@ -30,11 +30,11 @@
</MkSelect>
</div>
<div class="inputs">
<MkInput v-model="searchUsername" style="flex: 1;" type="text" spellcheck="false" @update:modelValue="$refs.users.reload()">
<MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" @update:modelValue="$refs.users.reload()">
<template #prefix>@</template>
<template #label>{{ $ts.username }}</template>
</MkInput>
<MkInput v-model="searchHost" style="flex: 1;" type="text" spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:modelValue="$refs.users.reload()">
<MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:modelValue="$refs.users.reload()">
<template #prefix>@</template>
<template #label>{{ $ts.host }}</template>
</MkInput>

View file

@ -74,8 +74,8 @@ const props = defineProps<{
postId: string;
}>();
const post = $ref(null);
const error = $ref(null);
let post = $ref(null);
let error = $ref(null);
const otherPostsPagination = {
endpoint: 'users/gallery/posts' as const,
limit: 6,

View file

@ -46,6 +46,7 @@
<script lang="ts" setup>
import { watch } from 'vue';
import * as Acct from 'misskey-js/built/acct';
import MkButton from '@/components/ui/button.vue';
import MkInput from '@/components/form/input.vue';
import MkTextarea from '@/components/form/textarea.vue';

View file

@ -41,6 +41,7 @@ import MkButton from '@/components/ui/button.vue';
import * as os from '@/os';
import { mainRouter } from '@/router';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
const props = defineProps<{
listId: string;

View file

@ -32,7 +32,7 @@ const props = withDefaults(defineProps<{
let values: string = $ref(props.value.values.join('\n'));
watch(values, () => {
props.value.values = values.split('\n')
props.value.values = values.split('\n');
}, {
deep: true
});

View file

@ -1,6 +1,6 @@
<template>
<MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs" v-model:tab="tab"/></template>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="700">
<div class="jqqmcavi">
<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="fas fa-external-link-square-alt"></i> {{ $ts._pages.viewPage }}</MkButton>
@ -82,7 +82,7 @@
</template>
<script lang="ts" setup>
import { defineComponent, defineAsyncComponent, computed, provide, watch } from 'vue';
import { defineAsyncComponent, computed, provide, watch } from 'vue';
import 'prismjs';
import { highlight, languages } from 'prismjs/components/prism-core';
import 'prismjs/components/prism-clike';
@ -93,7 +93,6 @@ import { v4 as uuid } from 'uuid';
import XVariable from './page-editor.script-block.vue';
import XBlocks from './page-editor.blocks.vue';
import MkTextarea from '@/components/form/textarea.vue';
import MkContainer from '@/components/ui/container.vue';
import MkButton from '@/components/ui/button.vue';
import MkSelect from '@/components/form/select.vue';
import MkSwitch from '@/components/form/switch.vue';
@ -168,15 +167,15 @@ function save() {
const options = getSaveOptions();
const onError = err => {
if (err.id == '3d81ceae-475f-4600-b2a8-2bc116157532') {
if (err.info.param == 'name') {
if (err.id === '3d81ceae-475f-4600-b2a8-2bc116157532') {
if (err.info.param === 'name') {
os.alert({
type: 'error',
title: i18n.ts._pages.invalidNameTitle,
text: i18n.ts._pages.invalidNameText,
});
}
} else if (err.code == 'NAME_ALREADY_EXISTS') {
} else if (err.code === 'NAME_ALREADY_EXISTS') {
os.alert({
type: 'error',
text: i18n.ts._pages.nameAlreadyExists,
@ -310,7 +309,7 @@ function getPageBlockList() {
function getScriptBlockList(type: string = null) {
const list = [];
const blocks = blockDefs.filter(block => type === null || block.out === null || block.out === type || typeof block.out === 'number');
const blocks = blockDefs.filter(block => type == null || block.out == null || block.out === type || typeof block.out === 'number');
for (const block of blocks) {
const category = list.find(x => x.category === block.category);
@ -345,8 +344,8 @@ function getScriptBlockList(type: string = null) {
return list;
}
function setEyeCatchingImage(e) {
selectFile(e.currentTarget ?? e.target, null).then(file => {
function setEyeCatchingImage(img) {
selectFile(img.currentTarget ?? img.target, null).then(file => {
eyeCatchingImageId = file.id;
});
}

View file

@ -55,7 +55,7 @@
<li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li>
<li>
{{ i18n.ts._2fa.step3 }}<br>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput>
<MkButton primary @click="submit">{{ i18n.ts.done }}</MkButton>
</li>
</ol>

View file

@ -31,16 +31,6 @@ const alwaysShowMainColumn = computed(deckStore.makeGetterSetter('alwaysShowMain
const columnAlign = computed(deckStore.makeGetterSetter('columnAlign'));
const profile = computed(deckStore.makeGetterSetter('profile'));
watch(navWindow, async () => {
const { canceled } = await os.confirm({
type: 'info',
text: i18n.ts.reloadToApplySetting,
});
if (canceled) return;
unisonReload();
});
async function setProfile() {
const { canceled, result: name } = await os.inputText({
title: i18n.ts._deck.profile,

View file

@ -78,6 +78,7 @@ import FormButton from '@/components/ui/button.vue';
import FormTextarea from '@/components/form/textarea.vue';
import FormFolder from '@/components/form/folder.vue';
import { $i } from '@/account';
import { Theme, applyTheme } from '@/scripts/theme';
import lightTheme from '@/themes/_light.json5';
import darkTheme from '@/themes/_dark.json5';
@ -118,7 +119,7 @@ const fgColors = [
{ color: 'pink', forLight: '#84667d', forDark: '#e4d1e0', forPreview: '#b12390' },
];
const theme = $ref<Partial<Theme>>({
let theme = $ref<Partial<Theme>>({
base: 'light',
props: lightTheme.props,
});

View file

@ -112,6 +112,17 @@
<MkFileListForAdmin :pagination="filesPagination" view-mode="grid"/>
</FormFolder>
<FormSection>
<template #label>Drive Capacity Override</template>
<FormInput v-if="user.host == null" v-model="driveCapacityOverrideMb" inline :manual-save="true" type="number" :placeholder="i18n.t('defaultValueIs', { value: instance.driveCapacityPerLocalUserMb })" @update:model-value="applyDriveCapacityOverride">
<template #label>{{ i18n.ts.driveCapOverrideLabel }}</template>
<template #suffix>MB</template>
<template #caption>
{{ i18n.ts.driveCapOverrideCaption }}
</template>
</FormInput>
</FormSection>
</div>
<div v-else-if="tab === 'chart'" class="_formRoot">
<div class="cmhjzshm">
@ -141,7 +152,7 @@
</template>
<script lang="ts" setup>
import { computed, defineAsyncComponent, defineComponent, watch } from 'vue';
import { computed, watch } from 'vue';
import * as misskey from 'misskey-js';
import MkChart from '@/components/chart.vue';
import MkObjectView from '@/components/object-view.vue';
@ -150,6 +161,8 @@ import FormSwitch from '@/components/form/switch.vue';
import FormLink from '@/components/form/link.vue';
import FormSection from '@/components/form/section.vue';
import FormButton from '@/components/ui/button.vue';
import FormInput from '@/components/form/input.vue';
import FormSplit from '@/components/form/split.vue';
import FormFolder from '@/components/form/folder.vue';
import MkKeyValue from '@/components/key-value.vue';
import MkSelect from '@/components/form/select.vue';
@ -164,6 +177,7 @@ import { userPage, acct } from '@/filters/user';
import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import { iAmAdmin, iAmModerator } from '@/account';
import { instance } from '@/instance';
const props = defineProps<{
userId: string;
@ -172,13 +186,14 @@ const props = defineProps<{
let tab = $ref('overview');
let chartSrc = $ref('per-user-notes');
let user = $ref<null | misskey.entities.UserDetailed>();
let init = $ref();
let init = $ref<ReturnType<typeof createFetcher>>();
let info = $ref();
let ips = $ref(null);
let ap = $ref(null);
let moderator = $ref(false);
let silenced = $ref(false);
let suspended = $ref(false);
let driveCapacityOverrideMb: number | null = $ref(0);
let moderationNote = $ref('');
const filesPagination = {
endpoint: 'admin/drive/files' as const,
@ -203,6 +218,7 @@ function createFetcher() {
moderator = info.isModerator;
silenced = info.isSilenced;
suspended = info.isSuspended;
driveCapacityOverrideMb = user.driveCapacityOverrideMb;
moderationNote = info.moderationNote;
watch($$(moderationNote), async () => {
@ -289,6 +305,22 @@ async function deleteAllFiles() {
await refreshUser();
}
async function applyDriveCapacityOverride() {
let driveCapOrMb = driveCapacityOverrideMb;
if (driveCapacityOverrideMb && driveCapacityOverrideMb < 0) {
driveCapOrMb = null;
}
try {
await os.apiWithDialog('admin/drive-capacity-override', { userId: user.id, overrideMb: driveCapOrMb });
await refreshUser();
} catch (err) {
os.alert({
type: 'error',
text: err.toString(),
});
}
}
async function deleteAccount() {
const confirm = await os.confirm({
type: 'warning',
@ -319,7 +351,7 @@ watch(() => props.userId, () => {
immediate: true,
});
watch(() => user, () => {
watch($$(user), () => {
os.api('ap/get', {
uri: user.uri ?? `${url}/users/${user.id}`,
}).then(res => {

View file

@ -23,6 +23,7 @@
<span class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
</div>
<div class="action">

View file

@ -9,6 +9,7 @@
<img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">

View file

@ -21,6 +21,7 @@
<img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span>
</h1>
<div class="about">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div>
</div>
<div class="action">

View file

@ -3,7 +3,7 @@
<h1>Welcome to Misskey!</h1>
<div class="_formRoot">
<p>{{ $ts.intro }}</p>
<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" spellcheck="false" required data-cy-admin-username class="_formBlock">
<MkInput v-model="username" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-admin-username class="_formBlock">
<template #label>{{ $ts.username }}</template>
<template #prefix>@</template>
<template #suffix>@{{ host }}</template>

View file

@ -38,7 +38,7 @@ export function install(plugin) {
function createPluginEnv(opts) {
const config = new Map();
for (const [k, v] of Object.entries(opts.plugin.config || {})) {
config.set(k, jsToVal(opts.plugin.configData.hasOwnProperty(k) ? opts.plugin.configData[k] : v.default));
config.set(k, jsToVal(typeof opts.plugin.configData[k] !== 'undefined' ? opts.plugin.configData[k] : v.default));
}
return {

View file

@ -98,7 +98,7 @@ export function groupOn<T, S>(f: (x: T) => S, xs: T[]): T[][] {
export function groupByX<T>(collections: T[], keySelector: (x: T) => string) {
return collections.reduce((obj: Record<string, T[]>, item: T) => {
const key = keySelector(item);
if (!obj.hasOwnProperty(key)) {
if (typeof obj[key] === 'undefined') {
obj[key] = [];
}

View file

@ -8,7 +8,7 @@ export class Autocomplete {
x: Ref<number>;
y: Ref<number>;
q: Ref<string | null>;
close: Function;
close: () => void;
} | null;
private textarea: HTMLInputElement | HTMLTextAreaElement;
private currentType: string;

View file

@ -21,7 +21,6 @@ export async function genSearchQuery(v: any, q: string) {
}
}
}
}
return {
query: q.split(' ').filter(x => !x.startsWith('/') && !x.startsWith('@')).join(' '),

View file

@ -1,6 +1,8 @@
import keyCode from './keycode';
type Keymap = Record<string, Function>;
type Callback = (ev: KeyboardEvent) => void;
type Keymap = Record<string, Callback>;
type Pattern = {
which: string[];
@ -11,14 +13,14 @@ type Pattern = {
type Action = {
patterns: Pattern[];
callback: Function;
callback: Callback;
allowRepeat: boolean;
};
const parseKeymap = (keymap: Keymap) => Object.entries(keymap).map(([patterns, callback]): Action => {
const result = {
patterns: [],
callback: callback,
callback,
allowRepeat: true
} as Action;

View file

@ -159,7 +159,6 @@ export class Hpml {
@autobind
private evaluate(expr: Expr, scope: HpmlScope): any {
if (isLiteralValue(expr)) {
if (expr.type === null) {
return null;

View file

@ -14,13 +14,13 @@ export type Fn = {
export type Type = 'string' | 'number' | 'boolean' | 'stringArray' | null;
export const literalDefs: Record<string, { out: any; category: string; icon: any; }> = {
text: { out: 'string', category: 'value', icon: 'fas fa-quote-right', },
multiLineText: { out: 'string', category: 'value', icon: 'fas fa-align-left', },
textList: { out: 'stringArray', category: 'value', icon: 'fas fa-list', },
number: { out: 'number', category: 'value', icon: 'fas fa-sort-numeric-up', },
ref: { out: null, category: 'value', icon: 'fas fa-magic', },
aiScriptVar: { out: null, category: 'value', icon: 'fas fa-magic', },
fn: { out: 'function', category: 'value', icon: 'fas fa-square-root-alt', },
text: { out: 'string', category: 'value', icon: 'fas fa-quote-right', },
multiLineText: { out: 'string', category: 'value', icon: 'fas fa-align-left', },
textList: { out: 'stringArray', category: 'value', icon: 'fas fa-list', },
number: { out: 'number', category: 'value', icon: 'fas fa-sort-numeric-up', },
ref: { out: null, category: 'value', icon: 'fas fa-magic', },
aiScriptVar: { out: null, category: 'value', icon: 'fas fa-magic', },
fn: { out: 'function', category: 'value', icon: 'fas fa-square-root-alt', },
};
export const blockDefs = [

View file

@ -125,55 +125,56 @@ export function initAiLib(hpml: Hpml) {
}
});
*/
})
}),
};
}
export const funcDefs: Record<string, { in: any[]; out: any; category: string; icon: any; }> = {
if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: 'fas fa-share-alt', },
for: { in: ['number', 'function'], out: null, category: 'flow', icon: 'fas fa-recycle', },
not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag', },
or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag', },
and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag', },
add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-plus', },
subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-minus', },
multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-times', },
divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide', },
mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide', },
round: { in: ['number'], out: 'number', category: 'operation', icon: 'fas fa-calculator', },
eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-equals', },
notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-not-equal', },
gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than', },
lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than', },
gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than-equal', },
ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than-equal', },
strLen: { in: ['string'], out: 'number', category: 'text', icon: 'fas fa-quote-right', },
strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: 'fas fa-quote-right', },
strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: 'fas fa-quote-right', },
strReverse: { in: ['string'], out: 'string', category: 'text', icon: 'fas fa-quote-right', },
join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: 'fas fa-quote-right', },
stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: 'fas fa-exchange-alt', },
numberToString: { in: ['number'], out: 'string', category: 'convert', icon: 'fas fa-exchange-alt', },
splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: 'fas fa-exchange-alt', },
pick: { in: [null, 'number'], out: null, category: 'list', icon: 'fas fa-indent', },
listLen: { in: [null], out: 'number', category: 'list', icon: 'fas fa-indent', },
rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice', },
dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice', },
seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice', },
random: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice', },
dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice', },
seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: 'fas fa-dice', },
randomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice', },
dailyRandomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice', },
seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: 'fas fa-dice', },
DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: 'fas fa-dice', }, // dailyRandomPickWithProbabilityMapping
if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: 'fas fa-share-alt' },
for: { in: ['number', 'function'], out: null, category: 'flow', icon: 'fas fa-recycle' },
not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' },
or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' },
and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: 'fas fa-flag' },
add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-plus' },
subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-minus' },
multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-times' },
divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide' },
mod: { in: ['number', 'number'], out: 'number', category: 'operation', icon: 'fas fa-divide' },
round: { in: ['number'], out: 'number', category: 'operation', icon: 'fas fa-calculator' },
eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-equals' },
notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: 'fas fa-not-equal' },
gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than' },
lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than' },
gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-greater-than-equal' },
ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: 'fas fa-less-than-equal' },
strLen: { in: ['string'], out: 'number', category: 'text', icon: 'fas fa-quote-right' },
strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: 'fas fa-quote-right' },
strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: 'fas fa-quote-right' },
strReverse: { in: ['string'], out: 'string', category: 'text', icon: 'fas fa-quote-right' },
join: { in: ['stringArray', 'string'], out: 'string', category: 'text', icon: 'fas fa-quote-right' },
stringToNumber: { in: ['string'], out: 'number', category: 'convert', icon: 'fas fa-exchange-alt' },
numberToString: { in: ['number'], out: 'string', category: 'convert', icon: 'fas fa-exchange-alt' },
splitStrByLine: { in: ['string'], out: 'stringArray', category: 'convert', icon: 'fas fa-exchange-alt' },
pick: { in: [null, 'number'], out: null, category: 'list', icon: 'fas fa-indent' },
listLen: { in: [null], out: 'number', category: 'list', icon: 'fas fa-indent' },
rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' },
dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' },
seedRannum: { in: [null, 'number', 'number'], out: 'number', category: 'random', icon: 'fas fa-dice' },
random: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' },
dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' },
seedRandom: { in: [null, 'number'], out: 'boolean', category: 'random', icon: 'fas fa-dice' },
randomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice' },
dailyRandomPick: { in: [0], out: 0, category: 'random', icon: 'fas fa-dice' },
seedRandomPick: { in: [null, 0], out: 0, category: 'random', icon: 'fas fa-dice' },
DRPWPM: { in: ['stringArray'], out: 'string', category: 'random', icon: 'fas fa-dice' }, // dailyRandomPickWithProbabilityMapping
};
export function initHpmlLib(expr: Expr, scope: HpmlScope, randomSeed: string, visitor?: any) {
const date = new Date();
const day = `${visitor ? visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
// SHOULD be fine to ignore since it's intended + function shape isn't defined
// eslint-disable-next-line @typescript-eslint/ban-types
const funcs: Record<string, Function> = {
not: (a: boolean) => !a,
or: (a: boolean, b: boolean) => a || b,
@ -189,7 +190,7 @@ export function initHpmlLib(expr: Expr, scope: HpmlScope, randomSeed: string, vi
const result: any[] = [];
for (let i = 0; i < times; i++) {
result.push(fn.exec({
[fn.slots[0]]: i + 1
[fn.slots[0]]: i + 1,
}));
}
return result;

View file

@ -1,4 +1,4 @@
export function query(obj: {}): string {
export function query(obj: Record<string, any>): string {
const params = Object.entries(obj)
.filter(([, v]) => Array.isArray(v) ? v.length : v !== undefined)
.reduce((a, [k, v]) => (a[k] = v, a), {} as Record<string, any>);

View file

@ -72,7 +72,6 @@ export const defaultStore = markRaw(new Storage('base', {
'drive',
'followRequests',
'-',
'featured',
'explore',
'announcements',
'search',

View file

@ -39,14 +39,6 @@ html {
scrollbar-color: var(--scrollbarHandle) inherit;
scrollbar-width: thin;
&:hover {
scrollbar-color: var(--scrollbarHandleHover) inherit;
}
&:active {
scrollbar-color: var(--accent) inherit;
}
&::-webkit-scrollbar {
width: 6px;
height: 6px;

View file

@ -60,8 +60,8 @@ const DESKTOP_THRESHOLD = 1100;
let isDesktop = $ref(window.innerWidth >= DESKTOP_THRESHOLD);
let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
const widgetsShowing = $ref(false);
const fullView = $ref(false);
let widgetsShowing = $ref(false);
let fullView = $ref(false);
let globalHeaderHeight = $ref(0);
const wallpaper = localStorage.getItem('wallpaper') != null;
const showMenuOnTop = $computed(() => defaultStore.state.menuDisplay === 'top');

View file

@ -33,7 +33,8 @@
<div>{{ i18n.ts._deck.introduction2 }}</div>
</div>
<div class="sideMenu">
<button v-tooltip="i18n.ts._deck.addColumn" class="_button button" @click="addColumn"><i class="fas fa-plus"></i></button>
<button v-tooltip.left="i18n.ts._deck.addColumn" class="_button button" @click="addColumn"><i class="fas fa-plus"></i></button>
<button v-tooltip.left="i18n.ts.settings" class="_button button settings" @click="showSettings"><i class="fas fa-cog"></i></button>
</div>
</div>
</div>
@ -79,12 +80,14 @@ import { i18n } from '@/i18n';
import { mainRouter } from '@/router';
const XStatusBars = defineAsyncComponent(() => import('@/ui/_common_/statusbars.vue'));
if (deckStore.state.navWindow) {
mainRouter.navHook = (path) => {
mainRouter.navHook = (path): boolean => {
const noMainColumn = !deckStore.state.columns.some(x => x.type === 'main');
if (deckStore.state.navWindow || noMainColumn) {
os.pageWindow(path);
return true;
};
}
}
return false;
};
const isMobile = ref(window.innerWidth <= 500);
window.addEventListener('resize', () => {
@ -108,6 +111,10 @@ const menuIndicated = computed(() => {
return false;
});
function showSettings() {
os.pageWindow('/settings/deck');
}
let columnsEl = $ref<HTMLElement>();
const addColumn = async (ev) => {

View file

@ -13,7 +13,7 @@ type ColumnWidget = {
export type Column = {
id: string;
type: string;
type: 'main' | 'widgets' | 'notifications' | 'tl' | 'antenna' | 'list' | 'mentions' | 'direct';
name: string | null;
width: number;
widgets?: ColumnWidget[];

View file

@ -53,7 +53,7 @@ function onContextmenu(ev: MouseEvent) {
if (isLink(ev.target as HTMLElement)) return;
if (['INPUT', 'TEXTAREA', 'IMG', 'VIDEO', 'CANVAS'].includes((ev.target as HTMLElement).tagName) || (ev.target as HTMLElement).attributes['contenteditable']) return;
if (window.getSelection()?.toString() !== '') return;
const path = router.currentRoute.value.path;
const path = mainRouter.currentRoute.value.path;
os.contextMenu([{
type: 'label',
text: path,

View file

@ -3,6 +3,7 @@
<template #header><i class="fas fa-window-maximize" style="margin-right: 8px;"></i>{{ column.name }}</template>
<div class="wtdtxvec">
<div v-if="!(column.widgets && column.widgets.length > 0) && !edit" class="intro">{{ i18n.ts._deck.widgetsIntroduction }}</div>
<XWidgets :edit="edit" :widgets="column.widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="edit = false"/>
</div>
</XColumn>
@ -13,6 +14,7 @@ import { } from 'vue';
import XColumn from './column.vue';
import { addColumnWidget, Column, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store';
import XWidgets from '@/components/widgets.vue';
import { i18n } from '@/i18n';
const props = defineProps<{
column: Column;
@ -52,5 +54,10 @@ function func() {
--panelBorder: none;
padding: 0 var(--margin);
> .intro {
padding: 16px;
text-align: center;
}
}
</style>

View file

@ -4,6 +4,7 @@
<div>
<h1 v-if="meta"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span></h1>
<div v-if="meta" class="about">
<!-- eslint-disable-next-line vue/no-v-html -->
<div class="desc" v-html="meta.description || $ts.introMisskey"></div>
</div>
<div class="action">
@ -101,13 +102,18 @@ export default defineComponent({
},
methods: {
setParallax(el) {
//new simpleParallax(el);
},
// @ThatOneCalculator: Are these methods even used?
// I can't find references to them anywhere else in the code...
// setParallax(el) {
// new simpleParallax(el);
// },
changePage(page) {
if (page == null) return;
// eslint-disable-next-line no-undef
if (page[symbols.PAGE_INFO]) {
// eslint-disable-next-line no-undef
this.pageInfo = page[symbols.PAGE_INFO];
}
},

View file

@ -1,10 +1,11 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<div class="rwqkcmrc" :style="{ backgroundImage: transparent ? 'none' : `url(${ $instance.backgroundImageUrl })` }">
<div class="back" :class="{ transparent }"></div>
<div class="contents">
<div class="wrapper">
<h1 v-if="meta" :class="{ full }">
<MkA to="/" class="link"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl"><span v-else class="text">{{ instanceName }}</span></MkA>
<MkA to="/" class="link"><img v-if="meta.logoImageUrl" class="logo" :src="meta.logoImageUrl" alt="logo"><span v-else class="text">{{ instanceName }}</span></MkA>
</h1>
<template v-if="full">
<div v-if="meta" class="about">
@ -21,7 +22,7 @@
<div class="title">{{ announcement.title }}</div>
<div class="content">
<Mfm :text="announcement.text"/>
<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
<img v-if="announcement.imageUrl" :src="announcement.imageUrl" alt="announcement image"/>
</div>
</section>
</MkPagination>

View file

@ -36,8 +36,9 @@ export const useWidgetPropsManager = <F extends Form & Record<string, { default:
const mergeProps = () => {
for (const prop of Object.keys(propsDef)) {
if (widgetProps.hasOwnProperty(prop)) continue;
widgetProps[prop] = propsDef[prop].default;
if (typeof widgetProps[prop] === 'undefined') {
widgetProps[prop] = propsDef[prop].default;
}
}
};
watch(widgetProps, () => {

View file

@ -4215,10 +4215,10 @@ verror@1.10.0:
core-util-is "1.0.2"
extsprintf "^1.2.0"
vite@3.0.0-beta.5:
version "3.0.0-beta.5"
resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.0-beta.5.tgz#708d5b732dee98d77877cb094b567f5596508b5b"
integrity sha512-SfesZuCME4fEmLy4hgsJAg55HRiTgDhH3oPM44XePrdKP5FqYvDkzpSWl6ldDOJYTskKWafGyyuYfXoxodv40Q==
vite@3.0.0-beta.6:
version "3.0.0-beta.6"
resolved "https://registry.yarnpkg.com/vite/-/vite-3.0.0-beta.6.tgz#dd54c304ce7ceca243be8a114f28c431bbc447a1"
integrity sha512-jAxxCGXs6oIO3dFh7gwDEP9RqFzYY+ULDWawS1dd3HfM4FCr8rkOnLljDoBBIDdTNM8M7pDzdoYSmpPEOJqyZQ==
dependencies:
esbuild "^0.14.47"
postcss "^8.4.14"