Merge branch 'develop'
This commit is contained in:
@ -242,7 +242,7 @@ export default Vue.extend({
|
||||
|
||||
> div:nth-child(1)
|
||||
> .thumbnail
|
||||
display block
|
||||
display flex
|
||||
width 64px
|
||||
height 64px
|
||||
background-size cover
|
||||
|
@ -195,7 +195,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$root.getMeta().then(meta => {
|
||||
this.$root.getMeta(true).then(meta => {
|
||||
this.maintainerName = meta.maintainerName;
|
||||
this.maintainerEmail = meta.maintainerEmail;
|
||||
this.disableRegistration = meta.disableRegistration;
|
||||
|
@ -134,7 +134,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
reactDirectly(reaction) {
|
||||
(this.$root.api('notes/reactions/create', {
|
||||
this.$root.api('notes/reactions/create', {
|
||||
noteId: this.appearNote.id,
|
||||
reaction: reaction
|
||||
});
|
||||
|
@ -85,7 +85,10 @@ export default Vue.extend({
|
||||
}
|
||||
} else {
|
||||
if (items[0].kind == 'file') {
|
||||
alert(this.$t('only-one-file-attached'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('only-one-file-attached')
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -107,7 +110,10 @@ export default Vue.extend({
|
||||
return;
|
||||
} else if (e.dataTransfer.files.length > 1) {
|
||||
e.preventDefault();
|
||||
alert(this.$t('only-one-file-attached'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('only-one-file-attached')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,10 @@ export default Vue.extend({
|
||||
this.form.upload(e.dataTransfer.files[0]);
|
||||
return;
|
||||
} else if (e.dataTransfer.files.length > 1) {
|
||||
alert(this.$t('only-one-file-attached'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('only-one-file-attached')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="skeikyzd" v-show="files.length != 0">
|
||||
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
|
||||
<x-draggable class="files" :list="files" animation="150">
|
||||
<div v-for="file in files" :key="file.id" @click="showFileMenu(file, $event)" @contextmenu.prevent="showFileMenu(file, $event)">
|
||||
<x-file-thumbnail :data-id="file.id" class="thumbnail" :file="file" fit="cover"/>
|
||||
<img class="remove" @click.stop="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
|
||||
|
@ -84,7 +84,7 @@
|
||||
<ui-info v-else warn>{{ $t('email-not-verified') }}</ui-info>
|
||||
</template>
|
||||
<ui-input v-model="email" type="email"><span>{{ $t('email-address') }}</span></ui-input>
|
||||
<ui-button @click="updateEmail()"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
<ui-button @click="updateEmail()" :disabled="email === $store.state.i.email"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
@ -542,8 +542,8 @@ export default Vue.extend({
|
||||
this.latestVersion = newer;
|
||||
if (newer == null) {
|
||||
this.$root.dialog({
|
||||
title: this.$t('no-updates'),
|
||||
text: this.$t('no-updates-desc')
|
||||
title: this.$t('@._settings.no-updates'),
|
||||
text: this.$t('@._settings.no-updates-desc')
|
||||
});
|
||||
} else {
|
||||
this.$root.dialog({
|
||||
|
@ -79,7 +79,10 @@ export default Vue.extend({
|
||||
localStorage.setItem('i', res.i);
|
||||
location.reload();
|
||||
}).catch(() => {
|
||||
alert(this.$t('login-failed'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('login-failed')
|
||||
});
|
||||
this.signing = false;
|
||||
});
|
||||
}
|
||||
|
@ -153,7 +153,10 @@ export default Vue.extend({
|
||||
location.href = '/';
|
||||
});
|
||||
}).catch(() => {
|
||||
alert(this.$t('some-error'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('some-error')
|
||||
});
|
||||
|
||||
if (this.meta.enableRecaptcha) {
|
||||
(window as any).grecaptcha.reset();
|
||||
|
@ -21,7 +21,7 @@
|
||||
<option value="users">{{ $t('@.widgets.users') }}</option>
|
||||
<option value="polls">{{ $t('@.widgets.polls') }}</option>
|
||||
<option value="post-form">{{ $t('@.widgets.post-form') }}</option>
|
||||
<option value="messaging">{{ $t('@.widgets.messaging') }}</option>
|
||||
<option value="messaging">{{ $t('@.messaging') }}</option>
|
||||
<option value="memo">{{ $t('@.widgets.memo') }}</option>
|
||||
<option value="hashtags">{{ $t('@.widgets.hashtags') }}</option>
|
||||
<option value="posts-monitor">{{ $t('@.widgets.posts-monitor') }}</option>
|
||||
@ -33,7 +33,7 @@
|
||||
</header>
|
||||
<x-draggable
|
||||
:list="column.widgets"
|
||||
:options="{ animation: 150 }"
|
||||
animation="150"
|
||||
@sort="onWidgetSort"
|
||||
>
|
||||
<div v-for="widget in column.widgets" class="customize-container" :key="widget.id" @contextmenu.stop.prevent="widgetFunc(widget.id)">
|
||||
|
@ -188,7 +188,10 @@ export default define({
|
||||
}).then(data => {
|
||||
this.clear();
|
||||
}).catch(err => {
|
||||
alert('Something happened');
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('something-happened')
|
||||
});
|
||||
}).then(() => {
|
||||
this.posting = false;
|
||||
this.$nextTick(() => {
|
||||
|
@ -161,7 +161,10 @@ export default Vue.extend({
|
||||
});
|
||||
break;
|
||||
default:
|
||||
alert(this.$t('unhandled-error'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('unhandled-error')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -320,7 +320,10 @@ export default Vue.extend({
|
||||
});
|
||||
break;
|
||||
default:
|
||||
alert(this.$t('unhandled-error'));
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('unhandled-error')
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -364,7 +364,10 @@ export default Vue.extend({
|
||||
|
||||
setGeo() {
|
||||
if (navigator.geolocation == null) {
|
||||
alert(this.$t('geolocation-alert'));
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('geolocation-alert')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -372,7 +375,11 @@ export default Vue.extend({
|
||||
this.geo = pos.coords;
|
||||
this.$emit('geo-attached', this.geo);
|
||||
}, err => {
|
||||
alert(`%i18n:@error%: ${err.message}`);
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('error')
|
||||
text: err.message
|
||||
});
|
||||
}, {
|
||||
enableHighAccuracy: true
|
||||
});
|
||||
|
@ -34,7 +34,7 @@
|
||||
<button @click="addWidget">{{ $t('add') }}</button>
|
||||
</div>
|
||||
<div class="trash">
|
||||
<x-draggable v-model="trash" :options="{ group: 'x' }" @add="onTrash"></x-draggable>
|
||||
<x-draggable v-model="trash" group="x" @add="onTrash"></x-draggable>
|
||||
<p>{{ $t('@.trash') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -45,7 +45,8 @@
|
||||
:list="widgets[place]"
|
||||
:class="place"
|
||||
:data-place="place"
|
||||
:options="{ group: 'x', animation: 150 }"
|
||||
group="x"
|
||||
animation="150"
|
||||
@sort="onWidgetSort"
|
||||
:key="place"
|
||||
>
|
||||
|
@ -34,7 +34,7 @@ export default Vue.extend({
|
||||
document.title = title;
|
||||
},
|
||||
onOpenFolder(folder) {
|
||||
const title = folder.name + ' | %i18n:@title%';
|
||||
const title = `${folder.name} | ${this.$t('title')}`;
|
||||
|
||||
// Rewrite URL
|
||||
history.pushState(null, title, `/i/drive/folder/${folder.id}`);
|
||||
|
@ -4,7 +4,7 @@ import { EventEmitter } from 'eventemitter3';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
import initStore from './store';
|
||||
import { apiUrl, version } from './config';
|
||||
import { apiUrl, version, locale } from './config';
|
||||
import Progress from './common/scripts/loading';
|
||||
|
||||
import Err from './common/views/components/connect-failed.vue';
|
||||
@ -281,7 +281,7 @@ export default class MiOS extends EventEmitter {
|
||||
// トークンが再生成されたとき
|
||||
// このままではMisskeyが利用できないので強制的にサインアウトさせる
|
||||
main.on('myTokenRegenerated', () => {
|
||||
alert('%i18n:common.my-token-regenerated%');
|
||||
alert(locale['common']['my-token-regenerated'])
|
||||
this.signout();
|
||||
});
|
||||
}
|
||||
|
@ -136,7 +136,10 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
showCreatedAt() {
|
||||
alert(new Date(this.file.createdAt).toLocaleString());
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: (new Date(this.file.createdAt)).toLocaleString()
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -150,11 +153,13 @@ export default Vue.extend({
|
||||
|
||||
> .preview
|
||||
width fit-content
|
||||
width -moz-fit-content
|
||||
max-width 100%
|
||||
margin 0 auto
|
||||
box-shadow 1px 1px 4px rgba(#000, 0.2)
|
||||
overflow hidden
|
||||
color var(--driveFileIcon)
|
||||
justify-content center
|
||||
|
||||
> footer
|
||||
padding 8px 8px 0 8px
|
||||
|
@ -283,14 +283,21 @@ export default Vue.extend({
|
||||
|
||||
setGeo() {
|
||||
if (navigator.geolocation == null) {
|
||||
alert(this.$t('location-alert'));
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('geolocation-alert')
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.geolocation.getCurrentPosition(pos => {
|
||||
this.geo = pos.coords;
|
||||
}, err => {
|
||||
alert(`%i18n:@error%: ${err.message}`);
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('error')
|
||||
text: err.message
|
||||
});
|
||||
}, {
|
||||
enableHighAccuracy: true
|
||||
});
|
||||
|
@ -32,7 +32,7 @@ export default Vue.extend({
|
||||
this.fetch();
|
||||
},
|
||||
mounted() {
|
||||
document.title = `${this.$root.instanceName} | %i18n:@notifications%`;
|
||||
document.title = `${this.$root.instanceName} | ${this.$t('@.favorites')}`;
|
||||
},
|
||||
methods: {
|
||||
fetch() {
|
||||
|
@ -15,7 +15,7 @@ export default Vue.extend({
|
||||
XReversi: () => import('../../../../common/views/components/games/reversi/reversi.vue').then(m => m.default)
|
||||
},
|
||||
mounted() {
|
||||
document.title = `${this.$root.instanceName} %i18n:@reversi%`;
|
||||
document.title = `${this.$root.instanceName} | ${this.$t('reversi')}`;
|
||||
},
|
||||
methods: {
|
||||
nav(game, actualNav) {
|
||||
|
@ -50,7 +50,7 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.title = `%i18n:@search%: ${this.q} | ${this.$root.instanceName}`;
|
||||
document.title = `${this.$t('search')}: ${this.q} | ${this.$root.instanceName}`;
|
||||
},
|
||||
methods: {
|
||||
inited() {
|
||||
|
@ -29,7 +29,8 @@
|
||||
</header>
|
||||
<x-draggable
|
||||
:list="widgets"
|
||||
:options="{ handle: '.handle', animation: 150 }"
|
||||
handle=".handle"
|
||||
animation="150"
|
||||
@sort="onWidgetSort"
|
||||
>
|
||||
<div v-for="widget in widgets" class="customize-container" :key="widget.id">
|
||||
@ -106,7 +107,10 @@ export default Vue.extend({
|
||||
|
||||
methods: {
|
||||
hint() {
|
||||
alert(this.$t('widgets-hints'));
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: this.$t('widgets-hints')
|
||||
});
|
||||
},
|
||||
|
||||
widgetFunc(id) {
|
||||
|
@ -1,4 +1,4 @@
|
||||
@charset 'utf-8'
|
||||
@charset "utf-8"
|
||||
|
||||
/*
|
||||
::selection
|
||||
|
@ -130,10 +130,12 @@ Misskeyのストリームに接続しただけでは、まだリアルタイム
|
||||
```json
|
||||
{
|
||||
type: 'api',
|
||||
id: 'xxxxxxxxxxxxxxxx',
|
||||
endpoint: 'notes/create',
|
||||
data: {
|
||||
text: 'yee haw!'
|
||||
body: {
|
||||
id: 'xxxxxxxxxxxxxxxx',
|
||||
endpoint: 'notes/create',
|
||||
data: {
|
||||
text: 'yee haw!'
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -1,14 +1,21 @@
|
||||
import { Meta } from '../models/entities/meta';
|
||||
import { Metas } from '../models';
|
||||
import { genId } from './gen-id';
|
||||
import { getConnection } from 'typeorm';
|
||||
|
||||
export default async function(): Promise<Meta> {
|
||||
const meta = await Metas.findOne();
|
||||
if (meta) {
|
||||
return meta;
|
||||
} else {
|
||||
return Metas.save({
|
||||
id: genId(),
|
||||
} as Meta);
|
||||
}
|
||||
return await getConnection().transaction(async transactionalEntityManager => {
|
||||
// バグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
|
||||
const meta = await transactionalEntityManager.findOne(Meta, {
|
||||
order: {
|
||||
id: 'DESC'
|
||||
}
|
||||
});
|
||||
|
||||
if (meta) {
|
||||
return meta;
|
||||
} else {
|
||||
return await transactionalEntityManager.save(Meta, {
|
||||
id: 'x'
|
||||
}) as Meta;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,9 +1,11 @@
|
||||
import { Entity, Column, PrimaryColumn } from 'typeorm';
|
||||
import { id } from '../id';
|
||||
|
||||
@Entity()
|
||||
export class Meta {
|
||||
@PrimaryColumn(id())
|
||||
@PrimaryColumn({
|
||||
type: 'varchar',
|
||||
length: 32
|
||||
})
|
||||
public id: string;
|
||||
|
||||
@Column('varchar', {
|
||||
|
@ -93,12 +93,12 @@ export class Note {
|
||||
})
|
||||
public localOnly: boolean;
|
||||
|
||||
@Column('integer', {
|
||||
@Column('smallint', {
|
||||
default: 0
|
||||
})
|
||||
public renoteCount: number;
|
||||
|
||||
@Column('integer', {
|
||||
@Column('smallint', {
|
||||
default: 0
|
||||
})
|
||||
public repliesCount: number;
|
||||
@ -129,12 +129,14 @@ export class Note {
|
||||
})
|
||||
public score: number;
|
||||
|
||||
@Index()
|
||||
@Column({
|
||||
...id(),
|
||||
array: true, default: '{}'
|
||||
})
|
||||
public fileIds: DriveFile['id'][];
|
||||
|
||||
@Index()
|
||||
@Column('varchar', {
|
||||
length: 256, array: true, default: '{}'
|
||||
})
|
||||
|
@ -103,7 +103,7 @@ export class NoteRepository extends Repository<Note> {
|
||||
const host = note.userHost;
|
||||
|
||||
async function populatePoll() {
|
||||
const poll = await Polls.findOne({ noteId: note.id }).then(ensure);
|
||||
const poll = await Polls.findOne(note.id).then(ensure);
|
||||
const choices = poll.choices.map(c => ({
|
||||
text: c,
|
||||
votes: poll.votes[poll.choices.indexOf(c)],
|
||||
|
@ -84,6 +84,8 @@ export class UserRepository extends Repository<User> {
|
||||
const pins = opts.detail ? await UserNotePinings.find({ userId: user.id }) : [];
|
||||
const profile = opts.detail ? await UserProfiles.findOne({ userId: user.id }).then(ensure) : null;
|
||||
|
||||
const falsy = opts.detail ? false : undefined;
|
||||
|
||||
return await rap({
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
@ -91,10 +93,10 @@ export class UserRepository extends Repository<User> {
|
||||
host: user.host,
|
||||
avatarUrl: user.avatarUrl ? user.avatarUrl : config.url + '/avatar/' + user.id,
|
||||
avatarColor: user.avatarColor,
|
||||
isAdmin: user.isAdmin || undefined,
|
||||
isBot: user.isBot || undefined,
|
||||
isCat: user.isCat || undefined,
|
||||
isVerified: user.isVerified || undefined,
|
||||
isAdmin: user.isAdmin || falsy,
|
||||
isBot: user.isBot || falsy,
|
||||
isCat: user.isCat || falsy,
|
||||
isVerified: user.isVerified || falsy,
|
||||
|
||||
// カスタム絵文字添付
|
||||
emojis: user.emojis.length > 0 ? Emojis.find({
|
||||
@ -123,7 +125,7 @@ export class UserRepository extends Repository<User> {
|
||||
bannerUrl: user.bannerUrl,
|
||||
bannerColor: user.bannerColor,
|
||||
isLocked: user.isLocked,
|
||||
isModerator: user.isModerator || undefined,
|
||||
isModerator: user.isModerator || falsy,
|
||||
description: profile!.description,
|
||||
location: profile!.location,
|
||||
birthday: profile!.birthday,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import { Metas } from '../../../../models';
|
||||
import { getConnection } from 'typeorm';
|
||||
import { Meta } from '../../../../models/entities/meta';
|
||||
|
||||
export const meta = {
|
||||
@ -505,11 +505,17 @@ export default define(meta, async (ps) => {
|
||||
set.swPrivateKey = ps.swPrivateKey;
|
||||
}
|
||||
|
||||
const meta = await Metas.findOne();
|
||||
await getConnection().transaction(async transactionalEntityManager => {
|
||||
const meta = await transactionalEntityManager.findOne(Meta, {
|
||||
order: {
|
||||
id: 'DESC'
|
||||
}
|
||||
});
|
||||
|
||||
if (meta) {
|
||||
await Metas.update(meta.id, set);
|
||||
} else {
|
||||
await Metas.save(set);
|
||||
}
|
||||
if (meta) {
|
||||
await transactionalEntityManager.update(Meta, meta.id, set);
|
||||
} else {
|
||||
await transactionalEntityManager.save(Meta, set);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -79,7 +79,7 @@ export default abstract class Chart<T extends Record<string, any>> {
|
||||
flatColumns(v.properties, p);
|
||||
} else {
|
||||
columns[this.columnPrefix + p] = {
|
||||
type: 'integer',
|
||||
type: 'bigint',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import * as Minio from 'minio';
|
||||
import config from '../../config';
|
||||
import { DriveFile } from '../../models/entities/drive-file';
|
||||
import { InternalStorage } from './internal-storage';
|
||||
import { DriveFiles, Instances } from '../../models';
|
||||
import { DriveFiles, Instances, Notes } from '../../models';
|
||||
import { driveChart, perUserDriveChart, instanceChart } from '../chart';
|
||||
|
||||
export default async function(file: DriveFile, isExpired = false) {
|
||||
@ -40,6 +40,11 @@ export default async function(file: DriveFile, isExpired = false) {
|
||||
});
|
||||
} else {
|
||||
DriveFiles.delete(file.id);
|
||||
|
||||
// TODO: トランザクション
|
||||
Notes.createQueryBuilder().delete()
|
||||
.where(':id = ANY(fileIds)', { id: file.id })
|
||||
.execute();
|
||||
}
|
||||
|
||||
// 統計を更新
|
||||
|
Reference in New Issue
Block a user