Compare commits

...

14 Commits

Author SHA1 Message Date
b587fefe44 12.7.0 2020-02-10 23:45:31 +09:00
2511114c28 なんかもうめっちゃ変えた
Resolve #5846
2020-02-10 23:17:42 +09:00
9cd267fee5 🎨 2020-02-10 21:41:35 +09:00
aeac96a4f7 New Crowdin translations (#5901)
* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)
2020-02-10 21:40:30 +09:00
88a75e2a99 🎨 2020-02-10 21:37:37 +09:00
e14760775b 🎨 2020-02-10 21:30:59 +09:00
9e7e464ba5 Clean up 2020-02-10 21:28:29 +09:00
060d4fd27f 🎨 2020-02-10 21:27:17 +09:00
940578d062 🎨 2020-02-10 21:16:04 +09:00
9cf42d8b33 Better self link detection 2020-02-10 20:51:17 +09:00
1d62d2924e 🎨 2020-02-10 20:47:02 +09:00
e23bac47ba 🎨 2020-02-10 20:44:59 +09:00
b5d38adfcc 🎨 2020-02-10 20:32:57 +09:00
f30513b20b Fix #5902 2020-02-10 20:29:44 +09:00
36 changed files with 319 additions and 278 deletions

View File

@ -1,6 +1,15 @@
ChangeLog
=========
12.7.0 (2020/02/10)
--------------------
### ✨Improvements
* ノートの文字数制限の設定を復活
* デザインの調整
### 🐛Fixes
* 中国語で表示できない問題を修正
12.6.0 (2020/02/10)
--------------------
### ✨Improvements

View File

@ -25,7 +25,7 @@ const languages = [
'ko-KR',
//'nl-NL',
//'pl-PL',
//'zh-CN',
'zh-CN',
//'zh-TW',
];

View File

@ -376,6 +376,7 @@ next: "次"
retype: "再入力"
noteOf: "{user}のノート"
inviteToGroup: "グループに招待"
maxNoteTextLength: "ノートの文字数制限"
_tutorial:
title: "Misskeyの使い方"

View File

@ -65,8 +65,14 @@ import: "导入"
export: "导出"
files: "文件"
download: "下载"
driveFileDeleteConfirm: "要删除「{name}」文件吗?附加此文件的帖子也会消失。"
unfollowConfirm: "要取消对{name}的关注吗?"
exportRequested: "导出请求已提交。可能需要花一些时间。导出的文件将保存到网盘中。"
importRequested: "导入请求已提交。这可能需要花一点时间。"
lists: "列表"
noLists: "列表为空"
note: "帖子"
notes: "帖子"
following: "关注中"
followers: "关注者"
followsYou: "关注了你"
@ -87,15 +93,18 @@ enterEmoji: "输入Emoji"
renote: "转发"
unrenote: "取消转发"
quote: "引用"
pinnedNote: "已置顶的帖子"
you: "您"
clickToShow: "点击以显示"
sensitive: "阅读注意"
add: "添加"
reaction: "反应"
reactionSettingDescription: "快速选择回应中的自定义表情符号,以换行符分隔。"
rememberNoteVisibility: "记录公开范围"
renameFile: "重命名文件"
attachCancel: "删除附件"
markAsSensitive: "阅读注意"
unmarkAsSensitive: "取消标记为敏感内容"
enterFileName: "请输入文件名"
mute: "屏蔽"
unmute: "解除屏蔽"
@ -113,14 +122,20 @@ emojiName: "Emoji 名称"
emojiUrl: "emoji 地址"
addEmoji: "添加Emoji"
cacheRemoteFiles: "远程文件缓存"
cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程实例载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。"
flagAsBot: "这个账户是Bot"
flagAsCat: "这个账户是Cat"
autoAcceptFollowed: "自动允许关注"
addAcount: "添加账户"
loginFailed: "登录失败"
showOnRemote: "转到所在实例显示"
general: "常规设置"
wallpaper: "壁纸"
removeWallpaper: "移除壁纸"
searchWith: "搜索:{q}"
youHaveNoLists: "列表为空"
followConfirm: "你确定要关注{name}吗?"
proxyAccount: "代理账户"
host: "主机名"
selectUser: "选择用户"
recipient: "收件人"
@ -130,36 +145,51 @@ instances: "实例"
latestRequestSentAt: "上次发送的请求"
latestRequestReceivedAt: "上次收到的请求"
storageUsage: "已用存储"
charts: "图表"
perHour: "每小时"
perDay: "每天"
operations: "操作"
software: "软件"
version: "版本"
metadata: "元数据"
withNFiles: "{n}个文件"
monitor: "监视器"
jobQueue: "作业队列"
cpuAndMemory: "CPU使用量"
network: "网络"
disk: "存储"
instanceInfo: "实例情报"
statistics: "统计"
clearQueue: "清除队列"
clearQueueConfirmTitle: "确定清除队列?"
clearCachedFiles: "清除缓存"
clearCachedFilesConfirm: "确定要清除缓存文件?"
blockedInstances: "被阻拦的实例"
blockedInstancesDescription: "设定要阻拦的实例,以换行来进行分割。被阻拦的实例将无法与本实例进行交换通讯。"
muteAndBlock: "屏蔽/拉黑"
mutedUsers: "禁言用户"
blockedUsers: "已屏蔽用户"
noUsers: "无用户"
editProfile: "编辑资料"
noteDeleteConfirm: "要删除该帖子吗?"
pinLimitExceeded: "无法置顶更多了"
intro: "Misskey的部署结束啦填写管理员账号吧"
done: "完成"
processing: "处理中"
preview: "预览"
noCustomEmojis: "无自定义Emoji"
customEmojisOfRemote: "远程Emoji"
noJobs: "没有任务"
federating: "联合中"
blocked: "已拦截"
suspended: "停止推流"
all: "全部"
subscribing: "已订阅"
publishing: "直播中"
notResponding: "没有响应"
instanceFollowing: "关注实例"
instanceFollowers: "关注实例"
instanceUsers: "实例用户"
changePassword: "修改密码"
security: "安全性"
retypedNotMatch: "两次输入不一致!"
@ -169,22 +199,31 @@ newPasswordRetype: "重新输入密码:"
attachFile: "插入附件"
more: "更多!"
featured: "高亮"
usernameOrUserId: "用户名或用户ID"
noSuchUser: "用户不存在"
lookup: "查询"
announcements: "公告"
imageUrl: "图片URL"
remove: "删除"
removed: "已删除"
removeAreYouSure: "要删掉「{x}」吗?"
saved: "已保存"
messaging: "聊天"
upload: "上传"
fromDrive: "从网盘中"
fromUrl: "从 URL"
explore: "发现"
games: "Misskey游戏"
messageRead: "已读"
recentUsedEmojis: "最近使用的Emoji表情"
noMoreHistory: "没有更多的历史记录"
startMessaging: "开始聊天"
nUsersRead: "{n}人已读"
agreeTo: "{0}人同意"
tos: "服务条款"
start: "开始"
home: "首页"
remoteUserCaution: "由于是远程用户,信息不完整。"
activity: "活动"
images: "图片"
birthday: "生日"
@ -229,6 +268,7 @@ connectSerice: "已连接"
disconnectSerice: "断开连接"
enableLocalTimeline: "启用本地时间线功能"
enableGlobalTimeline: "启用全局时间线"
disablingTimelinesInfo: "即使时间线功能被禁用,出于便利性的原因,管理员和数据图表也可以继续使用。"
registration: "注册"
enableRegistration: "允许新用户注册"
invite: "邀请"
@ -241,11 +281,13 @@ iconUrl: "图标URL"
bannerUrl: "Banner URL"
basicInfo: "基本信息"
pinnedUsers: "置顶用户"
pinnedUsersDescription: "在「发现」页面中使用换行标记想要置顶的用户。"
recaptcha: "reCAPTCHA"
enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)"
recaptchaSiteKey: "网站密钥"
recaptchaSecretKey: "reCAPTCHA 密钥"
name: "名称"
antennaKeywordsDescription: "使用空格分隔会产生AND规范并且使用换行符分隔会产生OR规范"
serviceworker: "ServiceWorker"
enableServiceworker: "启用ServiceWorker"
caseSensitive: "区分大小写"
@ -302,6 +344,14 @@ invites: "邀请"
groupName: "群组名"
members: "成员"
transfer: "转让"
enable: "启用"
next: "下一个"
retype: "重新输入"
noteOf: "{user}的帖子"
inviteToGroup: "群组邀请"
_tutorial:
title: "Misskey的使用方法"
step1_1: "欢迎!"
_2fa:
alreadyRegistered: "此设备已被注册"
registerDevice: "注册设备"
@ -384,7 +434,12 @@ _visibility:
_profile:
name: "名称"
username: "用户名"
description: "个人简介"
metadata: "额外信息"
metadataLabel: "标签"
metadataContent: "内容"
_exportOrImport:
allNotes: "所有帖子"
followingList: "关注中"
muteList: "屏蔽"
blockingList: "屏蔽"
@ -447,9 +502,13 @@ _pages:
blocks:
text: "文本"
image: "图片"
if: "如果"
_if:
variable: "变量"
post: "投稿窗口"
_post:
text: "内容"
textInput: "文本输入"
_textInput:
name: "变量名"
text: "标题"
@ -482,12 +541,30 @@ _pages:
dialog: "显示对话框"
_dialog:
content: "内容"
resetRandom: "重置随机值"
pushEvent: "发送事件"
_pushEvent:
event: "事件名称"
message: "按下时显示的消息"
variable: "发送的变量"
no-variable: "空"
radioButton: "选择项"
_radioButton:
name: "变量名"
title: "标题"
values: "使用换行区分的选择项"
default: "默认值"
script:
categories:
flow: "控制"
logical: "逻辑运算"
operation: "计算"
comparison: "比较"
random: "随机"
value: "值"
fn: "函数"
text: "文本操作"
convert: "转换"
list: "列表"
blocks:
text: "文本"
@ -638,12 +715,14 @@ _pages:
_for:
arg1: "次数"
arg2: "处理"
thereIsEmptySlot: "槽函数{slot}为空!"
types:
string: "文字"
number: "数值"
boolean: "Flag"
array: "列表"
stringArray: "文本列表"
emptySlot: "空白槽函数"
enviromentVariables: "环境变量"
pageVariables: "页面元素"
argVariables: "输入变量"

View File

@ -1,7 +1,7 @@
{
"name": "misskey",
"author": "syuilo <syuilotan@yahoo.co.jp>",
"version": "12.6.0",
"version": "12.7.0",
"codename": "indigo",
"repository": {
"type": "git",

View File

@ -838,7 +838,8 @@ export default Vue.extend({
padding: 8px 0;
> .divider {
margin: 8px 0;
margin: 8px auto;
width: calc(100% - 32px);
}
}

View File

@ -143,7 +143,7 @@ export default Vue.extend({
this.setPosition();
//#region Construct Emoji DB
const customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
const customEmojis = this.$store.state.instance.meta.emojis;
const emojiDefinitions: EmojiDef[] = [];
for (const x of customEmojis) {

View File

@ -140,7 +140,7 @@ export default Vue.extend({
},
created() {
let local = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
let local = this.$store.state.instance.meta.emojis;
local = groupByX(local, (x: any) => x.category || '');
this.customEmojis = local;
},

View File

@ -55,38 +55,35 @@ export default Vue.extend({
useOsDefaultEmojis(): boolean {
return this.$store.state.device.useOsDefaultEmojis && !this.isReaction;
},
ce() {
let ce = [];
if (this.customEmojis) ce = ce.concat(this.customEmojis);
if (this.$store.state.instance.meta && this.$store.state.instance.meta.emojis) ce = ce.concat(this.$store.state.instance.meta.emojis);
return ce;
}
},
watch: {
customEmojis() {
if (this.name) {
const customEmoji = this.customEmojis.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
ce: {
handler() {
if (this.name) {
const customEmoji = this.ce.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
}
}
}
},
immediate: true
},
},
created() {
if (this.name) {
const customEmoji = this.customEmojis.find(x => x.name == this.name);
if (customEmoji) {
this.customEmoji = customEmoji;
this.url = this.$store.state.device.disableShowingAnimatedImages
? getStaticImageUrl(customEmoji.url)
: customEmoji.url;
} else {
//const emoji = lib[this.name];
//if (emoji) {
// this.char = emoji.char;
//}
}
} else {
if (!this.name) {
this.char = this.emoji;
}

View File

@ -1,5 +1,5 @@
<template>
<component :is="hasRoute ? 'router-link' : 'a'" class="xlcxczvw _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target"
<component :is="self ? 'router-link' : 'a'" class="xlcxczvw _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
:title="url"
@ -27,18 +27,12 @@ export default Vue.extend({
}
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute = isSelf && (
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/'));
const self = this.url.startsWith(local);
return {
local,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
showTimer: null,
hideTimer: null,
preview: null,

View File

@ -1,30 +1,47 @@
<template>
<div class="yxspomdl">
<fa :icon="faSpinner" pulse fixed-width class="icon"/>
<div class="ring"></div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
data() {
return {
faSpinner
};
},
});
</script>
<style lang="scss" scoped>
@keyframes ring {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
.yxspomdl {
padding: 32px;
text-align: center;
> .icon {
font-size: 32px;
opacity: 0.5;
> .ring {
display: inline-block;
width: 80px;
height: 80px;
opacity: 0.7;
}
> .ring:after {
content: " ";
display: block;
width: 64px;
height: 64px;
margin: 8px;
border-radius: 50%;
border: solid 6px;
border-color: var(--fg) transparent transparent transparent;
animation: ring 0.5s linear infinite;
}
}
</style>

View File

@ -234,7 +234,6 @@ export default Vue.component('misskey-flavored-markdown', {
}
case 'emoji': {
const customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
return [createElement('mk-emoji', {
key: Math.random(),
attrs: {
@ -242,7 +241,7 @@ export default Vue.component('misskey-flavored-markdown', {
name: token.node.props.name
},
props: {
customEmojis: this.customEmojis || customEmojis,
customEmojis: this.customEmojis,
normal: this.plain
}
})];

View File

@ -8,7 +8,7 @@
<header>
<button class="cancel _button" @click="cancel"><fa :icon="faTimes"/></button>
<div>
<span class="text-count" :class="{ over: trimmedLength(text) > 500 }">{{ 500 - trimmedLength(text) }}</span>
<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
<button class="_button visibility" @click="setVisibility" ref="visibilityButton">
<span v-if="visibility === 'public'"><fa :icon="faGlobe"/></span>
<span v-if="visibility === 'home'"><fa :icon="faHome"/></span>
@ -172,8 +172,12 @@ export default Vue.extend({
canPost(): boolean {
return !this.posting &&
(1 <= this.text.length || 1 <= this.files.length || this.poll || this.renote) &&
(length(this.text.trim()) <= 500) &&
(length(this.text.trim()) <= this.max) &&
(!this.poll || this.pollChoices.length >= 2);
},
max(): number {
return this.$store.state.instance.meta ? this.$store.state.instance.meta.maxNoteTextLength : 1000;
}
},

View File

@ -1,5 +1,5 @@
<template>
<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :is-reaction="true" :custom-emojis="customEmojis" :normal="true" :no-style="noStyle"/>
<mk-emoji :emoji="reaction.startsWith(':') ? null : reaction" :name="reaction.startsWith(':') ? reaction.substr(1, reaction.length - 2) : null" :is-reaction="true" :normal="true" :no-style="noStyle"/>
</template>
<script lang="ts">
@ -18,15 +18,5 @@ export default Vue.extend({
default: false
},
},
data() {
return {
customEmojis: []
};
},
created() {
this.$root.getMeta().then(meta => {
if (meta && meta.emojis) this.customEmojis = meta.emojis;
});
},
});
</script>

View File

@ -82,7 +82,6 @@ export default Vue.extend({
token: '',
apiUrl,
host: toUnicode(host),
meta: null,
totpLogin: false,
credential: null,
challengeData: null,
@ -91,11 +90,13 @@ export default Vue.extend({
};
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
created() {
if (this.autoSet) {
this.$once('login', res => {
localStorage.setItem('i', res.i);

View File

@ -79,7 +79,6 @@ export default Vue.extend({
usernameState: null,
passwordStrength: '',
passwordRetypeState: null,
meta: {},
submitting: false,
ToSAgreement: false,
faLock, faExclamationTriangle, faSpinner, faCheck
@ -87,6 +86,10 @@ export default Vue.extend({
},
computed: {
meta() {
return this.$store.state.instance.meta;
},
shouldShowProfileUrl(): boolean {
return (this.username != '' &&
this.usernameState != 'invalid-format' &&
@ -95,12 +98,6 @@ export default Vue.extend({
}
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
},
mounted() {
const head = document.getElementsByTagName('head')[0];
const script = document.createElement('script');

View File

@ -124,6 +124,7 @@ export default Vue.extend({
&.primary {
color: #fff;
background: var(--accent);
box-shadow: 0 6px 16px var(--accentShadow);
&:not(:disabled):hover {
background: var(--jkhztclx);

View File

@ -10,7 +10,7 @@
</div>
<div v-else class="mk-url-preview" v-size="[{ max: 400 }, { max: 350 }]">
<transition name="zoom" mode="out-in">
<component :is="hasRoute ? 'router-link' : 'a'" :class="{ compact }" :[attr]="hasRoute ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<component :is="self ? 'router-link' : 'a'" :class="{ compact }" :[attr]="self ? url.substr(local.length) : url" rel="nofollow noopener" :target="target" :title="url" v-if="!fetching">
<div class="thumbnail" v-if="thumbnail" :style="`background-image: url('${thumbnail}')`">
<button class="_button" v-if="!playerEnabled && player.url" @click.prevent="playerEnabled = true" :title="$t('enable-player')"><fa :icon="faPlayCircle"/></button>
</div>
@ -58,12 +58,7 @@ export default Vue.extend({
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute =
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/');
const self = this.url.startsWith(local);
return {
local,
fetching: true,
@ -79,10 +74,9 @@ export default Vue.extend({
},
tweetUrl: null,
playerEnabled: false,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
faPlayCircle
};
},

View File

@ -1,5 +1,5 @@
<template>
<component :is="hasRoute ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="hasRoute ? url.substr(local.length) : url" :rel="rel" :target="target"
<component :is="self ? 'router-link' : 'a'" class="ieqqeuvs _link" :[attr]="self ? url.substr(local.length) : url" :rel="rel" :target="target"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
>
@ -37,12 +37,7 @@ export default Vue.extend({
}
},
data() {
const isSelf = this.url.startsWith(local);
const hasRoute = isSelf && (
(this.url.substr(local.length) === '/') ||
this.url.substr(local.length).startsWith('/@') ||
this.url.substr(local.length).startsWith('/notes/') ||
this.url.substr(local.length).startsWith('/tags/'));
const self = this.url.startsWith(local);
return {
local,
schema: null as string | null,
@ -51,10 +46,9 @@ export default Vue.extend({
pathname: null as string | null,
query: null as string | null,
hash: null as string | null,
self: isSelf,
hasRoute: hasRoute,
attr: hasRoute ? 'to' : 'href',
target: hasRoute ? null : '_blank',
self: self,
attr: self ? 'to' : 'href',
target: self ? null : '_blank',
showTimer: null,
hideTimer: null,
preview: null,

View File

@ -157,8 +157,6 @@ os.init(async () => {
},
methods: {
api: os.api,
getMeta: os.getMeta,
getMetaSync: os.getMetaSync,
signout: os.signout,
new(vm, props) {
const x = new vm({

View File

@ -17,16 +17,6 @@ let pending = 0;
* Misskey Operating System
*/
export default class MiOS extends EventEmitter {
/**
* Misskeyの /meta で取得できるメタ情報
*/
private meta: {
data: { [x: string]: any };
chachedAt: Date;
};
private isMetaFetching = false;
public app: Vue;
public store: ReturnType<typeof initStore>;
@ -88,7 +78,7 @@ export default class MiOS extends EventEmitter {
// When failure
.catch(() => {
// Render the error screen
document.body.innerHTML = '<div id="err">Error</div>';
document.body.innerHTML = '<div id="err">Oops!</div>';
Progress.done();
});
@ -107,9 +97,9 @@ export default class MiOS extends EventEmitter {
// Finish init
callback();
// Init service worker
this.getMeta().then(data => {
if (data.swPublickey) this.registerSw(data.swPublickey);
this.store.dispatch('instance/fetch').then(() => {
// Init service worker
if (this.store.state.instance.meta.swPublickey) this.registerSw(this.store.state.instance.meta.swPublickey);
});
};
@ -350,49 +340,6 @@ export default class MiOS extends EventEmitter {
return promise;
}
/**
* Misskeyのメタ情報を取得します
*/
@autobind
public getMetaSync() {
return this.meta ? this.meta.data : null;
}
/**
* Misskeyのメタ情報を取得します
* @param force キャッシュを無視するか否か
*/
@autobind
public getMeta(force = false) {
return new Promise<{ [x: string]: any }>(async (res, rej) => {
if (this.isMetaFetching) {
this.once('_meta_fetched_', () => {
res(this.meta.data);
});
return;
}
const expire = 1000 * 60; // 1min
// forceが有効, meta情報を保持していない or 期限切れ
if (force || this.meta == null || Date.now() - this.meta.chachedAt.getTime() > expire) {
this.isMetaFetching = true;
const meta = await this.api('meta', {
detail: false
});
this.meta = {
data: meta,
chachedAt: new Date()
};
this.isMetaFetching = false;
this.emit('_meta_fetched_');
res(meta);
} else {
res(this.meta.data);
}
});
}
}
/**

View File

@ -84,18 +84,19 @@ export default Vue.extend({
data() {
return {
version,
meta: null,
stats: null,
serverInfo: null,
faInfoCircle
}
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
created() {
this.$root.api('stats').then(res => {
this.stats = res;
});

View File

@ -115,13 +115,15 @@ export default Vue.extend({
tagsLocal: [],
tagsRemote: [],
stats: null,
meta: null,
num: Vue.filter('number'),
faBookmark, faChartLine, faCommentAlt, faPlus, faHashtag, faRocket
};
},
computed: {
meta() {
return this.$store.state.instance.meta;
},
tagUsers(): any {
return {
endpoint: 'hashtags/users',
@ -159,9 +161,6 @@ export default Vue.extend({
this.$root.api('stats').then(stats => {
this.stats = stats;
});
this.$root.getMeta().then(meta => {
this.meta = meta;
});
},
});
</script>

View File

@ -83,15 +83,6 @@ export default Vue.extend({
},
created() {
this.$root.getMeta().then((meta: Record<string, any>) => {
if (!(
this.enableGlobalTimeline = !meta.disableGlobalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin
) && this.src === 'global') this.src = 'local';
if (!(
this.enableLocalTimeline = !meta.disableLocalTimeline || this.$store.state.i.isModerator || this.$store.state.i.isAdmin
) && ['local', 'social'].includes(this.src)) this.src = 'home';
});
this.src = this.$store.state.deviceUser.tl.src;
if (this.src === 'list') {
this.list = this.$store.state.deviceUser.tl.arg;

View File

@ -1,10 +1,10 @@
<template>
<div class="rsqzvsbo">
<div class="_panel about">
<div class="banner" :style="{ backgroundImage: `url(${ banner })` }"></div>
<div class="_panel about" v-if="meta">
<div class="banner" :style="{ backgroundImage: `url(${ meta.bannerUrl })` }"></div>
<div class="body">
<h1 class="name" v-html="name || host"></h1>
<div class="desc" v-html="description || $t('introMisskey')"></div>
<h1 class="name" v-html="meta.name || host"></h1>
<div class="desc" v-html="meta.description || $t('introMisskey')"></div>
<mk-button @click="signup()" style="display: inline-block; margin-right: 16px;" primary>{{ $t('signup') }}</mk-button>
<mk-button @click="signin()" style="display: inline-block;">{{ $t('login') }}</mk-button>
</div>
@ -39,23 +39,16 @@ export default Vue.extend({
noPaging: true,
},
host: toUnicode(host),
meta: null,
name: null,
description: null,
banner: null,
announcements: [],
};
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
this.name = meta.name;
this.description = meta.description;
this.announcements = meta.announcements;
this.banner = meta.bannerUrl;
});
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
created() {
this.$root.api('stats').then(stats => {
this.stats = stats;
});

View File

@ -20,15 +20,14 @@ export default Vue.extend({
data() {
return {
meta: null,
instanceName: getInstanceName(),
}
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
});
}
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
});
</script>

View File

@ -98,7 +98,7 @@
<div class="operations">
<span class="label">{{ $t('operations') }}</span>
<mk-switch v-model="isSuspended" class="switch">{{ $t('stopActivityDelivery') }}</mk-switch>
<mk-switch v-model="isBlocked" class="switch">{{ $t('blockThisInstance') }}</mk-switch>
<mk-switch :value="isBlocked" class="switch" @change="changeBlock">{{ $t('blockThisInstance') }}</mk-switch>
</div>
<details class="metadata">
<summary class="label">{{ $t('metadata') }}</summary>
@ -147,9 +147,7 @@ export default Vue.extend({
data() {
return {
meta: null,
isSuspended: false,
isBlocked: false,
isSuspended: this.instance.isSuspended,
now: null,
chart: null,
chartInstance: null,
@ -184,6 +182,14 @@ export default Vue.extend({
null;
return stats;
},
meta() {
return this.$store.state.instance.meta;
},
isBlocked() {
return this.meta && this.meta.blockedHosts.includes(this.instance.host);
}
},
@ -195,12 +201,6 @@ export default Vue.extend({
});
},
isBlocked() {
this.$root.api('admin/update-meta', {
blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host)
});
},
chartSrc() {
this.renderChart();
},
@ -210,13 +210,7 @@ export default Vue.extend({
}
},
async created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
this.isSuspended = this.instance.isSuspended;
this.isBlocked = this.meta.blockedHosts.includes(this.instance.host);
});
async created() {
this.now = new Date();
const [perHour, perDay] = await Promise.all([
@ -235,6 +229,12 @@ export default Vue.extend({
},
methods: {
changeBlock(e) {
this.$root.api('admin/update-meta', {
blockedHosts: this.isBlocked ? this.meta.blockedHosts.concat([this.instance.host]) : this.meta.blockedHosts.filter(x => x !== this.instance.host)
});
},
setSrc(src) {
this.chartSrc = src;
},

View File

@ -20,6 +20,9 @@
</section>
<section class="_card info">
<div class="_content">
<mk-input v-model="maxNoteTextLength" type="number" :save="() => save()" style="margin:0;"><template #icon><fa :icon="faPencilAlt"/></template>{{ $t('maxNoteTextLength') }}</mk-input>
</div>
<div class="_content">
<mk-switch v-model="enableLocalTimeline" @change="save()">{{ $t('enableLocalTimeline') }}</mk-switch>
<mk-switch v-model="enableGlobalTimeline" @change="save()">{{ $t('enableGlobalTimeline') }}</mk-switch>
@ -171,7 +174,7 @@
<script lang="ts">
import Vue from 'vue';
import { faShareAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faThumbtack, faUser, faShieldAlt, faKey, faBolt } from '@fortawesome/free-solid-svg-icons';
import { faPencilAlt, faShareAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faThumbtack, faUser, faShieldAlt, faKey, faBolt } from '@fortawesome/free-solid-svg-icons';
import { faTrashAlt, faEnvelope } from '@fortawesome/free-regular-svg-icons';
import { faTwitter, faDiscord, faGithub } from '@fortawesome/free-brands-svg-icons';
import MkButton from '../../components/ui/button.vue';
@ -205,7 +208,6 @@ export default Vue.extend({
return {
version,
url,
meta: null,
stats: null,
serverInfo: null,
proxyAccount: null,
@ -223,6 +225,7 @@ export default Vue.extend({
tosUrl: null,
bannerUrl: null,
iconUrl: null,
maxNoteTextLength: 0,
enableRegistration: false,
enableLocalTimeline: false,
enableGlobalTimeline: false,
@ -241,52 +244,56 @@ export default Vue.extend({
enableDiscordIntegration: false,
discordClientId: null,
discordClientSecret: null,
faTwitter, faDiscord, faGithub, faShareAlt, faTrashAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faEnvelope, faThumbtack, faUser, faShieldAlt, faKey, faBolt
faPencilAlt, faTwitter, faDiscord, faGithub, faShareAlt, faTrashAlt, faGhost, faCog, faPlus, faCloud, faInfoCircle, faBan, faSave, faServer, faLink, faEnvelope, faThumbtack, faUser, faShieldAlt, faKey, faBolt
}
},
created() {
this.$root.getMeta().then(meta => {
this.meta = meta;
this.name = this.meta.name;
this.description = this.meta.description;
this.tosUrl = this.meta.tosUrl;
this.bannerUrl = this.meta.bannerUrl;
this.iconUrl = this.meta.iconUrl;
this.maintainerName = this.meta.maintainerName;
this.maintainerEmail = this.meta.maintainerEmail;
this.enableRegistration = !this.meta.disableRegistration;
this.enableLocalTimeline = !this.meta.disableLocalTimeline;
this.enableGlobalTimeline = !this.meta.disableGlobalTimeline;
this.enableRecaptcha = this.meta.enableRecaptcha;
this.recaptchaSiteKey = this.meta.recaptchaSiteKey;
this.recaptchaSecretKey = this.meta.recaptchaSecretKey;
this.proxyAccountId = this.meta.proxyAccountId;
this.cacheRemoteFiles = this.meta.cacheRemoteFiles;
this.proxyRemoteFiles = this.meta.proxyRemoteFiles;
this.localDriveCapacityMb = this.meta.driveCapacityPerLocalUserMb;
this.remoteDriveCapacityMb = this.meta.driveCapacityPerRemoteUserMb;
this.blockedHosts = this.meta.blockedHosts.join('\n');
this.pinnedUsers = this.meta.pinnedUsers.join('\n');
this.enableServiceWorker = this.meta.enableServiceWorker;
this.swPublicKey = this.meta.swPublickey;
this.swPrivateKey = this.meta.swPrivateKey;
this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
this.twitterConsumerKey = this.meta.twitterConsumerKey;
this.twitterConsumerSecret = this.meta.twitterConsumerSecret;
this.enableGithubIntegration = this.meta.enableGithubIntegration;
this.githubClientId = this.meta.githubClientId;
this.githubClientSecret = this.meta.githubClientSecret;
this.enableDiscordIntegration = this.meta.enableDiscordIntegration;
this.discordClientId = this.meta.discordClientId;
this.discordClientSecret = this.meta.discordClientSecret;
computed: {
meta() {
return this.$store.state.instance.meta;
},
},
if (this.proxyAccountId) {
this.$root.api('users/show', { userId: this.proxyAccountId }).then(proxyAccount => {
this.proxyAccount = proxyAccount;
});
}
});
created() {
this.name = this.meta.name;
this.description = this.meta.description;
this.tosUrl = this.meta.tosUrl;
this.bannerUrl = this.meta.bannerUrl;
this.iconUrl = this.meta.iconUrl;
this.maintainerName = this.meta.maintainerName;
this.maintainerEmail = this.meta.maintainerEmail;
this.maxNoteTextLength = this.meta.maxNoteTextLength;
this.enableRegistration = !this.meta.disableRegistration;
this.enableLocalTimeline = !this.meta.disableLocalTimeline;
this.enableGlobalTimeline = !this.meta.disableGlobalTimeline;
this.enableRecaptcha = this.meta.enableRecaptcha;
this.recaptchaSiteKey = this.meta.recaptchaSiteKey;
this.recaptchaSecretKey = this.meta.recaptchaSecretKey;
this.proxyAccountId = this.meta.proxyAccountId;
this.cacheRemoteFiles = this.meta.cacheRemoteFiles;
this.proxyRemoteFiles = this.meta.proxyRemoteFiles;
this.localDriveCapacityMb = this.meta.driveCapacityPerLocalUserMb;
this.remoteDriveCapacityMb = this.meta.driveCapacityPerRemoteUserMb;
this.blockedHosts = this.meta.blockedHosts.join('\n');
this.pinnedUsers = this.meta.pinnedUsers.join('\n');
this.enableServiceWorker = this.meta.enableServiceWorker;
this.swPublicKey = this.meta.swPublickey;
this.swPrivateKey = this.meta.swPrivateKey;
this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
this.twitterConsumerKey = this.meta.twitterConsumerKey;
this.twitterConsumerSecret = this.meta.twitterConsumerSecret;
this.enableGithubIntegration = this.meta.enableGithubIntegration;
this.githubClientId = this.meta.githubClientId;
this.githubClientSecret = this.meta.githubClientSecret;
this.enableDiscordIntegration = this.meta.enableDiscordIntegration;
this.discordClientId = this.meta.discordClientId;
this.discordClientSecret = this.meta.discordClientSecret;
if (this.proxyAccountId) {
this.$root.api('users/show', { userId: this.proxyAccountId }).then(proxyAccount => {
this.proxyAccount = proxyAccount;
});
}
this.$root.api('admin/server-info').then(res => {
this.serverInfo = res;
@ -347,6 +354,7 @@ export default Vue.extend({
iconUrl: this.iconUrl,
maintainerName: this.maintainerName,
maintainerEmail: this.maintainerEmail,
maxNoteTextLength: this.maxNoteTextLength,
disableRegistration: !this.enableRegistration,
disableLocalTimeline: !this.enableLocalTimeline,
disableGlobalTimeline: !this.enableGlobalTimeline,
@ -373,6 +381,7 @@ export default Vue.extend({
discordClientId: this.discordClientId,
discordClientSecret: this.discordClientSecret,
}).then(() => {
this.$store.dispatch('instance/fetch');
if (withDialog) {
this.$root.dialog({
type: 'success',

View File

@ -271,12 +271,8 @@ export default Vue.extend({
margin: 0;
padding: 16px;
font-size: 1em;
color: #aaa;
transition: color 0.1s ease;
&:hover {
color: var(--accent);
}
color: var(--accent);
&:active {
color: var(--accentDarken);
@ -338,7 +334,6 @@ export default Vue.extend({
font-size: 1em;
font-weight: normal;
text-decoration: none;
color: #aaa;
transition: color 0.1s ease;
&:hover {

View File

@ -231,7 +231,6 @@ export default Vue.extend({
display: block;
margin: 2px 0 0 0;
font-size: 10px;
color: var(--messagingRoomMessageInfo);
> .read {
margin: 0 8px;
@ -288,6 +287,7 @@ export default Vue.extend({
> .balloon {
background: $me-balloon-color;
box-shadow: 0 6px 16px var(--accentShadow);
text-align: left;
&[data-no-text] {

View File

@ -56,15 +56,17 @@ export default Vue.extend({
computed: {
integrations() {
return this.$store.state.i.integrations;
}
},
meta() {
return this.$store.state.instance.meta;
},
},
created() {
this.$root.getMeta().then(meta => {
this.enableTwitterIntegration = meta.enableTwitterIntegration;
this.enableDiscordIntegration = meta.enableDiscordIntegration;
this.enableGithubIntegration = meta.enableGithubIntegration;
});
this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
this.enableDiscordIntegration = this.meta.enableDiscordIntegration;
this.enableGithubIntegration = this.meta.enableGithubIntegration;
},
mounted() {

View File

@ -41,13 +41,13 @@ const defaultDeviceSettings = {
userData: {},
};
function copy(data) {
function copy<T>(data: T): T {
return JSON.parse(JSON.stringify(data));
}
export default (os: MiOS) => new Vuex.Store({
plugins: [createPersistedState({
paths: ['i', 'device', 'deviceUser', 'settings']
paths: ['i', 'device', 'deviceUser', 'settings', 'instance']
})],
state: {
@ -111,6 +111,30 @@ export default (os: MiOS) => new Vuex.Store({
},
modules: {
instance: {
namespaced: true,
state: {
meta: null
},
mutations: {
set(state, meta) {
state.meta = meta;
},
},
actions: {
async fetch(ctx) {
const meta = await os.api('meta', {
detail: false
});
ctx.commit('set', meta);
}
}
},
device: {
namespaced: true,

View File

@ -89,6 +89,8 @@ body {
width: 64px;
height: 64px;
animation: ini 0.6s infinite linear;
color: var(--accent);
fill: currentColor;
}
}
@ -196,6 +198,7 @@ a {
@extend ._button;
color: #fff;
background: var(--accent);
box-shadow: 0 6px 16px var(--accentShadow);
&:not(:disabled):hover {
background: var(--jkhztclx);
@ -385,7 +388,7 @@ a {
}
@keyframes blink {
0% { opacity: 1; }
30% { opacity: 1; }
90% { opacity: 0; }
0% { opacity: 1; transform: scale(1); }
30% { opacity: 1; transform: scale(1); }
90% { opacity: 0; transform: scale(0.5); }
}

View File

@ -10,6 +10,7 @@
accent: '#86b300',
accentDarken: ':darken<10<@accent',
accentLighten: ':lighten<10<@accent',
accentShadow: ':alpha<0.3<@accent',
focus: ':alpha<0.3<@accent',
bg: '#000',
fg: '#c7d1d8',

View File

@ -10,6 +10,7 @@
accent: '#86b300',
accentDarken: ':darken<10<@accent',
accentLighten: ':lighten<10<@accent',
accentShadow: ':alpha<0.4<@accent',
focus: ':alpha<0.3<@accent',
bg: '#fafafa',
fg: '#5c6a73',

View File

@ -55,6 +55,6 @@ html
| Please turn on your JavaScript
div#ini.
<svg viewBox="0 0 50 50">
<path fill=#fb4e4e d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" />
<path d="M25.251,6.461c-10.318,0-18.683,8.365-18.683,18.683h4.068c0-8.071,6.543-14.615,14.615-14.615V6.461z" />
</svg>
block content