Merge branch 'develop' into mkjs-n
This commit is contained in:
commit
e76c1d8956
94 changed files with 1134 additions and 421 deletions
|
@ -29,6 +29,8 @@
|
||||||
- AiScriptを0.13.3に更新
|
- AiScriptを0.13.3に更新
|
||||||
- Fix: URLプレビューで情報が取得できなかった際の挙動を修正
|
- Fix: URLプレビューで情報が取得できなかった際の挙動を修正
|
||||||
- Fix: Safari、Firefoxでの新規登録時、パスワードマネージャーにメールアドレスが登録されていた挙動を修正
|
- Fix: Safari、Firefoxでの新規登録時、パスワードマネージャーにメールアドレスが登録されていた挙動を修正
|
||||||
|
- fix:ロールタイムラインが無効でも投稿が流れてしまう問題の修正
|
||||||
|
- fix:ロールタイムラインにて全ての投稿が流れてしまう問題の修正
|
||||||
|
|
||||||
## 13.12.2
|
## 13.12.2
|
||||||
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
DONATORS
|
|
||||||
========
|
|
||||||
The list of people who have sent donation for Misskey.
|
|
||||||
|
|
||||||
(In random order, honorific titles are omitted.)
|
|
||||||
|
|
||||||
* らふぁ
|
|
||||||
* 俺様
|
|
||||||
* なぎうり
|
|
||||||
* スルメ https://surume.tk/
|
|
||||||
* 藍
|
|
||||||
* 音船 https://otofune.me/
|
|
||||||
* aqz https://misskey.xyz/aqz
|
|
||||||
* kotodu "虚無創作中"
|
|
||||||
* Maya Minatsuki
|
|
||||||
* Knzk https://knzk.me/@Knzk
|
|
||||||
* ねじりわさび https://knzk.me/@y
|
|
||||||
* NCLS https://knzk.me/@imncls]
|
|
||||||
* こじま @skoji@sandbox.skoji.jp
|
|
||||||
|
|
||||||
:heart: Thanks for donating, guys!
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
If your name is missing, please contact us!
|
|
|
@ -306,6 +306,14 @@ export class RoleService implements OnApplicationShutdown {
|
||||||
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
|
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@bindThis
|
||||||
|
public async isExplorable(role: { id: Role['id']} | null): Promise<boolean> {
|
||||||
|
if (role == null) return false;
|
||||||
|
const check = await this.rolesRepository.findOneBy({ id: role.id });
|
||||||
|
if (check == null) return false;
|
||||||
|
return check.isExplorable;
|
||||||
|
}
|
||||||
|
|
||||||
@bindThis
|
@bindThis
|
||||||
public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> {
|
public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> {
|
||||||
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
|
||||||
|
|
|
@ -93,6 +93,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
|
||||||
|
|
||||||
const query = this.notesRepository.createQueryBuilder('note')
|
const query = this.notesRepository.createQueryBuilder('note')
|
||||||
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
.where('note.id IN (:...noteIds)', { noteIds: noteIds })
|
||||||
|
.andWhere('(note.visibility = \'public\')')
|
||||||
.innerJoinAndSelect('note.user', 'user')
|
.innerJoinAndSelect('note.user', 'user')
|
||||||
.leftJoinAndSelect('note.reply', 'reply')
|
.leftJoinAndSelect('note.reply', 'reply')
|
||||||
.leftJoinAndSelect('note.renote', 'renote')
|
.leftJoinAndSelect('note.renote', 'renote')
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
||||||
import { bindThis } from '@/decorators.js';
|
import { bindThis } from '@/decorators.js';
|
||||||
import Channel from '../channel.js';
|
import Channel from '../channel.js';
|
||||||
import { StreamMessages } from '../types.js';
|
import { StreamMessages } from '../types.js';
|
||||||
|
import { RoleService } from '@/core/RoleService.js';
|
||||||
|
|
||||||
class RoleTimelineChannel extends Channel {
|
class RoleTimelineChannel extends Channel {
|
||||||
public readonly chName = 'roleTimeline';
|
public readonly chName = 'roleTimeline';
|
||||||
|
@ -14,6 +15,7 @@ class RoleTimelineChannel extends Channel {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private noteEntityService: NoteEntityService,
|
private noteEntityService: NoteEntityService,
|
||||||
|
private roleservice: RoleService,
|
||||||
|
|
||||||
id: string,
|
id: string,
|
||||||
connection: Channel['connection'],
|
connection: Channel['connection'],
|
||||||
|
@ -34,6 +36,11 @@ class RoleTimelineChannel extends Channel {
|
||||||
if (data.type === 'note') {
|
if (data.type === 'note') {
|
||||||
const note = data.body;
|
const note = data.body;
|
||||||
|
|
||||||
|
if (!(await this.roleservice.isExplorable({ id: this.roleId }))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (note.visibility !== 'public') return;
|
||||||
|
|
||||||
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
|
||||||
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
if (isUserRelated(note, this.userIdsWhoMeMuting)) return;
|
||||||
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
|
||||||
|
@ -61,6 +68,7 @@ export class RoleTimelineChannelService {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private noteEntityService: NoteEntityService,
|
private noteEntityService: NoteEntityService,
|
||||||
|
private roleservice: RoleService,
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +76,7 @@ export class RoleTimelineChannelService {
|
||||||
public create(id: string, connection: Channel['connection']): RoleTimelineChannel {
|
public create(id: string, connection: Channel['connection']): RoleTimelineChannel {
|
||||||
return new RoleTimelineChannel(
|
return new RoleTimelineChannel(
|
||||||
this.noteEntityService,
|
this.noteEntityService,
|
||||||
|
this.roleservice,
|
||||||
id,
|
id,
|
||||||
connection,
|
connection,
|
||||||
);
|
);
|
||||||
|
|
653
packages/backend/test/e2e/antennas.ts
Normal file
653
packages/backend/test/e2e/antennas.ts
Normal file
|
@ -0,0 +1,653 @@
|
||||||
|
process.env.NODE_ENV = 'test';
|
||||||
|
|
||||||
|
import * as assert from 'assert';
|
||||||
|
import { inspect } from 'node:util';
|
||||||
|
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||||
|
import type { Packed } from '@/misc/json-schema.js';
|
||||||
|
import {
|
||||||
|
signup,
|
||||||
|
post,
|
||||||
|
userList,
|
||||||
|
page,
|
||||||
|
role,
|
||||||
|
startServer,
|
||||||
|
api,
|
||||||
|
successfulApiCall,
|
||||||
|
failedApiCall,
|
||||||
|
uploadFile,
|
||||||
|
testPaginationConsistency,
|
||||||
|
} from '../utils.js';
|
||||||
|
import type * as misskey from 'misskey-js';
|
||||||
|
import type { INestApplicationContext } from '@nestjs/common';
|
||||||
|
|
||||||
|
const compareBy = <T extends { id: string }>(selector: (s: T) => string = (s: T): string => s.id) => (a: T, b: T): number => {
|
||||||
|
return selector(a).localeCompare(selector(b));
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('アンテナ', () => {
|
||||||
|
// エンティティとしてのアンテナを主眼においたテストを記述する
|
||||||
|
// (Antennaを返すエンドポイント、Antennaエンティティを書き換えるエンドポイント、Antennaからノートを取得するエンドポイントをテストする)
|
||||||
|
|
||||||
|
// BUG misskey-jsとjson-schemaが一致していない。
|
||||||
|
// - srcのenumにgroupが残っている
|
||||||
|
// - userGroupIdが残っている, isActiveがない
|
||||||
|
type Antenna = misskey.entities.Antenna | Packed<'Antenna'>;
|
||||||
|
type User = misskey.entities.MeDetailed & { token: string };
|
||||||
|
type Note = misskey.entities.Note;
|
||||||
|
|
||||||
|
// アンテナを作成できる最小のパラメタ
|
||||||
|
const defaultParam = {
|
||||||
|
caseSensitive: false,
|
||||||
|
excludeKeywords: [['']],
|
||||||
|
keywords: [['keyword']],
|
||||||
|
name: 'test',
|
||||||
|
notify: false,
|
||||||
|
src: 'all' as const,
|
||||||
|
userListId: null,
|
||||||
|
users: [''],
|
||||||
|
withFile: false,
|
||||||
|
withReplies: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
let app: INestApplicationContext;
|
||||||
|
|
||||||
|
let root: User;
|
||||||
|
let alice: User;
|
||||||
|
let bob: User;
|
||||||
|
let carol: User;
|
||||||
|
|
||||||
|
let alicePost: Note;
|
||||||
|
let aliceList: misskey.entities.UserList;
|
||||||
|
let bobFile: misskey.entities.DriveFile;
|
||||||
|
let bobList: misskey.entities.UserList;
|
||||||
|
|
||||||
|
let userNotExplorable: User;
|
||||||
|
let userLocking: User;
|
||||||
|
let userSilenced: User;
|
||||||
|
let userSuspended: User;
|
||||||
|
let userDeletedBySelf: User;
|
||||||
|
let userDeletedByAdmin: User;
|
||||||
|
let userFollowingAlice: User;
|
||||||
|
let userFollowedByAlice: User;
|
||||||
|
let userBlockingAlice: User;
|
||||||
|
let userBlockedByAlice: User;
|
||||||
|
let userMutingAlice: User;
|
||||||
|
let userMutedByAlice: User;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
app = await startServer();
|
||||||
|
}, 1000 * 60 * 2);
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
root = await signup({ username: 'root' });
|
||||||
|
alice = await signup({ username: 'alice' });
|
||||||
|
alicePost = await post(alice, { text: 'test' });
|
||||||
|
aliceList = await userList(alice, {});
|
||||||
|
bob = await signup({ username: 'bob' });
|
||||||
|
aliceList = await userList(alice, {});
|
||||||
|
bobFile = (await uploadFile(bob)).body;
|
||||||
|
bobList = await userList(bob);
|
||||||
|
carol = await signup({ username: 'carol' });
|
||||||
|
await api('users/lists/push', { listId: aliceList.id, userId: bob.id }, alice);
|
||||||
|
await api('users/lists/push', { listId: aliceList.id, userId: carol.id }, alice);
|
||||||
|
|
||||||
|
userNotExplorable = await signup({ username: 'userNotExplorable' });
|
||||||
|
await post(userNotExplorable, { text: 'test' });
|
||||||
|
await api('i/update', { isExplorable: false }, userNotExplorable);
|
||||||
|
userLocking = await signup({ username: 'userLocking' });
|
||||||
|
await post(userLocking, { text: 'test' });
|
||||||
|
await api('i/update', { isLocked: true }, userLocking);
|
||||||
|
userSilenced = await signup({ username: 'userSilenced' });
|
||||||
|
await post(userSilenced, { text: 'test' });
|
||||||
|
const roleSilenced = await role(root, {}, { canPublicNote: { priority: 0, useDefault: false, value: false } });
|
||||||
|
await api('admin/roles/assign', { userId: userSilenced.id, roleId: roleSilenced.id }, root);
|
||||||
|
userSuspended = await signup({ username: 'userSuspended' });
|
||||||
|
await post(userSuspended, { text: 'test' });
|
||||||
|
await successfulApiCall({ endpoint: 'i/update', parameters: { description: '#user_testuserSuspended' }, user: userSuspended });
|
||||||
|
await api('admin/suspend-user', { userId: userSuspended.id }, root);
|
||||||
|
userDeletedBySelf = await signup({ username: 'userDeletedBySelf', password: 'userDeletedBySelf' });
|
||||||
|
await post(userDeletedBySelf, { text: 'test' });
|
||||||
|
await api('i/delete-account', { password: 'userDeletedBySelf' }, userDeletedBySelf);
|
||||||
|
userDeletedByAdmin = await signup({ username: 'userDeletedByAdmin' });
|
||||||
|
await post(userDeletedByAdmin, { text: 'test' });
|
||||||
|
await api('admin/delete-account', { userId: userDeletedByAdmin.id }, root);
|
||||||
|
userFollowedByAlice = await signup({ username: 'userFollowedByAlice' });
|
||||||
|
await post(userFollowedByAlice, { text: 'test' });
|
||||||
|
await api('following/create', { userId: userFollowedByAlice.id }, alice);
|
||||||
|
userFollowingAlice = await signup({ username: 'userFollowingAlice' });
|
||||||
|
await post(userFollowingAlice, { text: 'test' });
|
||||||
|
await api('following/create', { userId: alice.id }, userFollowingAlice);
|
||||||
|
userBlockingAlice = await signup({ username: 'userBlockingAlice' });
|
||||||
|
await post(userBlockingAlice, { text: 'test' });
|
||||||
|
await api('blocking/create', { userId: alice.id }, userBlockingAlice);
|
||||||
|
userBlockedByAlice = await signup({ username: 'userBlockedByAlice' });
|
||||||
|
await post(userBlockedByAlice, { text: 'test' });
|
||||||
|
await api('blocking/create', { userId: userBlockedByAlice.id }, alice);
|
||||||
|
userMutingAlice = await signup({ username: 'userMutingAlice' });
|
||||||
|
await post(userMutingAlice, { text: 'test' });
|
||||||
|
await api('mute/create', { userId: alice.id }, userMutingAlice);
|
||||||
|
userMutedByAlice = await signup({ username: 'userMutedByAlice' });
|
||||||
|
await post(userMutedByAlice, { text: 'test' });
|
||||||
|
await api('mute/create', { userId: userMutedByAlice.id }, alice);
|
||||||
|
}, 1000 * 60 * 10);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await app.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
// テスト間で影響し合わないように毎回全部消す。
|
||||||
|
for (const user of [alice, bob]) {
|
||||||
|
const list = await api('/antennas/list', {}, user);
|
||||||
|
for (const antenna of list.body) {
|
||||||
|
await api('/antennas/delete', { antennaId: antenna.id }, user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//#region 作成(antennas/create)
|
||||||
|
|
||||||
|
test('が作成できること、キーが過不足なく入っていること。', async () => {
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
assert.match(response.id, /[0-9a-z]{10}/);
|
||||||
|
const expected = {
|
||||||
|
id: response.id,
|
||||||
|
caseSensitive: false,
|
||||||
|
createdAt: new Date(response.createdAt).toISOString(),
|
||||||
|
excludeKeywords: [['']],
|
||||||
|
hasUnreadNote: false,
|
||||||
|
isActive: true,
|
||||||
|
keywords: [['keyword']],
|
||||||
|
name: 'test',
|
||||||
|
notify: false,
|
||||||
|
src: 'all',
|
||||||
|
userListId: null,
|
||||||
|
users: [''],
|
||||||
|
withFile: false,
|
||||||
|
withReplies: false,
|
||||||
|
} as Antenna;
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('が上限いっぱいまで作成できること', async () => {
|
||||||
|
// antennaLimit + 1まで作れるのがキモ
|
||||||
|
const response = await Promise.all([...Array(DEFAULT_POLICIES.antennaLimit + 1)].map(() => successfulApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam },
|
||||||
|
user: alice,
|
||||||
|
})));
|
||||||
|
|
||||||
|
const expected = await successfulApiCall({ endpoint: 'antennas/list', parameters: {}, user: alice });
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response.sort(compareBy(s => s.id)),
|
||||||
|
expected.sort(compareBy(s => s.id)));
|
||||||
|
|
||||||
|
failedApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam },
|
||||||
|
user: alice,
|
||||||
|
}, {
|
||||||
|
status: 400,
|
||||||
|
code: 'TOO_MANY_ANTENNAS',
|
||||||
|
id: 'faf47050-e8b5-438c-913c-db2b1576fde4',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('を作成するとき他人のリストを指定したらエラーになる', async () => {
|
||||||
|
failedApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam, src: 'list', userListId: bobList.id },
|
||||||
|
user: alice,
|
||||||
|
}, {
|
||||||
|
status: 400,
|
||||||
|
code: 'NO_SUCH_USER_LIST',
|
||||||
|
id: '95063e93-a283-4b8b-9aa5-bcdb8df69a7f',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const antennaParamPattern = [
|
||||||
|
{ parameters: (): object => ({ name: 'x'.repeat(100) }) },
|
||||||
|
{ parameters: (): object => ({ name: 'x' }) },
|
||||||
|
{ parameters: (): object => ({ src: 'home' }) },
|
||||||
|
{ parameters: (): object => ({ src: 'all' }) },
|
||||||
|
{ parameters: (): object => ({ src: 'users' }) },
|
||||||
|
{ parameters: (): object => ({ src: 'list' }) },
|
||||||
|
{ parameters: (): object => ({ userListId: null }) },
|
||||||
|
{ parameters: (): object => ({ src: 'list', userListId: aliceList.id }) },
|
||||||
|
{ parameters: (): object => ({ keywords: [['x']] }) },
|
||||||
|
{ parameters: (): object => ({ keywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
|
||||||
|
{ parameters: (): object => ({ excludeKeywords: [['a', 'b', 'c'], ['x'], ['y'], ['z']] }) },
|
||||||
|
{ parameters: (): object => ({ users: [alice.username] }) },
|
||||||
|
{ parameters: (): object => ({ users: [alice.username, bob.username, carol.username] }) },
|
||||||
|
{ parameters: (): object => ({ caseSensitive: false }) },
|
||||||
|
{ parameters: (): object => ({ caseSensitive: true }) },
|
||||||
|
{ parameters: (): object => ({ withReplies: false }) },
|
||||||
|
{ parameters: (): object => ({ withReplies: true }) },
|
||||||
|
{ parameters: (): object => ({ withFile: false }) },
|
||||||
|
{ parameters: (): object => ({ withFile: true }) },
|
||||||
|
{ parameters: (): object => ({ notify: false }) },
|
||||||
|
{ parameters: (): object => ({ notify: true }) },
|
||||||
|
];
|
||||||
|
test.each(antennaParamPattern)('を作成できること($#)', async ({ parameters }) => {
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam, ...parameters() },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const expected = { ...response, ...parameters() };
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region 更新(antennas/update)
|
||||||
|
|
||||||
|
test.each(antennaParamPattern)('を変更できること($#)', async ({ parameters }) => {
|
||||||
|
const antenna = await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: alice });
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/update',
|
||||||
|
parameters: { antennaId: antenna.id, ...defaultParam, ...parameters() },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const expected = { ...response, ...parameters() };
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
test.todo('は他人のものは変更できない');
|
||||||
|
|
||||||
|
test('を変更するとき他人のリストを指定したらエラーになる', async () => {
|
||||||
|
const antenna = await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: alice });
|
||||||
|
failedApiCall({
|
||||||
|
endpoint: 'antennas/update',
|
||||||
|
parameters: { antennaId: antenna.id, ...defaultParam, src: 'list', userListId: bobList.id },
|
||||||
|
user: alice,
|
||||||
|
}, {
|
||||||
|
status: 400,
|
||||||
|
code: 'NO_SUCH_USER_LIST',
|
||||||
|
id: '1c6b35c9-943e-48c2-81e4-2844989407f7',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region 表示(antennas/show)
|
||||||
|
|
||||||
|
test('をID指定で表示できること。', async () => {
|
||||||
|
const antenna = await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: alice });
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/show',
|
||||||
|
parameters: { antennaId: antenna.id },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const expected = { ...antenna };
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
test.todo('は他人のものをID指定で表示できない');
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region 一覧(antennas/list)
|
||||||
|
|
||||||
|
test('をリスト形式で取得できること。', async () => {
|
||||||
|
const antenna = await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: alice });
|
||||||
|
await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: bob });
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/list',
|
||||||
|
parameters: {},
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const expected = [{ ...antenna }];
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
//#region 削除(antennas/delete)
|
||||||
|
|
||||||
|
test('を削除できること。', async () => {
|
||||||
|
const antenna = await successfulApiCall({ endpoint: 'antennas/create', parameters: defaultParam, user: alice });
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/delete',
|
||||||
|
parameters: { antennaId: antenna.id },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
assert.deepStrictEqual(response, null);
|
||||||
|
const list = await successfulApiCall({ endpoint: 'antennas/list', parameters: {}, user: alice });
|
||||||
|
assert.deepStrictEqual(list, []);
|
||||||
|
});
|
||||||
|
test.todo('は他人のものを削除できない');
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
describe('のノート', () => {
|
||||||
|
//#region アンテナのノート取得(antennas/notes)
|
||||||
|
|
||||||
|
test('を取得できること。', async () => {
|
||||||
|
const keyword = 'キーワード';
|
||||||
|
await post(bob, { text: `test ${keyword} beforehand` });
|
||||||
|
const antenna = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam, keywords: [[keyword]] },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const note = await post(bob, { text: `test ${keyword}` });
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/notes',
|
||||||
|
parameters: { antennaId: antenna.id },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const expected = [note];
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
const keyword = 'キーワード';
|
||||||
|
test.each([
|
||||||
|
{
|
||||||
|
label: '全体から',
|
||||||
|
parameters: (): object => ({ src: 'all' }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(carol, { text: `test ${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// BUG e4144a1 以降home指定は壊れている(allと同じ)
|
||||||
|
label: 'ホーム指定はallと同じ',
|
||||||
|
parameters: (): object => ({ src: 'home' }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(alice, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(carol, { text: `test ${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// https://github.com/misskey-dev/misskey/issues/9025
|
||||||
|
label: 'ただし、フォロワー限定投稿とDM投稿を含まない。フォロワーであっても。',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'public' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'home' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'followers' }) },
|
||||||
|
{ note: (): Promise<Note> => post(userFollowedByAlice, { text: `${keyword}`, visibility: 'specified', visibleUserIds: [alice.id] }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ブロックしているユーザーのノートは含む',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userBlockedByAlice, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ブロックされているユーザーのノートは含まない',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userBlockingAlice, { text: `${keyword}` }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ミュートしているユーザーのノートは含まない',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userMutedByAlice, { text: `${keyword}` }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ミュートされているユーザーのノートは含む',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userMutingAlice, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '「見つけやすくする」がOFFのユーザーのノートも含まれる',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userNotExplorable, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '鍵付きユーザーのノートも含まれる',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userLocking, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'サイレンスのノートも含まれる',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userSilenced, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '削除ユーザーのノートも含まれる',
|
||||||
|
parameters: (): object => ({}),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(userDeletedBySelf, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(userDeletedByAdmin, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'ユーザー指定で',
|
||||||
|
parameters: (): object => ({ src: 'users', users: [`@${bob.username}`, `@${carol.username}`] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(carol, { text: `test ${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'リスト指定で',
|
||||||
|
parameters: (): object => ({ src: 'list', userListId: aliceList.id }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(alice, { text: `test ${keyword}` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(carol, { text: `test ${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'CWにもマッチする',
|
||||||
|
parameters: (): object => ({ keywords: [[keyword]] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test', cw: `cw ${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'キーワード1つ',
|
||||||
|
parameters: (): object => ({ keywords: [[keyword]] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(alice, { text: 'test' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(carol, { text: 'test' }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'キーワード3つ(AND)',
|
||||||
|
parameters: (): object => ({ keywords: [['A', 'B', 'C']] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test A' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test A B' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test B C' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test A B C' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test C B A A B C' }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'キーワード3つ(OR)',
|
||||||
|
parameters: (): object => ({ keywords: [['A'], ['B'], ['C']] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test A' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test A B' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test B C' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test B C A' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test C B' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'test C' }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '除外ワード3つ(AND)',
|
||||||
|
parameters: (): object => ({ excludeKeywords: [['A', 'B', 'C']] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A B` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} B C` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} B C A` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} C B` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} C` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '除外ワード3つ(OR)',
|
||||||
|
parameters: (): object => ({ excludeKeywords: [['A'], ['B'], ['C']] }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} A B` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} B C` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} B C A` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} C B` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `test ${keyword} C` }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'キーワード1つ(大文字小文字区別する)',
|
||||||
|
parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: true }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'keyword' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'KEYWORD' }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'キーワード1つ(大文字小文字区別しない)',
|
||||||
|
parameters: (): object => ({ keywords: [['KEYWORD']], caseSensitive: false }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'keyword' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'kEyWoRd' }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: 'KEYWORD' }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '除外ワード1つ(大文字小文字区別する)',
|
||||||
|
parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: true }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} kEyWoRd` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} KEYWORD` }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '除外ワード1つ(大文字小文字区別しない)',
|
||||||
|
parameters: (): object => ({ excludeKeywords: [['KEYWORD']], caseSensitive: false }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} keyword` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} kEyWoRd` }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword} KEYWORD` }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '添付ファイルを問わない',
|
||||||
|
parameters: (): object => ({ withFile: false }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '添付ファイル付きのみ',
|
||||||
|
parameters: (): object => ({ withFile: true }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, fileIds: [bobFile.id] }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'リプライ以外',
|
||||||
|
parameters: (): object => ({ withReplies: false }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }) },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'リプライも含む',
|
||||||
|
parameters: (): object => ({ withReplies: true }),
|
||||||
|
posts: [
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}`, replyId: alicePost.id }), included: true },
|
||||||
|
{ note: (): Promise<Note> => post(bob, { text: `${keyword}` }), included: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])('が取得できること($label)', async ({ parameters, posts }) => {
|
||||||
|
const antenna = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam, keywords: [[keyword]], ...parameters() },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
|
||||||
|
const notes = await posts.reduce(async (prev, current) => {
|
||||||
|
// includedに関わらずnote()は評価して投稿する。
|
||||||
|
const p = await prev;
|
||||||
|
const n = await current.note();
|
||||||
|
if (current.included) return p.concat(n);
|
||||||
|
return p;
|
||||||
|
}, Promise.resolve([] as Note[]));
|
||||||
|
|
||||||
|
// alice視点でNoteを取り直す
|
||||||
|
const expected = await Promise.all(notes.reverse().map(s => successfulApiCall({
|
||||||
|
endpoint: 'notes/show',
|
||||||
|
parameters: { noteId: s.id },
|
||||||
|
user: alice,
|
||||||
|
})));
|
||||||
|
|
||||||
|
const response = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/notes',
|
||||||
|
parameters: { antennaId: antenna.id },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
response.map(({ userId, id, text }) => ({ userId, id, text })),
|
||||||
|
expected.map(({ userId, id, text }) => ({ userId, id, text })));
|
||||||
|
assert.deepStrictEqual(response, expected);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.skip('が取得でき、日付指定のPaginationに一貫性があること', async () => { });
|
||||||
|
test.each([
|
||||||
|
{ label: 'ID指定', offsetBy: 'id' },
|
||||||
|
|
||||||
|
// BUG sinceDate, untilDateはsinceIdや他のエンドポイントとは異なり、その時刻に一致するレコードを含んでしまう。
|
||||||
|
// { label: '日付指定', offsetBy: 'createdAt' },
|
||||||
|
] as const)('が取得でき、$labelのPaginationに一貫性があること', async ({ offsetBy }) => {
|
||||||
|
const antenna = await successfulApiCall({
|
||||||
|
endpoint: 'antennas/create',
|
||||||
|
parameters: { ...defaultParam, keywords: [[keyword]] },
|
||||||
|
user: alice,
|
||||||
|
});
|
||||||
|
const notes = await [...Array(30)].reduce(async (prev, current, index) => {
|
||||||
|
const p = await prev;
|
||||||
|
const n = await post(alice, { text: `${keyword} (${index})` });
|
||||||
|
return [n].concat(p);
|
||||||
|
}, Promise.resolve([] as Note[]));
|
||||||
|
|
||||||
|
// antennas/notesは降順のみで、昇順をサポートしない。
|
||||||
|
await testPaginationConsistency(notes, async (paginationParam) => {
|
||||||
|
return successfulApiCall({
|
||||||
|
endpoint: 'antennas/notes',
|
||||||
|
parameters: { antennaId: antenna.id, ...paginationParam },
|
||||||
|
user: alice,
|
||||||
|
}) as any as Note[];
|
||||||
|
}, offsetBy, 'desc');
|
||||||
|
});
|
||||||
|
|
||||||
|
// BUG 7日過ぎると作り直すしかない。 https://github.com/misskey-dev/misskey/issues/10476
|
||||||
|
test.todo('を取得したときActiveに戻る');
|
||||||
|
|
||||||
|
//#endregion
|
||||||
|
});
|
||||||
|
});
|
|
@ -124,6 +124,13 @@ export const react = async (user: any, note: any, reaction: string): Promise<any
|
||||||
}, user);
|
}, user);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const userList = async (user: any, userList: any = {}): Promise<any> => {
|
||||||
|
const res = await api('users/lists/create', {
|
||||||
|
name: 'test',
|
||||||
|
}, user);
|
||||||
|
return res.body;
|
||||||
|
};
|
||||||
|
|
||||||
export const page = async (user: any, page: any = {}): Promise<any> => {
|
export const page = async (user: any, page: any = {}): Promise<any> => {
|
||||||
const res = await api('pages/create', {
|
const res = await api('pages/create', {
|
||||||
alignCenter: false,
|
alignCenter: false,
|
||||||
|
@ -380,6 +387,96 @@ export const simpleGet = async (path: string, accept = '*/*', cookie: any = unde
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* あるAPIエンドポイントのPaginationが複数の条件で一貫した挙動であることをテストします。
|
||||||
|
* (sinceId, untilId, sinceDate, untilDate, offset, limit)
|
||||||
|
* @param expected 期待値となるEntityの並び(例:Note[])昇順降順が一致している必要がある
|
||||||
|
* @param fetchEntities Entity[]を返却するテスト対象のAPIを呼び出す関数
|
||||||
|
* @param offsetBy 何をキーとしてPaginationするか。
|
||||||
|
* @param ordering 昇順・降順
|
||||||
|
*/
|
||||||
|
export async function testPaginationConsistency<Entity extends { id: string, createdAt?: string }>(
|
||||||
|
expected: Entity[],
|
||||||
|
fetchEntities: (paginationParam: {
|
||||||
|
limit?: number,
|
||||||
|
offset?: number,
|
||||||
|
sinceId?: string,
|
||||||
|
untilId?: string,
|
||||||
|
sinceDate?: number,
|
||||||
|
untilDate?: number,
|
||||||
|
}) => Promise<Entity[]>,
|
||||||
|
offsetBy: 'offset' | 'id' | 'createdAt' = 'id',
|
||||||
|
ordering: 'desc' | 'asc' = 'desc'): Promise<void> {
|
||||||
|
const rangeToParam = (p: { limit?: number, until?: Entity, since?: Entity }): object => {
|
||||||
|
if (offsetBy === 'id') {
|
||||||
|
return { limit: p.limit, sinceId: p.since?.id, untilId: p.until?.id };
|
||||||
|
} else {
|
||||||
|
const sinceDate = p.since?.createdAt !== undefined ? new Date(p.since.createdAt).getTime() : undefined;
|
||||||
|
const untilDate = p.until?.createdAt !== undefined ? new Date(p.until.createdAt).getTime() : undefined;
|
||||||
|
return { limit: p.limit, sinceDate, untilDate };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const limit of [1, 5, 10, 100, undefined]) {
|
||||||
|
// 1. sinceId/DateとuntilId/Dateで両端を指定して取得した結果が期待通りになっていること
|
||||||
|
if (ordering === 'desc') {
|
||||||
|
const end = expected[expected.length - 1];
|
||||||
|
let last = await fetchEntities(rangeToParam({ limit, since: end }));
|
||||||
|
const actual: Entity[] = [];
|
||||||
|
while (last.length !== 0) {
|
||||||
|
actual.push(...last);
|
||||||
|
last = await fetchEntities(rangeToParam({ limit, until: last[last.length - 1], since: end }));
|
||||||
|
}
|
||||||
|
actual.push(end);
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
actual.map(({ id, createdAt }) => id + ':' + createdAt),
|
||||||
|
expected.map(({ id, createdAt }) => id + ':' + createdAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. sinceId/Date指定+limitで取得してつなぎ合わせた結果が期待通りになっていること
|
||||||
|
if (ordering === 'asc') {
|
||||||
|
// 昇順にしたときの先頭(一番古いもの)をもってくる(expected[1]を基準に降順にして0番目)
|
||||||
|
let last = await fetchEntities({ limit: 1, untilId: expected[1].id });
|
||||||
|
const actual: Entity[] = [];
|
||||||
|
while (last.length !== 0) {
|
||||||
|
actual.push(...last);
|
||||||
|
last = await fetchEntities(rangeToParam({ limit, since: last[last.length - 1] }));
|
||||||
|
}
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
actual.map(({ id, createdAt }) => id + ':' + createdAt),
|
||||||
|
expected.map(({ id, createdAt }) => id + ':' + createdAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. untilId指定+limitで取得してつなぎ合わせた結果が期待通りになっていること
|
||||||
|
if (ordering === 'desc') {
|
||||||
|
let last = await fetchEntities({ limit });
|
||||||
|
const actual: Entity[] = [];
|
||||||
|
while (last.length !== 0) {
|
||||||
|
actual.push(...last);
|
||||||
|
last = await fetchEntities(rangeToParam({ limit, until: last[last.length - 1] }));
|
||||||
|
}
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
actual.map(({ id, createdAt }) => id + ':' + createdAt),
|
||||||
|
expected.map(({ id, createdAt }) => id + ':' + createdAt));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. offset指定+limitで取得してつなぎ合わせた結果が期待通りになっていること
|
||||||
|
if (offsetBy === 'offset') {
|
||||||
|
let last = await fetchEntities({ limit, offset: 0 });
|
||||||
|
let offset = limit ?? 10;
|
||||||
|
const actual: Entity[] = [];
|
||||||
|
while (last.length !== 0) {
|
||||||
|
actual.push(...last);
|
||||||
|
last = await fetchEntities({ limit, offset });
|
||||||
|
offset += limit ?? 10;
|
||||||
|
}
|
||||||
|
assert.deepStrictEqual(
|
||||||
|
actual.map(({ id, createdAt }) => id + ':' + createdAt),
|
||||||
|
expected.map(({ id, createdAt }) => id + ':' + createdAt));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function initTestDb(justBorrow = false, initEntities?: any[]) {
|
export async function initTestDb(justBorrow = false, initEntities?: any[]) {
|
||||||
if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test';
|
if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test';
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div>
|
<div>
|
||||||
<div class="reports">
|
<div class="reports">
|
||||||
<div class="">
|
<div class="">
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="uqshojas">
|
<div>
|
||||||
<div v-for="ad in ads" class="_panel _gaps_m ad">
|
<div v-for="ad in ads" class="_panel _gaps_m" :class="$style.ad">
|
||||||
<MkAd v-if="ad.url" :specify="ad"/>
|
<MkAd v-if="ad.url" :specify="ad"/>
|
||||||
<MkInput v-model="ad.url" type="url">
|
<MkInput v-model="ad.url" type="url">
|
||||||
<template #label>URL</template>
|
<template #label>URL</template>
|
||||||
|
@ -196,14 +196,12 @@ definePageMetadata({
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.uqshojas {
|
.ad {
|
||||||
> .ad {
|
|
||||||
padding: 32px;
|
padding: 32px;
|
||||||
|
|
||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
margin-bottom: var(--margin);
|
margin-bottom: var(--margin);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<section v-for="announcement in announcements" class="">
|
<section v-for="announcement in announcements" class="">
|
||||||
<div class="_panel _gaps_m" style="padding: 24px;">
|
<div class="_panel _gaps_m" style="padding: 24px;">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="800" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="800" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
|
<FormSuspense v-slot="{ result: database }" :p="databasePromiseFactory">
|
||||||
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
|
<MkKeyValue v-for="table in database" :key="table[0]" oneline style="margin: 1em 0;">
|
||||||
<template #key>{{ table[0] }}</template>
|
<template #key>{{ table[0] }}</template>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :tabs="headerTabs"/></template>
|
<template #header><XHeader :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="enableEmail">
|
<MkSwitch v-model="enableEmail">
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
<template #label>{{ i18n.ts.smtpConfig }}</template>
|
<template #label>{{ i18n.ts.smtpConfig }}</template>
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<FormSplit :min-width="280">
|
<FormSplit :minWidth="280">
|
||||||
<MkInput v-model="smtpHost">
|
<MkInput v-model="smtpHost">
|
||||||
<template #label>{{ i18n.ts.smtpHost }}</template>
|
<template #label>{{ i18n.ts.smtpHost }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -26,7 +26,7 @@
|
||||||
<template #label>{{ i18n.ts.smtpPort }}</template>
|
<template #label>{{ i18n.ts.smtpPort }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
</FormSplit>
|
</FormSplit>
|
||||||
<FormSplit :min-width="280">
|
<FormSplit :minWidth="280">
|
||||||
<MkInput v-model="smtpUser">
|
<MkInput v-model="smtpUser">
|
||||||
<template #label>{{ i18n.ts.smtpUser }}</template>
|
<template #label>{{ i18n.ts.smtpUser }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="16">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
|
||||||
<div class="_buttons">
|
<div class="_buttons">
|
||||||
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
<MkButton rounded @click="testEmail"><i class="ti ti-send"></i> {{ i18n.ts.testEmail }}</MkButton>
|
<MkButton rounded @click="testEmail"><i class="ti ti-send"></i> {{ i18n.ts.testEmail }}</MkButton>
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
<div>
|
<div>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions"/></template>
|
<template #header><XHeader :actions="headerActions"/></template>
|
||||||
<MkSpacer :content-max="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="taeiyrib">
|
<div>
|
||||||
<div class="query">
|
<div>
|
||||||
<MkInput v-model="host" :debounce="true" class="">
|
<MkInput v-model="host" :debounce="true" class="">
|
||||||
<template #prefix><i class="ti ti-search"></i></template>
|
<template #prefix><i class="ti ti-search"></i></template>
|
||||||
<template #label>{{ i18n.ts.host }}</template>
|
<template #label>{{ i18n.ts.host }}</template>
|
||||||
|
@ -39,8 +39,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
<MkPagination v-slot="{items}" ref="instances" :key="host + state" :pagination="pagination">
|
||||||
<div class="dqokceoj">
|
<div :class="$style.instances">
|
||||||
<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" class="instance" :to="`/instance-info/${instance.host}`">
|
<MkA v-for="instance in items" :key="instance.id" v-tooltip.mfm="`Status: ${getStatus(instance)}`" :class="$style.instance" :to="`/instance-info/${instance.host}`">
|
||||||
<MkInstanceCardMini :instance="instance"/>
|
<MkInstanceCardMini :instance="instance"/>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
|
@ -100,21 +100,14 @@ definePageMetadata(computed(() => ({
|
||||||
})));
|
})));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.taeiyrib {
|
.instances {
|
||||||
> .query {
|
|
||||||
background: var(--bg);
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.dqokceoj {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||||
grid-gap: 12px;
|
grid-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
> .instance:hover {
|
.instance:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions"/></template>
|
<template #header><XHeader :actions="headerActions"/></template>
|
||||||
<MkSpacer :content-max="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<div class="inputs" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
|
<div class="inputs" style="display: flex; gap: var(--margin); flex-wrap: wrap;">
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
<template #label>MIME type</template>
|
<template #label>MIME type</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
</div>
|
</div>
|
||||||
<MkFileListForAdmin :pagination="pagination" :view-mode="viewMode"/>
|
<MkFileListForAdmin :pagination="pagination" :viewMode="viewMode"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div ref="el" class="hiyeyicy" :class="{ wide: !narrow }">
|
<div ref="el" class="hiyeyicy" :class="{ wide: !narrow }">
|
||||||
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
|
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
|
||||||
<MkSpacer :content-max="700" :margin-min="16">
|
<MkSpacer :contentMax="700" :marginMin="16">
|
||||||
<div class="lxpfedzu">
|
<div class="lxpfedzu">
|
||||||
<div class="banner">
|
<div class="banner">
|
||||||
<img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
|
<img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<MkTextarea v-model="blockedHosts">
|
<MkTextarea v-model="blockedHosts">
|
||||||
<span>{{ i18n.ts.blockedInstances }}</span>
|
<span>{{ i18n.ts.blockedInstances }}</span>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :tabs="headerTabs"/></template>
|
<template #header><XHeader :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="enableRegistration">
|
<MkSwitch v-model="enableRegistration">
|
||||||
|
@ -34,7 +34,7 @@
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="16">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
|
||||||
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :tabs="headerTabs"/></template>
|
<template #header><XHeader :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch>
|
<MkSwitch v-model="useObjectStorage">{{ i18n.ts.useObjectStorage }}</MkSwitch>
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
<template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
|
<template #caption>{{ i18n.ts.objectStorageRegionDesc }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
||||||
<FormSplit :min-width="280">
|
<FormSplit :minWidth="280">
|
||||||
<MkInput v-model="objectStorageAccessKey">
|
<MkInput v-model="objectStorageAccessKey">
|
||||||
<template #prefix><i class="ti ti-key"></i></template>
|
<template #prefix><i class="ti ti-key"></i></template>
|
||||||
<template #label>Access key</template>
|
<template #label>Access key</template>
|
||||||
|
@ -69,7 +69,7 @@
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="16">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="16">
|
||||||
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="wbrkwale">
|
<div>
|
||||||
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" mode="out-in">
|
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" mode="out-in">
|
||||||
<MkLoading v-if="fetching"/>
|
<MkLoading v-if="fetching"/>
|
||||||
<div v-else class="instances">
|
<div v-else :class="$style.instances">
|
||||||
<MkA v-for="(instance, i) in instances" :key="instance.id" v-tooltip.mfm.noDelay="`${instance.name}\n${instance.host}\n${instance.softwareName} ${instance.softwareVersion}`" :to="`/instance-info/${instance.host}`" class="instance">
|
<MkA v-for="(instance, i) in instances" :key="instance.id" v-tooltip.mfm.noDelay="`${instance.name}\n${instance.host}\n${instance.softwareName} ${instance.softwareVersion}`" :to="`/instance-info/${instance.host}`" :class="$style.instance">
|
||||||
<MkInstanceCardMini :instance="instance"/>
|
<MkInstanceCardMini :instance="instance"/>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,16 +36,14 @@ useInterval(fetch, 1000 * 60, {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.wbrkwale {
|
.instances {
|
||||||
> .instances {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
|
||||||
grid-gap: 12px;
|
grid-gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
> .instance:hover {
|
.instance:hover {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="1000">
|
<MkSpacer :contentMax="1000">
|
||||||
<div ref="rootEl" :class="$style.root">
|
<div ref="rootEl" :class="$style.root">
|
||||||
<MkFoldableSection class="item">
|
<MkFoldableSection class="item">
|
||||||
<template #header>Stats</template>
|
<template #header>Stats</template>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<FormSuspense :p="init">
|
<FormSuspense :p="init">
|
||||||
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
|
<MkInfo>{{ i18n.ts.proxyAccountDescription }}</MkInfo>
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
|
|
|
@ -1,30 +1,30 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="pumxzjhg _gaps">
|
<div class="_gaps">
|
||||||
<div :class="$style.status">
|
<div :class="$style.status">
|
||||||
<div class="item _panel"><div class="label">Process</div>{{ number(activeSincePrevTick) }}</div>
|
<div :class="$style.statusItem" class="_panel"><div :class="$style.statusLabel">Process</div>{{ number(activeSincePrevTick) }}</div>
|
||||||
<div class="item _panel"><div class="label">Active</div>{{ number(active) }}</div>
|
<div :class="$style.statusItem" class="_panel"><div :class="$style.statusLabel">Active</div>{{ number(active) }}</div>
|
||||||
<div class="item _panel"><div class="label">Waiting</div>{{ number(waiting) }}</div>
|
<div :class="$style.statusItem" class="_panel"><div :class="$style.statusLabel">Waiting</div>{{ number(waiting) }}</div>
|
||||||
<div class="item _panel"><div class="label">Delayed</div>{{ number(delayed) }}</div>
|
<div :class="$style.statusItem" class="_panel"><div :class="$style.statusLabel">Delayed</div>{{ number(delayed) }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="charts">
|
<div :class="$style.charts">
|
||||||
<div class="chart">
|
<div :class="$style.chart">
|
||||||
<div class="title">Process</div>
|
<div :class="$style.chartTitle">Process</div>
|
||||||
<XChart ref="chartProcess" type="process"/>
|
<XChart ref="chartProcess" type="process"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart">
|
<div :class="$style.chart">
|
||||||
<div class="title">Active</div>
|
<div :class="$style.chartTitle">Active</div>
|
||||||
<XChart ref="chartActive" type="active"/>
|
<XChart ref="chartActive" type="active"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart">
|
<div :class="$style.chart">
|
||||||
<div class="title">Delayed</div>
|
<div :class="$style.chartTitle">Delayed</div>
|
||||||
<XChart ref="chartDelayed" type="delayed"/>
|
<XChart ref="chartDelayed" type="delayed"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="chart">
|
<div :class="$style.chart">
|
||||||
<div class="title">Waiting</div>
|
<div :class="$style.chartTitle">Waiting</div>
|
||||||
<XChart ref="chartWaiting" type="waiting"/>
|
<XChart ref="chartWaiting" type="waiting"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<MkFolder :default-open="true" :max-height="250">
|
<MkFolder :defaultOpen="true" :max-height="250">
|
||||||
<template #icon><i class="ti ti-alert-triangle"></i></template>
|
<template #icon><i class="ti ti-alert-triangle"></i></template>
|
||||||
<template #label>Errored instances</template>
|
<template #label>Errored instances</template>
|
||||||
<template #suffix>({{ number(jobs.reduce((a, b) => a + b[1], 0)) }} jobs)</template>
|
<template #suffix>({{ number(jobs.reduce((a, b) => a + b[1], 0)) }} jobs)</template>
|
||||||
|
@ -118,44 +118,38 @@ onUnmounted(() => {
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.pumxzjhg {
|
.charts {
|
||||||
> .charts {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr;
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
> .chart {
|
.chart {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
background: var(--panel);
|
background: var(--panel);
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
|
}
|
||||||
|
|
||||||
> .title {
|
.chartTitle {
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<style lang="scss" module>
|
|
||||||
.status {
|
.status {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||||
grid-gap: 10px;
|
grid-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
&:global {
|
.statusItem {
|
||||||
> .item {
|
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
> .label {
|
.statusLabel {
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
opacity: 0.6;
|
opacity: 0.6;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.jobs {
|
.jobs {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="800">
|
<MkSpacer :contentMax="800">
|
||||||
<XQueue v-if="tab === 'deliver'" domain="deliver"/>
|
<XQueue v-if="tab === 'deliver'" domain="deliver"/>
|
||||||
<XQueue v-else-if="tab === 'inbox'" domain="inbox"/>
|
<XQueue v-else-if="tab === 'inbox'" domain="inbox"/>
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
|
<div v-for="relay in relays" :key="relay.inbox" class="relaycxt _panel" style="padding: 16px;">
|
||||||
<div>{{ relay.inbox }}</div>
|
<div>{{ relay.inbox }}</div>
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
<div>
|
<div>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :tabs="headerTabs"/></template>
|
<template #header><XHeader :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="600" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="600" :marginMin="16" :marginMax="32">
|
||||||
<XEditor v-if="data" v-model="data"/>
|
<XEditor v-if="data" v-model="data"/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div :class="$style.footer">
|
<div :class="$style.footer">
|
||||||
<MkSpacer :content-max="600" :margin-min="16" :margin-max="16">
|
<MkSpacer :contentMax="600" :marginMin="16" :marginMax="16">
|
||||||
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton primary rounded @click="save"><i class="ti ti-check"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
<option value="conditional">{{ i18n.ts._role.conditional }}</option>
|
<option value="conditional">{{ i18n.ts._role.conditional }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
|
||||||
<MkFolder v-if="role.target === 'conditional'" default-open>
|
<MkFolder v-if="role.target === 'conditional'" defaultOpen>
|
||||||
<template #label>{{ i18n.ts._role.condition }}</template>
|
<template #label>{{ i18n.ts._role.condition }}</template>
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<RolesEditorFormula v-model="role.condFormula"/>
|
<RolesEditorFormula v-model="role.condFormula"/>
|
||||||
|
@ -81,11 +81,11 @@
|
||||||
<MkSwitch v-model="role.policies.rateLimitFactor.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.rateLimitFactor.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
<template #label>{{ i18n.ts._role.useBaseValue }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange :model-value="role.policies.rateLimitFactor.value * 100" :min="0" :max="400" :step="10" :text-converter="(v) => `${v}%`" @update:model-value="v => role.policies.rateLimitFactor.value = (v / 100)">
|
<MkRange :modelValue="role.policies.rateLimitFactor.value * 100" :min="0" :max="400" :step="10" :textConverter="(v) => `${v}%`" @update:modelValue="v => role.policies.rateLimitFactor.value = (v / 100)">
|
||||||
<template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template>
|
<template #label>{{ i18n.ts._role._options.rateLimitFactor }}</template>
|
||||||
<template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template>
|
<template #caption>{{ i18n.ts._role._options.descriptionOfRateLimitFactor }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
<MkRange v-model="role.policies.rateLimitFactor.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.rateLimitFactor.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
<MkSwitch v-model="role.policies.gtlAvailable.value" :disabled="role.policies.gtlAvailable.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.gtlAvailable.value" :disabled="role.policies.gtlAvailable.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.gtlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.gtlAvailable.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -125,7 +125,7 @@
|
||||||
<MkSwitch v-model="role.policies.ltlAvailable.value" :disabled="role.policies.ltlAvailable.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.ltlAvailable.value" :disabled="role.policies.ltlAvailable.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.ltlAvailable.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.ltlAvailable.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -145,7 +145,7 @@
|
||||||
<MkSwitch v-model="role.policies.canPublicNote.value" :disabled="role.policies.canPublicNote.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.canPublicNote.value" :disabled="role.policies.canPublicNote.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.canPublicNote.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.canPublicNote.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
<MkSwitch v-model="role.policies.canInvite.value" :disabled="role.policies.canInvite.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.canInvite.value" :disabled="role.policies.canInvite.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.canInvite.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.canInvite.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -185,7 +185,7 @@
|
||||||
<MkSwitch v-model="role.policies.canManageCustomEmojis.value" :disabled="role.policies.canManageCustomEmojis.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.canManageCustomEmojis.value" :disabled="role.policies.canManageCustomEmojis.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.canManageCustomEmojis.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.canManageCustomEmojis.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -205,7 +205,7 @@
|
||||||
<MkSwitch v-model="role.policies.canSearchNotes.value" :disabled="role.policies.canSearchNotes.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.canSearchNotes.value" :disabled="role.policies.canSearchNotes.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.canSearchNotes.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.canSearchNotes.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -225,7 +225,7 @@
|
||||||
<MkInput v-model="role.policies.driveCapacityMb.value" :disabled="role.policies.driveCapacityMb.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.driveCapacityMb.value" :disabled="role.policies.driveCapacityMb.useDefault" type="number" :readonly="readonly">
|
||||||
<template #suffix>MB</template>
|
<template #suffix>MB</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.driveCapacityMb.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.driveCapacityMb.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -245,7 +245,7 @@
|
||||||
<MkSwitch v-model="role.policies.alwaysMarkNsfw.value" :disabled="role.policies.alwaysMarkNsfw.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.alwaysMarkNsfw.value" :disabled="role.policies.alwaysMarkNsfw.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.alwaysMarkNsfw.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.alwaysMarkNsfw.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -264,7 +264,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.pinLimit.value" :disabled="role.policies.pinLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.pinLimit.value" :disabled="role.policies.pinLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.pinLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.pinLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -283,7 +283,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.antennaLimit.value" :disabled="role.policies.antennaLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.antennaLimit.value" :disabled="role.policies.antennaLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.antennaLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.antennaLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -303,7 +303,7 @@
|
||||||
<MkInput v-model="role.policies.wordMuteLimit.value" :disabled="role.policies.wordMuteLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.wordMuteLimit.value" :disabled="role.policies.wordMuteLimit.useDefault" type="number" :readonly="readonly">
|
||||||
<template #suffix>chars</template>
|
<template #suffix>chars</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.wordMuteLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.wordMuteLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -322,7 +322,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.webhookLimit.value" :disabled="role.policies.webhookLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.webhookLimit.value" :disabled="role.policies.webhookLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.webhookLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.webhookLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -341,7 +341,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.clipLimit.value" :disabled="role.policies.clipLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.clipLimit.value" :disabled="role.policies.clipLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.clipLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.clipLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -360,7 +360,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.noteEachClipsLimit.value" :disabled="role.policies.noteEachClipsLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.noteEachClipsLimit.value" :disabled="role.policies.noteEachClipsLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.noteEachClipsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.noteEachClipsLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -379,7 +379,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.userListLimit.value" :disabled="role.policies.userListLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.userListLimit.value" :disabled="role.policies.userListLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.userListLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.userListLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -398,7 +398,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="role.policies.userEachUserListsLimit.value" :disabled="role.policies.userEachUserListsLimit.useDefault" type="number" :readonly="readonly">
|
<MkInput v-model="role.policies.userEachUserListsLimit.value" :disabled="role.policies.userEachUserListsLimit.useDefault" type="number" :readonly="readonly">
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="role.policies.userEachUserListsLimit.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.userEachUserListsLimit.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
@ -418,7 +418,7 @@
|
||||||
<MkSwitch v-model="role.policies.canHideAds.value" :disabled="role.policies.canHideAds.useDefault" :readonly="readonly">
|
<MkSwitch v-model="role.policies.canHideAds.value" :disabled="role.policies.canHideAds.useDefault" :readonly="readonly">
|
||||||
<template #label>{{ i18n.ts.enable }}</template>
|
<template #label>{{ i18n.ts.enable }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkRange v-model="role.policies.canHideAds.priority" :min="0" :max="2" :step="1" easing :text-converter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
<MkRange v-model="role.policies.canHideAds.priority" :min="0" :max="2" :step="1" easing :textConverter="(v) => v === 0 ? i18n.ts._role._priority.low : v === 1 ? i18n.ts._role._priority.middle : v === 2 ? i18n.ts._role._priority.high : ''">
|
||||||
<template #label>{{ i18n.ts._role.priority }}</template>
|
<template #label>{{ i18n.ts._role.priority }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,13 +2,13 @@
|
||||||
<div>
|
<div>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :tabs="headerTabs"/></template>
|
<template #header><XHeader :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700" :margin-min="16" :margin-max="32">
|
<MkSpacer :contentMax="700" :marginMin="16" :marginMax="32">
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<div>{{ i18n.ts._serverRules.description }}</div>
|
<div>{{ i18n.ts._serverRules.description }}</div>
|
||||||
<Sortable
|
<Sortable
|
||||||
v-model="serverRules"
|
v-model="serverRules"
|
||||||
class="_gaps_m"
|
class="_gaps_m"
|
||||||
:item-key="(_, i) => i"
|
:itemKey="(_, i) => i"
|
||||||
:animation="150"
|
:animation="150"
|
||||||
:handle="'.' + $style.itemHandle"
|
:handle="'.' + $style.itemHandle"
|
||||||
@start="e => e.item.classList.add('active')"
|
@start="e => e.item.classList.add('active')"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div>
|
<div>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><XHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="900">
|
<MkSpacer :contentMax="900">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<div :class="$style.inputs">
|
<div :class="$style.inputs">
|
||||||
<MkSelect v-model="sort" style="flex: 1;">
|
<MkSelect v-model="sort" style="flex: 1;">
|
||||||
|
@ -28,11 +28,11 @@
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
</div>
|
</div>
|
||||||
<div :class="$style.inputs">
|
<div :class="$style.inputs">
|
||||||
<MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" @update:model-value="$refs.users.reload()">
|
<MkInput v-model="searchUsername" style="flex: 1;" type="text" :spellcheck="false" @update:modelValue="$refs.users.reload()">
|
||||||
<template #prefix>@</template>
|
<template #prefix>@</template>
|
||||||
<template #label>{{ i18n.ts.username }}</template>
|
<template #label>{{ i18n.ts.username }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkInput v-model="searchHost" style="flex: 1;" type="text" :spellcheck="false" :disabled="pagination.params.origin === 'local'" @update:model-value="$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 #prefix>@</template>
|
||||||
<template #label>{{ i18n.ts.host }}</template>
|
<template #label>{{ i18n.ts.host }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
|
@ -2,15 +2,15 @@
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions"/></template>
|
<template #header><MkPageHeader :actions="headerActions"/></template>
|
||||||
<MkSpacer :contentMax="800">
|
<MkSpacer :contentMax="800">
|
||||||
<div v-if="clip">
|
<div v-if="clip" class="_gaps">
|
||||||
<div class="okzinsic _panel">
|
<div class="_panel">
|
||||||
<div v-if="clip.description" class="description">
|
<div v-if="clip.description" :class="$style.description">
|
||||||
<Mfm :text="clip.description" :isNote="false" :i="$i"/>
|
<Mfm :text="clip.description" :isNote="false" :i="$i"/>
|
||||||
</div>
|
</div>
|
||||||
<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike class="button" rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
<MkButton v-if="favorited" v-tooltip="i18n.ts.unfavorite" asLike rounded primary @click="unfavorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
||||||
<MkButton v-else v-tooltip="i18n.ts.favorite" asLike class="button" rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
<MkButton v-else v-tooltip="i18n.ts.favorite" asLike rounded @click="favorite()"><i class="ti ti-heart"></i><span v-if="clip.favoritedCount > 0" style="margin-left: 6px;">{{ clip.favoritedCount }}</span></MkButton>
|
||||||
<div class="user">
|
<div :class="$style.user">
|
||||||
<MkAvatar :user="clip.user" class="avatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
|
<MkAvatar :user="clip.user" :class="$style.avatar" indicator link preview/> <MkUserName :user="clip.user" :nowrap="false"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -147,25 +147,20 @@ definePageMetadata(computed(() => clip ? {
|
||||||
} : null));
|
} : null));
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.okzinsic {
|
.description {
|
||||||
position: relative;
|
|
||||||
margin-bottom: var(--margin);
|
|
||||||
|
|
||||||
> .description {
|
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .user {
|
.user {
|
||||||
$height: 32px;
|
--height: 32px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
border-top: solid 0.5px var(--divider);
|
border-top: solid 0.5px var(--divider);
|
||||||
line-height: $height;
|
line-height: var(--height);
|
||||||
|
}
|
||||||
|
|
||||||
> .avatar {
|
.avatar {
|
||||||
width: $height;
|
width: var(--height);
|
||||||
height: $height;
|
height: var(--height);
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkInput v-model="title">
|
<MkInput v-model="title">
|
||||||
<template #label>{{ i18n.ts._play.title }}</template>
|
<template #label>{{ i18n.ts._play.title }}</template>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div v-if="tab === 'featured'" class="">
|
<div v-if="tab === 'featured'" class="">
|
||||||
<MkPagination v-slot="{items}" :pagination="featuredFlashsPagination">
|
<MkPagination v-slot="{items}" :pagination="featuredFlashsPagination">
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
|
<Transition :name="defaultStore.state.animation ? 'fade' : ''" mode="out-in">
|
||||||
<div v-if="flash" :key="flash.id">
|
<div v-if="flash" :key="flash.id">
|
||||||
<Transition :name="defaultStore.state.animation ? 'zoom' : ''" mode="out-in">
|
<Transition :name="defaultStore.state.animation ? 'zoom' : ''" mode="out-in">
|
||||||
|
@ -10,8 +10,8 @@
|
||||||
<MkAsUi v-if="root" :component="root" :components="components"/>
|
<MkAsUi v-if="root" :component="root" :components="components"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions _panel">
|
<div class="actions _panel">
|
||||||
<MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" as-like class="button" rounded primary @click="unlike()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
|
<MkButton v-if="flash.isLiked" v-tooltip="i18n.ts.unlike" asLike class="button" rounded primary @click="unlike()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
|
||||||
<MkButton v-else v-tooltip="i18n.ts.like" as-like class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
|
<MkButton v-else v-tooltip="i18n.ts.like" asLike class="button" rounded @click="like()"><i class="ti ti-heart"></i><span v-if="flash.likedCount > 0" style="margin-left: 6px;">{{ flash.likedCount }}</span></MkButton>
|
||||||
<MkButton v-tooltip="i18n.ts.shareWithNote" class="button" rounded @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></MkButton>
|
<MkButton v-tooltip="i18n.ts.shareWithNote" class="button" rounded @click="shareWithNote"><i class="ti ti-repeat ti-fw"></i></MkButton>
|
||||||
<MkButton v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton>
|
<MkButton v-tooltip="i18n.ts.share" class="button" rounded @click="share"><i class="ti ti-share ti-fw"></i></MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,7 +27,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
<MkFolder :default-open="false" :max-height="280" class="_margin">
|
<MkFolder :defaultOpen="false" :max-height="280" class="_margin">
|
||||||
<template #icon><i class="ti ti-code"></i></template>
|
<template #icon><i class="ti ti-code"></i></template>
|
||||||
<template #label>{{ i18n.ts._play.viewSource }}</template>
|
<template #label>{{ i18n.ts._play.viewSource }}</template>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="geegznzt">
|
<div>
|
||||||
<XAntenna :antenna="draft" @created="onAntennaCreated"/>
|
<XAntenna :antenna="draft" @created="onAntennaCreated"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div>
|
<div>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkInput v-model="name">
|
<MkInput v-model="name">
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<template><MkStickyContainer>
|
<template>
|
||||||
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="ieepwinx">
|
<div class="ieepwinx">
|
||||||
<MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
<MkButton :link="true" to="/my/antennas/create" primary class="add"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||||
|
|
||||||
|
@ -12,7 +13,8 @@
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</MkSpacer></MkStickyContainer>
|
</MkSpacer>
|
||||||
|
</MkStickyContainer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div v-if="tab === 'my'" class="_gaps">
|
<div v-if="tab === 'my'" class="_gaps">
|
||||||
<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
<MkButton primary rounded class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.add }}</MkButton>
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="qkcjvfiv">
|
<div class="qkcjvfiv">
|
||||||
<MkButton primary class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton>
|
<MkButton primary class="add" @click="create"><i class="ti ti-plus"></i> {{ i18n.ts.createList }}</MkButton>
|
||||||
|
|
||||||
<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists">
|
<MkPagination v-slot="{items}" ref="pagingComponent" :pagination="pagination" class="lists">
|
||||||
<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
|
<MkA v-for="list in items" :key="list.id" class="list _panel" :to="`/my/lists/${ list.id }`">
|
||||||
<div class="name">{{ list.name }}</div>
|
<div class="name">{{ list.name }}</div>
|
||||||
<MkAvatars :user-ids="list.userIds"/>
|
<MkAvatars :userIds="list.userIds"/>
|
||||||
</MkA>
|
</MkA>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
<template>
|
<template>
|
||||||
<Sortable :model-value="modelValue" tag="div" item-key="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swap-threshold="0.5" @update:model-value="v => $emit('update:modelValue', v)">
|
<Sortable :modelValue="modelValue" tag="div" itemKey="id" handle=".drag-handle" :group="{ name: 'blocks' }" :animation="150" :swapThreshold="0.5" @update:modelValue="v => $emit('update:modelValue', v)">
|
||||||
<template #item="{element}">
|
<template #item="{element}">
|
||||||
<div :class="$style.item">
|
<div :class="$style.item">
|
||||||
<!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
|
<!-- divが無いとエラーになる https://github.com/SortableJS/vue.draggable.next/issues/189 -->
|
||||||
<component :is="'x-' + element.type" :model-value="element" @update:model-value="updateItem" @remove="() => removeItem(element)"/>
|
<component :is="'x-' + element.type" :modelValue="element" @update:modelValue="updateItem" @remove="() => removeItem(element)"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Sortable>
|
</Sortable>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="jqqmcavi">
|
<div class="jqqmcavi">
|
||||||
<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
|
<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ti ti-external-link"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
|
||||||
<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
|
<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ti ti-device-floppy"></i> {{ i18n.ts.save }}</MkButton>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<MkModal
|
<MkModal
|
||||||
ref="dialogEl"
|
ref="dialogEl"
|
||||||
:prefer-type="'dialog'"
|
:preferType="'dialog'"
|
||||||
:z-priority="'low'"
|
:zPriority="'low'"
|
||||||
@click="cancel"
|
@click="cancel"
|
||||||
@close="cancel"
|
@close="cancel"
|
||||||
@closed="emit('closed')"
|
@closed="emit('closed')"
|
||||||
|
|
|
@ -51,7 +51,7 @@
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :model-value="usePasswordLessLogin" @update:model-value="v => updatePasswordLessLogin(v)">
|
<MkSwitch :disabled="!$i.twoFactorEnabled || $i.securityKeysList.length === 0" :modelValue="usePasswordLessLogin" @update:modelValue="v => updatePasswordLessLogin(v)">
|
||||||
<template #label>{{ i18n.ts.passwordLessLogin }}</template>
|
<template #label>{{ i18n.ts.passwordLessLogin }}</template>
|
||||||
<template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template>
|
<template #caption>{{ i18n.ts.passwordLessLoginDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
|
@ -15,13 +15,13 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, ref } from 'vue';
|
import { defineAsyncComponent, ref } from 'vue';
|
||||||
|
import type * as Misskey from 'misskey-js';
|
||||||
import FormSuspense from '@/components/form/suspense.vue';
|
import FormSuspense from '@/components/form/suspense.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account';
|
import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import type * as Misskey from 'misskey-js';
|
|
||||||
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
import MkUserCardMini from '@/components/MkUserCardMini.vue';
|
||||||
|
|
||||||
const storedAccounts = ref<any>(null);
|
const storedAccounts = ref<any>(null);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo>
|
<FormInfo warn>{{ i18n.ts.customCssWarn }}</FormInfo>
|
||||||
|
|
||||||
<MkTextarea v-model="localCustomCss" manual-save tall class="_monospace" style="tab-size: 2;">
|
<MkTextarea v-model="localCustomCss" manualSave tall class="_monospace" style="tab-size: 2;">
|
||||||
<template #label>CSS</template>
|
<template #label>CSS</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts.statistics }}</template>
|
<template #label>{{ i18n.ts.statistics }}</template>
|
||||||
<MkChart src="per-user-drive" :args="{ user: $i }" span="day" :limit="7 * 5" :bar="true" :stacked="true" :detailed="false" :aspect-ratio="6"/>
|
<MkChart src="per-user-drive" :args="{ user: $i }" span="day" :limit="7 * 5" :bar="true" :stacked="true" :detailed="false" :aspectRatio="6"/>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
@ -39,10 +39,10 @@
|
||||||
<template #label>{{ i18n.ts.keepOriginalUploading }}</template>
|
<template #label>{{ i18n.ts.keepOriginalUploading }}</template>
|
||||||
<template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template>
|
<template #caption>{{ i18n.ts.keepOriginalUploadingDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSwitch v-model="alwaysMarkNsfw" @update:model-value="saveProfile()">
|
<MkSwitch v-model="alwaysMarkNsfw" @update:modelValue="saveProfile()">
|
||||||
<template #label>{{ i18n.ts.alwaysMarkSensitive }}</template>
|
<template #label>{{ i18n.ts.alwaysMarkSensitive }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSwitch v-model="autoSensitive" @update:model-value="saveProfile()">
|
<MkSwitch v-model="autoSensitive" @update:modelValue="saveProfile()">
|
||||||
<template #label>{{ i18n.ts.enableAutoSensitive }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
<template #label>{{ i18n.ts.enableAutoSensitive }}<span class="_beta">{{ i18n.ts.beta }}</span></template>
|
||||||
<template #caption>{{ i18n.ts.enableAutoSensitiveDescription }}</template>
|
<template #caption>{{ i18n.ts.enableAutoSensitiveDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div v-if="instance.enableEmail" class="_gaps_m">
|
<div v-if="instance.enableEmail" class="_gaps_m">
|
||||||
<FormSection first>
|
<FormSection first>
|
||||||
<template #label>{{ i18n.ts.emailAddress }}</template>
|
<template #label>{{ i18n.ts.emailAddress }}</template>
|
||||||
<MkInput v-model="emailAddress" type="email" manual-save>
|
<MkInput v-model="emailAddress" type="email" manualSave>
|
||||||
<template #prefix><i class="ti ti-mail"></i></template>
|
<template #prefix><i class="ti ti-mail"></i></template>
|
||||||
<template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template>
|
<template v-if="$i.email && !$i.emailVerified" #caption>{{ i18n.ts.verificationEmailSent }}</template>
|
||||||
<template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--success);"></i> {{ i18n.ts.emailVerified }}</template>
|
<template v-else-if="emailAddress === $i.email && $i.emailVerified" #caption><i class="ti ti-check" style="color: var(--success);"></i> {{ i18n.ts.emailVerified }}</template>
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<MkSwitch :model-value="$i.receiveAnnouncementEmail" @update:model-value="onChangeReceiveAnnouncementEmail">
|
<MkSwitch :modelValue="$i.receiveAnnouncementEmail" @update:modelValue="onChangeReceiveAnnouncementEmail">
|
||||||
{{ i18n.ts.receiveAnnouncementFromInstance }}
|
{{ i18n.ts.receiveAnnouncementFromInstance }}
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="900" :margin-min="20" :margin-max="32">
|
<MkSpacer :contentMax="900" :marginMin="20" :marginMax="32">
|
||||||
<div ref="el" class="vvcocwet" :class="{ wide: !narrow }">
|
<div ref="el" class="vvcocwet" :class="{ wide: !narrow }">
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
|
<div v-if="!narrow || currentPage?.route.name == null" class="nav">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<FormInfo warn>
|
<FormInfo warn>
|
||||||
{{ i18n.ts.thisIsExperimentalFeature }}
|
{{ i18n.ts.thisIsExperimentalFeature }}
|
||||||
</FormInfo>
|
</FormInfo>
|
||||||
<MkFolder :default-open="true">
|
<MkFolder :defaultOpen="true">
|
||||||
<template #icon><i class="ti ti-plane-arrival"></i></template>
|
<template #icon><i class="ti ti-plane-arrival"></i></template>
|
||||||
<template #label>{{ i18n.ts._accountMigration.moveFrom }}</template>
|
<template #label>{{ i18n.ts._accountMigration.moveFrom }}</template>
|
||||||
<template #caption>{{ i18n.ts._accountMigration.moveFromSub }}</template>
|
<template #caption>{{ i18n.ts._accountMigration.moveFromSub }}</template>
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
</div>
|
</div>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
|
|
||||||
<MkFolder :default-open="!!$i?.movedTo">
|
<MkFolder :defaultOpen="!!$i?.movedTo">
|
||||||
<template #icon><i class="ti ti-plane-departure"></i></template>
|
<template #icon><i class="ti ti-plane-departure"></i></template>
|
||||||
<template #label>{{ i18n.ts._accountMigration.moveTo }}</template>
|
<template #label>{{ i18n.ts._accountMigration.moveTo }}</template>
|
||||||
|
|
||||||
|
@ -57,6 +57,8 @@
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { toString } from 'misskey-js/built/acct';
|
||||||
|
import { UserDetailed } from 'misskey-js/built/entities';
|
||||||
import FormInfo from '@/components/MkInfo.vue';
|
import FormInfo from '@/components/MkInfo.vue';
|
||||||
import MkInput from '@/components/MkInput.vue';
|
import MkInput from '@/components/MkInput.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
|
@ -66,8 +68,6 @@ import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import { definePageMetadata } from '@/scripts/page-metadata';
|
import { definePageMetadata } from '@/scripts/page-metadata';
|
||||||
import { $i } from '@/account';
|
import { $i } from '@/account';
|
||||||
import { toString } from 'misskey-js/built/acct';
|
|
||||||
import { UserDetailed } from 'misskey-js/built/entities';
|
|
||||||
import { unisonReload } from '@/scripts/unison-reload';
|
import { unisonReload } from '@/scripts/unison-reload';
|
||||||
|
|
||||||
const moveToAccount = ref('');
|
const moveToAccount = ref('');
|
||||||
|
|
|
@ -2,10 +2,10 @@
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<FormSlot>
|
<FormSlot>
|
||||||
<template #label>{{ i18n.ts.navbar }}</template>
|
<template #label>{{ i18n.ts.navbar }}</template>
|
||||||
<MkContainer :show-header="false">
|
<MkContainer :showHeader="false">
|
||||||
<Sortable
|
<Sortable
|
||||||
v-model="items"
|
v-model="items"
|
||||||
item-key="id"
|
itemKey="id"
|
||||||
:animation="150"
|
:animation="150"
|
||||||
:handle="'.' + $style.itemHandle"
|
:handle="'.' + $style.itemHandle"
|
||||||
@start="e => e.item.classList.add('active')"
|
@start="e => e.item.classList.add('active')"
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkPushNotificationAllowButton ref="allowButton"/>
|
<MkPushNotificationAllowButton ref="allowButton"/>
|
||||||
<MkSwitch :disabled="!pushRegistrationInServer" :model-value="sendReadMessage" @update:model-value="onChangeSendReadMessage">
|
<MkSwitch :disabled="!pushRegistrationInServer" :modelValue="sendReadMessage" @update:modelValue="onChangeSendReadMessage">
|
||||||
<template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template>
|
<template #label>{{ i18n.ts.sendPushNotificationReadMessage }}</template>
|
||||||
<template #caption>
|
<template #caption>
|
||||||
<I18n :src="i18n.ts.sendPushNotificationReadMessageCaption">
|
<I18n :src="i18n.ts.sendPushNotificationReadMessageCaption">
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_s" style="padding: 20px;">
|
<div v-for="plugin in plugins" :key="plugin.id" class="_panel _gaps_s" style="padding: 20px;">
|
||||||
<span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
|
<span style="display: flex;"><b>{{ plugin.name }}</b><span style="margin-left: auto;">v{{ plugin.version }}</span></span>
|
||||||
|
|
||||||
<MkSwitch :model-value="plugin.active" @update:model-value="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</MkSwitch>
|
<MkSwitch :modelValue="plugin.active" @update:modelValue="changeActive(plugin, $event)">{{ i18n.ts.makeActive }}</MkSwitch>
|
||||||
|
|
||||||
<MkKeyValue>
|
<MkKeyValue>
|
||||||
<template #key>{{ i18n.ts.author }}</template>
|
<template #key>{{ i18n.ts.author }}</template>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="isLocked" @update:model-value="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch>
|
<MkSwitch v-model="isLocked" @update:modelValue="save()">{{ i18n.ts.makeFollowManuallyApprove }}<template #caption>{{ i18n.ts.lockedAccountInfo }}</template></MkSwitch>
|
||||||
<MkSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:model-value="save()">{{ i18n.ts.autoAcceptFollowed }}</MkSwitch>
|
<MkSwitch v-if="isLocked" v-model="autoAcceptFollowed" @update:modelValue="save()">{{ i18n.ts.autoAcceptFollowed }}</MkSwitch>
|
||||||
|
|
||||||
<MkSwitch v-model="publicReactions" @update:model-value="save()">
|
<MkSwitch v-model="publicReactions" @update:modelValue="save()">
|
||||||
{{ i18n.ts.makeReactionsPublic }}
|
{{ i18n.ts.makeReactionsPublic }}
|
||||||
<template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template>
|
<template #caption>{{ i18n.ts.makeReactionsPublicDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
||||||
<MkSelect v-model="ffVisibility" @update:model-value="save()">
|
<MkSelect v-model="ffVisibility" @update:modelValue="save()">
|
||||||
<template #label>{{ i18n.ts.ffVisibility }}</template>
|
<template #label>{{ i18n.ts.ffVisibility }}</template>
|
||||||
<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
|
<option value="public">{{ i18n.ts._ffVisibility.public }}</option>
|
||||||
<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
|
<option value="followers">{{ i18n.ts._ffVisibility.followers }}</option>
|
||||||
|
@ -16,26 +16,26 @@
|
||||||
<template #caption>{{ i18n.ts.ffVisibilityDescription }}</template>
|
<template #caption>{{ i18n.ts.ffVisibilityDescription }}</template>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
|
||||||
<MkSwitch v-model="hideOnlineStatus" @update:model-value="save()">
|
<MkSwitch v-model="hideOnlineStatus" @update:modelValue="save()">
|
||||||
{{ i18n.ts.hideOnlineStatus }}
|
{{ i18n.ts.hideOnlineStatus }}
|
||||||
<template #caption>{{ i18n.ts.hideOnlineStatusDescription }}</template>
|
<template #caption>{{ i18n.ts.hideOnlineStatusDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSwitch v-model="noCrawle" @update:model-value="save()">
|
<MkSwitch v-model="noCrawle" @update:modelValue="save()">
|
||||||
{{ i18n.ts.noCrawle }}
|
{{ i18n.ts.noCrawle }}
|
||||||
<template #caption>{{ i18n.ts.noCrawleDescription }}</template>
|
<template #caption>{{ i18n.ts.noCrawleDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSwitch v-model="preventAiLearning" @update:model-value="save()">
|
<MkSwitch v-model="preventAiLearning" @update:modelValue="save()">
|
||||||
{{ i18n.ts.preventAiLearning }}<span class="_beta">{{ i18n.ts.beta }}</span>
|
{{ i18n.ts.preventAiLearning }}<span class="_beta">{{ i18n.ts.beta }}</span>
|
||||||
<template #caption>{{ i18n.ts.preventAiLearningDescription }}</template>
|
<template #caption>{{ i18n.ts.preventAiLearningDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkSwitch v-model="isExplorable" @update:model-value="save()">
|
<MkSwitch v-model="isExplorable" @update:modelValue="save()">
|
||||||
{{ i18n.ts.makeExplorable }}
|
{{ i18n.ts.makeExplorable }}
|
||||||
<template #caption>{{ i18n.ts.makeExplorableDescription }}</template>
|
<template #caption>{{ i18n.ts.makeExplorableDescription }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkSwitch v-model="rememberNoteVisibility" @update:model-value="save()">{{ i18n.ts.rememberNoteVisibility }}</MkSwitch>
|
<MkSwitch v-model="rememberNoteVisibility" @update:modelValue="save()">{{ i18n.ts.rememberNoteVisibility }}</MkSwitch>
|
||||||
<MkFolder v-if="!rememberNoteVisibility">
|
<MkFolder v-if="!rememberNoteVisibility">
|
||||||
<template #label>{{ i18n.ts.defaultNoteVisibility }}</template>
|
<template #label>{{ i18n.ts.defaultNoteVisibility }}</template>
|
||||||
<template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template>
|
<template v-if="defaultNoteVisibility === 'public'" #suffix>{{ i18n.ts._visibility.public }}</template>
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
|
|
||||||
<MkSwitch v-model="keepCw" @update:model-value="save()">{{ i18n.ts.keepCw }}</MkSwitch>
|
<MkSwitch v-model="keepCw" @update:modelValue="save()">{{ i18n.ts.keepCw }}</MkSwitch>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<FromSlot>
|
<FromSlot>
|
||||||
<template #label>{{ i18n.ts.reactionSettingDescription }}</template>
|
<template #label>{{ i18n.ts.reactionSettingDescription }}</template>
|
||||||
<div v-panel style="border-radius: 6px;">
|
<div v-panel style="border-radius: 6px;">
|
||||||
<Sortable v-model="reactions" :class="$style.reactions" :item-key="item => item" :animation="150" :delay="100" :delay-on-touch-only="true">
|
<Sortable v-model="reactions" :class="$style.reactions" :itemKey="item => item" :animation="150" :delay="100" :delayOnTouchOnly="true">
|
||||||
<template #item="{element}">
|
<template #item="{element}">
|
||||||
<button class="_button" :class="$style.reactionsItem" @click="remove(element, $event)">
|
<button class="_button" :class="$style.reactionsItem" @click="remove(element, $event)">
|
||||||
<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
|
<MkCustomEmoji v-if="element[0] === ':'" :name="element" :normal="true"/>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<FormSection first>
|
<FormSection first>
|
||||||
<template #label>{{ i18n.ts.rolesAssignedToMe }}</template>
|
<template #label>{{ i18n.ts.rolesAssignedToMe }}</template>
|
||||||
<div class="_gaps_s">
|
<div class="_gaps_s">
|
||||||
<MkRolePreview v-for="role in $i.roles" :key="role.id" :role="role" :for-moderation="false"/>
|
<MkRolePreview v-for="role in $i.roles" :key="role.id" :role="role" :forModeration="false"/>
|
||||||
</div>
|
</div>
|
||||||
</FormSection>
|
</FormSection>
|
||||||
<FormSection>
|
<FormSection>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
<FormSection>
|
<FormSection>
|
||||||
<template #label>{{ i18n.ts.signinHistory }}</template>
|
<template #label>{{ i18n.ts.signinHistory }}</template>
|
||||||
<MkPagination :pagination="pagination" disable-auto-load>
|
<MkPagination :pagination="pagination" disableAutoLoad>
|
||||||
<template #default="{items}">
|
<template #default="{items}">
|
||||||
<div>
|
<div>
|
||||||
<div v-for="item in items" :key="item.id" v-panel class="timnmucd">
|
<div v-for="item in items" :key="item.id" v-panel class="timnmucd">
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
<template #label>{{ i18n.ts.sound }}</template>
|
<template #label>{{ i18n.ts.sound }}</template>
|
||||||
<option v-for="x in soundsTypes" :key="x" :value="x">{{ x == null ? i18n.ts.none : x }}</option>
|
<option v-for="x in soundsTypes" :key="x" :value="x">{{ x == null ? i18n.ts.none : x }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<MkRange v-model="volume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`">
|
<MkRange v-model="volume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`">
|
||||||
<template #label>{{ i18n.ts.volume }}</template>
|
<template #label>{{ i18n.ts.volume }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="_gaps_m">
|
<div class="_gaps_m">
|
||||||
<MkRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :text-converter="(v) => `${Math.floor(v * 100)}%`">
|
<MkRange v-model="masterVolume" :min="0" :max="1" :step="0.05" :textConverter="(v) => `${Math.floor(v * 100)}%`">
|
||||||
<template #label>{{ i18n.ts.masterVolume }}</template>
|
<template #label>{{ i18n.ts.masterVolume }}</template>
|
||||||
</MkRange>
|
</MkRange>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<option value="userList">User list timeline</option>
|
<option value="userList">User list timeline</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
|
|
||||||
<MkInput v-model="statusbar.name" manual-save>
|
<MkInput v-model="statusbar.name" manualSave>
|
||||||
<template #label>{{ i18n.ts.label }}</template>
|
<template #label>{{ i18n.ts.label }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
|
|
||||||
|
@ -25,13 +25,13 @@
|
||||||
</MkRadios>
|
</MkRadios>
|
||||||
|
|
||||||
<template v-if="statusbar.type === 'rss'">
|
<template v-if="statusbar.type === 'rss'">
|
||||||
<MkInput v-model="statusbar.props.url" manual-save type="url">
|
<MkInput v-model="statusbar.props.url" manualSave type="url">
|
||||||
<template #label>URL</template>
|
<template #label>URL</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkSwitch v-model="statusbar.props.shuffle">
|
<MkSwitch v-model="statusbar.props.shuffle">
|
||||||
<template #label>{{ i18n.ts.shuffle }}</template>
|
<template #label>{{ i18n.ts.shuffle }}</template>
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
<MkInput v-model="statusbar.props.refreshIntervalSec" manual-save type="number">
|
<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number">
|
||||||
<template #label>{{ i18n.ts.refreshInterval }}</template>
|
<template #label>{{ i18n.ts.refreshInterval }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
|
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
</MkSwitch>
|
</MkSwitch>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="statusbar.type === 'federation'">
|
<template v-else-if="statusbar.type === 'federation'">
|
||||||
<MkInput v-model="statusbar.props.refreshIntervalSec" manual-save type="number">
|
<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number">
|
||||||
<template #label>{{ i18n.ts.refreshInterval }}</template>
|
<template #label>{{ i18n.ts.refreshInterval }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
|
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
<template #label>{{ i18n.ts.userList }}</template>
|
<template #label>{{ i18n.ts.userList }}</template>
|
||||||
<option v-for="list in userLists" :value="list.id">{{ list.name }}</option>
|
<option v-for="list in userLists" :value="list.id">{{ list.name }}</option>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<MkInput v-model="statusbar.props.refreshIntervalSec" manual-save type="number">
|
<MkInput v-model="statusbar.props.refreshIntervalSec" manualSave type="number">
|
||||||
<template #label>{{ i18n.ts.refreshInterval }}</template>
|
<template #label>{{ i18n.ts.refreshInterval }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
|
<MkRange v-model="statusbar.props.marqueeDuration" :min="5" :max="150" :step="1">
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<MkFolder v-for="x in statusbars" :key="x.id">
|
<MkFolder v-for="x in statusbars" :key="x.id">
|
||||||
<template #label>{{ x.type ?? i18n.ts.notSet }}</template>
|
<template #label>{{ x.type ?? i18n.ts.notSet }}</template>
|
||||||
<template #suffix>{{ x.name }}</template>
|
<template #suffix>{{ x.name }}</template>
|
||||||
<XStatusbar :_id="x.id" :user-lists="userLists"/>
|
<XStatusbar :_id="x.id" :userLists="userLists"/>
|
||||||
</MkFolder>
|
</MkFolder>
|
||||||
<MkButton primary @click="add">{{ i18n.ts.add }}</MkButton>
|
<MkButton primary @click="add">{{ i18n.ts.add }}</MkButton>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,13 +10,13 @@
|
||||||
</optgroup>
|
</optgroup>
|
||||||
</MkSelect>
|
</MkSelect>
|
||||||
<template v-if="selectedTheme">
|
<template v-if="selectedTheme">
|
||||||
<MkInput readonly :model-value="selectedTheme.author">
|
<MkInput readonly :modelValue="selectedTheme.author">
|
||||||
<template #label>{{ i18n.ts.author }}</template>
|
<template #label>{{ i18n.ts.author }}</template>
|
||||||
</MkInput>
|
</MkInput>
|
||||||
<MkTextarea v-if="selectedTheme.desc" readonly :model-value="selectedTheme.desc">
|
<MkTextarea v-if="selectedTheme.desc" readonly :modelValue="selectedTheme.desc">
|
||||||
<template #label>{{ i18n.ts._theme.description }}</template>
|
<template #label>{{ i18n.ts._theme.description }}</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
<MkTextarea readonly tall :model-value="selectedThemeCode">
|
<MkTextarea readonly tall :modelValue="selectedThemeCode">
|
||||||
<template #label>{{ i18n.ts._theme.code }}</template>
|
<template #label>{{ i18n.ts._theme.code }}</template>
|
||||||
<template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template>
|
<template #caption><button class="_textButton" @click="copyThemeCode()">{{ i18n.ts.copy }}</button></template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="1200">
|
<MkSpacer :contentMax="1200">
|
||||||
<MkAchievements :user="user" :with-locked="false" :with-description="$i != null && (props.user.id === $i.id)"/>
|
<MkAchievements :user="user" :withLocked="false" :withDescription="$i != null && (props.user.id === $i.id)"/>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="_gaps">
|
<div class="_gaps">
|
||||||
<MkFoldableSection class="item">
|
<MkFoldableSection class="item">
|
||||||
<template #header><i class="ti ti-activity"></i> Heatmap</template>
|
<template #header><i class="ti ti-activity"></i> Heatmap</template>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<div class="pages-user-clips">
|
<div>
|
||||||
<MkPagination v-slot="{items}" ref="list" :pagination="pagination" class="list">
|
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
|
||||||
<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin">
|
<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" :class="$style.item" class="_panel _margin">
|
||||||
<b>{{ item.name }}</b>
|
<b>{{ item.name }}</b>
|
||||||
<div v-if="item.description" class="description">{{ item.description }}</div>
|
<div v-if="item.description" :class="$style.description">{{ item.description }}</div>
|
||||||
</MkA>
|
</MkA>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
|
@ -29,19 +29,15 @@ const pagination = {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.pages-user-clips {
|
.item {
|
||||||
> .list {
|
|
||||||
> .item {
|
|
||||||
display: block;
|
display: block;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
> .description {
|
.description {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding-top: 8px;
|
padding-top: 8px;
|
||||||
border-top: solid 0.5px var(--divider);
|
border-top: solid 0.5px var(--divider);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<MkPagination v-slot="{items}" ref="list" :pagination="type === 'following' ? followingPagination : followersPagination" class="mk-following-or-followers">
|
<MkPagination v-slot="{items}" ref="list" :pagination="type === 'following' ? followingPagination : followersPagination">
|
||||||
<div class="users">
|
<div :class="$style.users">
|
||||||
<MkUserInfo v-for="user in items.map(x => type === 'following' ? x.followee : x.follower)" :key="user.id" class="user" :user="user"/>
|
<MkUserInfo v-for="user in items.map(x => type === 'following' ? x.followee : x.follower)" :key="user.id" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,12 +36,10 @@ const followersPagination = {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.mk-following-or-followers {
|
.users {
|
||||||
> .users {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
grid-gap: var(--margin);
|
grid-gap: var(--margin);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="1000">
|
<MkSpacer :contentMax="1000">
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade" mode="out-in">
|
||||||
<div v-if="user">
|
<div v-if="user">
|
||||||
<XFollowList :user="user" type="followers"/>
|
<XFollowList :user="user" type="followers"/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
|
||||||
<MkSpacer :content-max="1000">
|
<MkSpacer :contentMax="1000">
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade" mode="out-in">
|
||||||
<div v-if="user">
|
<div v-if="user">
|
||||||
<XFollowList :user="user" type="following"/>
|
<XFollowList :user="user" type="following"/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<MkPagination v-slot="{items}" :pagination="pagination">
|
<MkPagination v-slot="{items}" :pagination="pagination">
|
||||||
<div class="jrnovfpt">
|
<div :class="$style.root">
|
||||||
<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
|
<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
|
||||||
</div>
|
</div>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
|
@ -28,8 +28,8 @@ const pagination = {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.jrnovfpt {
|
.root {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
|
||||||
grid-gap: 12px;
|
grid-gap: 12px;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="narrow ? 800 : 1100">
|
<MkSpacer :contentMax="narrow ? 800 : 1100">
|
||||||
<div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;">
|
<div ref="rootEl" class="ftskorzw" :class="{ wide: !narrow }" style="container-type: inline-size;">
|
||||||
<div class="main _gaps">
|
<div class="main _gaps">
|
||||||
<!-- TODO -->
|
<!-- TODO -->
|
||||||
|
@ -7,7 +7,7 @@
|
||||||
<!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> -->
|
<!-- <div class="punished" v-if="user.isSilenced"><i class="ti ti-alert-triangle" style="margin-right: 8px;"></i> {{ i18n.ts.userSilenced }}</div> -->
|
||||||
|
|
||||||
<div class="profile _gaps">
|
<div class="profile _gaps">
|
||||||
<MkAccountMoved v-if="user.movedTo" :moved-to="user.movedTo"/>
|
<MkAccountMoved v-if="user.movedTo" :movedTo="user.movedTo"/>
|
||||||
<MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/>
|
<MkRemoteCaution v-if="user.host != null" :href="user.url ?? user.uri!" class="warn"/>
|
||||||
|
|
||||||
<div :key="user.id" class="main _panel">
|
<div :key="user.id" class="main _panel">
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="iAmModerator" class="moderationNote">
|
<div v-if="iAmModerator" class="moderationNote">
|
||||||
<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manual-save>
|
<MkTextarea v-if="editModerationNote || (moderationNote != null && moderationNote !== '')" v-model="moderationNote" manualSave>
|
||||||
<template #label>Moderation note</template>
|
<template #label>Moderation note</template>
|
||||||
</MkTextarea>
|
</MkTextarea>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
<XPhotos :key="user.id" :user="user"/>
|
<XPhotos :key="user.id" :user="user"/>
|
||||||
<XActivity :key="user.id" :user="user"/>
|
<XActivity :key="user.id" :user="user"/>
|
||||||
</template>
|
</template>
|
||||||
<MkNotes v-if="!disableNotes" :class="$style.tl" :no-gap="true" :pagination="pagination"/>
|
<MkNotes v-if="!disableNotes" :class="$style.tl" :noGap="true" :pagination="pagination"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;">
|
<div v-if="!narrow" class="sub _gaps" style="container-type: inline-size;">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="800" style="padding-top: 0">
|
<MkSpacer :contentMax="800" style="padding-top: 0">
|
||||||
<MkStickyContainer>
|
<MkStickyContainer>
|
||||||
<template #header>
|
<template #header>
|
||||||
<MkTab v-model="include" :class="$style.tab">
|
<MkTab v-model="include" :class="$style.tab">
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
<option value="files">{{ i18n.ts.withFiles }}</option>
|
<option value="files">{{ i18n.ts.withFiles }}</option>
|
||||||
</MkTab>
|
</MkTab>
|
||||||
</template>
|
</template>
|
||||||
<MkNotes :no-gap="true" :pagination="pagination" :class="$style.tl"/>
|
<MkNotes :noGap="true" :pagination="pagination" :class="$style.tl"/>
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
</MkSpacer>
|
</MkSpacer>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
|
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
|
||||||
<MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/>
|
<MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_margin"/>
|
||||||
</MkPagination>
|
</MkPagination>
|
||||||
|
@ -24,7 +24,3 @@ const pagination = {
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<MkSpacer :content-max="700">
|
<MkSpacer :contentMax="700">
|
||||||
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
|
<MkPagination v-slot="{items}" ref="list" :pagination="pagination">
|
||||||
<div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _margin afdcfbfb">
|
<div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="_panel _margin">
|
||||||
<div class="header">
|
<div :class="$style.header">
|
||||||
<MkAvatar class="avatar" :user="user"/>
|
<MkAvatar :class="$style.avatar" :user="user"/>
|
||||||
<MkReactionIcon class="reaction" :reaction="item.type" :no-style="true"/>
|
<MkReactionIcon :class="$style.reaction" :reaction="item.type" :noStyle="true"/>
|
||||||
<MkTime :time="item.createdAt" class="createdAt"/>
|
<MkTime :time="item.createdAt" :class="$style.createdAt"/>
|
||||||
</div>
|
</div>
|
||||||
<MkNote :key="item.id" :note="item.note"/>
|
<MkNote :key="item.id" :note="item.note"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -33,29 +33,27 @@ const pagination = {
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" module>
|
||||||
.afdcfbfb {
|
.header {
|
||||||
> .header {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
border-bottom: solid 2px var(--divider);
|
border-bottom: solid 2px var(--divider);
|
||||||
|
}
|
||||||
|
|
||||||
> .avatar {
|
.avatar {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
height: 24px;
|
height: 24px;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .reaction {
|
.reaction {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
> .createdAt {
|
.createdAt {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -11,11 +11,11 @@
|
||||||
|
|
||||||
<TransitionGroup
|
<TransitionGroup
|
||||||
tag="div" :class="[$style.notifications, $style[`notificationsPosition-${defaultStore.state.notificationPosition}`], $style[`notificationsStackAxis-${defaultStore.state.notificationStackAxis}`]]"
|
tag="div" :class="[$style.notifications, $style[`notificationsPosition-${defaultStore.state.notificationPosition}`], $style[`notificationsStackAxis-${defaultStore.state.notificationStackAxis}`]]"
|
||||||
:move-class="defaultStore.state.animation ? $style.transition_notification_move : ''"
|
:moveClass="defaultStore.state.animation ? $style.transition_notification_move : ''"
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_notification_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_notification_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_notification_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_notification_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
|
<div v-for="notification in notifications" :key="notification.id" :class="$style.notification">
|
||||||
<XNotification :notification="notification"/>
|
<XNotification :notification="notification"/>
|
||||||
|
|
|
@ -8,25 +8,25 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="middle">
|
<div class="middle">
|
||||||
<MkA v-click-anime class="item index" active-class="active" to="/" exact>
|
<MkA v-click-anime class="item index" activeClass="active" to="/" exact>
|
||||||
<i class="icon ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
<i class="icon ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<template v-for="item in menu">
|
<template v-for="item in menu">
|
||||||
<div v-if="item === '-'" class="divider"></div>
|
<div v-if="item === '-'" class="divider"></div>
|
||||||
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="[item, { active: navbarItemDef[item].active }]" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="[item, { active: navbarItemDef[item].active }]" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
||||||
<i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
|
<i class="icon ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
|
||||||
<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="icon _indicatorCircle"></i></span>
|
<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="icon _indicatorCircle"></i></span>
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" active-class="active" to="/admin">
|
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" activeClass="active" to="/admin">
|
||||||
<i class="icon ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
<i class="icon ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<button v-click-anime class="item _button" @click="more">
|
<button v-click-anime class="item _button" @click="more">
|
||||||
<i class="icon ti ti-grid-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
<i class="icon ti ti-grid-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
||||||
<span v-if="otherMenuItemIndicated" class="indicator"><i class="icon _indicatorCircle"></i></span>
|
<span v-if="otherMenuItemIndicated" class="indicator"><i class="icon _indicatorCircle"></i></span>
|
||||||
</button>
|
</button>
|
||||||
<MkA v-click-anime class="item" active-class="active" to="/settings">
|
<MkA v-click-anime class="item" activeClass="active" to="/settings">
|
||||||
<i class="icon ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
<i class="icon ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="middle">
|
<div class="middle">
|
||||||
<MkA v-click-anime v-tooltip.noDelay.right="i18n.ts.timeline" class="item index" active-class="active" to="/" exact>
|
<MkA v-click-anime v-tooltip.noDelay.right="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
|
||||||
<i class="icon ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
<i class="icon ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<template v-for="item in menu">
|
<template v-for="item in menu">
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
v-tooltip.noDelay.right="navbarItemDef[item].title"
|
v-tooltip.noDelay.right="navbarItemDef[item].title"
|
||||||
class="item _button"
|
class="item _button"
|
||||||
:class="[item, { active: navbarItemDef[item].active }]"
|
:class="[item, { active: navbarItemDef[item].active }]"
|
||||||
active-class="active"
|
activeClass="active"
|
||||||
:to="navbarItemDef[item].to"
|
:to="navbarItemDef[item].to"
|
||||||
v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"
|
v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}"
|
||||||
>
|
>
|
||||||
|
@ -29,14 +29,14 @@
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip.noDelay.right="i18n.ts.controlPanel" class="item" active-class="active" to="/admin">
|
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip.noDelay.right="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin">
|
||||||
<i class="icon ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
<i class="icon ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<button v-click-anime class="item _button" @click="more">
|
<button v-click-anime class="item _button" @click="more">
|
||||||
<i class="icon ti ti-grid-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
<i class="icon ti ti-grid-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
||||||
<span v-if="otherMenuItemIndicated" class="indicator"><i class="icon _indicatorCircle"></i></span>
|
<span v-if="otherMenuItemIndicated" class="indicator"><i class="icon _indicatorCircle"></i></span>
|
||||||
</button>
|
</button>
|
||||||
<MkA v-click-anime v-tooltip.noDelay.right="i18n.ts.settings" class="item" active-class="active" to="/settings">
|
<MkA v-click-anime v-tooltip.noDelay.right="i18n.ts.settings" class="item" activeClass="active" to="/settings">
|
||||||
<i class="icon ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
<i class="icon ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
}]"
|
}]"
|
||||||
>
|
>
|
||||||
<span class="name">{{ x.name }}</span>
|
<span class="name">{{ x.name }}</span>
|
||||||
<XRss v-if="x.type === 'rss'" class="body" :refresh-interval-sec="x.props.refreshIntervalSec" :marquee-duration="x.props.marqueeDuration" :marquee-reverse="x.props.marqueeReverse" :display="x.props.display" :url="x.props.url" :shuffle="x.props.shuffle"/>
|
<XRss v-if="x.type === 'rss'" class="body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :url="x.props.url" :shuffle="x.props.shuffle"/>
|
||||||
<XFederation v-else-if="x.type === 'federation'" class="body" :refresh-interval-sec="x.props.refreshIntervalSec" :marquee-duration="x.props.marqueeDuration" :marquee-reverse="x.props.marqueeReverse" :display="x.props.display" :colored="x.props.colored"/>
|
<XFederation v-else-if="x.type === 'federation'" class="body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :colored="x.props.colored"/>
|
||||||
<XUserList v-else-if="x.type === 'userList'" class="body" :refresh-interval-sec="x.props.refreshIntervalSec" :marquee-duration="x.props.marqueeDuration" :marquee-reverse="x.props.marqueeReverse" :display="x.props.display" :user-list-id="x.props.userListId"/>
|
<XUserList v-else-if="x.type === 'userList'" class="body" :refreshIntervalSec="x.props.refreshIntervalSec" :marqueeDuration="x.props.marqueeDuration" :marqueeReverse="x.props.marqueeReverse" :display="x.props.display" :userListId="x.props.userListId"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -5,18 +5,18 @@
|
||||||
<button v-click-anime class="item _button instance" @click="openInstanceMenu">
|
<button v-click-anime class="item _button instance" @click="openInstanceMenu">
|
||||||
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" class="_ghost"/>
|
<img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" class="_ghost"/>
|
||||||
</button>
|
</button>
|
||||||
<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" active-class="active" to="/" exact>
|
<MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact>
|
||||||
<i class="ti ti-home ti-fw"></i>
|
<i class="ti ti-home ti-fw"></i>
|
||||||
</MkA>
|
</MkA>
|
||||||
<template v-for="item in menu">
|
<template v-for="item in menu">
|
||||||
<div v-if="item === '-'" class="divider"></div>
|
<div v-if="item === '-'" class="divider"></div>
|
||||||
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime v-tooltip="navbarItemDef[item].title" class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
||||||
<i class="ti-fw" :class="navbarItemDef[item].icon"></i>
|
<i class="ti-fw" :class="navbarItemDef[item].icon"></i>
|
||||||
<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span>
|
<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span>
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" active-class="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
|
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime v-tooltip="i18n.ts.controlPanel" class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
|
||||||
<i class="ti ti-dashboard ti-fw"></i>
|
<i class="ti ti-dashboard ti-fw"></i>
|
||||||
</MkA>
|
</MkA>
|
||||||
<button v-click-anime class="item _button" @click="more">
|
<button v-click-anime class="item _button" @click="more">
|
||||||
|
@ -25,7 +25,7 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
|
<MkA v-click-anime v-tooltip="i18n.ts.settings" class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
|
||||||
<i class="ti ti-settings ti-fw"></i>
|
<i class="ti ti-settings ti-fw"></i>
|
||||||
</MkA>
|
</MkA>
|
||||||
<button v-click-anime class="item _button account" @click="openAccountMenu">
|
<button v-click-anime class="item _button account" @click="openAccountMenu">
|
||||||
|
|
|
@ -9,25 +9,25 @@
|
||||||
</MkButton>
|
</MkButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<MkA v-click-anime class="item index" active-class="active" to="/" exact>
|
<MkA v-click-anime class="item index" activeClass="active" to="/" exact>
|
||||||
<i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
<i class="ti ti-home ti-fw"></i><span class="text">{{ i18n.ts.timeline }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<template v-for="item in menu">
|
<template v-for="item in menu">
|
||||||
<div v-if="item === '-'" class="divider"></div>
|
<div v-if="item === '-'" class="divider"></div>
|
||||||
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" active-class="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
<component :is="navbarItemDef[item].to ? 'MkA' : 'button'" v-else-if="navbarItemDef[item] && (navbarItemDef[item].show !== false)" v-click-anime class="item _button" :class="item" activeClass="active" :to="navbarItemDef[item].to" v-on="navbarItemDef[item].action ? { click: navbarItemDef[item].action } : {}">
|
||||||
<i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
|
<i class="ti-fw" :class="navbarItemDef[item].icon"></i><span class="text">{{ navbarItemDef[item].title }}</span>
|
||||||
<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span>
|
<span v-if="navbarItemDef[item].indicated" class="indicator"><i class="_indicatorCircle"></i></span>
|
||||||
</component>
|
</component>
|
||||||
</template>
|
</template>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" active-class="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
|
<MkA v-if="$i.isAdmin || $i.isModerator" v-click-anime class="item" activeClass="active" to="/admin" :behavior="settingsWindowed ? 'modalWindow' : null">
|
||||||
<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
<i class="ti ti-dashboard ti-fw"></i><span class="text">{{ i18n.ts.controlPanel }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<button v-click-anime class="item _button" @click="more">
|
<button v-click-anime class="item _button" @click="more">
|
||||||
<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
<i class="ti ti-dots ti-fw"></i><span class="text">{{ i18n.ts.more }}</span>
|
||||||
<span v-if="otherNavItemIndicated" class="indicator"><i class="_indicatorCircle"></i></span>
|
<span v-if="otherNavItemIndicated" class="indicator"><i class="_indicatorCircle"></i></span>
|
||||||
</button>
|
</button>
|
||||||
<MkA v-click-anime class="item" active-class="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
|
<MkA v-click-anime class="item" activeClass="active" to="/settings" :behavior="settingsWindowed ? 'modalWindow' : null">
|
||||||
<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
<i class="ti ti-settings ti-fw"></i><span class="text">{{ i18n.ts.settings }}</span>
|
||||||
</MkA>
|
</MkA>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
<XSidebar/>
|
<XSidebar/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else ref="widgetsLeft" class="widgets left">
|
<div v-else ref="widgetsLeft" class="widgets left">
|
||||||
<XWidgets place="left" :margin-top="'var(--margin)'" @mounted="attachSticky(widgetsLeft)"/>
|
<XWidgets place="left" :marginTop="'var(--margin)'" @mounted="attachSticky(widgetsLeft)"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main class="main" @contextmenu.stop="onContextmenu">
|
<main class="main" @contextmenu.stop="onContextmenu">
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<div v-if="isDesktop" ref="widgetsRight" class="widgets right">
|
<div v-if="isDesktop" ref="widgetsRight" class="widgets right">
|
||||||
<XWidgets :place="showMenuOnTop ? 'right' : null" :margin-top="showMenuOnTop ? '0' : 'var(--margin)'" @mounted="attachSticky(widgetsRight)"/>
|
<XWidgets :place="showMenuOnTop ? 'right' : null" :marginTop="showMenuOnTop ? '0' : 'var(--margin)'" @mounted="attachSticky(widgetsRight)"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
:class="$style.folder"
|
:class="$style.folder"
|
||||||
:style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
|
:style="columns.filter(c => ids.includes(c.id)).some(c => c.flexible) ? { flex: 1, minWidth: '350px' } : { width: Math.max(...columns.filter(c => ids.includes(c.id)).map(c => c.width)) + 'px' }"
|
||||||
>
|
>
|
||||||
<DeckColumnCore v-for="id in ids" :ref="id" :key="id" :column="columns.find(c => c.id === id)" :is-stacked="true" @parent-focus="moveFocus(id, $event)"/>
|
<DeckColumnCore v-for="id in ids" :ref="id" :key="id" :column="columns.find(c => c.id === id)" :isStacked="true" @parentFocus="moveFocus(id, $event)"/>
|
||||||
</section>
|
</section>
|
||||||
<DeckColumnCore
|
<DeckColumnCore
|
||||||
v-else
|
v-else
|
||||||
|
@ -20,9 +20,9 @@
|
||||||
:key="ids[0]"
|
:key="ids[0]"
|
||||||
:class="$style.column"
|
:class="$style.column"
|
||||||
:column="columns.find(c => c.id === ids[0])"
|
:column="columns.find(c => c.id === ids[0])"
|
||||||
:is-stacked="false"
|
:isStacked="false"
|
||||||
:style="columns.find(c => c.id === ids[0])!.flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0])!.width + 'px' }"
|
:style="columns.find(c => c.id === ids[0])!.flexible ? { flex: 1, minWidth: '350px' } : { width: columns.find(c => c.id === ids[0])!.width + 'px' }"
|
||||||
@parent-focus="moveFocus(ids[0], $event)"
|
@parentFocus="moveFocus(ids[0], $event)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
|
<div v-if="layout.length === 0" class="_panel" :class="$style.onboarding">
|
||||||
|
@ -53,10 +53,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="drawerMenuShowing"
|
v-if="drawerMenuShowing"
|
||||||
|
@ -68,10 +68,10 @@
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div v-if="drawerMenuShowing" :class="$style.menu">
|
<div v-if="drawerMenuShowing" :class="$style.menu">
|
||||||
<XDrawerMenu/>
|
<XDrawerMenu/>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :menu="menu" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :menu="menu" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
<i class="ti ti-antenna"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :menu="menu" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :menu="menu" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<i class="ti ti-device-tv"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
<i class="ti ti-device-tv"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -14,13 +14,13 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
import * as misskey from 'misskey-js';
|
||||||
import XColumn from './column.vue';
|
import XColumn from './column.vue';
|
||||||
import { updateColumn, Column } from './deck-store';
|
import { updateColumn, Column } from './deck-store';
|
||||||
import MkTimeline from '@/components/MkTimeline.vue';
|
import MkTimeline from '@/components/MkTimeline.vue';
|
||||||
import MkButton from '@/components/MkButton.vue';
|
import MkButton from '@/components/MkButton.vue';
|
||||||
import * as os from '@/os';
|
import * as os from '@/os';
|
||||||
import { i18n } from '@/i18n';
|
import { i18n } from '@/i18n';
|
||||||
import * as misskey from 'misskey-js';
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
column: Column;
|
column: Column;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- TODO: リファクタの余地がありそう -->
|
<!-- TODO: リファクタの余地がありそう -->
|
||||||
<div v-if="!column">たぶん見えちゃいけないやつ</div>
|
<div v-if="!column">たぶん見えちゃいけないやつ</div>
|
||||||
<XMainColumn v-else-if="column.type === 'main'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XMainColumn v-else-if="column.type === 'main'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XWidgetsColumn v-else-if="column.type === 'widgets'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XWidgetsColumn v-else-if="column.type === 'widgets'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XNotificationsColumn v-else-if="column.type === 'notifications'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XNotificationsColumn v-else-if="column.type === 'notifications'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XTlColumn v-else-if="column.type === 'tl'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XTlColumn v-else-if="column.type === 'tl'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XListColumn v-else-if="column.type === 'list'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XListColumn v-else-if="column.type === 'list'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XChannelColumn v-else-if="column.type === 'channel'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XChannelColumn v-else-if="column.type === 'channel'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XAntennaColumn v-else-if="column.type === 'antenna'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XAntennaColumn v-else-if="column.type === 'antenna'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XMentionsColumn v-else-if="column.type === 'mentions'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XMentionsColumn v-else-if="column.type === 'mentions'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XDirectColumn v-else-if="column.type === 'direct'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XDirectColumn v-else-if="column.type === 'direct'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
<XRoleTimelineColumn v-else-if="column.type === 'roleTimeline'" :column="column" :is-stacked="isStacked" @parent-focus="emit('parent-focus', $event)"/>
|
<XRoleTimelineColumn v-else-if="column.type === 'roleTimeline'" :column="column" :isStacked="isStacked" @parentFocus="emit('parent-focus', $event)"/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header><i class="ti ti-mail" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
<template #header><i class="ti ti-mail" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
||||||
|
|
||||||
<MkNotes :pagination="pagination"/>
|
<MkNotes :pagination="pagination"/>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :menu="menu" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :menu="menu" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
<i class="ti ti-list"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn v-if="deckStore.state.alwaysShowMainColumn || mainRouter.currentRoute.value.name !== 'index'" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn v-if="deckStore.state.alwaysShowMainColumn || mainRouter.currentRoute.value.name !== 'index'" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<template v-if="pageMetadata?.value">
|
<template v-if="pageMetadata?.value">
|
||||||
<i :class="pageMetadata?.value.icon"></i>
|
<i :class="pageMetadata?.value.icon"></i>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header><i class="ti ti-at" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
<template #header><i class="ti ti-at" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
||||||
|
|
||||||
<MkNotes :pagination="pagination"/>
|
<MkNotes :pagination="pagination"/>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :column="column" :is-stacked="isStacked" :menu="menu" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :column="column" :isStacked="isStacked" :menu="menu" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header><i class="ti ti-bell" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
<template #header><i class="ti ti-bell" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
||||||
|
|
||||||
<XNotifications :include-types="column.includingTypes"/>
|
<XNotifications :includeTypes="column.includingTypes"/>
|
||||||
</XColumn>
|
</XColumn>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :menu="menu" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :menu="menu" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
<i class="ti ti-badge"></i><span style="margin-left: 8px;">{{ column.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
|
@ -35,7 +35,7 @@ onMounted(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
async function setRole() {
|
async function setRole() {
|
||||||
const roles = await os.api('roles/list');
|
const roles = (await os.api('roles/list')).filter(x => x.isExplorable);
|
||||||
const { canceled, result: role } = await os.select({
|
const { canceled, result: role } = await os.select({
|
||||||
title: i18n.ts.role,
|
title: i18n.ts.role,
|
||||||
items: roles.map(x => ({
|
items: roles.map(x => ({
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :menu="menu" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :menu="menu" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header>
|
<template #header>
|
||||||
<i v-if="column.tl === 'home'" class="ti ti-home"></i>
|
<i v-if="column.tl === 'home'" class="ti ti-home"></i>
|
||||||
<i v-else-if="column.tl === 'local'" class="ti ti-planet"></i>
|
<i v-else-if="column.tl === 'local'" class="ti ti-planet"></i>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<XColumn :menu="menu" :naked="true" :column="column" :is-stacked="isStacked" @parent-focus="$event => emit('parent-focus', $event)">
|
<XColumn :menu="menu" :naked="true" :column="column" :isStacked="isStacked" @parentFocus="$event => emit('parent-focus', $event)">
|
||||||
<template #header><i class="ti ti-apps" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
<template #header><i class="ti ti-apps" style="margin-right: 8px;"></i>{{ column.name }}</template>
|
||||||
|
|
||||||
<div :class="$style.root">
|
<div :class="$style.root">
|
||||||
<div v-if="!(column.widgets && column.widgets.length > 0) && !edit" :class="$style.intro">{{ i18n.ts._deck.widgetsIntroduction }}</div>
|
<div v-if="!(column.widgets && column.widgets.length > 0) && !edit" :class="$style.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"/>
|
<XWidgets :edit="edit" :widgets="column.widgets ?? []" @addWidget="addWidget" @removeWidget="removeWidget" @updateWidget="updateWidget" @updateWidgets="updateWidgets" @exit="edit = false"/>
|
||||||
</div>
|
</div>
|
||||||
</XColumn>
|
</XColumn>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
</MkStickyContainer>
|
</MkStickyContainer>
|
||||||
|
|
||||||
<div v-if="isDesktop" ref="widgetsEl" :class="$style.widgets">
|
<div v-if="isDesktop" ref="widgetsEl" :class="$style.widgets">
|
||||||
<XWidgets :margin-top="'var(--margin)'" @mounted="attachSticky"/>
|
<XWidgets :marginTop="'var(--margin)'" @mounted="attachSticky"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button v-if="!isDesktop && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button>
|
<button v-if="!isDesktop && !isMobile" :class="$style.widgetButton" class="_button" @click="widgetsShowing = true"><i class="ti ti-apps"></i></button>
|
||||||
|
@ -27,10 +27,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawerBg_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="drawerMenuShowing"
|
v-if="drawerMenuShowing"
|
||||||
|
@ -42,10 +42,10 @@
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_menuDrawer_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_menuDrawer_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div v-if="drawerMenuShowing" :class="$style.menuDrawer">
|
<div v-if="drawerMenuShowing" :class="$style.menuDrawer">
|
||||||
<XDrawerMenu/>
|
<XDrawerMenu/>
|
||||||
|
@ -53,10 +53,10 @@
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawerBg_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="widgetsShowing"
|
v-if="widgetsShowing"
|
||||||
|
@ -68,10 +68,10 @@
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
||||||
<Transition
|
<Transition
|
||||||
:enter-active-class="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''"
|
:enterActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterActive : ''"
|
||||||
:leave-active-class="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''"
|
:leaveActiveClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveActive : ''"
|
||||||
:enter-from-class="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''"
|
:enterFromClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_enterFrom : ''"
|
||||||
:leave-to-class="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''"
|
:leaveToClass="defaultStore.state.animation ? $style.transition_widgetsDrawer_leaveTo : ''"
|
||||||
>
|
>
|
||||||
<div v-if="widgetsShowing" :class="$style.widgetsDrawer">
|
<div v-if="widgetsShowing" :class="$style.widgetsDrawer">
|
||||||
<button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button>
|
<button class="_button" :class="$style.widgetsCloseButton" @click="widgetsShowing = false"><i class="ti ti-x"></i></button>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="$style.root" :style="{ paddingTop: marginTop }">
|
<div :class="$style.root" :style="{ paddingTop: marginTop }">
|
||||||
<XWidgets :class="$style.widgets" :edit="editMode" :widgets="widgets" @add-widget="addWidget" @remove-widget="removeWidget" @update-widget="updateWidget" @update-widgets="updateWidgets" @exit="editMode = false"/>
|
<XWidgets :class="$style.widgets" :edit="editMode" :widgets="widgets" @addWidget="addWidget" @removeWidget="removeWidget" @updateWidget="updateWidget" @updateWidgets="updateWidgets" @exit="editMode = false"/>
|
||||||
|
|
||||||
<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
|
<button v-if="editMode" class="_textButton" style="font-size: 0.9em;" @click="editMode = false"><i class="ti ti-check"></i> {{ i18n.ts.editWidgetsExit }}</button>
|
||||||
<button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
|
<button v-else class="_textButton" data-cy-widget-edit :class="$style.edit" style="font-size: 0.9em;" @click="editMode = true"><i class="ti ti-pencil"></i> {{ i18n.ts.editWidgets }}</button>
|
||||||
|
|
|
@ -12,10 +12,10 @@
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<div v-if="!root" class="header">
|
<div v-if="!root" class="header">
|
||||||
<div v-if="narrow === false" class="wide">
|
<div v-if="narrow === false" class="wide">
|
||||||
<MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i> {{ i18n.ts.home }}</MkA>
|
<MkA to="/" class="link" activeClass="active"><i class="ti ti-home icon"></i> {{ i18n.ts.home }}</MkA>
|
||||||
<MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i> {{ i18n.ts.timeline }}</MkA>
|
<MkA v-if="isTimelineAvailable" to="/timeline" class="link" activeClass="active"><i class="ti ti-message icon"></i> {{ i18n.ts.timeline }}</MkA>
|
||||||
<MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i> {{ i18n.ts.explore }}</MkA>
|
<MkA to="/explore" class="link" activeClass="active"><i class="ti ti-hash icon"></i> {{ i18n.ts.explore }}</MkA>
|
||||||
<MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i> {{ i18n.ts.channel }}</MkA>
|
<MkA to="/channels" class="link" activeClass="active"><i class="ti ti-device-tv icon"></i> {{ i18n.ts.channel }}</MkA>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="narrow === true" class="narrow">
|
<div v-else-if="narrow === true" class="narrow">
|
||||||
<button class="menu _button" @click="showMenu = true">
|
<button class="menu _button" @click="showMenu = true">
|
||||||
|
@ -44,15 +44,15 @@
|
||||||
|
|
||||||
<Transition :name="'tray'">
|
<Transition :name="'tray'">
|
||||||
<div v-if="showMenu" class="menu">
|
<div v-if="showMenu" class="menu">
|
||||||
<MkA to="/" class="link" active-class="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
|
<MkA to="/" class="link" activeClass="active"><i class="ti ti-home icon"></i>{{ i18n.ts.home }}</MkA>
|
||||||
<MkA v-if="isTimelineAvailable" to="/timeline" class="link" active-class="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
|
<MkA v-if="isTimelineAvailable" to="/timeline" class="link" activeClass="active"><i class="ti ti-message icon"></i>{{ i18n.ts.timeline }}</MkA>
|
||||||
<MkA to="/explore" class="link" active-class="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
|
<MkA to="/explore" class="link" activeClass="active"><i class="ti ti-hash icon"></i>{{ i18n.ts.explore }}</MkA>
|
||||||
<MkA to="/announcements" class="link" active-class="active"><i class="ti ti-speakerphone icon"></i>{{ i18n.ts.announcements }}</MkA>
|
<MkA to="/announcements" class="link" activeClass="active"><i class="ti ti-speakerphone icon"></i>{{ i18n.ts.announcements }}</MkA>
|
||||||
<MkA to="/channels" class="link" active-class="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
|
<MkA to="/channels" class="link" activeClass="active"><i class="ti ti-device-tv icon"></i>{{ i18n.ts.channel }}</MkA>
|
||||||
<div class="divider"></div>
|
<div class="divider"></div>
|
||||||
<MkA to="/pages" class="link" active-class="active"><i class="ti ti-news icon"></i>{{ i18n.ts.pages }}</MkA>
|
<MkA to="/pages" class="link" activeClass="active"><i class="ti ti-news icon"></i>{{ i18n.ts.pages }}</MkA>
|
||||||
<MkA to="/play" class="link" active-class="active"><i class="ti ti-player-play icon"></i>Play</MkA>
|
<MkA to="/play" class="link" activeClass="active"><i class="ti ti-player-play icon"></i>Play</MkA>
|
||||||
<MkA to="/gallery" class="link" active-class="active"><i class="ti ti-icons icon"></i>{{ i18n.ts.gallery }}</MkA>
|
<MkA to="/gallery" class="link" activeClass="active"><i class="ti ti-icons icon"></i>{{ i18n.ts.gallery }}</MkA>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
|
<button class="_buttonPrimary" @click="signup()">{{ i18n.ts.signup }}</button>
|
||||||
<button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
|
<button class="_button" @click="signin()">{{ i18n.ts.login }}</button>
|
||||||
|
|
Loading…
Reference in a new issue