Merge pull request #2 from sim1222/develop

vsddgvsgdsgsdsg
This commit is contained in:
こけっち 2021-12-28 02:27:13 +09:00 committed by GitHub
commit 3f6691bd5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 2234 additions and 460 deletions

View File

@ -7,6 +7,14 @@
--> -->
## 12.x.x (unreleased)
### Improvements
- クライアント: ノートプレビューの精度を改善
### Bugfixes
- クライアント: 一部のコンポーネントが裏に隠れるのを修正
## 12.100.2 (2021/12/18) ## 12.100.2 (2021/12/18)
### Bugfixes ### Bugfixes

View File

@ -87,7 +87,7 @@ Configuration files are located in [`/.github/workflows`](/.github/workflows).
## Vue ## Vue
Misskey uses Vue(v3) as its front-end framework. Misskey uses Vue(v3) as its front-end framework.
**When creating a new component, please use the Composition API instead of the Options API.** **When creating a new component, please use the Composition API (and [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html)) instead of the Options API.**
Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome. Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
## Adding MisskeyRoom items ## Adding MisskeyRoom items

View File

@ -448,6 +448,7 @@ uiLanguage: "UIの表示言語"
groupInvited: "グループに招待されました" groupInvited: "グループに招待されました"
aboutX: "{x}について" aboutX: "{x}について"
useOsNativeEmojis: "OSネイティブの絵文字を使用" useOsNativeEmojis: "OSネイティブの絵文字を使用"
disableDrawer: "メニューをドロワーで表示しない"
youHaveNoGroups: "グループがありません" youHaveNoGroups: "グループがありません"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。" joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
noHistory: "履歴はありません" noHistory: "履歴はありません"
@ -613,7 +614,6 @@ regenerateLoginToken: "ログイントークンを再生成"
regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。" regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。"
setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。" setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
fileIdOrUrl: "ファイルIDまたはURL" fileIdOrUrl: "ファイルIDまたはURL"
chatOpenBehavior: "チャットを開くときの動作"
behavior: "動作" behavior: "動作"
sample: "サンプル" sample: "サンプル"
abuseReports: "通報" abuseReports: "通報"
@ -818,6 +818,7 @@ leaveGroup: "グループから抜ける"
leaveGroupConfirm: "「{name}」から抜けますか?" leaveGroupConfirm: "「{name}」から抜けますか?"
useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示" useDrawerReactionPickerForMobile: "モバイルデバイスのときドロワーで表示"
welcomeBackWithName: "おかえりなさい、{name}さん" welcomeBackWithName: "おかえりなさい、{name}さん"
clickToFinishEmailVerification: "[{ok}]を押して、メールアドレスの確認を完了してください。"
_emailUnavailable: _emailUnavailable:
used: "既に使用されています" used: "既に使用されています"

1779
locales/ja-NYA.yml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "12.100.2", "version": "12.100.2-simkey",
"codename": "indigo", "codename": "indigo",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -1,2 +1,47 @@
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
// ブラウザで直接表示することを許可するファイルの種類のリスト
// ここに含まれないものは application/octet-stream としてレスポンスされる
// SVGはXSSを生むので許可しない
export const FILE_TYPE_BROWSERSAFE = [
// Images
'image/png',
'image/gif',
'image/jpeg',
'image/webp',
'image/apng',
'image/bmp',
'image/tiff',
'image/x-icon',
// OggS
'audio/opus',
'video/ogg',
'audio/ogg',
'application/ogg',
// ISO/IEC base media file format
'video/quicktime',
'video/mp4',
'audio/mp4',
'video/x-m4v',
'audio/x-m4a',
'video/3gpp',
'video/3gpp2',
'video/mpeg',
'audio/mpeg',
'video/webm',
'audio/webm',
'audio/aac',
'audio/x-flac',
'audio/vnd.wave',
];
/*
https://github.com/sindresorhus/file-type/blob/main/supported.js
https://github.com/sindresorhus/file-type/blob/main/core.js
https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Containers
*/

View File

@ -99,7 +99,10 @@ export async function getFileInfo(path: string): Promise<FileInfo> {
/** /**
* Detect MIME Type and extension * Detect MIME Type and extension
*/ */
export async function detectType(path: string) { export async function detectType(path: string): Promise<{
mime: string;
ext: string | null;
}> {
// Check 0 byte // Check 0 byte
const fileSize = await getFileSize(path); const fileSize = await getFileSize(path);
if (fileSize === 0) { if (fileSize === 0) {

View File

@ -14,6 +14,7 @@ import { detectType } from '@/misc/get-file-info';
import { convertToJpeg, convertToPngOrJpeg } from '@/services/drive/image-processor'; import { convertToJpeg, convertToPngOrJpeg } from '@/services/drive/image-processor';
import { GenerateVideoThumbnail } from '@/services/drive/generate-video-thumbnail'; import { GenerateVideoThumbnail } from '@/services/drive/generate-video-thumbnail';
import { StatusError } from '@/misc/fetch'; import { StatusError } from '@/misc/fetch';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
//const _filename = fileURLToPath(import.meta.url); //const _filename = fileURLToPath(import.meta.url);
const _filename = __filename; const _filename = __filename;
@ -27,6 +28,7 @@ const commonReadableHandlerGenerator = (ctx: Koa.Context) => (e: Error): void =>
ctx.set('Cache-Control', 'max-age=300'); ctx.set('Cache-Control', 'max-age=300');
}; };
// eslint-disable-next-line import/no-default-export
export default async function(ctx: Koa.Context) { export default async function(ctx: Koa.Context) {
const key = ctx.params.key; const key = ctx.params.key;
@ -81,7 +83,7 @@ export default async function(ctx: Koa.Context) {
const image = await convertFile(); const image = await convertFile();
ctx.body = image.data; ctx.body = image.data;
ctx.set('Content-Type', image.type); ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(image.type) ? image.type : 'application/octet-stream');
ctx.set('Cache-Control', 'max-age=31536000, immutable'); ctx.set('Cache-Control', 'max-age=31536000, immutable');
} catch (e) { } catch (e) {
serverLogger.error(`${e}`); serverLogger.error(`${e}`);
@ -112,14 +114,14 @@ export default async function(ctx: Koa.Context) {
}).toString(); }).toString();
ctx.body = InternalStorage.read(key); ctx.body = InternalStorage.read(key);
ctx.set('Content-Type', mime); ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(mime) ? mime : 'application/octet-stream');
ctx.set('Cache-Control', 'max-age=31536000, immutable'); ctx.set('Cache-Control', 'max-age=31536000, immutable');
ctx.set('Content-Disposition', contentDisposition('inline', filename)); ctx.set('Content-Disposition', contentDisposition('inline', filename));
} else { } else {
const readable = InternalStorage.read(file.accessKey!); const readable = InternalStorage.read(file.accessKey!);
readable.on('error', commonReadableHandlerGenerator(ctx)); readable.on('error', commonReadableHandlerGenerator(ctx));
ctx.body = readable; ctx.body = readable;
ctx.set('Content-Type', file.type); ctx.set('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.type) ? file.type : 'application/octet-stream');
ctx.set('Cache-Control', 'max-age=31536000, immutable'); ctx.set('Cache-Control', 'max-age=31536000, immutable');
ctx.set('Content-Disposition', contentDisposition('inline', file.name)); ctx.set('Content-Disposition', contentDisposition('inline', file.name));
} }

View File

@ -6,6 +6,7 @@ import { createTemp } from '@/misc/create-temp';
import { downloadUrl } from '@/misc/download-url'; import { downloadUrl } from '@/misc/download-url';
import { detectType } from '@/misc/get-file-info'; import { detectType } from '@/misc/get-file-info';
import { StatusError } from '@/misc/fetch'; import { StatusError } from '@/misc/fetch';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
export async function proxyMedia(ctx: Koa.Context) { export async function proxyMedia(ctx: Koa.Context) {
const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url; const url = 'url' in ctx.query ? ctx.query.url : 'https://' + ctx.params.url;
@ -18,7 +19,7 @@ export async function proxyMedia(ctx: Koa.Context) {
const { mime, ext } = await detectType(path); const { mime, ext } = await detectType(path);
if (!mime.startsWith('image/')) throw 403; if (!FILE_TYPE_BROWSERSAFE.includes(mime)) throw 403;
let image: IImage; let image: IImage;

View File

@ -4,6 +4,7 @@ block vars
- const user = note.user; - const user = note.user;
- const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`; - const title = user.name ? `${user.name} (@${user.username})` : `@${user.username}`;
- const url = `${config.url}/notes/${note.id}`; - const url = `${config.url}/notes/${note.id}`;
- const isRenote = note.renote && note.text == null && note.fileIds.length == 0 && note.poll == null;
block title block title
= `${title} | ${instanceName}` = `${title} | ${instanceName}`
@ -19,7 +20,7 @@ block og
meta(property='og:image' content= user.avatarUrl) meta(property='og:image' content= user.avatarUrl)
block meta block meta
if user.host || profile.noCrawle if user.host || isRenote || profile.noCrawle
meta(name='robots' content='noindex') meta(name='robots' content='noindex')
meta(name='misskey:user-username' content=user.username) meta(name='misskey:user-username' content=user.username)

View File

@ -20,6 +20,7 @@ import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error';
import * as S3 from 'aws-sdk/clients/s3'; import * as S3 from 'aws-sdk/clients/s3';
import { getS3 } from './s3'; import { getS3 } from './s3';
import * as sharp from 'sharp'; import * as sharp from 'sharp';
import { FILE_TYPE_BROWSERSAFE } from '@/const';
const logger = driveLogger.createSubLogger('register', 'yellow'); const logger = driveLogger.createSubLogger('register', 'yellow');
@ -241,6 +242,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
*/ */
async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) { async function upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) {
if (type === 'image/apng') type = 'image/png'; if (type === 'image/apng') type = 'image/png';
if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream';
const meta = await fetchMeta(); const meta = await fetchMeta();

View File

@ -3,10 +3,33 @@ import config from '@/config/index';
import { SwSubscriptions } from '@/models/index'; import { SwSubscriptions } from '@/models/index';
import { fetchMeta } from '@/misc/fetch-meta'; import { fetchMeta } from '@/misc/fetch-meta';
import { Packed } from '@/misc/schema'; import { Packed } from '@/misc/schema';
import { getNoteSummary } from '@/misc/get-note-summary';
type notificationType = 'notification' | 'unreadMessagingMessage'; type notificationType = 'notification' | 'unreadMessagingMessage';
type notificationBody = Packed<'Notification'> | Packed<'MessagingMessage'>; type notificationBody = Packed<'Notification'> | Packed<'MessagingMessage'>;
// プッシュメッセージサーバーには文字数制限があるため、内容を削減します
function truncateNotification(notification: Packed<'Notification'>): any {
if (notification.note) {
return {
...notification,
note: {
...notification.note,
// textをgetNoteSummaryしたものに置き換える
text: getNoteSummary(notification.type === 'renote' ? notification.note.renote as Packed<'Note'> : notification.note),
...{
cw: undefined,
reply: undefined,
renote: undefined,
user: undefined as any, // 通知を受け取ったユーザーである場合が多いのでこれも捨てる
}
}
};
}
return notification;
}
export default async function(userId: string, type: notificationType, body: notificationBody) { export default async function(userId: string, type: notificationType, body: notificationBody) {
const meta = await fetchMeta(); const meta = await fetchMeta();
@ -32,7 +55,9 @@ export default async function(userId: string, type: notificationType, body: noti
}; };
push.sendNotification(pushSubscription, JSON.stringify({ push.sendNotification(pushSubscription, JSON.stringify({
type, body, type,
body: type === 'notification' ? truncateNotification(body as Packed<'Notification'>) : body,
userId,
}), { }), {
proxy: config.proxy, proxy: config.proxy,
}).catch((err: any) => { }).catch((err: any) => {

View File

@ -77,7 +77,7 @@
import { defineComponent, markRaw } from 'vue'; import { defineComponent, markRaw } from 'vue';
import { emojilist } from '@/scripts/emojilist'; import { emojilist } from '@/scripts/emojilist';
import { getStaticImageUrl } from '@/scripts/get-static-image-url'; import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import Particle from '@/components/particle.vue'; import Ripple from '@/components/ripple.vue';
import * as os from '@/os'; import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch'; import { isTouchUsing } from '@/scripts/touch';
import { isMobile } from '@/scripts/is-mobile'; import { isMobile } from '@/scripts/is-mobile';
@ -296,9 +296,9 @@ export default defineComponent({
if (ev) { if (ev) {
const el = ev.currentTarget || ev.target; const el = ev.currentTarget || ev.target;
const rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2); const x = rect.left + (el.offsetWidth / 2);
const y = rect.top + (el.clientHeight / 2); const y = rect.top + (el.offsetHeight / 2);
os.popup(Particle, { x, y }, {}, 'end'); os.popup(Ripple, { x, y }, {}, 'end');
} }
const key = this.getKey(emoji); const key = this.getKey(emoji);

View File

@ -5,7 +5,7 @@
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div> <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
<input ref="inputEl" <input ref="inputEl"
v-model="v" v-model="v"
v-panel v-adaptive-border
:type="type" :type="type"
:disabled="disabled" :disabled="disabled"
:required="required" :required="required"
@ -243,7 +243,8 @@ export default defineComponent({
font-weight: normal; font-weight: normal;
font-size: 1em; font-size: 1em;
color: var(--fg); color: var(--fg);
border: solid 0.5px var(--panel); background: var(--panel);
border: solid 1px var(--panel);
border-radius: 6px; border-radius: 6px;
outline: none; outline: none;
box-shadow: none; box-shadow: none;
@ -251,7 +252,7 @@ export default defineComponent({
transition: border-color 0.1s ease-out; transition: border-color 0.1s ease-out;
&:hover { &:hover {
border-color: var(--inputBorderHover); border-color: var(--inputBorderHover) !important;
} }
} }
@ -298,7 +299,7 @@ export default defineComponent({
&.focused { &.focused {
> input { > input {
border-color: var(--accent); border-color: var(--accent) !important;
//box-shadow: 0 0 0 4px var(--focus); //box-shadow: 0 0 0 4px var(--focus);
} }
} }

View File

@ -3,7 +3,9 @@
<div class="label" @click="focus"><slot name="label"></slot></div> <div class="label" @click="focus"><slot name="label"></slot></div>
<div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick"> <div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick">
<div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div> <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div>
<select ref="inputEl" v-model="v" v-panel <select ref="inputEl"
v-model="v"
v-adaptive-border
class="select" class="select"
:disabled="disabled" :disabled="disabled"
:required="required" :required="required"
@ -226,7 +228,7 @@ export default defineComponent({
&:hover { &:hover {
> .select { > .select {
border-color: var(--inputBorderHover); border-color: var(--inputBorderHover) !important;
} }
} }
@ -242,6 +244,7 @@ export default defineComponent({
font-weight: normal; font-weight: normal;
font-size: 1em; font-size: 1em;
color: var(--fg); color: var(--fg);
background: var(--panel);
border: solid 1px var(--panel); border: solid 1px var(--panel);
border-radius: 6px; border-radius: 6px;
outline: none; outline: none;
@ -295,7 +298,7 @@ export default defineComponent({
&.focused { &.focused {
> select { > select {
border-color: var(--accent); border-color: var(--accent) !important;
} }
} }

View File

@ -2,10 +2,6 @@
<div <div
class="ziffeoms" class="ziffeoms"
:class="{ disabled, checked }" :class="{ disabled, checked }"
role="switch"
:aria-checked="checked"
:aria-disabled="disabled"
@click.prevent="toggle"
> >
<input <input
ref="input" ref="input"
@ -13,18 +9,20 @@
:disabled="disabled" :disabled="disabled"
@keydown.enter="toggle" @keydown.enter="toggle"
> >
<span v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button"> <span ref="button" v-adaptive-border v-tooltip="checked ? $ts.itsOn : $ts.itsOff" class="button" @click.prevent="toggle">
<span class="handle"></span> <i class="check fas fa-check"></i>
</span> </span>
<span class="label"> <span class="label">
<span><slot></slot></span> <span @click="toggle"><slot></slot></span>
<p class="caption"><slot name="caption"></slot></p> <p class="caption"><slot name="caption"></slot></p>
</span> </span>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, ref, toRefs } from 'vue';
import * as os from '@/os';
import Ripple from '@/components/ripple.vue';
export default defineComponent({ export default defineComponent({
props: { props: {
@ -37,17 +35,28 @@ export default defineComponent({
default: false default: false
} }
}, },
computed: {
checked(): boolean { setup(props, context) {
return this.modelValue; const button = ref<HTMLElement>();
} const checked = toRefs(props).modelValue;
const toggle = () => {
if (props.disabled) return;
context.emit('update:modelValue', !checked.value);
if (!checked.value) {
const rect = button.value.getBoundingClientRect();
const x = rect.left + (button.value.offsetWidth / 2);
const y = rect.top + (button.value.offsetHeight / 2);
os.popup(Ripple, { x, y, particle: false }, {}, 'end');
}
};
return {
button,
checked,
toggle,
};
}, },
methods: {
toggle() {
if (this.disabled) return;
this.$emit('update:modelValue', !this.checked);
}
}
}); });
</script> </script>
@ -55,16 +64,7 @@ export default defineComponent({
.ziffeoms { .ziffeoms {
position: relative; position: relative;
display: flex; display: flex;
cursor: pointer; transition: all 0.2s ease;
transition: all 0.3s;
&:first-child {
margin-top: 0;
}
&:last-child {
margin-bottom: 0;
}
> * { > * {
user-select: none; user-select: none;
@ -80,27 +80,32 @@ export default defineComponent({
> .button { > .button {
position: relative; position: relative;
display: inline-block; display: inline-flex;
flex-shrink: 0; flex-shrink: 0;
margin: 0; margin: 0;
width: 36px; box-sizing: border-box;
height: 26px; width: 23px;
background: var(--switchBg); height: 23px;
outline: none; outline: none;
border-radius: 999px; background: var(--panel);
border: solid 1px var(--panel);
border-radius: 4px;
cursor: pointer;
transition: inherit; transition: inherit;
> .handle { > .check {
position: absolute; margin: auto;
top: 0; opacity: 0;
bottom: 0; color: var(--fgOnAccent);
left: 5px; font-size: 13px;
margin: auto 0; transform: scale(0.5);
border-radius: 100%; transition: all 0.2s ease;
transition: background-color 0.3s, transform 0.3s; }
width: 16px; }
height: 16px;
background-color: #fff; &:hover {
> .button {
border-color: var(--inputBorderHover) !important;
} }
} }
@ -108,13 +113,13 @@ export default defineComponent({
margin-left: 16px; margin-left: 16px;
margin-top: 2px; margin-top: 2px;
display: block; display: block;
cursor: pointer;
transition: inherit; transition: inherit;
color: var(--fg); color: var(--fg);
> span { > span {
display: block; display: block;
line-height: 20px; line-height: 20px;
cursor: pointer;
transition: inherit; transition: inherit;
} }
@ -129,12 +134,6 @@ export default defineComponent({
} }
} }
&:hover {
> .button {
background-color: var(--accentedBg);
}
}
&.disabled { &.disabled {
opacity: 0.6; opacity: 0.6;
cursor: not-allowed; cursor: not-allowed;
@ -142,11 +141,12 @@ export default defineComponent({
&.checked { &.checked {
> .button { > .button {
background-color: var(--accent); background-color: var(--accent) !important;
border-color: var(--accent); border-color: var(--accent) !important;
> .handle { > .check {
transform: translateX(10px); opacity: 1;
transform: scale(1);
} }
} }
} }

View File

@ -4,7 +4,7 @@
<div class="input" :class="{ disabled, focused, tall, pre }"> <div class="input" :class="{ disabled, focused, tall, pre }">
<textarea ref="inputEl" <textarea ref="inputEl"
v-model="v" v-model="v"
v-panel v-adaptive-border
:class="{ code, _monospace: code }" :class="{ code, _monospace: code }"
:disabled="disabled" :disabled="disabled"
:required="required" :required="required"
@ -210,7 +210,8 @@ export default defineComponent({
font-weight: normal; font-weight: normal;
font-size: 1em; font-size: 1em;
color: var(--fg); color: var(--fg);
border: solid 0.5px var(--panel); background: var(--panel);
border: solid 1px var(--panel);
border-radius: 6px; border-radius: 6px;
outline: none; outline: none;
box-shadow: none; box-shadow: none;
@ -218,13 +219,13 @@ export default defineComponent({
transition: border-color 0.1s ease-out; transition: border-color 0.1s ease-out;
&:hover { &:hover {
border-color: var(--inputBorderHover); border-color: var(--inputBorderHover) !important;
} }
} }
&.focused { &.focused {
> textarea { > textarea {
border-color: var(--accent); border-color: var(--accent) !important;
} }
} }

View File

@ -106,11 +106,6 @@ export default defineComponent({
return; return;
} }
if (this.to.startsWith('/my/messaging')) {
if (ColdDeviceStorage.get('chatOpenBehavior') === 'window') return this.window();
if (ColdDeviceStorage.get('chatOpenBehavior') === 'popout') return this.popout();
}
if (this.behavior) { if (this.behavior) {
if (this.behavior === 'window') { if (this.behavior === 'window') {
return this.window(); return this.window();

View File

@ -6,7 +6,7 @@
<i v-else-if="info.icon" class="icon" :class="info.icon"></i> <i v-else-if="info.icon" class="icon" :class="info.icon"></i>
<div class="title"> <div class="title">
<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/> <MkUserName v-if="info.userName" :user="info.userName" :nowrap="true" class="title"/>
<div v-else-if="info.title" class="title">{{ info.title }}</div> <div v-else-if="info.title" class="title">{{ info.title }}</div>
<div v-if="!narrow && info.subtitle" class="subtitle"> <div v-if="!narrow && info.subtitle" class="subtitle">
{{ info.subtitle }} {{ info.subtitle }}
@ -268,6 +268,7 @@ export default defineComponent({
> .titleContainer { > .titleContainer {
display: flex; display: flex;
align-items: center; align-items: center;
max-width: 400px;
overflow: auto; overflow: auto;
white-space: nowrap; white-space: nowrap;
text-align: left; text-align: left;

View File

@ -1,7 +1,5 @@
<template> <template>
<component :is="self ? 'MkA' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target" <component :is="self ? 'MkA' : 'a'" ref="el" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
@contextmenu.stop="() => {}" @contextmenu.stop="() => {}"
> >
<template v-if="!self"> <template v-if="!self">
@ -20,11 +18,11 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, ref } from 'vue';
import { toUnicode as decodePunycode } from 'punycode/'; import { toUnicode as decodePunycode } from 'punycode/';
import { url as local } from '@/config'; import { url as local } from '@/config';
import { isTouchUsing } from '@/scripts/touch';
import * as os from '@/os'; import * as os from '@/os';
import { useTooltip } from '@/scripts/use-tooltip';
export default defineComponent({ export default defineComponent({
props: { props: {
@ -35,74 +33,36 @@ export default defineComponent({
rel: { rel: {
type: String, type: String,
required: false, required: false,
default: null,
} }
}, },
data() { setup(props) {
const self = this.url.startsWith(local); const self = props.url.startsWith(local);
const url = new URL(props.url);
const el = ref();
useTooltip(el, (showing) => {
os.popup(import('@/components/url-preview-popup.vue'), {
showing,
url: props.url,
source: el.value,
}, {}, 'closed');
});
return { return {
local, local,
schema: null as string | null, schema: url.protocol,
hostname: null as string | null, hostname: decodePunycode(url.hostname),
port: null as string | null, port: url.port,
pathname: null as string | null, pathname: decodeURIComponent(url.pathname),
query: null as string | null, query: decodeURIComponent(url.search),
hash: null as string | null, hash: decodeURIComponent(url.hash),
self: self, self: self,
attr: self ? 'to' : 'href', attr: self ? 'to' : 'href',
target: self ? null : '_blank', target: self ? null : '_blank',
showTimer: null, el,
hideTimer: null,
checkTimer: null,
close: null,
}; };
}, },
created() {
const url = new URL(this.url);
this.schema = url.protocol;
this.hostname = decodePunycode(url.hostname);
this.port = url.port;
this.pathname = decodeURIComponent(url.pathname);
this.query = decodeURIComponent(url.search);
this.hash = decodeURIComponent(url.hash);
},
methods: {
async showPreview() {
if (!document.body.contains(this.$el)) return;
if (this.close) return;
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
url: this.url,
source: this.$el
});
this.close = () => {
dispose();
};
this.checkTimer = setInterval(() => {
if (!document.body.contains(this.$el)) this.closePreview();
}, 1000);
},
closePreview() {
if (this.close) {
clearInterval(this.checkTimer);
this.close();
this.close = null;
}
},
onMouseover() {
if (isTouchUsing) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
if (isTouchUsing) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
}
}
}); });
</script> </script>

View File

@ -105,6 +105,7 @@ export default defineComponent({
return { return {
previewable, previewable,
gallery, gallery,
pswpZIndex: os.claimZIndex('middle'),
}; };
}, },
}); });
@ -188,3 +189,11 @@ export default defineComponent({
} }
} }
</style> </style>
<style lang="scss">
.pswp {
//
//z-index: v-bind(pswpZIndex);
z-index: 2000000;
}
</style>

View File

@ -7,7 +7,7 @@
</div> </div>
<div class="body"> <div class="body">
<div class="content"> <div class="content">
<Mfm :text="text" :author="$i" :i="$i"/> <Mfm :text="text.trim()" :author="$i" :i="$i"/>
</div> </div>
</div> </div>
</div> </div>
@ -61,6 +61,7 @@ export default defineComponent({
width: 40px; width: 40px;
height: 40px; height: 40px;
border-radius: 8px; border-radius: 8px;
pointer-events: none;
} }
> .main { > .main {
@ -69,6 +70,7 @@ export default defineComponent({
> .header { > .header {
margin-bottom: 2px; margin-bottom: 2px;
font-weight: bold;
} }
> .body { > .body {

View File

@ -16,7 +16,13 @@
<template #headerLeft> <template #headerLeft>
<button v-if="history.length > 0" v-tooltip="$ts.goBack" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button> <button v-if="history.length > 0" v-tooltip="$ts.goBack" class="_button" @click="back()"><i class="fas fa-arrow-left"></i></button>
</template> </template>
<div class="yrolvcoq"> <template #headerRight>
<button v-tooltip="$ts.showInPage" class="_button" @click="expand()"><i class="fas fa-expand-alt"></i></button>
<button v-tooltip="$ts.popout" class="_button" @click="popout()"><i class="fas fa-external-link-alt"></i></button>
<button class="_button" @click="menu"><i class="fas fa-ellipsis-h"></i></button>
</template>
<div class="yrolvcoq" :style="{ background: pageInfo?.bg }">
<MkStickyContainer> <MkStickyContainer>
<template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template> <template #header><MkHeader v-if="pageInfo && !pageInfo.hideHeader" :info="pageInfo"/></template>
<component :is="component" v-bind="props" :ref="changePage"/> <component :is="component" v-bind="props" :ref="changePage"/>
@ -33,6 +39,7 @@ import copyToClipboard from '@/scripts/copy-to-clipboard';
import { resolve } from '@/router'; import { resolve } from '@/router';
import { url } from '@/config'; import { url } from '@/config';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
import * as os from '@/os';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -139,6 +146,23 @@ export default defineComponent({
this.props = props; this.props = props;
}, },
menu(ev) {
os.popupMenu([{
icon: 'fas fa-external-link-alt',
text: this.$ts.openInNewTab,
action: () => {
window.open(this.url, '_blank');
this.$refs.window.close();
}
}, {
icon: 'fas fa-link',
text: this.$ts.copyLink,
action: () => {
copyToClipboard(this.url);
}
}], ev.currentTarget || ev.target);
},
back() { back() {
this.navigate(this.history.pop(), false); this.navigate(this.history.pop(), false);
}, },

View File

@ -2,7 +2,7 @@
<button <button
v-if="count > 0" v-if="count > 0"
ref="buttonRef" ref="buttonRef"
v-particle="canToggle" v-ripple="canToggle"
class="hkzvhatu _button" class="hkzvhatu _button"
:class="{ reacted: note.myReaction == reaction, canToggle }" :class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction()" @click="toggleReaction()"

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="vswabwbm" :style="{ top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }"> <div class="vswabwbm" :style="{ zIndex, top: `${y - 64}px`, left: `${x - 64}px` }" :class="{ active }">
<svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg"> <svg width="128" height="128" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
<circle fill="none" cx="64" cy="64"> <circle fill="none" cx="64" cy="64">
<animate attributeName="r" <animate attributeName="r"
@ -52,7 +52,8 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, onMounted } from 'vue';
import * as os from '@/os';
export default defineComponent({ export default defineComponent({
props: { props: {
@ -63,37 +64,46 @@ export default defineComponent({
y: { y: {
type: Number, type: Number,
required: true required: true
},
particle: {
type: Boolean,
required: false,
default: true,
} }
}, },
emits: ['end'], emits: ['end'],
data() { setup(props, context) {
const particles = []; const particles = [];
const origin = 64; const origin = 64;
const colors = ['#FF1493', '#00FFFF', '#FFE202']; const colors = ['#FF1493', '#00FFFF', '#FFE202'];
for (let i = 0; i < 12; i++) { if (props.particle) {
const angle = Math.random() * (Math.PI * 2); for (let i = 0; i < 12; i++) {
const pos = Math.random() * 16; const angle = Math.random() * (Math.PI * 2);
const velocity = 16 + (Math.random() * 48); const pos = Math.random() * 16;
particles.push({ const velocity = 16 + (Math.random() * 48);
size: 4 + (Math.random() * 8), particles.push({
xA: origin + (Math.sin(angle) * pos), size: 4 + (Math.random() * 8),
yA: origin + (Math.cos(angle) * pos), xA: origin + (Math.sin(angle) * pos),
xB: origin + (Math.sin(angle) * (pos + velocity)), yA: origin + (Math.cos(angle) * pos),
yB: origin + (Math.cos(angle) * (pos + velocity)), xB: origin + (Math.sin(angle) * (pos + velocity)),
color: colors[Math.floor(Math.random() * colors.length)] yB: origin + (Math.cos(angle) * (pos + velocity)),
}); color: colors[Math.floor(Math.random() * colors.length)]
});
}
} }
onMounted(() => {
setTimeout(() => {
context.emit('end');
}, 1100);
});
return { return {
particles particles,
zIndex: os.claimZIndex('high'),
}; };
}, },
mounted() {
setTimeout(() => {
this.$emit('end');
}, 1100);
}
}); });
</script> </script>
@ -101,7 +111,6 @@ export default defineComponent({
.vswabwbm { .vswabwbm {
pointer-events: none; pointer-events: none;
position: fixed; position: fixed;
z-index: 1000000;
width: 128px; width: 128px;
height: 128px; height: 128px;

View File

@ -51,14 +51,13 @@
<span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span> <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="fas fa-exclamation-triangle fa-fw"></i> {{ $ts.passwordNotMatched }}</span>
</template> </template>
</MkInput> </MkInput>
<label v-if="meta.tosUrl" class="_formBlock tou"> <MkSwitch v-if="meta.tosUrl" v-model="ToSAgreement" class="_formBlock tou">
<input v-model="ToSAgreement" type="checkbox">
<I18n :src="$ts.agreeTo"> <I18n :src="$ts.agreeTo">
<template #0> <template #0>
<a :href="meta.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a> <a :href="meta.tosUrl" class="_link" target="_blank">{{ $ts.tos }}</a>
</template> </template>
</I18n> </I18n>
</label> </MkSwitch>
<captcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/> <captcha v-if="meta.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" class="_formBlock captcha" provider="hcaptcha" :sitekey="meta.hcaptchaSiteKey"/>
<captcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/> <captcha v-if="meta.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" class="_formBlock captcha" provider="recaptcha" :sitekey="meta.recaptchaSiteKey"/>
<MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton> <MkButton class="_formBlock" type="submit" :disabled="shouldDisableSubmitting" gradate data-cy-signup-submit>{{ $ts.start }}</MkButton>
@ -258,11 +257,5 @@ export default defineComponent({
.captcha { .captcha {
margin: 16px 0; margin: 16px 0;
} }
> .tou {
display: block;
margin: 16px 0;
cursor: pointer;
}
} }
</style> </style>

View File

@ -284,7 +284,7 @@ export default defineComponent({
} }
&.asDrawer { &.asDrawer {
padding: 12px 0; padding: 12px 0 calc(env(safe-area-inset-bottom, 0px) + 12px) 0;
width: 100%; width: 100%;
> .item { > .item {

View File

@ -13,6 +13,7 @@
import { defineComponent, nextTick, onMounted, computed, PropType, ref, watch } from 'vue'; import { defineComponent, nextTick, onMounted, computed, PropType, ref, watch } from 'vue';
import * as os from '@/os'; import * as os from '@/os';
import { isTouchUsing } from '@/scripts/touch'; import { isTouchUsing } from '@/scripts/touch';
import { defaultStore } from '@/store';
function getFixedContainer(el: Element | null): Element | null { function getFixedContainer(el: Element | null): Element | null {
if (el == null || el.tagName === 'BODY') return null; if (el == null || el.tagName === 'BODY') return null;
@ -77,7 +78,7 @@ export default defineComponent({
const zIndex = os.claimZIndex(props.zPriority); const zIndex = os.claimZIndex(props.zPriority);
const type = computed(() => { const type = computed(() => {
if (props.preferType === 'auto') { if (props.preferType === 'auto') {
if (isTouchUsing && window.innerWidth < 500 && window.innerHeight < 1000) { if (!defaultStore.state.disableDrawer && isTouchUsing && window.innerWidth < 500 && window.innerHeight < 1000) {
return 'drawer'; return 'drawer';
} else { } else {
return props.src != null ? 'popup' : 'dialog'; return props.src != null ? 'popup' : 'dialog';

View File

@ -5,7 +5,12 @@
<MkError v-else-if="error" @retry="init()"/> <MkError v-else-if="error" @retry="init()"/>
<div v-else-if="empty" key="_empty_" class="empty"> <div v-else-if="empty" key="_empty_" class="empty">
<slot name="empty"></slot> <slot name="empty">
<div class="_fullinfo">
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
<div>{{ $ts.nothing }}</div>
</div>
</slot>
</div> </div>
<div v-else class="cxiknjgy"> <div v-else class="cxiknjgy">

View File

@ -414,6 +414,10 @@ export default defineComponent({
} }
} }
> .left {
min-width: 16px;
}
> .title { > .title {
flex: 1; flex: 1;
position: relative; position: relative;
@ -421,7 +425,6 @@ export default defineComponent({
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
text-align: center;
cursor: move; cursor: move;
} }
} }

View File

@ -0,0 +1,24 @@
import { Directive } from 'vue';
export default {
mounted(src, binding, vn) {
const getBgColor = (el: HTMLElement) => {
const style = window.getComputedStyle(el);
if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) {
return style.backgroundColor;
} else {
return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
}
}
const parentBg = getBgColor(src.parentElement);
const myBg = window.getComputedStyle(src).backgroundColor;
if (parentBg === myBg) {
src.style.borderColor = 'var(--divider)';
} else {
src.style.borderColor = myBg;
}
},
} as Directive;

View File

@ -3,7 +3,7 @@ import { App } from 'vue';
import userPreview from './user-preview'; import userPreview from './user-preview';
import size from './size'; import size from './size';
import getSize from './get-size'; import getSize from './get-size';
import particle from './particle'; import ripple from './ripple';
import tooltip from './tooltip'; import tooltip from './tooltip';
import hotkey from './hotkey'; import hotkey from './hotkey';
import appear from './appear'; import appear from './appear';
@ -11,13 +11,14 @@ import anim from './anim';
import stickyContainer from './sticky-container'; import stickyContainer from './sticky-container';
import clickAnime from './click-anime'; import clickAnime from './click-anime';
import panel from './panel'; import panel from './panel';
import adaptiveBorder from './adaptive-border';
export default function(app: App) { export default function(app: App) {
app.directive('userPreview', userPreview); app.directive('userPreview', userPreview);
app.directive('user-preview', userPreview); app.directive('user-preview', userPreview);
app.directive('size', size); app.directive('size', size);
app.directive('get-size', getSize); app.directive('get-size', getSize);
app.directive('particle', particle); app.directive('ripple', ripple);
app.directive('tooltip', tooltip); app.directive('tooltip', tooltip);
app.directive('hotkey', hotkey); app.directive('hotkey', hotkey);
app.directive('appear', appear); app.directive('appear', appear);
@ -25,4 +26,5 @@ export default function(app: App) {
app.directive('click-anime', clickAnime); app.directive('click-anime', clickAnime);
app.directive('sticky-container', stickyContainer); app.directive('sticky-container', stickyContainer);
app.directive('panel', panel); app.directive('panel', panel);
app.directive('adaptive-border', adaptiveBorder);
} }

View File

@ -7,7 +7,7 @@ export default {
if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) { if (style.backgroundColor && !['rgba(0, 0, 0, 0)', 'rgba(0,0,0,0)', 'transparent'].includes(style.backgroundColor)) {
return style.backgroundColor; return style.backgroundColor;
} else { } else {
return getBgColor(el.parentElement); return el.parentElement ? getBgColor(el.parentElement) : 'transparent';
} }
} }

View File

@ -1,4 +1,4 @@
import Particle from '@/components/particle.vue'; import Ripple from '@/components/ripple.vue';
import { popup } from '@/os'; import { popup } from '@/os';
export default { export default {
@ -9,10 +9,10 @@ export default {
el.addEventListener('click', () => { el.addEventListener('click', () => {
const rect = el.getBoundingClientRect(); const rect = el.getBoundingClientRect();
const x = rect.left + (el.clientWidth / 2); const x = rect.left + (el.offsetWidth / 2);
const y = rect.top + (el.clientHeight / 2); const y = rect.top + (el.offsetHeight / 2);
popup(Particle, { x, y }, {}, 'end'); popup(Ripple, { x, y }, {}, 'end');
}); });
} }
}; };

View File

@ -40,7 +40,6 @@ import MkButton from '@/components/ui/button.vue';
import MkSwitch from '@/components/form/switch.vue'; import MkSwitch from '@/components/form/switch.vue';
import XModalWindow from '@/components/ui/modal-window.vue'; import XModalWindow from '@/components/ui/modal-window.vue';
import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue'; import MkDriveFileThumbnail from '@/components/drive-file-thumbnail.vue';
import Progress from '@/scripts/loading';
import bytes from '@/filters/bytes'; import bytes from '@/filters/bytes';
import * as os from '@/os'; import * as os from '@/os';
@ -74,11 +73,9 @@ export default defineComponent({
methods: { methods: {
async fetch() { async fetch() {
Progress.start();
this.file = await os.api('drive/files/show', { fileId: this.fileId }); this.file = await os.api('drive/files/show', { fileId: this.fileId });
this.info = await os.api('admin/drive/show-file', { fileId: this.fileId }); this.info = await os.api('admin/drive/show-file', { fileId: this.fileId });
this.isSensitive = this.file.isSensitive; this.isSensitive = this.file.isSensitive;
Progress.done();
}, },
showUser() { showUser() {

View File

@ -7,8 +7,6 @@
src="antenna" src="antenna"
:antenna="antennaId" :antenna="antennaId"
:sound="true" :sound="true"
@before="before()"
@after="after()"
@queue="queueUpdated" @queue="queueUpdated"
/> />
</div> </div>
@ -17,7 +15,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue'; import { defineComponent, defineAsyncComponent, computed } from 'vue';
import Progress from '@/scripts/loading';
import XTimeline from '@/components/timeline.vue'; import XTimeline from '@/components/timeline.vue';
import { scroll } from '@/scripts/scroll'; import { scroll } from '@/scripts/scroll';
import * as os from '@/os'; import * as os from '@/os';
@ -76,14 +73,6 @@ export default defineComponent({
}, },
methods: { methods: {
before() {
Progress.start();
},
after() {
Progress.done();
},
queueUpdated(q) { queueUpdated(q) {
this.queue = q; this.queue = q;
}, },

View File

@ -1,28 +1,26 @@
<template> <template>
<div> <MkSpacer :content-max="700">
<div class="_section"> <div class="_formRoot">
<div class="_content"> <MkInput v-model="name" class="_formBlock">
<MkInput v-model="name"> <template #label>{{ $ts.name }}</template>
<template #label>{{ $ts.name }}</template> </MkInput>
</MkInput>
<MkTextarea v-model="description"> <MkTextarea v-model="description" class="_formBlock">
<template #label>{{ $ts.description }}</template> <template #label>{{ $ts.description }}</template>
</MkTextarea> </MkTextarea>
<div class="banner"> <div class="banner">
<MkButton v-if="bannerId == null" @click="setBannerImage"><i class="fas fa-plus"></i> {{ $ts._channel.setBanner }}</MkButton> <MkButton v-if="bannerId == null" @click="setBannerImage"><i class="fas fa-plus"></i> {{ $ts._channel.setBanner }}</MkButton>
<div v-else-if="bannerUrl"> <div v-else-if="bannerUrl">
<img :src="bannerUrl" style="width: 100%;"/> <img :src="bannerUrl" style="width: 100%;"/>
<MkButton @click="removeBannerImage()"><i class="fas fa-trash-alt"></i> {{ $ts._channel.removeBanner }}</MkButton> <MkButton @click="removeBannerImage()"><i class="fas fa-trash-alt"></i> {{ $ts._channel.removeBanner }}</MkButton>
</div>
</div> </div>
</div> </div>
<div class="_footer"> <div class="_formBlock">
<MkButton primary @click="save()"><i class="fas fa-save"></i> {{ channelId ? $ts.save : $ts.create }}</MkButton> <MkButton primary @click="save()"><i class="fas fa-save"></i> {{ channelId ? $ts.save : $ts.create }}</MkButton>
</div> </div>
</div> </div>
</div> </MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -51,9 +49,11 @@ export default defineComponent({
[symbols.PAGE_INFO]: computed(() => this.channelId ? { [symbols.PAGE_INFO]: computed(() => this.channelId ? {
title: this.$ts._channel.edit, title: this.$ts._channel.edit,
icon: 'fas fa-satellite-dish', icon: 'fas fa-satellite-dish',
bg: 'var(--bg)',
} : { } : {
title: this.$ts._channel.create, title: this.$ts._channel.create,
icon: 'fas fa-satellite-dish', icon: 'fas fa-satellite-dish',
bg: 'var(--bg)',
}), }),
channel: null, channel: null,
name: null, name: null,

View File

@ -1,29 +1,31 @@
<template> <template>
<div v-if="channel" class="_section"> <MkSpacer :content-max="700">
<div class="wpgynlbz _content _panel _gap" :class="{ hide: !showBanner }"> <div v-if="channel">
<XChannelFollowButton :channel="channel" :full="true" class="subscribe"/> <div class="wpgynlbz _panel _gap" :class="{ hide: !showBanner }">
<button class="_button toggle" @click="() => showBanner = !showBanner"> <XChannelFollowButton :channel="channel" :full="true" class="subscribe"/>
<template v-if="showBanner"><i class="fas fa-angle-up"></i></template> <button class="_button toggle" @click="() => showBanner = !showBanner">
<template v-else><i class="fas fa-angle-down"></i></template> <template v-if="showBanner"><i class="fas fa-angle-up"></i></template>
</button> <template v-else><i class="fas fa-angle-down"></i></template>
<div v-if="!showBanner" class="hideOverlay"> </button>
</div> <div v-if="!showBanner" class="hideOverlay">
<div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner"> </div>
<div class="status"> <div :style="{ backgroundImage: channel.bannerUrl ? `url(${channel.bannerUrl})` : null }" class="banner">
<div><i class="fas fa-users fa-fw"></i><I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div> <div class="status">
<div><i class="fas fa-pencil-alt fa-fw"></i><I18n :src="$ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div> <div><i class="fas fa-users fa-fw"></i><I18n :src="$ts._channel.usersCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.usersCount }}</b></template></I18n></div>
<div><i class="fas fa-pencil-alt fa-fw"></i><I18n :src="$ts._channel.notesCount" tag="span" style="margin-left: 4px;"><template #n><b>{{ channel.notesCount }}</b></template></I18n></div>
</div>
<div class="fade"></div>
</div>
<div v-if="channel.description" class="description">
<Mfm :text="channel.description" :is-note="false" :i="$i"/>
</div> </div>
<div class="fade"></div>
</div>
<div v-if="channel.description" class="description">
<Mfm :text="channel.description" :is-note="false" :i="$i"/>
</div> </div>
<XPostForm v-if="$i" :channel="channel" class="post-form _panel _gap" fixed/>
<XTimeline :key="channelId" class="_gap" src="channel" :channel="channelId" @before="before" @after="after"/>
</div> </div>
</MkSpacer>
<XPostForm v-if="$i" :channel="channel" class="post-form _content _panel _gap" fixed/>
<XTimeline :key="channelId" class="_content _gap" src="channel" :channel="channelId" @before="before" @after="after"/>
</div>
</template> </template>
<script lang="ts"> <script lang="ts">
@ -55,6 +57,12 @@ export default defineComponent({
[symbols.PAGE_INFO]: computed(() => this.channel ? { [symbols.PAGE_INFO]: computed(() => this.channel ? {
title: this.channel.name, title: this.channel.name,
icon: 'fas fa-satellite-dish', icon: 'fas fa-satellite-dish',
bg: 'var(--bg)',
actions: [...(this.$i && this.$i.id === this.channel.userId ? [{
icon: 'fas fa-cog',
text: this.$ts.edit,
handler: this.edit,
}] : [])],
} : null), } : null),
channel: null, channel: null,
showBanner: true, showBanner: true,
@ -79,8 +87,10 @@ export default defineComponent({
} }
}, },
created() { methods: {
edit() {
this.$router.push(`/channels/${this.channel.id}/edit`);
}
}, },
}); });
</script> </script>

View File

@ -1,58 +1,63 @@
<template> <template>
<div> <MkSpacer :content-max="700">
<div v-if="$i" class="_section" style="padding: 0;"> <div v-if="tab === 'featured'" class="_content grwlizim featured">
<MkTab v-model="tab" class="_content"> <MkPagination v-slot="{items}" :pagination="featuredPagination">
<option value="featured"><i class="fas fa-fire-alt"></i> {{ $ts._channel.featured }}</option> <MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/>
<option value="following"><i class="fas fa-heart"></i> {{ $ts._channel.following }}</option> </MkPagination>
<option value="owned"><i class="fas fa-edit"></i> {{ $ts._channel.owned }}</option>
</MkTab>
</div> </div>
<div v-else-if="tab === 'following'" class="_content grwlizim following">
<div class="_section"> <MkPagination v-slot="{items}" :pagination="followingPagination">
<div v-if="tab === 'featured'" class="_content grwlizim featured"> <MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/>
<MkPagination v-slot="{items}" :pagination="featuredPagination"> </MkPagination>
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/>
</MkPagination>
</div>
<div v-if="tab === 'following'" class="_content grwlizim following">
<MkPagination v-slot="{items}" :pagination="followingPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/>
</MkPagination>
</div>
<div v-if="tab === 'owned'" class="_content grwlizim owned">
<MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton>
<MkPagination v-slot="{items}" :pagination="ownedPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/>
</MkPagination>
</div>
</div> </div>
</div> <div v-else-if="tab === 'owned'" class="_content grwlizim owned">
<MkButton class="new" @click="create()"><i class="fas fa-plus"></i></MkButton>
<MkPagination v-slot="{items}" :pagination="ownedPagination">
<MkChannelPreview v-for="channel in items" :key="channel.id" class="_gap" :channel="channel"/>
</MkPagination>
</div>
</MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import MkChannelPreview from '@/components/channel-preview.vue'; import MkChannelPreview from '@/components/channel-preview.vue';
import MkPagination from '@/components/ui/pagination.vue'; import MkPagination from '@/components/ui/pagination.vue';
import MkButton from '@/components/ui/button.vue'; import MkButton from '@/components/ui/button.vue';
import MkTab from '@/components/tab.vue';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
export default defineComponent({ export default defineComponent({
components: { components: {
MkChannelPreview, MkPagination, MkButton, MkTab MkChannelPreview, MkPagination, MkButton,
}, },
data() { data() {
return { return {
[symbols.PAGE_INFO]: { [symbols.PAGE_INFO]: computed(() => ({
title: this.$ts.channel, title: this.$ts.channel,
icon: 'fas fa-satellite-dish', icon: 'fas fa-satellite-dish',
action: { bg: 'var(--bg)',
actions: [{
icon: 'fas fa-plus', icon: 'fas fa-plus',
handler: this.create text: this.$ts.create,
} handler: this.create,
}, }],
tabs: [{
active: this.tab === 'featured',
title: this.$ts._channel.featured,
icon: 'fas fa-fire-alt',
onClick: () => { this.tab = 'featured'; },
}, {
active: this.tab === 'following',
title: this.$ts._channel.following,
icon: 'fas fa-heart',
onClick: () => { this.tab = 'following'; },
}, {
active: this.tab === 'owned',
title: this.$ts._channel.owned,
icon: 'fas fa-edit',
onClick: () => { this.tab = 'owned'; },
},]
})),
tab: 'featured', tab: 'featured',
featuredPagination: { featuredPagination: {
endpoint: 'channels/featured', endpoint: 'channels/featured',

View File

@ -1,14 +1,13 @@
<template> <template>
<div class="jmelgwjh"> <div class="jmelgwjh">
<div class="body"> <div class="body">
<XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'" @before="before()" @after="after()"/> <XNotes class="notes" :pagination="pagination" :detail="true" :prop="'note'"/>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as os from '@/os'; import * as os from '@/os';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -33,16 +32,6 @@ export default defineComponent({
}, },
}; };
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -1,12 +1,11 @@
<template> <template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/> <XNotes ref="notes" :pagination="pagination"/>
</MkSpacer> </MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -29,15 +28,5 @@ export default defineComponent({
}, },
}; };
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -1,12 +1,11 @@
<template> <template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<XNotes :pagination="pagination" @before="before()" @after="after()"/> <XNotes :pagination="pagination"/>
</MkSpacer> </MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -28,15 +27,5 @@ export default defineComponent({
}, },
}; };
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -1,12 +1,11 @@
<template> <template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<XNotes :pagination="pagination" @before="before()" @after="after()"/> <XNotes :pagination="pagination"/>
</MkSpacer> </MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -31,15 +30,5 @@ export default defineComponent({
}, },
}; };
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -35,7 +35,6 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import MkButton from '@/components/ui/button.vue'; import MkButton from '@/components/ui/button.vue';
import * as os from '@/os'; import * as os from '@/os';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -73,7 +72,6 @@ export default defineComponent({
methods: { methods: {
fetch() { fetch() {
Progress.start();
os.api('users/groups/show', { os.api('users/groups/show', {
groupId: this.groupId groupId: this.groupId
}).then(group => { }).then(group => {
@ -82,7 +80,6 @@ export default defineComponent({
userIds: this.group.userIds userIds: this.group.userIds
}).then(users => { }).then(users => {
this.users = users; this.users = users;
Progress.done();
}); });
}); });
}, },

View File

@ -36,7 +36,6 @@
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import MkButton from '@/components/ui/button.vue'; import MkButton from '@/components/ui/button.vue';
import * as os from '@/os'; import * as os from '@/os';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -68,7 +67,6 @@ export default defineComponent({
methods: { methods: {
fetch() { fetch() {
Progress.start();
os.api('users/lists/show', { os.api('users/lists/show', {
listId: this.$route.params.list listId: this.$route.params.list
}).then(list => { }).then(list => {
@ -77,7 +75,6 @@ export default defineComponent({
userIds: this.list.userIds userIds: this.list.userIds
}).then(users => { }).then(users => {
this.users = users; this.users = users;
Progress.done();
}); });
}); });
}, },

View File

@ -1,14 +1,13 @@
<template> <template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<div class="clupoqwt"> <div class="clupoqwt">
<XNotifications class="notifications" :include-types="includeTypes" :unread-only="tab === 'unread'" @before="before" @after="after"/> <XNotifications class="notifications" :include-types="includeTypes" :unread-only="tab === 'unread'"/>
</div> </div>
</MkSpacer> </MkSpacer>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotifications from '@/components/notifications.vue'; import XNotifications from '@/components/notifications.vue';
import * as os from '@/os'; import * as os from '@/os';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -53,14 +52,6 @@ export default defineComponent({
}, },
methods: { methods: {
before() {
Progress.start();
},
after() {
Progress.done();
},
setFilter(ev) { setFilter(ev) {
const typeItems = notificationTypes.map(t => ({ const typeItems = notificationTypes.map(t => ({
text: this.$t(`_notification._types.${t}`), text: this.$t(`_notification._types.${t}`),

View File

@ -1,14 +1,13 @@
<template> <template>
<div class="_section"> <div class="_section">
<div class="_content"> <div class="_content">
<XNotes ref="notes" :pagination="pagination" @before="before" @after="after"/> <XNotes ref="notes" :pagination="pagination"/>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -39,15 +38,5 @@ export default defineComponent({
(this.$refs.notes as any).reload(); (this.$refs.notes as any).reload();
} }
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -43,6 +43,7 @@
<FormSwitch v-model="useOsNativeEmojis" class="_formBlock">{{ $ts.useOsNativeEmojis }} <FormSwitch v-model="useOsNativeEmojis" class="_formBlock">{{ $ts.useOsNativeEmojis }}
<div><Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div> <div><Mfm :key="useOsNativeEmojis" text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></div>
</FormSwitch> </FormSwitch>
<FormSwitch v-model="disableDrawer" class="_formBlock">{{ $ts.disableDrawer }}</FormSwitch>
<FormRadios v-model="fontSize" class="_formBlock"> <FormRadios v-model="fontSize" class="_formBlock">
<template #label>{{ $ts.fontSize }}</template> <template #label>{{ $ts.fontSize }}</template>
@ -76,13 +77,6 @@
<FormSwitch v-model="defaultSideView">{{ $ts.openInSideView }}</FormSwitch> <FormSwitch v-model="defaultSideView">{{ $ts.openInSideView }}</FormSwitch>
</FormGroup> </FormGroup>
<FormSelect v-model="chatOpenBehavior" class="_formBlock">
<template #label>{{ $ts.chatOpenBehavior }}</template>
<option value="page">{{ $ts.showInPage }}</option>
<option value="window">{{ $ts.openInWindow }}</option>
<option value="popout">{{ $ts.popout }}</option>
</FormSelect>
<FormLink to="/settings/deck" class="_formBlock">{{ $ts.deck }}</FormLink> <FormLink to="/settings/deck" class="_formBlock">{{ $ts.deck }}</FormLink>
<FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="fas fa-code"></i></template>{{ $ts.customCss }}</FormLink> <FormLink to="/settings/custom-css" class="_formBlock"><template #icon><i class="fas fa-code"></i></template>{{ $ts.customCss }}</FormLink>
@ -140,6 +134,7 @@ export default defineComponent({
showGapBetweenNotesInTimeline: defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'), showGapBetweenNotesInTimeline: defaultStore.makeGetterSetter('showGapBetweenNotesInTimeline'),
disableAnimatedMfm: defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v), disableAnimatedMfm: defaultStore.makeGetterSetter('animatedMfm', v => !v, v => !v),
useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'), useOsNativeEmojis: defaultStore.makeGetterSetter('useOsNativeEmojis'),
disableDrawer: defaultStore.makeGetterSetter('disableDrawer'),
disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'), disableShowingAnimatedImages: defaultStore.makeGetterSetter('disableShowingAnimatedImages'),
loadRawImages: defaultStore.makeGetterSetter('loadRawImages'), loadRawImages: defaultStore.makeGetterSetter('loadRawImages'),
imageNewTab: defaultStore.makeGetterSetter('imageNewTab'), imageNewTab: defaultStore.makeGetterSetter('imageNewTab'),
@ -147,7 +142,6 @@ export default defineComponent({
disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'), disablePagesScript: defaultStore.makeGetterSetter('disablePagesScript'),
showFixedPostForm: defaultStore.makeGetterSetter('showFixedPostForm'), showFixedPostForm: defaultStore.makeGetterSetter('showFixedPostForm'),
defaultSideView: defaultStore.makeGetterSetter('defaultSideView'), defaultSideView: defaultStore.makeGetterSetter('defaultSideView'),
chatOpenBehavior: ColdDeviceStorage.makeGetterSetter('chatOpenBehavior'),
instanceTicker: defaultStore.makeGetterSetter('instanceTicker'), instanceTicker: defaultStore.makeGetterSetter('instanceTicker'),
enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'), enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'),
useReactionPickerForContextMenu: defaultStore.makeGetterSetter('useReactionPickerForContextMenu'), useReactionPickerForContextMenu: defaultStore.makeGetterSetter('useReactionPickerForContextMenu'),

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="_formRoot"> <div class="_formRoot">
<div v-panel class="rfqxtzch _formBlock"> <div v-adaptive-border class="rfqxtzch _panel _formBlock">
<div class="toggle"> <div class="toggle">
<div class="toggleWrapper"> <div class="toggleWrapper">
<input id="dn" v-model="darkMode" type="checkbox" class="dn"/> <input id="dn" v-model="darkMode" type="checkbox" class="dn"/>

View File

@ -31,12 +31,15 @@ export default defineComponent({
} }
}, },
mounted() { async mounted() {
os.apiWithDialog('signup-pending', { await os.alert({
code: this.code, type: 'info',
}).then(res => { text: this.$t('clickToFinishEmailVerification', { ok: this.$ts.gotIt }),
login(res.i, '/');
}); });
const res = await os.apiWithDialog('signup-pending', {
code: this.code,
});
login(res.i, '/');
}, },
methods: { methods: {

View File

@ -1,12 +1,11 @@
<template> <template>
<div class="_section"> <div class="_section">
<XNotes ref="notes" class="_content" :pagination="pagination" @before="before" @after="after"/> <XNotes ref="notes" class="_content" :pagination="pagination"/>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as symbols from '@/symbols'; import * as symbols from '@/symbols';
@ -43,15 +42,5 @@ export default defineComponent({
(this.$refs.notes as any).reload(); (this.$refs.notes as any).reload();
} }
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -10,8 +10,6 @@
class="tl" class="tl"
:src="src" :src="src"
:sound="true" :sound="true"
@before="before()"
@after="after()"
@queue="queueUpdated" @queue="queueUpdated"
/> />
</div> </div>
@ -21,7 +19,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue'; import { defineComponent, defineAsyncComponent, computed } from 'vue';
import Progress from '@/scripts/loading';
import XTimeline from '@/components/timeline.vue'; import XTimeline from '@/components/timeline.vue';
import XPostForm from '@/components/post-form.vue'; import XPostForm from '@/components/post-form.vue';
import { scroll } from '@/scripts/scroll'; import { scroll } from '@/scripts/scroll';
@ -118,14 +115,6 @@ export default defineComponent({
}, },
methods: { methods: {
before() {
Progress.start();
},
after() {
Progress.done();
},
queueUpdated(q) { queueUpdated(q) {
this.queue = q; this.queue = q;
}, },

View File

@ -7,8 +7,6 @@
src="list" src="list"
:list="listId" :list="listId"
:sound="true" :sound="true"
@before="before()"
@after="after()"
@queue="queueUpdated" @queue="queueUpdated"
/> />
</div> </div>
@ -17,7 +15,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, defineAsyncComponent, computed } from 'vue'; import { defineComponent, defineAsyncComponent, computed } from 'vue';
import Progress from '@/scripts/loading';
import XTimeline from '@/components/timeline.vue'; import XTimeline from '@/components/timeline.vue';
import { scroll } from '@/scripts/scroll'; import { scroll } from '@/scripts/scroll';
import * as os from '@/os'; import * as os from '@/os';
@ -76,14 +73,6 @@ export default defineComponent({
}, },
methods: { methods: {
before() {
Progress.start();
},
after() {
Progress.done();
},
queueUpdated(q) { queueUpdated(q) {
this.queue = q; this.queue = q;
}, },

View File

@ -205,7 +205,6 @@ import MkFolder from '@/components/ui/folder.vue';
import MkRemoteCaution from '@/components/remote-caution.vue'; import MkRemoteCaution from '@/components/remote-caution.vue';
import MkTab from '@/components/tab.vue'; import MkTab from '@/components/tab.vue';
import MkInfo from '@/components/ui/info.vue'; import MkInfo from '@/components/ui/info.vue';
import Progress from '@/scripts/loading';
import * as Acct from 'misskey-js/built/acct'; import * as Acct from 'misskey-js/built/acct';
import { getScrollPosition } from '@/scripts/scroll'; import { getScrollPosition } from '@/scripts/scroll';
import { getUserMenu } from '@/scripts/get-user-menu'; import { getUserMenu } from '@/scripts/get-user-menu';
@ -328,13 +327,10 @@ export default defineComponent({
fetch() { fetch() {
if (this.acct == null) return; if (this.acct == null) return;
this.user = null; this.user = null;
Progress.start();
os.api('users/show', Acct.parse(this.acct)).then(user => { os.api('users/show', Acct.parse(this.acct)).then(user => {
this.user = user; this.user = user;
}).catch(e => { }).catch(e => {
this.error = e; this.error = e;
}).finally(() => {
Progress.done();
}); });
}, },

View File

@ -1,11 +0,0 @@
export default {
start: () => {
// TODO
},
done: () => {
// TODO
},
set: val => {
// TODO
}
};

View File

@ -1,4 +1,4 @@
import { Ref, ref, watch } from 'vue'; import { Ref, ref, watch, onUnmounted } from 'vue';
export function useTooltip( export function useTooltip(
elRef: Ref<HTMLElement | { $el: HTMLElement } | null | undefined>, elRef: Ref<HTMLElement | { $el: HTMLElement } | null | undefined>,
@ -18,6 +18,9 @@ export function useTooltip(
const open = () => { const open = () => {
close(); close();
if (!isHovering) return; if (!isHovering) return;
if (elRef.value == null) return;
const el = elRef.value instanceof Element ? elRef.value : elRef.value.$el;
if (!document.body.contains(el)) return; // openしようとしたときに既に元要素がDOMから消えている場合があるため
const showing = ref(true); const showing = ref(true);
onShow(showing); onShow(showing);
@ -69,9 +72,14 @@ export function useTooltip(
el.addEventListener('mouseleave', onMouseleave, { passive: true }); el.addEventListener('mouseleave', onMouseleave, { passive: true });
el.addEventListener('touchstart', onTouchstart, { passive: true }); el.addEventListener('touchstart', onTouchstart, { passive: true });
el.addEventListener('touchend', onTouchend, { passive: true }); el.addEventListener('touchend', onTouchend, { passive: true });
el.addEventListener('click', close, { passive: true });
} }
}, { }, {
immediate: true, immediate: true,
flush: 'post', flush: 'post',
}); });
onUnmounted(() => {
close();
});
} }

View File

@ -138,6 +138,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device', where: 'device',
default: false default: false
}, },
disableDrawer: {
where: 'device',
default: false
},
useBlurEffectForModal: { useBlurEffectForModal: {
where: 'device', where: 'device',
default: true default: true
@ -241,7 +245,6 @@ export class ColdDeviceStorage {
lightTheme: require('@/themes/l-light.json5') as Theme, lightTheme: require('@/themes/l-light.json5') as Theme,
darkTheme: require('@/themes/d-dark.json5') as Theme, darkTheme: require('@/themes/d-dark.json5') as Theme,
syncDeviceDarkMode: true, syncDeviceDarkMode: true,
chatOpenBehavior: 'page' as 'page' | 'window' | 'popout',
plugins: [] as Plugin[], plugins: [] as Plugin[],
mediaVolume: 0.5, mediaVolume: 0.5,
sound_masterVolume: 0.3, sound_masterVolume: 0.3,

View File

@ -3,7 +3,6 @@
*/ */
declare var self: ServiceWorkerGlobalScope; declare var self: ServiceWorkerGlobalScope;
import { getNoteSummary } from '@/scripts/get-note-summary';
import * as misskey from 'misskey-js'; import * as misskey from 'misskey-js';
function getUserName(user: misskey.entities.User): string { function getUserName(user: misskey.entities.User): string {
@ -26,37 +25,37 @@ export default async function(type, data, i18n): Promise<[string, NotificationOp
switch (data.type) { switch (data.type) {
case 'mention': case 'mention':
return [i18n.t('_notification.youGotMention', { name: getUserName(data.user) }), { return [i18n.t('_notification.youGotMention', { name: getUserName(data.user) }), {
body: getNoteSummary(data.note, i18n.locale), body: data.note.text,
icon: data.user.avatarUrl icon: data.user.avatarUrl
}]; }];
case 'reply': case 'reply':
return [i18n.t('_notification.youGotReply', { name: getUserName(data.user) }), { return [i18n.t('_notification.youGotReply', { name: getUserName(data.user) }), {
body: getNoteSummary(data.note, i18n.locale), body: data.note.text,
icon: data.user.avatarUrl icon: data.user.avatarUrl
}]; }];
case 'renote': case 'renote':
return [i18n.t('_notification.youRenoted', { name: getUserName(data.user) }), { return [i18n.t('_notification.youRenoted', { name: getUserName(data.user) }), {
body: getNoteSummary(data.note, i18n.locale), body: data.note.text,
icon: data.user.avatarUrl icon: data.user.avatarUrl
}]; }];
case 'quote': case 'quote':
return [i18n.t('_notification.youGotQuote', { name: getUserName(data.user) }), { return [i18n.t('_notification.youGotQuote', { name: getUserName(data.user) }), {
body: getNoteSummary(data.note, i18n.locale), body: data.note.text,
icon: data.user.avatarUrl icon: data.user.avatarUrl
}]; }];
case 'reaction': case 'reaction':
return [`${data.reaction} ${getUserName(data.user)}`, { return [`${data.reaction} ${getUserName(data.user)}`, {
body: getNoteSummary(data.note, i18n.locale), body: data.note.text,
icon: data.user.avatarUrl icon: data.user.avatarUrl
}]; }];
case 'pollVote': case 'pollVote':
return [i18n.t('_notification.youGotPoll', { name: getUserName(data.user) }), { return [i18n.t('_notification.youGotPoll', { name: getUserName(data.user) }), {
body: getNoteSummary(data.note, i18n.locale), body: data.note.text,
icon: data.user.avatarUrl icon: data.user.avatarUrl
}]; }];

View File

@ -170,6 +170,8 @@ export default defineComponent({
} }
&:hover, &.active { &:hover, &.active {
color: var(--accent);
&:before { &:before {
content: ""; content: "";
display: block; display: block;
@ -283,8 +285,10 @@ export default defineComponent({
} }
&:before { &:before {
width: 100%; width: auto;
border-radius: 0; height: 100%;
aspect-ratio: 1/1;
border-radius: 8px;
} }
&.post { &.post {
@ -296,8 +300,9 @@ export default defineComponent({
} }
&.post:before { &.post:before {
width: calc(100% - 32px); width: calc(100% - 28px);
height: calc(100% - 32px); height: auto;
aspect-ratio: 1/1;
border-radius: 100%; border-radius: 100%;
} }
} }

View File

@ -8,7 +8,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XColumn from './column.vue'; import XColumn from './column.vue';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -41,15 +40,5 @@ export default defineComponent({
}, },
} }
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -8,7 +8,6 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import Progress from '@/scripts/loading';
import XColumn from './column.vue'; import XColumn from './column.vue';
import XNotes from '@/components/notes.vue'; import XNotes from '@/components/notes.vue';
import * as os from '@/os'; import * as os from '@/os';
@ -38,15 +37,5 @@ export default defineComponent({
}, },
} }
}, },
methods: {
before() {
Progress.start();
},
after() {
Progress.done();
}
}
}); });
</script> </script>

View File

@ -56,6 +56,7 @@ module.exports = {
'object-curly-spacing': ['error', 'always'], 'object-curly-spacing': ['error', 'always'],
'space-infix-ops': ['error'], 'space-infix-ops': ['error'],
'space-before-blocks': ['error', 'always'], 'space-before-blocks': ['error', 'always'],
'@typescript-eslint/no-unnecessary-condition': ['error'],
'@typescript-eslint/no-var-requires': ['warn'], '@typescript-eslint/no-var-requires': ['warn'],
'@typescript-eslint/no-inferrable-types': ['warn'], '@typescript-eslint/no-inferrable-types': ['warn'],
'@typescript-eslint/no-empty-function': ['off'], '@typescript-eslint/no-empty-function': ['off'],