summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/api/endpoints/i/update_home.ts9
-rw-r--r--src/api/endpoints/i/update_mobile_home.ts9
-rw-r--r--src/web/app/app.vue2
-rw-r--r--src/web/app/auth/script.ts23
-rw-r--r--src/web/app/common/define-widget.ts20
-rw-r--r--src/web/app/common/mios.ts15
-rw-r--r--src/web/app/common/views/components/autocomplete.vue234
-rw-r--r--src/web/app/common/views/components/index.ts30
-rw-r--r--src/web/app/common/views/components/messaging-room.form.vue8
-rw-r--r--src/web/app/common/views/components/messaging-room.vue13
-rw-r--r--src/web/app/common/views/components/post-html.ts6
-rw-r--r--src/web/app/common/views/directives/autocomplete.ts (renamed from src/web/app/desktop/views/directives/autocomplete.ts)91
-rw-r--r--src/web/app/common/views/directives/focus.ts5
-rw-r--r--src/web/app/common/views/directives/index.ts4
-rw-r--r--src/web/app/common/views/widgets/access-log.vue (renamed from src/web/app/common/views/components/widgets/access-log.vue)2
-rw-r--r--src/web/app/common/views/widgets/broadcast.vue (renamed from src/web/app/common/views/components/widgets/broadcast.vue)4
-rw-r--r--src/web/app/common/views/widgets/calendar.vue (renamed from src/web/app/common/views/components/widgets/calendar.vue)2
-rw-r--r--src/web/app/common/views/widgets/donation.vue (renamed from src/web/app/common/views/components/widgets/donation.vue)2
-rw-r--r--src/web/app/common/views/widgets/index.ts25
-rw-r--r--src/web/app/common/views/widgets/nav.vue (renamed from src/web/app/common/views/components/widgets/nav.vue)2
-rw-r--r--src/web/app/common/views/widgets/photo-stream.vue (renamed from src/web/app/common/views/components/widgets/photo-stream.vue)2
-rw-r--r--src/web/app/common/views/widgets/rss.vue (renamed from src/web/app/common/views/components/widgets/rss.vue)2
-rw-r--r--src/web/app/common/views/widgets/server.cpu-memory.vue (renamed from src/web/app/common/views/components/widgets/server.cpu-memory.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.cpu.vue (renamed from src/web/app/common/views/components/widgets/server.cpu.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.disk.vue (renamed from src/web/app/common/views/components/widgets/server.disk.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.info.vue (renamed from src/web/app/common/views/components/widgets/server.info.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.memory.vue (renamed from src/web/app/common/views/components/widgets/server.memory.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.pie.vue (renamed from src/web/app/common/views/components/widgets/server.pie.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.uptimes.vue (renamed from src/web/app/common/views/components/widgets/server.uptimes.vue)0
-rw-r--r--src/web/app/common/views/widgets/server.vue (renamed from src/web/app/common/views/components/widgets/server.vue)2
-rw-r--r--src/web/app/common/views/widgets/slideshow.vue (renamed from src/web/app/common/views/components/widgets/slideshow.vue)2
-rw-r--r--src/web/app/common/views/widgets/tips.vue (renamed from src/web/app/common/views/components/widgets/tips.vue)2
-rw-r--r--src/web/app/common/views/widgets/version.vue (renamed from src/web/app/common/views/components/widgets/version.vue)4
-rw-r--r--src/web/app/desktop/script.ts1
-rw-r--r--src/web/app/desktop/views/components/autocomplete.vue190
-rw-r--r--src/web/app/desktop/views/components/home.vue32
-rw-r--r--src/web/app/desktop/views/components/index.ts24
-rw-r--r--src/web/app/desktop/views/components/posts.post.vue49
-rw-r--r--src/web/app/desktop/views/directives/index.ts2
-rw-r--r--src/web/app/desktop/views/widgets/activity.vue (renamed from src/web/app/desktop/views/components/widgets/activity.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/channel.channel.form.vue (renamed from src/web/app/desktop/views/components/widgets/channel.channel.form.vue)0
-rw-r--r--src/web/app/desktop/views/widgets/channel.channel.post.vue (renamed from src/web/app/desktop/views/components/widgets/channel.channel.post.vue)0
-rw-r--r--src/web/app/desktop/views/widgets/channel.channel.vue (renamed from src/web/app/desktop/views/components/widgets/channel.channel.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/channel.vue (renamed from src/web/app/desktop/views/components/widgets/channel.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/index.ts23
-rw-r--r--src/web/app/desktop/views/widgets/messaging.vue (renamed from src/web/app/desktop/views/components/widgets/messaging.vue)4
-rw-r--r--src/web/app/desktop/views/widgets/notifications.vue (renamed from src/web/app/desktop/views/components/widgets/notifications.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/polls.vue (renamed from src/web/app/desktop/views/components/widgets/polls.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/post-form.vue (renamed from src/web/app/desktop/views/components/widgets/post-form.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/profile.vue (renamed from src/web/app/common/views/components/widgets/profile.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/timemachine.vue (renamed from src/web/app/desktop/views/components/widgets/timemachine.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/trends.vue (renamed from src/web/app/desktop/views/components/widgets/trends.vue)2
-rw-r--r--src/web/app/desktop/views/widgets/users.vue (renamed from src/web/app/desktop/views/components/widgets/users.vue)2
-rw-r--r--src/web/app/init.ts35
-rw-r--r--src/web/app/mobile/script.ts1
-rw-r--r--src/web/app/mobile/views/components/index.ts8
-rw-r--r--src/web/app/mobile/views/components/posts.post.vue28
-rw-r--r--src/web/app/mobile/views/pages/home.vue19
-rw-r--r--src/web/app/mobile/views/widgets/activity.vue (renamed from src/web/app/mobile/views/components/widgets/activity.vue)2
-rw-r--r--src/web/app/mobile/views/widgets/index.ts7
-rw-r--r--src/web/app/mobile/views/widgets/profile.vue62
-rw-r--r--src/web/app/stats/script.ts23
-rw-r--r--src/web/app/status/script.ts23
63 files changed, 596 insertions, 477 deletions
diff --git a/src/api/endpoints/i/update_home.ts b/src/api/endpoints/i/update_home.ts
index 5dfb7d7915..394686cbd1 100644
--- a/src/api/endpoints/i/update_home.ts
+++ b/src/api/endpoints/i/update_home.ts
@@ -3,6 +3,7 @@
*/
import $ from 'cafy';
import User from '../../models/user';
+import event from '../../event';
module.exports = async (params, user) => new Promise(async (res, rej) => {
// Get 'home' parameter
@@ -30,6 +31,10 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
});
res();
+
+ event(user._id, 'home_updated', {
+ home
+ });
} else {
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
@@ -47,5 +52,9 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
});
res();
+
+ event(user._id, 'home_updated', {
+ id, data
+ });
}
});
diff --git a/src/api/endpoints/i/update_mobile_home.ts b/src/api/endpoints/i/update_mobile_home.ts
index a87d89cad7..70181431a2 100644
--- a/src/api/endpoints/i/update_mobile_home.ts
+++ b/src/api/endpoints/i/update_mobile_home.ts
@@ -3,6 +3,7 @@
*/
import $ from 'cafy';
import User from '../../models/user';
+import event from '../../event';
module.exports = async (params, user) => new Promise(async (res, rej) => {
// Get 'home' parameter
@@ -29,6 +30,10 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
});
res();
+
+ event(user._id, 'mobile_home_updated', {
+ home
+ });
} else {
if (id == null && data == null) return rej('you need to set id and data params if home param unset');
@@ -46,5 +51,9 @@ module.exports = async (params, user) => new Promise(async (res, rej) => {
});
res();
+
+ event(user._id, 'mobile_home_updated', {
+ id, data
+ });
}
});
diff --git a/src/web/app/app.vue b/src/web/app/app.vue
index 321e003930..7a46e7dea0 100644
--- a/src/web/app/app.vue
+++ b/src/web/app/app.vue
@@ -1,3 +1,3 @@
<template>
- <router-view id="app"></router-view>
+<router-view id="app"></router-view>
</template>
diff --git a/src/web/app/auth/script.ts b/src/web/app/auth/script.ts
deleted file mode 100644
index dd598d1ed6..0000000000
--- a/src/web/app/auth/script.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Authorize Form
- */
-
-// Style
-import './style.styl';
-
-import * as riot from 'riot';
-require('./tags');
-import init from '../init';
-
-document.title = 'Misskey | アプリの連携';
-
-/**
- * init
- */
-init(() => {
- mount(document.createElement('mk-index'));
-});
-
-function mount(content) {
- riot.mount(document.getElementById('app').appendChild(content));
-}
diff --git a/src/web/app/common/define-widget.ts b/src/web/app/common/define-widget.ts
index 826f9cc636..97925cf443 100644
--- a/src/web/app/common/define-widget.ts
+++ b/src/web/app/common/define-widget.ts
@@ -21,7 +21,9 @@ export default function<T extends object>(data: {
},
data() {
return {
- props: data.props ? data.props() : {} as T
+ props: data.props ? data.props() : {} as T,
+ bakedOldProps: null,
+ preventSave: false
};
},
created() {
@@ -33,7 +35,18 @@ export default function<T extends object>(data: {
});
}
+ this.bakeProps();
+
this.$watch('props', newProps => {
+ if (this.preventSave) {
+ this.preventSave = false;
+ this.bakeProps();
+ return;
+ }
+ if (this.bakedOldProps == JSON.stringify(newProps)) return;
+
+ this.bakeProps();
+
if (this.isMobile) {
(this as any).api('i/update_mobile_home', {
id: this.id,
@@ -52,6 +65,11 @@ export default function<T extends object>(data: {
}, {
deep: true
});
+ },
+ methods: {
+ bakeProps() {
+ this.bakedOldProps = JSON.stringify(this.props);
+ }
}
});
}
diff --git a/src/web/app/common/mios.ts b/src/web/app/common/mios.ts
index e20f4bfe4e..6c95e5b9b5 100644
--- a/src/web/app/common/mios.ts
+++ b/src/web/app/common/mios.ts
@@ -1,5 +1,7 @@
import Vue from 'vue';
import { EventEmitter } from 'eventemitter3';
+
+import { apiUrl, swPublickey, version, lang } from '../config';
import api from './scripts/api';
import signout from './scripts/signout';
import Progress from './scripts/loading';
@@ -11,13 +13,6 @@ import MessagingIndexStreamManager from './scripts/streaming/messaging-index-str
import Err from '../common/views/components/connect-failed.vue';
-//#region environment variables
-declare const _VERSION_: string;
-declare const _LANG_: string;
-declare const _API_URL_: string;
-declare const _SW_PUBLICKEY_: string;
-//#endregion
-
export type API = {
chooseDriveFile: (opts: {
title?: string;
@@ -204,7 +199,7 @@ export default class MiOS extends EventEmitter {
}
// Fetch user
- fetch(`${_API_URL_}/i`, {
+ fetch(`${apiUrl}/i`, {
method: 'POST',
body: JSON.stringify({
i: token
@@ -311,7 +306,7 @@ export default class MiOS extends EventEmitter {
// A public key your push server will use to send
// messages to client apps via a push server.
- applicationServerKey: urlBase64ToUint8Array(_SW_PUBLICKEY_)
+ applicationServerKey: urlBase64ToUint8Array(swPublickey)
};
// Subscribe push notification
@@ -348,7 +343,7 @@ export default class MiOS extends EventEmitter {
});
// The path of service worker script
- const sw = `/sw.${_VERSION_}.${_LANG_}.js`;
+ const sw = `/sw.${version}.${lang}.js`;
// Register service worker
navigator.serviceWorker.register(sw).then(registration => {
diff --git a/src/web/app/common/views/components/autocomplete.vue b/src/web/app/common/views/components/autocomplete.vue
new file mode 100644
index 0000000000..b068ecc4fa
--- /dev/null
+++ b/src/web/app/common/views/components/autocomplete.vue
@@ -0,0 +1,234 @@
+<template>
+<div class="mk-autocomplete" @contextmenu.prevent="() => {}">
+ <ol class="users" ref="suggests" v-if="users.length > 0">
+ <li v-for="user in users" @click="complete(type, user)" @keydown="onKeydown" tabindex="-1">
+ <img class="avatar" :src="`${user.avatar_url}?thumbnail&size=32`" alt=""/>
+ <span class="name">{{ user.name }}</span>
+ <span class="username">@{{ user.username }}</span>
+ </li>
+ </ol>
+ <ol class="emojis" ref="suggests" v-if="emojis.length > 0">
+ <li v-for="emoji in emojis" @click="complete(type, pictograph.dic[emoji])" @keydown="onKeydown" tabindex="-1">
+ <span class="emoji">{{ pictograph.dic[emoji] }}</span>
+ <span class="name" v-html="emoji.replace(q, `<b>${q}</b>`)"></span>
+ </li>
+ </ol>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import * as pictograph from 'pictograph';
+import contains from '../../../common/scripts/contains';
+
+export default Vue.extend({
+ props: ['type', 'q', 'textarea', 'complete', 'close', 'x', 'y'],
+ data() {
+ return {
+ fetching: true,
+ users: [],
+ emojis: [],
+ select: -1,
+ pictograph
+ }
+ },
+ computed: {
+ items(): HTMLCollection {
+ return (this.$refs.suggests as Element).children;
+ }
+ },
+ updated() {
+ //#region 位置調整
+ const margin = 32;
+
+ if (this.x + this.$el.offsetWidth > window.innerWidth - margin) {
+ this.$el.style.left = (this.x - this.$el.offsetWidth) + 'px';
+ this.$el.style.marginLeft = '-16px';
+ } else {
+ this.$el.style.left = this.x + 'px';
+ this.$el.style.marginLeft = '0';
+ }
+
+ if (this.y + this.$el.offsetHeight > window.innerHeight - margin) {
+ this.$el.style.top = (this.y - this.$el.offsetHeight) + 'px';
+ this.$el.style.marginTop = '0';
+ } else {
+ this.$el.style.top = this.y + 'px';
+ this.$el.style.marginTop = 'calc(1em + 8px)';
+ }
+ //#endregion
+ },
+ mounted() {
+ this.textarea.addEventListener('keydown', this.onKeydown);
+
+ Array.from(document.querySelectorAll('body *')).forEach(el => {
+ el.addEventListener('mousedown', this.onMousedown);
+ });
+
+ if (this.type == 'user') {
+ (this as any).api('users/search_by_username', {
+ query: this.q,
+ limit: 30
+ }).then(users => {
+ this.users = users;
+ this.fetching = false;
+ });
+ } else if (this.type == 'emoji') {
+ const emojis = Object.keys(pictograph.dic).sort((a, b) => a.length - b.length);
+ const matched = emojis.filter(e => e.indexOf(this.q) > -1);
+ this.emojis = matched.filter((x, i) => i <= 30);
+ }
+ },
+ beforeDestroy() {
+ this.textarea.removeEventListener('keydown', this.onKeydown);
+
+ Array.from(document.querySelectorAll('body *')).forEach(el => {
+ el.removeEventListener('mousedown', this.onMousedown);
+ });
+ },
+ methods: {
+ onMousedown(e) {
+ if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
+ },
+
+ onKeydown(e) {
+ const cancel = () => {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ switch (e.which) {
+ case 10: // [ENTER]
+ case 13: // [ENTER]
+ if (this.select !== -1) {
+ cancel();
+ (this.items[this.select] as any).click();
+ } else {
+ this.close();
+ }
+ break;
+
+ case 27: // [ESC]
+ cancel();
+ this.close();
+ break;
+
+ case 38: // [↑]
+ if (this.select !== -1) {
+ cancel();
+ this.selectPrev();
+ } else {
+ this.close();
+ }
+ break;
+
+ case 9: // [TAB]
+ case 40: // [↓]
+ cancel();
+ this.selectNext();
+ break;
+
+ default:
+ this.close();
+ }
+ },
+
+ selectNext() {
+ if (++this.select >= this.items.length) this.select = 0;
+ this.applySelect();
+ },
+
+ selectPrev() {
+ if (--this.select < 0) this.select = this.items.length - 1;
+ this.applySelect();
+ },
+
+ applySelect() {
+ Array.from(this.items).forEach(el => {
+ el.removeAttribute('data-selected');
+ });
+
+ this.items[this.select].setAttribute('data-selected', 'true');
+ (this.items[this.select] as any).focus();
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mk-autocomplete
+ position fixed
+ z-index 65535
+ margin-top calc(1em + 8px)
+ overflow hidden
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.1)
+ border-radius 4px
+
+ > ol
+ display block
+ margin 0
+ padding 4px 0
+ max-height 190px
+ max-width 500px
+ overflow auto
+ list-style none
+
+ > li
+ display block
+ padding 4px 12px
+ white-space nowrap
+ overflow hidden
+ font-size 0.9em
+ color rgba(0, 0, 0, 0.8)
+ cursor default
+
+ &, *
+ user-select none
+
+ &:hover
+ &[data-selected='true']
+ background $theme-color
+
+ &, *
+ color #fff !important
+
+ &:active
+ background darken($theme-color, 10%)
+
+ &, *
+ color #fff !important
+
+ > .users > li
+
+ .avatar
+ vertical-align middle
+ min-width 28px
+ min-height 28px
+ max-width 28px
+ max-height 28px
+ margin 0 8px 0 0
+ border-radius 100%
+
+ .name
+ margin 0 8px 0 0
+ /*font-weight bold*/
+ font-weight normal
+ color rgba(0, 0, 0, 0.8)
+
+ .username
+ font-weight normal
+ color rgba(0, 0, 0, 0.3)
+
+ > .emojis > li
+
+ .emoji
+ display inline-block
+ margin 0 4px 0 0
+ width 24px
+
+ .name
+ font-weight normal
+ color rgba(0, 0, 0, 0.8)
+
+</style>
diff --git a/src/web/app/common/views/components/index.ts b/src/web/app/common/views/components/index.ts
index e66a323266..ab0f1767d4 100644
--- a/src/web/app/common/views/components/index.ts
+++ b/src/web/app/common/views/components/index.ts
@@ -21,21 +21,6 @@ import urlPreview from './url-preview.vue';
import twitterSetting from './twitter-setting.vue';
import fileTypeIcon from './file-type-icon.vue';
-//#region widgets
-import wAccessLog from './widgets/access-log.vue';
-import wVersion from './widgets/version.vue';
-import wRss from './widgets/rss.vue';
-import wProfile from './widgets/profile.vue';
-import wServer from './widgets/server.vue';
-import wBroadcast from './widgets/broadcast.vue';
-import wCalendar from './widgets/calendar.vue';
-import wPhotoStream from './widgets/photo-stream.vue';
-import wSlideshow from './widgets/slideshow.vue';
-import wTips from './widgets/tips.vue';
-import wDonation from './widgets/donation.vue';
-import wNav from './widgets/nav.vue';
-//#endregion
-
Vue.component('mk-signin', signin);
Vue.component('mk-signup', signup);
Vue.component('mk-forkit', forkit);
@@ -56,18 +41,3 @@ Vue.component('mk-messaging-room', messagingRoom);
Vue.component('mk-url-preview', urlPreview);
Vue.component('mk-twitter-setting', twitterSetting);
Vue.component('mk-file-type-icon', fileTypeIcon);
-
-//#region widgets
-Vue.component('mkw-nav', wNav);
-Vue.component('mkw-calendar', wCalendar);
-Vue.component('mkw-photo-stream', wPhotoStream);
-Vue.component('mkw-slideshow', wSlideshow);
-Vue.component('mkw-tips', wTips);
-Vue.component('mkw-donation', wDonation);
-Vue.component('mkw-broadcast', wBroadcast);
-Vue.component('mkw-profile', wProfile);
-Vue.component('mkw-server', wServer);
-Vue.component('mkw-rss', wRss);
-Vue.component('mkw-version', wVersion);
-Vue.component('mkw-access-log', wAccessLog);
-//#endregion
diff --git a/src/web/app/common/views/components/messaging-room.form.vue b/src/web/app/common/views/components/messaging-room.form.vue
index b89365a5d8..adc3372bed 100644
--- a/src/web/app/common/views/components/messaging-room.form.vue
+++ b/src/web/app/common/views/components/messaging-room.form.vue
@@ -1,6 +1,6 @@
<template>
<div class="mk-messaging-form">
- <textarea v-model="text" @keypress="onKeypress" @paste="onPaste" placeholder="%i18n:common.input-message-here%"></textarea>
+ <textarea v-model="text" ref="textarea" @keypress="onKeypress" @paste="onPaste" placeholder="%i18n:common.input-message-here%" v-autocomplete></textarea>
<div class="file" v-if="file">{{ file.name }}</div>
<mk-uploader ref="uploader"/>
<button class="send" @click="send" :disabled="sending" title="%i18n:common.send%">
@@ -18,6 +18,8 @@
<script lang="ts">
import Vue from 'vue';
+import * as autosize from 'autosize';
+
export default Vue.extend({
props: ['user'],
data() {
@@ -27,6 +29,9 @@ export default Vue.extend({
sending: false
};
},
+ mounted() {
+ autosize(this.$refs.textarea);
+ },
methods: {
onPaste(e) {
const data = e.clipboardData;
@@ -93,6 +98,7 @@ export default Vue.extend({
height 64px
margin 0
padding 8px
+ resize none
font-size 1em
color #000
outline none
diff --git a/src/web/app/common/views/components/messaging-room.vue b/src/web/app/common/views/components/messaging-room.vue
index 7af6b3fae3..310b56f6fc 100644
--- a/src/web/app/common/views/components/messaging-room.vue
+++ b/src/web/app/common/views/components/messaging-room.vue
@@ -16,7 +16,6 @@
</div>
<footer>
<div ref="notifications" class="notifications"></div>
- <div class="grippie" title="%i18n:common.tags.mk-messaging-room.resize-form%"></div>
<x-form :user="user"/>
</footer>
</div>
@@ -316,16 +315,4 @@ export default Vue.extend({
line-height 32px
font-size 16px
- > .grippie
- height 10px
- margin-top -10px
- background transparent
- cursor ns-resize
-
- &:hover
- //background rgba(0, 0, 0, 0.1)
-
- &:active
- //background rgba(0, 0, 0, 0.2)
-
</style>
diff --git a/src/web/app/common/views/components/post-html.ts b/src/web/app/common/views/components/post-html.ts
index 37954cd7e7..adb025589d 100644
--- a/src/web/app/common/views/components/post-html.ts
+++ b/src/web/app/common/views/components/post-html.ts
@@ -81,7 +81,11 @@ export default Vue.component('mk-post-html', {
case 'code':
return createElement('pre', [
- createElement('code', token.html)
+ createElement('code', {
+ domProps: {
+ innerHTML: token.html
+ }
+ })
]);
case 'inline-code':
diff --git a/src/web/app/desktop/views/directives/autocomplete.ts b/src/web/app/common/views/directives/autocomplete.ts
index 53fa5a4df2..4d6ba41df1 100644
--- a/src/web/app/desktop/views/directives/autocomplete.ts
+++ b/src/web/app/common/views/directives/autocomplete.ts
@@ -25,11 +25,11 @@ class Autocomplete {
* 対象のテキストエリアを与えてインスタンスを初期化します。
*/
constructor(textarea) {
- // BIND ---------------------------------
- this.onInput = this.onInput.bind(this);
+ //#region BIND
+ this.onInput = this.onInput.bind(this);
this.complete = this.complete.bind(this);
- this.close = this.close.bind(this);
- // --------------------------------------
+ this.close = this.close.bind(this);
+ //#endregion
this.suggestion = null;
this.textarea = textarea;
@@ -60,14 +60,19 @@ class Autocomplete {
const text = this.textarea.value.substr(0, caret);
const mentionIndex = text.lastIndexOf('@');
+ const emojiIndex = text.lastIndexOf(':');
- if (mentionIndex == -1) return;
+ if (mentionIndex != -1 && mentionIndex > emojiIndex) {
+ const username = text.substr(mentionIndex + 1);
+ if (!username.match(/^[a-zA-Z0-9-]+$/)) return;
+ this.open('user', username);
+ }
- const username = text.substr(mentionIndex + 1);
-
- if (!username.match(/^[a-zA-Z0-9-]+$/)) return;
-
- this.open('user', username);
+ if (emojiIndex != -1 && emojiIndex > mentionIndex) {
+ const emoji = text.substr(emojiIndex + 1);
+ if (!emoji.match(/^[\+\-a-z_]+$/)) return;
+ this.open('emoji', emoji);
+ }
}
/**
@@ -77,6 +82,15 @@ class Autocomplete {
// 既に開いているサジェストは閉じる
this.close();
+ //#region サジェストを表示すべき位置を計算
+ const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart);
+
+ const rect = this.textarea.getBoundingClientRect();
+
+ const x = rect.left + caretPosition.left - this.textarea.scrollLeft;
+ const y = rect.top + caretPosition.top - this.textarea.scrollTop;
+ //#endregion
+
// サジェスト要素作成
this.suggestion = new MkAutocomplete({
propsData: {
@@ -84,22 +98,12 @@ class Autocomplete {
complete: this.complete,
close: this.close,
type: type,
- q: q
+ q: q,
+ x,
+ y
}
}).$mount();
- // ~ サジェストを表示すべき位置を計算 ~
-
- const caretPosition = getCaretCoordinates(this.textarea, this.textarea.selectionStart);
-
- const rect = this.textarea.getBoundingClientRect();
-
- const x = rect.left + window.pageXOffset + caretPosition.left;
- const y = rect.top + window.pageYOffset + caretPosition.top;
-
- this.suggestion.$el.style.left = x + 'px';
- this.suggestion.$el.style.top = y + 'px';
-
// 要素追加
document.body.appendChild(this.suggestion.$el);
}
@@ -119,24 +123,39 @@ class Autocomplete {
/**
* オートコンプリートする
*/
- private complete(user) {
+ private complete(type, value) {
this.close();
- const value = user.username;
-
const caret = this.textarea.selectionStart;
- const source = this.textarea.value;
- const before = source.substr(0, caret);
- const trimmedBefore = before.substring(0, before.lastIndexOf('@'));
- const after = source.substr(caret);
+ if (type == 'user') {
+ const source = this.textarea.value;
- // 結果を挿入する
- this.textarea.value = trimmedBefore + '@' + value + ' ' + after;
+ const before = source.substr(0, caret);
+ const trimmedBefore = before.substring(0, before.lastIndexOf('@'));
+ const after = source.substr(caret);
- // キャレットを戻す
- this.textarea.focus();
- const pos = caret + value.length;
- this.textarea.setSelectionRange(pos, pos);
+ // 挿入
+ this.textarea.value = trimmedBefore + '@' + value.username + ' ' + after;
+
+ // キャレットを戻す
+ this.textarea.focus();
+ const pos = caret + value.username.length;
+ this.textarea.setSelectionRange(pos, pos);
+ } else if (type == 'emoji') {
+ const source = this.textarea.value;
+
+ const before = source.substr(0, caret);
+ const trimmedBefore = before.substring(0, before.lastIndexOf(':'));
+ const after = source.substr(caret);
+
+ // 挿入
+ this.textarea.value = trimmedBefore + value + after;
+
+ // キャレットを戻す
+ this.textarea.focus();
+ const pos = caret + value.length;
+ this.textarea.setSelectionRange(pos, pos);
+ }
}
}
diff --git a/src/web/app/common/views/directives/focus.ts b/src/web/app/common/views/directives/focus.ts
deleted file mode 100644
index b4fbcb6a87..0000000000
--- a/src/web/app/common/views/directives/focus.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default {
- inserted(el) {
- el.focus();
- }
-};
diff --git a/src/web/app/common/views/directives/index.ts b/src/web/app/common/views/directives/index.ts
index 358866f500..268f07a950 100644
--- a/src/web/app/common/views/directives/index.ts
+++ b/src/web/app/common/views/directives/index.ts
@@ -1,5 +1,5 @@
import Vue from 'vue';
-import focus from './focus';
+import autocomplete from './autocomplete';
-Vue.directive('focus', focus);
+Vue.directive('autocomplete', autocomplete);
diff --git a/src/web/app/common/views/components/widgets/access-log.vue b/src/web/app/common/views/widgets/access-log.vue
index c810c2d157..f7bb17d833 100644
--- a/src/web/app/common/views/components/widgets/access-log.vue
+++ b/src/web/app/common/views/widgets/access-log.vue
@@ -15,7 +15,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
import * as seedrandom from 'seedrandom';
export default define({
diff --git a/src/web/app/common/views/components/widgets/broadcast.vue b/src/web/app/common/views/widgets/broadcast.vue
index 0bb59caf43..bf41a5fc67 100644
--- a/src/web/app/common/views/components/widgets/broadcast.vue
+++ b/src/web/app/common/views/widgets/broadcast.vue
@@ -24,8 +24,8 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
-import { lang } from '../../../../config';
+import define from '../../../common/define-widget';
+import { lang } from '../../../config';
export default define({
name: 'broadcast',
diff --git a/src/web/app/common/views/components/widgets/calendar.vue b/src/web/app/common/views/widgets/calendar.vue
index bfcbd7f68d..2bcdb07f9e 100644
--- a/src/web/app/common/views/components/widgets/calendar.vue
+++ b/src/web/app/common/views/widgets/calendar.vue
@@ -36,7 +36,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'calendar',
props: () => ({
diff --git a/src/web/app/common/views/components/widgets/donation.vue b/src/web/app/common/views/widgets/donation.vue
index 08aab8ecd1..e218df06e1 100644
--- a/src/web/app/common/views/components/widgets/donation.vue
+++ b/src/web/app/common/views/widgets/donation.vue
@@ -12,7 +12,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'donation'
});
diff --git a/src/web/app/common/views/widgets/index.ts b/src/web/app/common/views/widgets/index.ts
new file mode 100644
index 0000000000..e41030e85a
--- /dev/null
+++ b/src/web/app/common/views/widgets/index.ts
@@ -0,0 +1,25 @@
+import Vue from 'vue';
+
+import wAccessLog from './access-log.vue';
+import wVersion from './version.vue';
+import wRss from './rss.vue';
+import wServer from './server.vue';
+import wBroadcast from './broadcast.vue';
+import wCalendar from './calendar.vue';
+import wPhotoStream from './photo-stream.vue';
+import wSlideshow from './slideshow.vue';
+import wTips from './tips.vue';
+import wDonation from './donation.vue';
+import wNav from './nav.vue';
+
+Vue.component('mkw-nav', wNav);
+Vue.component('mkw-calendar', wCalendar);
+Vue.component('mkw-photo-stream', wPhotoStream);
+Vue.component('mkw-slideshow', wSlideshow);
+Vue.component('mkw-tips', wTips);
+Vue.component('mkw-donation', wDonation);
+Vue.component('mkw-broadcast', wBroadcast);
+Vue.component('mkw-server', wServer);
+Vue.component('mkw-rss', wRss);
+Vue.component('mkw-version', wVersion);
+Vue.component('mkw-access-log', wAccessLog);
diff --git a/src/web/app/common/views/components/widgets/nav.vue b/src/web/app/common/views/widgets/nav.vue
index ce88e587a8..7bd5a7832f 100644
--- a/src/web/app/common/views/components/widgets/nav.vue
+++ b/src/web/app/common/views/widgets/nav.vue
@@ -9,7 +9,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'nav'
});
diff --git a/src/web/app/common/views/components/widgets/photo-stream.vue b/src/web/app/common/views/widgets/photo-stream.vue
index dcaa6624dd..78864cc8bf 100644
--- a/src/web/app/common/views/components/widgets/photo-stream.vue
+++ b/src/web/app/common/views/widgets/photo-stream.vue
@@ -13,7 +13,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'photo-stream',
props: () => ({
diff --git a/src/web/app/common/views/components/widgets/rss.vue b/src/web/app/common/views/widgets/rss.vue
index 186d495d00..4d74b2f7a4 100644
--- a/src/web/app/common/views/components/widgets/rss.vue
+++ b/src/web/app/common/views/widgets/rss.vue
@@ -13,7 +13,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'rss',
props: () => ({
diff --git a/src/web/app/common/views/components/widgets/server.cpu-memory.vue b/src/web/app/common/views/widgets/server.cpu-memory.vue
index d75a142568..d75a142568 100644
--- a/src/web/app/common/views/components/widgets/server.cpu-memory.vue
+++ b/src/web/app/common/views/widgets/server.cpu-memory.vue
diff --git a/src/web/app/common/views/components/widgets/server.cpu.vue b/src/web/app/common/views/widgets/server.cpu.vue
index 596c856da8..596c856da8 100644
--- a/src/web/app/common/views/components/widgets/server.cpu.vue
+++ b/src/web/app/common/views/widgets/server.cpu.vue
diff --git a/src/web/app/common/views/components/widgets/server.disk.vue b/src/web/app/common/views/widgets/server.disk.vue
index 2af1982a96..2af1982a96 100644
--- a/src/web/app/common/views/components/widgets/server.disk.vue
+++ b/src/web/app/common/views/widgets/server.disk.vue
diff --git a/src/web/app/common/views/components/widgets/server.info.vue b/src/web/app/common/views/widgets/server.info.vue
index bed6a1b743..bed6a1b743 100644
--- a/src/web/app/common/views/components/widgets/server.info.vue
+++ b/src/web/app/common/views/widgets/server.info.vue
diff --git a/src/web/app/common/views/components/widgets/server.memory.vue b/src/web/app/common/views/widgets/server.memory.vue
index 834a62671d..834a62671d 100644
--- a/src/web/app/common/views/components/widgets/server.memory.vue
+++ b/src/web/app/common/views/widgets/server.memory.vue
diff --git a/src/web/app/common/views/components/widgets/server.pie.vue b/src/web/app/common/views/widgets/server.pie.vue
index ce2cff1d00..ce2cff1d00 100644
--- a/src/web/app/common/views/components/widgets/server.pie.vue
+++ b/src/web/app/common/views/widgets/server.pie.vue
diff --git a/src/web/app/common/views/components/widgets/server.uptimes.vue b/src/web/app/common/views/widgets/server.uptimes.vue
index 06713d83ce..06713d83ce 100644
--- a/src/web/app/common/views/components/widgets/server.uptimes.vue
+++ b/src/web/app/common/views/widgets/server.uptimes.vue
diff --git a/src/web/app/common/views/components/widgets/server.vue b/src/web/app/common/views/widgets/server.vue
index 4ebc5767d6..3d5248998f 100644
--- a/src/web/app/common/views/components/widgets/server.vue
+++ b/src/web/app/common/views/widgets/server.vue
@@ -18,7 +18,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
import XCpuMemory from './server.cpu-memory.vue';
import XCpu from './server.cpu.vue';
import XMemory from './server.memory.vue';
diff --git a/src/web/app/common/views/components/widgets/slideshow.vue b/src/web/app/common/views/widgets/slideshow.vue
index c2f4eb70d3..56eb654c2b 100644
--- a/src/web/app/common/views/components/widgets/slideshow.vue
+++ b/src/web/app/common/views/widgets/slideshow.vue
@@ -12,7 +12,7 @@
<script lang="ts">
import * as anime from 'animejs';
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'slideshow',
props: () => ({
diff --git a/src/web/app/common/views/components/widgets/tips.vue b/src/web/app/common/views/widgets/tips.vue
index 2991fbc3b9..bdecc068e1 100644
--- a/src/web/app/common/views/components/widgets/tips.vue
+++ b/src/web/app/common/views/widgets/tips.vue
@@ -6,7 +6,7 @@
<script lang="ts">
import * as anime from 'animejs';
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
const tips = [
'<kbd>t</kbd>でタイムラインにフォーカスできます',
diff --git a/src/web/app/common/views/components/widgets/version.vue b/src/web/app/common/views/widgets/version.vue
index ad2b27bc40..5072d9b74a 100644
--- a/src/web/app/common/views/components/widgets/version.vue
+++ b/src/web/app/common/views/widgets/version.vue
@@ -3,8 +3,8 @@
</template>
<script lang="ts">
-import { version } from '../../../../config';
-import define from '../../../../common/define-widget';
+import { version } from '../../../config';
+import define from '../../../common/define-widget';
export default define({
name: 'version'
}).extend({
diff --git a/src/web/app/desktop/script.ts b/src/web/app/desktop/script.ts
index bbd8e9598b..f0412805ef 100644
--- a/src/web/app/desktop/script.ts
+++ b/src/web/app/desktop/script.ts
@@ -37,6 +37,7 @@ init(async (launch) => {
// Register components
require('./views/components');
+ require('./views/widgets');
// Launch the app
const [app, os] = launch(os => ({
diff --git a/src/web/app/desktop/views/components/autocomplete.vue b/src/web/app/desktop/views/components/autocomplete.vue
deleted file mode 100644
index a99d405e82..0000000000
--- a/src/web/app/desktop/views/components/autocomplete.vue
+++ /dev/null
@@ -1,190 +0,0 @@
-<template>
-<div class="mk-autocomplete">
- <ol class="users" ref="users" v-if="users.length > 0">
- <li v-for="user in users" @click="complete(user)" @keydown="onKeydown" tabindex="-1">
- <img class="avatar" :src="`${user.avatar_url}?thumbnail&size=32`" alt=""/>
- <span class="name">{{ user.name }}</span>
- <span class="username">@{{ user.username }}</span>
- </li>
- </ol>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import contains from '../../../common/scripts/contains';
-
-export default Vue.extend({
- props: ['q', 'textarea', 'complete', 'close'],
- data() {
- return {
- fetching: true,
- users: [],
- select: -1
- }
- },
- mounted() {
- this.textarea.addEventListener('keydown', this.onKeydown);
-
- Array.from(document.querySelectorAll('body *')).forEach(el => {
- el.addEventListener('mousedown', this.onMousedown);
- });
-
- (this as any).api('users/search_by_username', {
- query: this.q,
- limit: 30
- }).then(users => {
- this.users = users;
- this.fetching = false;
- });
- },
- beforeDestroy() {
- this.textarea.removeEventListener('keydown', this.onKeydown);
-
- Array.from(document.querySelectorAll('body *')).forEach(el => {
- el.removeEventListener('mousedown', this.onMousedown);
- });
- },
- methods: {
- onMousedown(e) {
- if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
- },
-
- onKeydown(e) {
- const cancel = () => {
- e.preventDefault();
- e.stopPropagation();
- };
-
- switch (e.which) {
- case 10: // [ENTER]
- case 13: // [ENTER]
- if (this.select !== -1) {
- cancel();
- this.complete(this.users[this.select]);
- } else {
- this.close();
- }
- break;
-
- case 27: // [ESC]
- cancel();
- this.close();
- break;
-
- case 38: // [↑]
- if (this.select !== -1) {
- cancel();
- this.selectPrev();
- } else {
- this.close();
- }
- break;
-
- case 9: // [TAB]
- case 40: // [↓]
- cancel();
- this.selectNext();
- break;
-
- default:
- this.close();
- }
- },
-
- selectNext() {
- if (++this.select >= this.users.length) this.select = 0;
- this.applySelect();
- },
-
- selectPrev() {
- if (--this.select < 0) this.select = this.users.length - 1;
- this.applySelect();
- },
-
- applySelect() {
- const els = (this.$refs.users as Element).children;
-
- Array.from(els).forEach(el => {
- el.removeAttribute('data-selected');
- });
-
- els[this.select].setAttribute('data-selected', 'true');
- (els[this.select] as any).focus();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-autocomplete
- position absolute
- z-index 65535
- margin-top calc(1em + 8px)
- overflow hidden
- background #fff
- border solid 1px rgba(0, 0, 0, 0.1)
- border-radius 4px
-
- > .users
- display block
- margin 0
- padding 4px 0
- max-height 190px
- max-width 500px
- overflow auto
- list-style none
-
- > li
- display block
- padding 4px 12px
- white-space nowrap
- overflow hidden
- font-size 0.9em
- color rgba(0, 0, 0, 0.8)
- cursor default
-
- &, *
- user-select none
-
- &:hover
- &[data-selected='true']
- color #fff
- background $theme-color
-
- .name
- color #fff
-
- .username
- color #fff
-
- &:active
- color #fff
- background darken($theme-color, 10%)
-
- .name
- color #fff
-
- .username
- color #fff
-
- .avatar
- vertical-align middle
- min-width 28px
- min-height 28px
- max-width 28px
- max-height 28px
- margin 0 8px 0 0
- border-radius 100%
-
- .name
- margin 0 8px 0 0
- /*font-weight bold*/
- font-weight normal
- color rgba(0, 0, 0, 0.8)
-
- .username
- font-weight normal
- color rgba(0, 0, 0, 0.3)
-
-</style>
diff --git a/src/web/app/desktop/views/components/home.vue b/src/web/app/desktop/views/components/home.vue
index 8a61c378ed..59fd2aa36e 100644
--- a/src/web/app/desktop/views/components/home.vue
+++ b/src/web/app/desktop/views/components/home.vue
@@ -60,7 +60,7 @@
</template>
<template v-else>
<div v-for="place in ['left', 'right']" :class="place">
- <component v-for="widget in widgets[place]" :is="`mkw-${widget.name}`" :key="widget.id" :widget="widget" @chosen="warp"/>
+ <component v-for="widget in widgets[place]" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" @chosen="warp"/>
</div>
<div class="main">
<mk-post-form v-if="os.i.client_settings.showPostFormOnTopOfTl"/>
@@ -90,6 +90,8 @@ export default Vue.extend({
},
data() {
return {
+ connection: null,
+ connectionId: null,
widgetAdderSelected: null,
trash: [],
widgets: {
@@ -131,6 +133,16 @@ export default Vue.extend({
deep: true
});
},
+ mounted() {
+ this.connection = (this as any).os.stream.getConnection();
+ this.connectionId = (this as any).os.stream.use();
+
+ this.connection.on('home_updated', this.onHomeUpdated);
+ },
+ beforeDestroy() {
+ this.connection.off('home_updated', this.onHomeUpdated);
+ (this as any).os.stream.dispose(this.connectionId);
+ },
methods: {
hint() {
(this as any).apis.dialog({
@@ -147,6 +159,22 @@ export default Vue.extend({
onTlLoaded() {
this.$emit('loaded');
},
+ onHomeUpdated(data) {
+ if (data.home) {
+ (this as any).os.i.client_settings.home = data.home;
+ this.widgets.left = data.home.filter(w => w.place == 'left');
+ this.widgets.right = data.home.filter(w => w.place == 'right');
+ } else {
+ const w = (this as any).os.i.client_settings.home.find(w => w.id == data.id);
+ if (w != null) {
+ w.data = data.data;
+ this.$refs[w.id][0].preventSave = true;
+ this.$refs[w.id][0].props = w.data;
+ this.widgets.left = (this as any).os.i.client_settings.home.filter(w => w.place == 'left');
+ this.widgets.right = (this as any).os.i.client_settings.home.filter(w => w.place == 'right');
+ }
+ }
+ },
onWidgetContextmenu(widgetId) {
const w = (this.$refs[widgetId] as any)[0];
if (w.func) w.func();
@@ -179,7 +207,7 @@ export default Vue.extend({
});
},
warp(date) {
- (this.$refs.tl as any)[0].warp(date);
+ (this.$refs.tl as any).warp(date);
}
}
});
diff --git a/src/web/app/desktop/views/components/index.ts b/src/web/app/desktop/views/components/index.ts
index 7584cb4983..52b9680baa 100644
--- a/src/web/app/desktop/views/components/index.ts
+++ b/src/web/app/desktop/views/components/index.ts
@@ -29,18 +29,6 @@ import following from './following.vue';
import usersList from './users-list.vue';
import widgetContainer from './widget-container.vue';
-//#region widgets
-import wNotifications from './widgets/notifications.vue';
-import wTimemachine from './widgets/timemachine.vue';
-import wActivity from './widgets/activity.vue';
-import wTrends from './widgets/trends.vue';
-import wUsers from './widgets/users.vue';
-import wPolls from './widgets/polls.vue';
-import wPostForm from './widgets/post-form.vue';
-import wMessaging from './widgets/messaging.vue';
-import wChannel from './widgets/channel.vue';
-//#endregion
-
Vue.component('mk-ui', ui);
Vue.component('mk-ui-notification', uiNotification);
Vue.component('mk-home', home);
@@ -69,15 +57,3 @@ Vue.component('mk-followers', followers);
Vue.component('mk-following', following);
Vue.component('mk-users-list', usersList);
Vue.component('mk-widget-container', widgetContainer);
-
-//#region widgets
-Vue.component('mkw-notifications', wNotifications);
-Vue.component('mkw-timemachine', wTimemachine);
-Vue.component('mkw-activity', wActivity);
-Vue.component('mkw-trends', wTrends);
-Vue.component('mkw-users', wUsers);
-Vue.component('mkw-polls', wPolls);
-Vue.component('mkw-post-form', wPostForm);
-Vue.component('mkw-messaging', wMessaging);
-Vue.component('mkw-channel', wChannel);
-//#endregion
diff --git a/src/web/app/desktop/views/components/posts.post.vue b/src/web/app/desktop/views/components/posts.post.vue
index 4ae980648c..4898de0b6b 100644
--- a/src/web/app/desktop/views/components/posts.post.vue
+++ b/src/web/app/desktop/views/components/posts.post.vue
@@ -37,7 +37,7 @@
<a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:
</p>
<a class="reply" v-if="p.reply">%fa:reply%</a>
- <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
+ <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i" :class="$style.text"/>
<a class="quote" v-if="p.repost">RP:</a>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
</div>
@@ -413,9 +413,6 @@ export default Vue.extend({
font-size 1.1em
color #717171
- > .dummy
- display none
-
.mk-url-preview
margin-top 8px
@@ -431,27 +428,6 @@ export default Vue.extend({
font-style oblique
color #a0bf46
- code
- padding 4px 8px
- margin 0 0.5em
- font-size 80%
- color #525252
- background #f8f8f8
- border-radius 2px
-
- pre > code
- padding 16px
- margin 0
-
- [data-is-me]:after
- content "you"
- padding 0 4px
- margin-left 4px
- font-size 80%
- color $theme-color-foreground
- background $theme-color
- border-radius 4px
-
> .mk-poll
font-size 80%
@@ -505,3 +481,26 @@ export default Vue.extend({
</style>
+<style lang="stylus" module>
+.text
+ code
+ padding 4px 8px
+ margin 0 0.5em
+ font-size 80%
+ color #525252
+ background #f8f8f8
+ border-radius 2px
+
+ pre > code
+ padding 16px
+ margin 0
+
+ [data-is-me]:after
+ content "you"
+ padding 0 4px
+ margin-left 4px
+ font-size 80%
+ color $theme-color-foreground
+ background $theme-color
+ border-radius 4px
+</style>
diff --git a/src/web/app/desktop/views/directives/index.ts b/src/web/app/desktop/views/directives/index.ts
index 3d0c73b6b2..324e07596d 100644
--- a/src/web/app/desktop/views/directives/index.ts
+++ b/src/web/app/desktop/views/directives/index.ts
@@ -1,8 +1,6 @@
import Vue from 'vue';
import userPreview from './user-preview';
-import autocomplete from './autocomplete';
Vue.directive('userPreview', userPreview);
Vue.directive('user-preview', userPreview);
-Vue.directive('autocomplete', autocomplete);
diff --git a/src/web/app/desktop/views/components/widgets/activity.vue b/src/web/app/desktop/views/widgets/activity.vue
index 2ff5fe4f03..0bdf4622af 100644
--- a/src/web/app/desktop/views/components/widgets/activity.vue
+++ b/src/web/app/desktop/views/widgets/activity.vue
@@ -7,7 +7,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'activity',
props: () => ({
diff --git a/src/web/app/desktop/views/components/widgets/channel.channel.form.vue b/src/web/app/desktop/views/widgets/channel.channel.form.vue
index 392ba5924b..392ba5924b 100644
--- a/src/web/app/desktop/views/components/widgets/channel.channel.form.vue
+++ b/src/web/app/desktop/views/widgets/channel.channel.form.vue
diff --git a/src/web/app/desktop/views/components/widgets/channel.channel.post.vue b/src/web/app/desktop/views/widgets/channel.channel.post.vue
index faaf0fb731..faaf0fb731 100644
--- a/src/web/app/desktop/views/components/widgets/channel.channel.post.vue
+++ b/src/web/app/desktop/views/widgets/channel.channel.post.vue
diff --git a/src/web/app/desktop/views/components/widgets/channel.channel.vue b/src/web/app/desktop/views/widgets/channel.channel.vue
index 09154390c4..70dac316cf 100644
--- a/src/web/app/desktop/views/components/widgets/channel.channel.vue
+++ b/src/web/app/desktop/views/widgets/channel.channel.vue
@@ -11,7 +11,7 @@
<script lang="ts">
import Vue from 'vue';
-import ChannelStream from '../../../../common/scripts/streaming/channel-stream';
+import ChannelStream from '../../../common/scripts/streaming/channel-stream';
import XForm from './channel.channel.form.vue';
import XPost from './channel.channel.post.vue';
diff --git a/src/web/app/desktop/views/components/widgets/channel.vue b/src/web/app/desktop/views/widgets/channel.vue
index 5c3afd9ecf..fc143bb1df 100644
--- a/src/web/app/desktop/views/components/widgets/channel.vue
+++ b/src/web/app/desktop/views/widgets/channel.vue
@@ -10,7 +10,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
import XChannel from './channel.channel.vue';
export default define({
diff --git a/src/web/app/desktop/views/widgets/index.ts b/src/web/app/desktop/views/widgets/index.ts
new file mode 100644
index 0000000000..77d771d6b3
--- /dev/null
+++ b/src/web/app/desktop/views/widgets/index.ts
@@ -0,0 +1,23 @@
+import Vue from 'vue';
+
+import wNotifications from './notifications.vue';
+import wTimemachine from './timemachine.vue';
+import wActivity from './activity.vue';
+import wTrends from './trends.vue';
+import wUsers from './users.vue';
+import wPolls from './polls.vue';
+import wPostForm from './post-form.vue';
+import wMessaging from './messaging.vue';
+import wChannel from './channel.vue';
+import wProfile from './profile.vue';
+
+Vue.component('mkw-notifications', wNotifications);
+Vue.component('mkw-timemachine', wTimemachine);
+Vue.component('mkw-activity', wActivity);
+Vue.component('mkw-trends', wTrends);
+Vue.component('mkw-users', wUsers);
+Vue.component('mkw-polls', wPolls);
+Vue.component('mkw-post-form', wPostForm);
+Vue.component('mkw-messaging', wMessaging);
+Vue.component('mkw-channel', wChannel);
+Vue.component('mkw-profile', wProfile);
diff --git a/src/web/app/desktop/views/components/widgets/messaging.vue b/src/web/app/desktop/views/widgets/messaging.vue
index ae7d6934af..2c9f473bd1 100644
--- a/src/web/app/desktop/views/components/widgets/messaging.vue
+++ b/src/web/app/desktop/views/widgets/messaging.vue
@@ -6,8 +6,8 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
-import MkMessagingRoomWindow from '../messaging-room-window.vue';
+import define from '../../../common/define-widget';
+import MkMessagingRoomWindow from '../components/messaging-room-window.vue';
export default define({
name: 'messaging',
diff --git a/src/web/app/desktop/views/components/widgets/notifications.vue b/src/web/app/desktop/views/widgets/notifications.vue
index 978cf5218e..1a2b3d3f89 100644
--- a/src/web/app/desktop/views/components/widgets/notifications.vue
+++ b/src/web/app/desktop/views/widgets/notifications.vue
@@ -9,7 +9,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'notifications',
props: () => ({
diff --git a/src/web/app/desktop/views/components/widgets/polls.vue b/src/web/app/desktop/views/widgets/polls.vue
index f1b34ceed0..fda4e17d87 100644
--- a/src/web/app/desktop/views/components/widgets/polls.vue
+++ b/src/web/app/desktop/views/widgets/polls.vue
@@ -15,7 +15,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'polls',
props: () => ({
diff --git a/src/web/app/desktop/views/components/widgets/post-form.vue b/src/web/app/desktop/views/widgets/post-form.vue
index ab87ba7217..e51b4f3577 100644
--- a/src/web/app/desktop/views/components/widgets/post-form.vue
+++ b/src/web/app/desktop/views/widgets/post-form.vue
@@ -9,7 +9,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'post-form',
props: () => ({
diff --git a/src/web/app/common/views/components/widgets/profile.vue b/src/web/app/desktop/views/widgets/profile.vue
index 68cf469788..e067a0eb24 100644
--- a/src/web/app/common/views/components/widgets/profile.vue
+++ b/src/web/app/desktop/views/widgets/profile.vue
@@ -21,7 +21,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'profile',
props: () => ({
diff --git a/src/web/app/desktop/views/components/widgets/timemachine.vue b/src/web/app/desktop/views/widgets/timemachine.vue
index 7420482168..6db3b14c62 100644
--- a/src/web/app/desktop/views/components/widgets/timemachine.vue
+++ b/src/web/app/desktop/views/widgets/timemachine.vue
@@ -5,7 +5,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'timemachine',
props: () => ({
diff --git a/src/web/app/desktop/views/components/widgets/trends.vue b/src/web/app/desktop/views/widgets/trends.vue
index 934351b8a5..09cad9ba4a 100644
--- a/src/web/app/desktop/views/components/widgets/trends.vue
+++ b/src/web/app/desktop/views/widgets/trends.vue
@@ -14,7 +14,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'trends',
props: () => ({
diff --git a/src/web/app/desktop/views/components/widgets/users.vue b/src/web/app/desktop/views/widgets/users.vue
index f3a1509cfd..f7af8205ec 100644
--- a/src/web/app/desktop/views/components/widgets/users.vue
+++ b/src/web/app/desktop/views/widgets/users.vue
@@ -22,7 +22,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
const limit = 3;
diff --git a/src/web/app/init.ts b/src/web/app/init.ts
index aa2ec25c96..4a8f34f8d1 100644
--- a/src/web/app/init.ts
+++ b/src/web/app/init.ts
@@ -2,11 +2,6 @@
* App initializer
*/
-declare const _VERSION_: string;
-declare const _LANG_: string;
-declare const _HOST_: string;
-//declare const __CONSTS__: any;
-
import Vue from 'vue';
import VueRouter from 'vue-router';
import VModal from 'vue-js-modal';
@@ -19,6 +14,7 @@ require('./common/views/directives');
// Register global components
require('./common/views/components');
+require('./common/views/widgets');
// Register global filters
require('./common/filters');
@@ -35,24 +31,28 @@ import App from './app.vue';
import checkForUpdate from './common/scripts/check-for-update';
import MiOS, { API } from './common/mios';
+import { version, host, lang } from './config';
/**
* APP ENTRY POINT!
*/
-console.info(`Misskey v${_VERSION_} (葵 aoi)`);
+console.info(`Misskey v${version} (葵 aoi)`);
+console.info(
+ '%cここにコードを入力したり張り付けたりしないでください。アカウントが不正利用される可能性があります。',
+ 'color: red; background: yellow; font-size: 16px;');
// BootTimer解除
window.clearTimeout((window as any).mkBootTimer);
delete (window as any).mkBootTimer;
-if (_HOST_ != 'localhost') {
- document.domain = _HOST_;
+if (host != 'localhost') {
+ document.domain = host;
}
//#region Set lang attr
const html = document.documentElement;
-html.setAttribute('lang', _LANG_);
+html.setAttribute('lang', lang);
//#endregion
//#region Set description meta tag
@@ -63,9 +63,6 @@ meta.setAttribute('content', '%i18n:common.misskey%');
head.appendChild(meta);
//#endregion
-// Set global configuration
-//(riot as any).mixin(__CONSTS__);
-
// iOSでプライベートモードだとlocalStorageが使えないので既存のメソッドを上書きする
try {
localStorage.setItem('kyoppie', 'yuppie');
@@ -129,10 +126,14 @@ export default (callback: (launch: (api: (os: MiOS) => API) => [Vue, MiOS]) => v
panic(e);
}
- // 更新チェック
- setTimeout(() => {
- checkForUpdate(os);
- }, 3000);
+ //#region 更新チェック
+ const preventUpdate = localStorage.getItem('preventUpdate') == 'true';
+ if (!preventUpdate) {
+ setTimeout(() => {
+ checkForUpdate(os);
+ }, 3000);
+ }
+ //#endregion
});
};
@@ -149,7 +150,7 @@ function panic(e) {
+ '<hr>'
+ `<p>エラーコード: ${e.toString()}</p>`
+ `<p>ブラウザ バージョン: ${navigator.userAgent}</p>`
- + `<p>クライアント バージョン: ${_VERSION_}</p>`
+ + `<p>クライアント バージョン: ${version}</p>`
+ '<hr>'
+ '<p>問題が解決しない場合は、上記の情報をお書き添えの上 syuilotan@yahoo.co.jp までご連絡ください。</p>'
+ '<p>Thank you for using Misskey.</p>'
diff --git a/src/web/app/mobile/script.ts b/src/web/app/mobile/script.ts
index fe73155c7c..eeadfd92b0 100644
--- a/src/web/app/mobile/script.ts
+++ b/src/web/app/mobile/script.ts
@@ -38,6 +38,7 @@ init((launch) => {
// Register components
require('./views/components');
+ require('./views/widgets');
// http://qiita.com/junya/items/3ff380878f26ca447f85
document.body.setAttribute('ontouchstart', '');
diff --git a/src/web/app/mobile/views/components/index.ts b/src/web/app/mobile/views/components/index.ts
index d372f22332..fe65aab207 100644
--- a/src/web/app/mobile/views/components/index.ts
+++ b/src/web/app/mobile/views/components/index.ts
@@ -21,10 +21,6 @@ import userTimeline from './user-timeline.vue';
import activity from './activity.vue';
import widgetContainer from './widget-container.vue';
-//#region widgets
-import wActivity from './widgets/activity.vue';
-//#endregion
-
Vue.component('mk-ui', ui);
Vue.component('mk-timeline', timeline);
Vue.component('mk-posts', posts);
@@ -45,7 +41,3 @@ Vue.component('mk-user-preview', userPreview);
Vue.component('mk-user-timeline', userTimeline);
Vue.component('mk-activity', activity);
Vue.component('mk-widget-container', widgetContainer);
-
-//#region widgets
-Vue.component('mkw-activity', wActivity);
-//#endregion
diff --git a/src/web/app/mobile/views/components/posts.post.vue b/src/web/app/mobile/views/components/posts.post.vue
index 87b8032c89..ae1dfc59ab 100644
--- a/src/web/app/mobile/views/components/posts.post.vue
+++ b/src/web/app/mobile/views/components/posts.post.vue
@@ -34,7 +34,7 @@
<a class="reply" v-if="p.reply">
%fa:reply%
</a>
- <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i"/>
+ <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i" :class="$style.text"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<a class="quote" v-if="p.repost != null">RP:</a>
</div>
@@ -364,18 +364,6 @@ export default Vue.extend({
font-style oblique
color #a0bf46
- code
- padding 4px 8px
- margin 0 0.5em
- font-size 80%
- color #525252
- background #f8f8f8
- border-radius 2px
-
- pre > code
- padding 16px
- margin 0
-
[data-is-me]:after
content "you"
padding 0 4px
@@ -445,3 +433,17 @@ export default Vue.extend({
</style>
+<style lang="stylus" module>
+.text
+ code
+ padding 4px 8px
+ margin 0 0.5em
+ font-size 80%
+ color #525252
+ background #f8f8f8
+ border-radius 2px
+
+ pre > code
+ padding 16px
+ margin 0
+</style>
diff --git a/src/web/app/mobile/views/pages/home.vue b/src/web/app/mobile/views/pages/home.vue
index e4ba2e7193..f4f458068a 100644
--- a/src/web/app/mobile/views/pages/home.vue
+++ b/src/web/app/mobile/views/pages/home.vue
@@ -51,7 +51,7 @@
</x-draggable>
</template>
<template v-else>
- <component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :widget="widget" :is-mobile="true" @chosen="warp"/>
+ <component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" :is-mobile="true" @chosen="warp"/>
</template>
</div>
</main>
@@ -124,12 +124,14 @@ export default Vue.extend({
this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onStreamPost);
+ this.connection.on('mobile_home_updated', this.onHomeUpdated);
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
Progress.start();
},
beforeDestroy() {
this.connection.off('post', this.onStreamPost);
+ this.connection.off('mobile_home_updated', this.onHomeUpdated);
(this as any).os.stream.dispose(this.connectionId);
document.removeEventListener('visibilitychange', this.onVisibilitychange);
},
@@ -152,6 +154,20 @@ export default Vue.extend({
document.title = 'Misskey';
}
},
+ onHomeUpdated(data) {
+ if (data.home) {
+ (this as any).os.i.client_settings.mobile_home = data.home;
+ this.widgets = data.home;
+ } else {
+ const w = (this as any).os.i.client_settings.mobile_home.find(w => w.id == data.id);
+ if (w != null) {
+ w.data = data.data;
+ this.$refs[w.id][0].preventSave = true;
+ this.$refs[w.id][0].props = w.data;
+ this.widgets = (this as any).os.i.client_settings.mobile_home;
+ }
+ }
+ },
hint() {
alert('ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。');
},
@@ -177,6 +193,7 @@ export default Vue.extend({
this.saveHome();
},
saveHome() {
+ (this as any).os.i.client_settings.mobile_home = this.widgets;
(this as any).api('i/update_mobile_home', {
home: this.widgets
});
diff --git a/src/web/app/mobile/views/components/widgets/activity.vue b/src/web/app/mobile/views/widgets/activity.vue
index c4d30b07af..48dcafb3ed 100644
--- a/src/web/app/mobile/views/components/widgets/activity.vue
+++ b/src/web/app/mobile/views/widgets/activity.vue
@@ -10,7 +10,7 @@
</template>
<script lang="ts">
-import define from '../../../../common/define-widget';
+import define from '../../../common/define-widget';
export default define({
name: 'activity',
diff --git a/src/web/app/mobile/views/widgets/index.ts b/src/web/app/mobile/views/widgets/index.ts
new file mode 100644
index 0000000000..4de912b64c
--- /dev/null
+++ b/src/web/app/mobile/views/widgets/index.ts
@@ -0,0 +1,7 @@
+import Vue from 'vue';
+
+import wActivity from './activity.vue';
+import wProfile from './profile.vue';
+
+Vue.component('mkw-activity', wActivity);
+Vue.component('mkw-profile', wProfile);
diff --git a/src/web/app/mobile/views/widgets/profile.vue b/src/web/app/mobile/views/widgets/profile.vue
new file mode 100644
index 0000000000..6bc7bfffc6
--- /dev/null
+++ b/src/web/app/mobile/views/widgets/profile.vue
@@ -0,0 +1,62 @@
+<template>
+<div class="mkw-profile">
+ <mk-widget-container>
+ <div :class="$style.banner"
+ :style="os.i.banner_url ? `background-image: url(${os.i.banner_url}?thumbnail&size=256)` : ''"
+ ></div>
+ <img :class="$style.avatar"
+ :src="`${os.i.avatar_url}?thumbnail&size=96`"
+ alt="avatar"
+ />
+ <router-link :class="$style.name" :to="`/${os.i.username}`">{{ os.i.name }}</router-link>
+ </mk-widget-container>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../common/define-widget';
+export default define({
+ name: 'profile'
+});
+</script>
+
+<style lang="stylus" module>
+.banner
+ height 100px
+ background-color #f5f5f5
+ background-size cover
+ background-position center
+ cursor pointer
+
+.banner:before
+ content ""
+ display block
+ width 100%
+ height 100%
+ background rgba(0, 0, 0, 0.5)
+
+.avatar
+ display block
+ position absolute
+ width 58px
+ height 58px
+ margin 0
+ vertical-align bottom
+ top ((100px - 58px) / 2)
+ left ((100px - 58px) / 2)
+ border none
+ border-radius 100%
+ box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
+
+.name
+ display block
+ position absolute
+ top 0
+ left 92px
+ margin 0
+ line-height 100px
+ color #fff
+ font-weight bold
+ text-shadow 0 0 8px rgba(0, 0, 0, 0.5)
+
+</style>
diff --git a/src/web/app/stats/script.ts b/src/web/app/stats/script.ts
deleted file mode 100644
index 3bbd80c339..0000000000
--- a/src/web/app/stats/script.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Stats
- */
-
-// Style
-import './style.styl';
-
-import * as riot from 'riot';
-require('./tags');
-import init from '../init';
-
-document.title = 'Misskey Statistics';
-
-/**
- * init
- */
-init(() => {
- mount(document.createElement('mk-index'));
-});
-
-function mount(content) {
- riot.mount(document.getElementById('app').appendChild(content));
-}
diff --git a/src/web/app/status/script.ts b/src/web/app/status/script.ts
deleted file mode 100644
index 84483acab7..0000000000
--- a/src/web/app/status/script.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Status
- */
-
-// Style
-import './style.styl';
-
-import * as riot from 'riot';
-require('./tags');
-import init from '../init';
-
-document.title = 'Misskey System Status';
-
-/**
- * init
- */
-init(() => {
- mount(document.createElement('mk-index'));
-});
-
-function mount(content) {
- riot.mount(document.getElementById('app').appendChild(content));
-}