summaryrefslogtreecommitdiff
path: root/src/server/web/app/mobile
diff options
context:
space:
mode:
Diffstat (limited to 'src/server/web/app/mobile')
-rw-r--r--src/server/web/app/mobile/api/choose-drive-file.ts18
-rw-r--r--src/server/web/app/mobile/api/choose-drive-folder.ts17
-rw-r--r--src/server/web/app/mobile/api/dialog.ts5
-rw-r--r--src/server/web/app/mobile/api/input.ts8
-rw-r--r--src/server/web/app/mobile/api/notify.ts3
-rw-r--r--src/server/web/app/mobile/api/post.ts43
-rw-r--r--src/server/web/app/mobile/script.ts84
-rw-r--r--src/server/web/app/mobile/style.styl15
-rw-r--r--src/server/web/app/mobile/views/components/activity.vue62
-rw-r--r--src/server/web/app/mobile/views/components/drive-file-chooser.vue98
-rw-r--r--src/server/web/app/mobile/views/components/drive-folder-chooser.vue78
-rw-r--r--src/server/web/app/mobile/views/components/drive.file-detail.vue295
-rw-r--r--src/server/web/app/mobile/views/components/drive.file.vue171
-rw-r--r--src/server/web/app/mobile/views/components/drive.folder.vue58
-rw-r--r--src/server/web/app/mobile/views/components/drive.vue581
-rw-r--r--src/server/web/app/mobile/views/components/follow-button.vue123
-rw-r--r--src/server/web/app/mobile/views/components/friends-maker.vue127
-rw-r--r--src/server/web/app/mobile/views/components/index.ts47
-rw-r--r--src/server/web/app/mobile/views/components/media-image.vue31
-rw-r--r--src/server/web/app/mobile/views/components/media-video.vue36
-rw-r--r--src/server/web/app/mobile/views/components/notification-preview.vue128
-rw-r--r--src/server/web/app/mobile/views/components/notification.vue164
-rw-r--r--src/server/web/app/mobile/views/components/notifications.vue168
-rw-r--r--src/server/web/app/mobile/views/components/notify.vue49
-rw-r--r--src/server/web/app/mobile/views/components/post-card.vue89
-rw-r--r--src/server/web/app/mobile/views/components/post-detail.sub.vue109
-rw-r--r--src/server/web/app/mobile/views/components/post-detail.vue447
-rw-r--r--src/server/web/app/mobile/views/components/post-form.vue275
-rw-r--r--src/server/web/app/mobile/views/components/post-preview.vue106
-rw-r--r--src/server/web/app/mobile/views/components/post.sub.vue115
-rw-r--r--src/server/web/app/mobile/views/components/post.vue523
-rw-r--r--src/server/web/app/mobile/views/components/posts.vue111
-rw-r--r--src/server/web/app/mobile/views/components/sub-post-content.vue43
-rw-r--r--src/server/web/app/mobile/views/components/timeline.vue109
-rw-r--r--src/server/web/app/mobile/views/components/ui.header.vue242
-rw-r--r--src/server/web/app/mobile/views/components/ui.nav.vue244
-rw-r--r--src/server/web/app/mobile/views/components/ui.vue75
-rw-r--r--src/server/web/app/mobile/views/components/user-card.vue69
-rw-r--r--src/server/web/app/mobile/views/components/user-preview.vue110
-rw-r--r--src/server/web/app/mobile/views/components/user-timeline.vue76
-rw-r--r--src/server/web/app/mobile/views/components/users-list.vue133
-rw-r--r--src/server/web/app/mobile/views/components/widget-container.vue68
-rw-r--r--src/server/web/app/mobile/views/directives/index.ts6
-rw-r--r--src/server/web/app/mobile/views/directives/user-preview.ts2
-rw-r--r--src/server/web/app/mobile/views/pages/drive.vue107
-rw-r--r--src/server/web/app/mobile/views/pages/followers.vue65
-rw-r--r--src/server/web/app/mobile/views/pages/following.vue65
-rw-r--r--src/server/web/app/mobile/views/pages/home.vue259
-rw-r--r--src/server/web/app/mobile/views/pages/index.vue16
-rw-r--r--src/server/web/app/mobile/views/pages/messaging-room.vue42
-rw-r--r--src/server/web/app/mobile/views/pages/messaging.vue23
-rw-r--r--src/server/web/app/mobile/views/pages/notifications.vue32
-rw-r--r--src/server/web/app/mobile/views/pages/othello.vue50
-rw-r--r--src/server/web/app/mobile/views/pages/post.vue85
-rw-r--r--src/server/web/app/mobile/views/pages/profile-setting.vue226
-rw-r--r--src/server/web/app/mobile/views/pages/search.vue93
-rw-r--r--src/server/web/app/mobile/views/pages/selectdrive.vue96
-rw-r--r--src/server/web/app/mobile/views/pages/settings.vue103
-rw-r--r--src/server/web/app/mobile/views/pages/signup.vue57
-rw-r--r--src/server/web/app/mobile/views/pages/user.vue247
-rw-r--r--src/server/web/app/mobile/views/pages/user/home.followers-you-know.vue67
-rw-r--r--src/server/web/app/mobile/views/pages/user/home.friends.vue54
-rw-r--r--src/server/web/app/mobile/views/pages/user/home.photos.vue83
-rw-r--r--src/server/web/app/mobile/views/pages/user/home.posts.vue57
-rw-r--r--src/server/web/app/mobile/views/pages/user/home.vue94
-rw-r--r--src/server/web/app/mobile/views/pages/welcome.vue206
-rw-r--r--src/server/web/app/mobile/views/widgets/activity.vue32
-rw-r--r--src/server/web/app/mobile/views/widgets/index.ts7
-rw-r--r--src/server/web/app/mobile/views/widgets/profile.vue62
69 files changed, 0 insertions, 7589 deletions
diff --git a/src/server/web/app/mobile/api/choose-drive-file.ts b/src/server/web/app/mobile/api/choose-drive-file.ts
deleted file mode 100644
index b1a78f2364..0000000000
--- a/src/server/web/app/mobile/api/choose-drive-file.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import Chooser from '../views/components/drive-file-chooser.vue';
-
-export default function(opts) {
- return new Promise((res, rej) => {
- const o = opts || {};
- const w = new Chooser({
- propsData: {
- title: o.title,
- multiple: o.multiple,
- initFolder: o.currentFolder
- }
- }).$mount();
- w.$once('selected', file => {
- res(file);
- });
- document.body.appendChild(w.$el);
- });
-}
diff --git a/src/server/web/app/mobile/api/choose-drive-folder.ts b/src/server/web/app/mobile/api/choose-drive-folder.ts
deleted file mode 100644
index d1f97d1487..0000000000
--- a/src/server/web/app/mobile/api/choose-drive-folder.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import Chooser from '../views/components/drive-folder-chooser.vue';
-
-export default function(opts) {
- return new Promise((res, rej) => {
- const o = opts || {};
- const w = new Chooser({
- propsData: {
- title: o.title,
- initFolder: o.currentFolder
- }
- }).$mount();
- w.$once('selected', folder => {
- res(folder);
- });
- document.body.appendChild(w.$el);
- });
-}
diff --git a/src/server/web/app/mobile/api/dialog.ts b/src/server/web/app/mobile/api/dialog.ts
deleted file mode 100644
index a2378767be..0000000000
--- a/src/server/web/app/mobile/api/dialog.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export default function(opts) {
- return new Promise<string>((res, rej) => {
- alert('dialog not implemented yet');
- });
-}
diff --git a/src/server/web/app/mobile/api/input.ts b/src/server/web/app/mobile/api/input.ts
deleted file mode 100644
index 38d0fb61eb..0000000000
--- a/src/server/web/app/mobile/api/input.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-export default function(opts) {
- return new Promise<string>((res, rej) => {
- const x = window.prompt(opts.title);
- if (x) {
- res(x);
- }
- });
-}
diff --git a/src/server/web/app/mobile/api/notify.ts b/src/server/web/app/mobile/api/notify.ts
deleted file mode 100644
index 82780d196f..0000000000
--- a/src/server/web/app/mobile/api/notify.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-export default function(message) {
- alert(message);
-}
diff --git a/src/server/web/app/mobile/api/post.ts b/src/server/web/app/mobile/api/post.ts
deleted file mode 100644
index 841103fee1..0000000000
--- a/src/server/web/app/mobile/api/post.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import PostForm from '../views/components/post-form.vue';
-//import RepostForm from '../views/components/repost-form.vue';
-import getPostSummary from '../../../../common/get-post-summary';
-
-export default (os) => (opts) => {
- const o = opts || {};
-
- if (o.repost) {
- /*const vm = new RepostForm({
- propsData: {
- repost: o.repost
- }
- }).$mount();
- vm.$once('cancel', recover);
- vm.$once('post', recover);
- document.body.appendChild(vm.$el);*/
-
- const text = window.prompt(`「${getPostSummary(o.repost)}」をRepost`);
- if (text == null) return;
- os.api('posts/create', {
- repostId: o.repost.id,
- text: text == '' ? undefined : text
- });
- } else {
- const app = document.getElementById('app');
- app.style.display = 'none';
-
- function recover() {
- app.style.display = 'block';
- }
-
- const vm = new PostForm({
- parent: os.app,
- propsData: {
- reply: o.reply
- }
- }).$mount();
- vm.$once('cancel', recover);
- vm.$once('post', recover);
- document.body.appendChild(vm.$el);
- (vm as any).focus();
- }
-};
diff --git a/src/server/web/app/mobile/script.ts b/src/server/web/app/mobile/script.ts
deleted file mode 100644
index 4776fccddb..0000000000
--- a/src/server/web/app/mobile/script.ts
+++ /dev/null
@@ -1,84 +0,0 @@
-/**
- * Mobile Client
- */
-
-import VueRouter from 'vue-router';
-
-// Style
-import './style.styl';
-import '../../element.scss';
-
-import init from '../init';
-
-import chooseDriveFolder from './api/choose-drive-folder';
-import chooseDriveFile from './api/choose-drive-file';
-import dialog from './api/dialog';
-import input from './api/input';
-import post from './api/post';
-import notify from './api/notify';
-
-import MkIndex from './views/pages/index.vue';
-import MkSignup from './views/pages/signup.vue';
-import MkUser from './views/pages/user.vue';
-import MkSelectDrive from './views/pages/selectdrive.vue';
-import MkDrive from './views/pages/drive.vue';
-import MkNotifications from './views/pages/notifications.vue';
-import MkMessaging from './views/pages/messaging.vue';
-import MkMessagingRoom from './views/pages/messaging-room.vue';
-import MkPost from './views/pages/post.vue';
-import MkSearch from './views/pages/search.vue';
-import MkFollowers from './views/pages/followers.vue';
-import MkFollowing from './views/pages/following.vue';
-import MkSettings from './views/pages/settings.vue';
-import MkProfileSetting from './views/pages/profile-setting.vue';
-import MkOthello from './views/pages/othello.vue';
-
-/**
- * init
- */
-init((launch) => {
- // Register directives
- require('./views/directives');
-
- // Register components
- require('./views/components');
- require('./views/widgets');
-
- // http://qiita.com/junya/items/3ff380878f26ca447f85
- document.body.setAttribute('ontouchstart', '');
-
- // Init router
- const router = new VueRouter({
- mode: 'history',
- routes: [
- { path: '/', name: 'index', component: MkIndex },
- { path: '/signup', name: 'signup', component: MkSignup },
- { path: '/i/settings', component: MkSettings },
- { path: '/i/settings/profile', component: MkProfileSetting },
- { path: '/i/notifications', component: MkNotifications },
- { path: '/i/messaging', component: MkMessaging },
- { path: '/i/messaging/:user', component: MkMessagingRoom },
- { path: '/i/drive', component: MkDrive },
- { path: '/i/drive/folder/:folder', component: MkDrive },
- { path: '/i/drive/file/:file', component: MkDrive },
- { path: '/selectdrive', component: MkSelectDrive },
- { path: '/search', component: MkSearch },
- { path: '/othello', component: MkOthello },
- { path: '/othello/:game', component: MkOthello },
- { path: '/@:user', component: MkUser },
- { path: '/@:user/followers', component: MkFollowers },
- { path: '/@:user/following', component: MkFollowing },
- { path: '/@:user/:post', component: MkPost }
- ]
- });
-
- // Launch the app
- launch(router, os => ({
- chooseDriveFolder,
- chooseDriveFile,
- dialog,
- input,
- post: post(os),
- notify
- }));
-}, true);
diff --git a/src/server/web/app/mobile/style.styl b/src/server/web/app/mobile/style.styl
deleted file mode 100644
index 81912a2483..0000000000
--- a/src/server/web/app/mobile/style.styl
+++ /dev/null
@@ -1,15 +0,0 @@
-@import "../app"
-@import "../reset"
-
-#wait
- top auto
- bottom 15px
- left 15px
-
-html
- height 100%
-
-body
- display flex
- flex-direction column
- min-height 100%
diff --git a/src/server/web/app/mobile/views/components/activity.vue b/src/server/web/app/mobile/views/components/activity.vue
deleted file mode 100644
index 2e44017e77..0000000000
--- a/src/server/web/app/mobile/views/components/activity.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<template>
-<div class="mk-activity">
- <svg v-if="data" ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none">
- <g v-for="(d, i) in data">
- <rect width="0.8" :height="d.postsH"
- :x="i + 0.1" :y="1 - d.postsH - d.repliesH - d.repostsH"
- fill="#41ddde"/>
- <rect width="0.8" :height="d.repliesH"
- :x="i + 0.1" :y="1 - d.repliesH - d.repostsH"
- fill="#f7796c"/>
- <rect width="0.8" :height="d.repostsH"
- :x="i + 0.1" :y="1 - d.repostsH"
- fill="#a1de41"/>
- </g>
- </svg>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['user'],
- data() {
- return {
- fetching: true,
- data: [],
- peak: null
- };
- },
- mounted() {
- (this as any).api('aggregation/users/activity', {
- userId: this.user.id,
- limit: 30
- }).then(data => {
- data.forEach(d => d.total = d.posts + d.replies + d.reposts);
- this.peak = Math.max.apply(null, data.map(d => d.total));
- data.forEach(d => {
- d.postsH = d.posts / this.peak;
- d.repliesH = d.replies / this.peak;
- d.repostsH = d.reposts / this.peak;
- });
- data.reverse();
- this.data = data;
- });
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-activity
- max-width 600px
- margin 0 auto
-
- > svg
- display block
- width 100%
- height 80px
-
- > rect
- transform-origin center
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/drive-file-chooser.vue b/src/server/web/app/mobile/views/components/drive-file-chooser.vue
deleted file mode 100644
index 6806af0f1e..0000000000
--- a/src/server/web/app/mobile/views/components/drive-file-chooser.vue
+++ /dev/null
@@ -1,98 +0,0 @@
-<template>
-<div class="mk-drive-file-chooser">
- <div class="body">
- <header>
- <h1>%i18n:mobile.tags.mk-drive-selector.select-file%<span class="count" v-if="files.length > 0">({{ files.length }})</span></h1>
- <button class="close" @click="cancel">%fa:times%</button>
- <button v-if="multiple" class="ok" @click="ok">%fa:check%</button>
- </header>
- <mk-drive ref="browser"
- :select-file="true"
- :multiple="multiple"
- @change-selection="onChangeSelection"
- @selected="onSelected"
- />
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['multiple'],
- data() {
- return {
- files: []
- };
- },
- methods: {
- onChangeSelection(files) {
- this.files = files;
- },
- onSelected(file) {
- this.$emit('selected', file);
- this.$destroy();
- },
- cancel() {
- this.$emit('canceled');
- this.$destroy();
- },
- ok() {
- this.$emit('selected', this.files);
- this.$destroy();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-drive-file-chooser
- position fixed
- z-index 2048
- top 0
- left 0
- width 100%
- height 100%
- padding 8px
- background rgba(0, 0, 0, 0.2)
-
- > .body
- width 100%
- height 100%
- background #fff
-
- > header
- border-bottom solid 1px #eee
-
- > h1
- margin 0
- padding 0
- text-align center
- line-height 42px
- font-size 1em
- font-weight normal
-
- > .count
- margin-left 4px
- opacity 0.5
-
- > .close
- position absolute
- top 0
- left 0
- line-height 42px
- width 42px
-
- > .ok
- position absolute
- top 0
- right 0
- line-height 42px
- width 42px
-
- > .mk-drive
- height calc(100% - 42px)
- overflow scroll
- -webkit-overflow-scrolling touch
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/drive-folder-chooser.vue b/src/server/web/app/mobile/views/components/drive-folder-chooser.vue
deleted file mode 100644
index 853078664f..0000000000
--- a/src/server/web/app/mobile/views/components/drive-folder-chooser.vue
+++ /dev/null
@@ -1,78 +0,0 @@
-<template>
-<div class="mk-drive-folder-chooser">
- <div class="body">
- <header>
- <h1>%i18n:mobile.tags.mk-drive-folder-selector.select-folder%</h1>
- <button class="close" @click="cancel">%fa:times%</button>
- <button class="ok" @click="ok">%fa:check%</button>
- </header>
- <mk-drive ref="browser"
- select-folder
- />
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- methods: {
- cancel() {
- this.$emit('canceled');
- this.$destroy();
- },
- ok() {
- this.$emit('selected', (this.$refs.browser as any).folder);
- this.$destroy();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-drive-folder-chooser
- position fixed
- z-index 2048
- top 0
- left 0
- width 100%
- height 100%
- padding 8px
- background rgba(0, 0, 0, 0.2)
-
- > .body
- width 100%
- height 100%
- background #fff
-
- > header
- border-bottom solid 1px #eee
-
- > h1
- margin 0
- padding 0
- text-align center
- line-height 42px
- font-size 1em
- font-weight normal
-
- > .close
- position absolute
- top 0
- left 0
- line-height 42px
- width 42px
-
- > .ok
- position absolute
- top 0
- right 0
- line-height 42px
- width 42px
-
- > .mk-drive
- height calc(100% - 42px)
- overflow scroll
- -webkit-overflow-scrolling touch
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/drive.file-detail.vue b/src/server/web/app/mobile/views/components/drive.file-detail.vue
deleted file mode 100644
index f3274f677f..0000000000
--- a/src/server/web/app/mobile/views/components/drive.file-detail.vue
+++ /dev/null
@@ -1,295 +0,0 @@
-<template>
-<div class="file-detail">
- <div class="preview">
- <img v-if="kind == 'image'" ref="img"
- :src="file.url"
- :alt="file.name"
- :title="file.name"
- @load="onImageLoaded"
- :style="style">
- <template v-if="kind != 'image'">%fa:file%</template>
- <footer v-if="kind == 'image' && file.properties && file.properties.width && file.properties.height">
- <span class="size">
- <span class="width">{{ file.properties.width }}</span>
- <span class="time">×</span>
- <span class="height">{{ file.properties.height }}</span>
- <span class="px">px</span>
- </span>
- <span class="separator"></span>
- <span class="aspect-ratio">
- <span class="width">{{ file.properties.width / gcd(file.properties.width, file.properties.height) }}</span>
- <span class="colon">:</span>
- <span class="height">{{ file.properties.height / gcd(file.properties.width, file.properties.height) }}</span>
- </span>
- </footer>
- </div>
- <div class="info">
- <div>
- <span class="type"><mk-file-type-icon :type="file.type"/>{{ file.type }}</span>
- <span class="separator"></span>
- <span class="data-size">{{ file.datasize | bytes }}</span>
- <span class="separator"></span>
- <span class="created-at" @click="showCreatedAt">%fa:R clock%<mk-time :time="file.createdAt"/></span>
- </div>
- </div>
- <div class="menu">
- <div>
- <a :href="`${file.url}?download`" :download="file.name">
- %fa:download%%i18n:mobile.tags.mk-drive-file-viewer.download%
- </a>
- <button @click="rename">
- %fa:pencil-alt%%i18n:mobile.tags.mk-drive-file-viewer.rename%
- </button>
- <button @click="move">
- %fa:R folder-open%%i18n:mobile.tags.mk-drive-file-viewer.move%
- </button>
- </div>
- </div>
- <div class="exif" v-show="exif">
- <div>
- <p>
- %fa:camera%%i18n:mobile.tags.mk-drive-file-viewer.exif%
- </p>
- <pre ref="exif" class="json">{{ exif ? JSON.stringify(exif, null, 2) : '' }}</pre>
- </div>
- </div>
- <div class="hash">
- <div>
- <p>
- %fa:hashtag%%i18n:mobile.tags.mk-drive-file-viewer.hash%
- </p>
- <code>{{ file.md5 }}</code>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import * as EXIF from 'exif-js';
-import * as hljs from 'highlight.js';
-import gcd from '../../../common/scripts/gcd';
-
-export default Vue.extend({
- props: ['file'],
- data() {
- return {
- gcd,
- exif: null
- };
- },
- computed: {
- browser(): any {
- return this.$parent;
- },
- kind(): string {
- return this.file.type.split('/')[0];
- },
- style(): any {
- return this.file.properties.avgColor ? {
- 'background-color': `rgb(${ this.file.properties.avgColor.join(',') })`
- } : {};
- }
- },
- methods: {
- rename() {
- const name = window.prompt('名前を変更', this.file.name);
- if (name == null || name == '' || name == this.file.name) return;
- (this as any).api('drive/files/update', {
- fileId: this.file.id,
- name: name
- }).then(() => {
- this.browser.cf(this.file, true);
- });
- },
- move() {
- (this as any).apis.chooseDriveFolder().then(folder => {
- (this as any).api('drive/files/update', {
- fileId: this.file.id,
- folderId: folder == null ? null : folder.id
- }).then(() => {
- this.browser.cf(this.file, true);
- });
- });
- },
- showCreatedAt() {
- alert(new Date(this.file.createdAt).toLocaleString());
- },
- onImageLoaded() {
- const self = this;
- EXIF.getData(this.$refs.img, function(this: any) {
- const allMetaData = EXIF.getAllTags(this);
- self.exif = allMetaData;
- hljs.highlightBlock(self.$refs.exif);
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.file-detail
-
- > .preview
- padding 8px
- background #f0f0f0
-
- > img
- display block
- max-width 100%
- max-height 300px
- margin 0 auto
- box-shadow 1px 1px 4px rgba(0, 0, 0, 0.2)
-
- > footer
- padding 8px 8px 0 8px
- font-size 0.8em
- color #888
- text-align center
-
- > .separator
- display inline
- padding 0 4px
-
- > .size
- display inline
-
- .time
- margin 0 2px
-
- .px
- margin-left 4px
-
- > .aspect-ratio
- display inline
- opacity 0.7
-
- &:before
- content "("
-
- &:after
- content ")"
-
- > .info
- padding 14px
- font-size 0.8em
- border-top solid 1px #dfdfdf
-
- > div
- max-width 500px
- margin 0 auto
-
- > .separator
- padding 0 4px
- color #cdcdcd
-
- > .type
- > .data-size
- color #9d9d9d
-
- > mk-file-type-icon
- margin-right 4px
-
- > .created-at
- color #bdbdbd
-
- > [data-fa]
- margin-right 2px
-
- > .menu
- padding 14px
- border-top solid 1px #dfdfdf
-
- > div
- max-width 500px
- margin 0 auto
-
- > *
- display block
- width 100%
- padding 10px 16px
- margin 0 0 12px 0
- color #333
- font-size 0.9em
- text-align center
- text-decoration none
- text-shadow 0 1px 0 rgba(255, 255, 255, 0.9)
- background-image linear-gradient(#fafafa, #eaeaea)
- border 1px solid #ddd
- border-bottom-color #cecece
- border-radius 3px
-
- &:last-child
- margin-bottom 0
-
- &:active
- background-color #767676
- background-image none
- border-color #444
- box-shadow 0 1px 3px rgba(0, 0, 0, 0.075), inset 0 0 5px rgba(0, 0, 0, 0.2)
-
- > [data-fa]
- margin-right 4px
-
- > .hash
- padding 14px
- border-top solid 1px #dfdfdf
-
- > div
- max-width 500px
- margin 0 auto
-
- > p
- display block
- margin 0
- padding 0
- color #555
- font-size 0.9em
-
- > [data-fa]
- margin-right 4px
-
- > code
- display block
- width 100%
- margin 6px 0 0 0
- padding 8px
- white-space nowrap
- overflow auto
- font-size 0.8em
- color #222
- border solid 1px #dfdfdf
- border-radius 2px
- background #f5f5f5
-
- > .exif
- padding 14px
- border-top solid 1px #dfdfdf
-
- > div
- max-width 500px
- margin 0 auto
-
- > p
- display block
- margin 0
- padding 0
- color #555
- font-size 0.9em
-
- > [data-fa]
- margin-right 4px
-
- > pre
- display block
- width 100%
- margin 6px 0 0 0
- padding 8px
- height 128px
- overflow auto
- font-size 0.9em
- border solid 1px #dfdfdf
- border-radius 2px
- background #f5f5f5
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/drive.file.vue b/src/server/web/app/mobile/views/components/drive.file.vue
deleted file mode 100644
index 7d1957042b..0000000000
--- a/src/server/web/app/mobile/views/components/drive.file.vue
+++ /dev/null
@@ -1,171 +0,0 @@
-<template>
-<a class="file" @click.prevent="onClick" :href="`/i/drive/file/${ file.id }`" :data-is-selected="isSelected">
- <div class="container">
- <div class="thumbnail" :style="thumbnail"></div>
- <div class="body">
- <p class="name">
- <span>{{ file.name.lastIndexOf('.') != -1 ? file.name.substr(0, file.name.lastIndexOf('.')) : file.name }}</span>
- <span class="ext" v-if="file.name.lastIndexOf('.') != -1">{{ file.name.substr(file.name.lastIndexOf('.')) }}</span>
- </p>
- <!--
- if file.tags.length > 0
- ul.tags
- each tag in file.tags
- li.tag(style={background: tag.color, color: contrast(tag.color)})= tag.name
- -->
- <footer>
- <p class="type"><mk-file-type-icon :type="file.type"/>{{ file.type }}</p>
- <p class="separator"></p>
- <p class="data-size">{{ file.datasize | bytes }}</p>
- <p class="separator"></p>
- <p class="created-at">
- %fa:R clock%<mk-time :time="file.createdAt"/>
- </p>
- </footer>
- </div>
- </div>
-</a>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['file'],
- data() {
- return {
- isSelected: false
- };
- },
- computed: {
- browser(): any {
- return this.$parent;
- },
- thumbnail(): any {
- return {
- 'background-color': this.file.properties.avgColor ? `rgb(${this.file.properties.avgColor.join(',')})` : 'transparent',
- 'background-image': `url(${this.file.url}?thumbnail&size=128)`
- };
- }
- },
- created() {
- this.isSelected = this.browser.selectedFiles.some(f => f.id == this.file.id)
-
- this.browser.$on('change-selection', this.onBrowserChangeSelection);
- },
- beforeDestroy() {
- this.browser.$off('change-selection', this.onBrowserChangeSelection);
- },
- methods: {
- onBrowserChangeSelection(selections) {
- this.isSelected = selections.some(f => f.id == this.file.id);
- },
- onClick() {
- this.browser.chooseFile(this.file);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.file
- display block
- text-decoration none !important
-
- *
- user-select none
- pointer-events none
-
- > .container
- max-width 500px
- margin 0 auto
- padding 16px
-
- &:after
- content ""
- display block
- clear both
-
- > .thumbnail
- display block
- float left
- width 64px
- height 64px
- background-size cover
- background-position center center
-
- > .body
- display block
- float left
- width calc(100% - 74px)
- margin-left 10px
-
- > .name
- display block
- margin 0
- padding 0
- font-size 0.9em
- font-weight bold
- color #555
- text-overflow ellipsis
- overflow-wrap break-word
-
- > .ext
- opacity 0.5
-
- > .tags
- display block
- margin 4px 0 0 0
- padding 0
- list-style none
- font-size 0.5em
-
- > .tag
- display inline-block
- margin 0 5px 0 0
- padding 1px 5px
- border-radius 2px
-
- > footer
- display block
- margin 4px 0 0 0
- font-size 0.7em
-
- > .separator
- display inline
- margin 0
- padding 0 4px
- color #CDCDCD
-
- > .type
- display inline
- margin 0
- padding 0
- color #9D9D9D
-
- > .mk-file-type-icon
- margin-right 4px
-
- > .data-size
- display inline
- margin 0
- padding 0
- color #9D9D9D
-
- > .created-at
- display inline
- margin 0
- padding 0
- color #BDBDBD
-
- > [data-fa]
- margin-right 2px
-
- &[data-is-selected]
- background $theme-color
-
- &, *
- color #fff !important
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/drive.folder.vue b/src/server/web/app/mobile/views/components/drive.folder.vue
deleted file mode 100644
index 22ff38fecb..0000000000
--- a/src/server/web/app/mobile/views/components/drive.folder.vue
+++ /dev/null
@@ -1,58 +0,0 @@
-<template>
-<a class="root folder" @click.prevent="onClick" :href="`/i/drive/folder/${ folder.id }`">
- <div class="container">
- <p class="name">%fa:folder%{{ folder.name }}</p>%fa:angle-right%
- </div>
-</a>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['folder'],
- computed: {
- browser(): any {
- return this.$parent;
- }
- },
- methods: {
- onClick() {
- this.browser.cd(this.folder);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.folder
- display block
- color #777
- text-decoration none !important
-
- *
- user-select none
- pointer-events none
-
- > .container
- max-width 500px
- margin 0 auto
- padding 16px
-
- > .name
- display block
- margin 0
- padding 0
-
- > [data-fa]
- margin-right 6px
-
- > [data-fa]
- position absolute
- top 0
- bottom 0
- right 20px
-
- > *
- height 100%
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/drive.vue b/src/server/web/app/mobile/views/components/drive.vue
deleted file mode 100644
index ff5366a0ad..0000000000
--- a/src/server/web/app/mobile/views/components/drive.vue
+++ /dev/null
@@ -1,581 +0,0 @@
-<template>
-<div class="mk-drive">
- <nav ref="nav">
- <a @click.prevent="goRoot()" href="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-drive.drive%</a>
- <template v-for="folder in hierarchyFolders">
- <span :key="folder.id + '>'">%fa:angle-right%</span>
- <a :key="folder.id" @click.prevent="cd(folder)" :href="`/i/drive/folder/${folder.id}`">{{ folder.name }}</a>
- </template>
- <template v-if="folder != null">
- <span>%fa:angle-right%</span>
- <p>{{ folder.name }}</p>
- </template>
- <template v-if="file != null">
- <span>%fa:angle-right%</span>
- <p>{{ file.name }}</p>
- </template>
- </nav>
- <mk-uploader ref="uploader"/>
- <div class="browser" :class="{ fetching }" v-if="file == null">
- <div class="info" v-if="info">
- <p v-if="folder == null">{{ (info.usage / info.capacity * 100).toFixed(1) }}% %i18n:mobile.tags.mk-drive.used%</p>
- <p v-if="folder != null && (folder.foldersCount > 0 || folder.filesCount > 0)">
- <template v-if="folder.foldersCount > 0">{{ folder.foldersCount }} %i18n:mobile.tags.mk-drive.folder-count%</template>
- <template v-if="folder.foldersCount > 0 && folder.filesCount > 0">%i18n:mobile.tags.mk-drive.count-separator%</template>
- <template v-if="folder.filesCount > 0">{{ folder.filesCount }} %i18n:mobile.tags.mk-drive.file-count%</template>
- </p>
- </div>
- <div class="folders" v-if="folders.length > 0">
- <x-folder v-for="folder in folders" :key="folder.id" :folder="folder"/>
- <p v-if="moreFolders">%i18n:mobile.tags.mk-drive.load-more%</p>
- </div>
- <div class="files" v-if="files.length > 0">
- <x-file v-for="file in files" :key="file.id" :file="file"/>
- <button class="more" v-if="moreFiles" @click="fetchMoreFiles">
- {{ fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-drive.load-more%' }}
- </button>
- </div>
- <div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
- <p v-if="folder == null">%i18n:mobile.tags.mk-drive.nothing-in-drive%</p>
- <p v-if="folder != null">%i18n:mobile.tags.mk-drive.folder-is-empty%</p>
- </div>
- </div>
- <div class="fetching" v-if="fetching && file == null && files.length == 0 && folders.length == 0">
- <div class="spinner">
- <div class="dot1"></div>
- <div class="dot2"></div>
- </div>
- </div>
- <input ref="file" class="file" type="file" multiple="multiple" @change="onChangeLocalFile"/>
- <x-file-detail v-if="file != null" :file="file"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import XFolder from './drive.folder.vue';
-import XFile from './drive.file.vue';
-import XFileDetail from './drive.file-detail.vue';
-
-export default Vue.extend({
- components: {
- XFolder,
- XFile,
- XFileDetail
- },
- props: ['initFolder', 'initFile', 'selectFile', 'multiple', 'isNaked', 'top'],
- data() {
- return {
- /**
- * 現在の階層(フォルダ)
- * * null でルートを表す
- */
- folder: null,
-
- file: null,
-
- files: [],
- folders: [],
- moreFiles: false,
- moreFolders: false,
- hierarchyFolders: [],
- selectedFiles: [],
- info: null,
- connection: null,
- connectionId: null,
-
- fetching: true,
- fetchingMoreFiles: false,
- fetchingMoreFolders: false
- };
- },
- computed: {
- isFileSelectMode(): boolean {
- return this.selectFile;
- }
- },
- mounted() {
- this.connection = (this as any).os.streams.driveStream.getConnection();
- this.connectionId = (this as any).os.streams.driveStream.use();
-
- this.connection.on('file_created', this.onStreamDriveFileCreated);
- this.connection.on('file_updated', this.onStreamDriveFileUpdated);
- this.connection.on('folder_created', this.onStreamDriveFolderCreated);
- this.connection.on('folder_updated', this.onStreamDriveFolderUpdated);
-
- if (this.initFolder) {
- this.cd(this.initFolder, true);
- } else if (this.initFile) {
- this.cf(this.initFile, true);
- } else {
- this.fetch();
- }
-
- if (this.isNaked) {
- (this.$refs.nav as any).style.top = `${this.top}px`;
- }
- },
- beforeDestroy() {
- this.connection.off('file_created', this.onStreamDriveFileCreated);
- this.connection.off('file_updated', this.onStreamDriveFileUpdated);
- this.connection.off('folder_created', this.onStreamDriveFolderCreated);
- this.connection.off('folder_updated', this.onStreamDriveFolderUpdated);
- (this as any).os.streams.driveStream.dispose(this.connectionId);
- },
- methods: {
- onStreamDriveFileCreated(file) {
- this.addFile(file, true);
- },
-
- onStreamDriveFileUpdated(file) {
- const current = this.folder ? this.folder.id : null;
- if (current != file.folderId) {
- this.removeFile(file);
- } else {
- this.addFile(file, true);
- }
- },
-
- onStreamDriveFolderCreated(folder) {
- this.addFolder(folder, true);
- },
-
- onStreamDriveFolderUpdated(folder) {
- const current = this.folder ? this.folder.id : null;
- if (current != folder.parentId) {
- this.removeFolder(folder);
- } else {
- this.addFolder(folder, true);
- }
- },
-
- dive(folder) {
- this.hierarchyFolders.unshift(folder);
- if (folder.parent) this.dive(folder.parent);
- },
-
- cd(target, silent = false) {
- this.file = null;
-
- if (target == null) {
- this.goRoot(silent);
- return;
- } else if (typeof target == 'object') {
- target = target.id;
- }
-
- this.fetching = true;
-
- (this as any).api('drive/folders/show', {
- folderId: target
- }).then(folder => {
- this.folder = folder;
- this.hierarchyFolders = [];
-
- if (folder.parent) this.dive(folder.parent);
-
- this.$emit('open-folder', this.folder, silent);
- this.fetch();
- });
- },
-
- addFolder(folder, unshift = false) {
- const current = this.folder ? this.folder.id : null;
- // 追加しようとしているフォルダが、今居る階層とは違う階層のものだったら中断
- if (current != folder.parentId) return;
-
- // 追加しようとしているフォルダを既に所有してたら中断
- if (this.folders.some(f => f.id == folder.id)) return;
-
- if (unshift) {
- this.folders.unshift(folder);
- } else {
- this.folders.push(folder);
- }
- },
-
- addFile(file, unshift = false) {
- const current = this.folder ? this.folder.id : null;
- // 追加しようとしているファイルが、今居る階層とは違う階層のものだったら中断
- if (current != file.folderId) return;
-
- if (this.files.some(f => f.id == file.id)) {
- const exist = this.files.map(f => f.id).indexOf(file.id);
- Vue.set(this.files, exist, file);
- return;
- }
-
- if (unshift) {
- this.files.unshift(file);
- } else {
- this.files.push(file);
- }
- },
-
- removeFolder(folder) {
- if (typeof folder == 'object') folder = folder.id;
- this.folders = this.folders.filter(f => f.id != folder);
- },
-
- removeFile(file) {
- if (typeof file == 'object') file = file.id;
- this.files = this.files.filter(f => f.id != file);
- },
-
- appendFile(file) {
- this.addFile(file);
- },
- appendFolder(folder) {
- this.addFolder(folder);
- },
- prependFile(file) {
- this.addFile(file, true);
- },
- prependFolder(folder) {
- this.addFolder(folder, true);
- },
-
- goRoot(silent = false) {
- if (this.folder || this.file) {
- this.file = null;
- this.folder = null;
- this.hierarchyFolders = [];
- this.$emit('move-root', silent);
- this.fetch();
- }
- },
-
- fetch() {
- this.folders = [];
- this.files = [];
- this.moreFolders = false;
- this.moreFiles = false;
- this.fetching = true;
-
- this.$emit('begin-fetch');
-
- let fetchedFolders = null;
- let fetchedFiles = null;
-
- const foldersMax = 20;
- const filesMax = 20;
-
- // フォルダ一覧取得
- (this as any).api('drive/folders', {
- folderId: this.folder ? this.folder.id : null,
- limit: foldersMax + 1
- }).then(folders => {
- if (folders.length == foldersMax + 1) {
- this.moreFolders = true;
- folders.pop();
- }
- fetchedFolders = folders;
- complete();
- });
-
- // ファイル一覧取得
- (this as any).api('drive/files', {
- folderId: this.folder ? this.folder.id : null,
- limit: filesMax + 1
- }).then(files => {
- if (files.length == filesMax + 1) {
- this.moreFiles = true;
- files.pop();
- }
- fetchedFiles = files;
- complete();
- });
-
- let flag = false;
- const complete = () => {
- if (flag) {
- fetchedFolders.forEach(this.appendFolder);
- fetchedFiles.forEach(this.appendFile);
- this.fetching = false;
-
- // 一連の読み込みが完了したイベントを発行
- this.$emit('fetched');
- } else {
- flag = true;
- // 一連の読み込みが半分完了したイベントを発行
- this.$emit('fetch-mid');
- }
- };
-
- if (this.folder == null) {
- // Fetch addtional drive info
- (this as any).api('drive').then(info => {
- this.info = info;
- });
- }
- },
-
- fetchMoreFiles() {
- this.fetching = true;
- this.fetchingMoreFiles = true;
-
- const max = 30;
-
- // ファイル一覧取得
- (this as any).api('drive/files', {
- folderId: this.folder ? this.folder.id : null,
- limit: max + 1,
- untilId: this.files[this.files.length - 1].id
- }).then(files => {
- if (files.length == max + 1) {
- this.moreFiles = true;
- files.pop();
- } else {
- this.moreFiles = false;
- }
- files.forEach(this.appendFile);
- this.fetching = false;
- this.fetchingMoreFiles = false;
- });
- },
-
- chooseFile(file) {
- if (this.isFileSelectMode) {
- if (this.multiple) {
- if (this.selectedFiles.some(f => f.id == file.id)) {
- this.selectedFiles = this.selectedFiles.filter(f => f.id != file.id);
- } else {
- this.selectedFiles.push(file);
- }
- this.$emit('change-selection', this.selectedFiles);
- } else {
- this.$emit('selected', file);
- }
- } else {
- this.cf(file);
- }
- },
-
- cf(file, silent = false) {
- if (typeof file == 'object') file = file.id;
-
- this.fetching = true;
-
- (this as any).api('drive/files/show', {
- fileId: file
- }).then(file => {
- this.file = file;
- this.folder = null;
- this.hierarchyFolders = [];
-
- if (file.folder) this.dive(file.folder);
-
- this.fetching = false;
-
- this.$emit('open-file', this.file, silent);
- });
- },
-
- openContextMenu() {
- const fn = window.prompt('何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>');
- if (fn == null || fn == '') return;
- switch (fn) {
- case '1':
- this.selectLocalFile();
- break;
- case '2':
- this.urlUpload();
- break;
- case '3':
- this.createFolder();
- break;
- case '4':
- this.renameFolder();
- break;
- case '5':
- this.moveFolder();
- break;
- case '6':
- alert('ごめんなさい!フォルダの削除は未実装です...。');
- break;
- }
- },
-
- selectLocalFile() {
- (this.$refs.file as any).click();
- },
-
- createFolder() {
- const name = window.prompt('フォルダー名');
- if (name == null || name == '') return;
- (this as any).api('drive/folders/create', {
- name: name,
- parentId: this.folder ? this.folder.id : undefined
- }).then(folder => {
- this.addFolder(folder, true);
- });
- },
-
- renameFolder() {
- if (this.folder == null) {
- alert('現在いる場所はルートで、フォルダではないため名前の変更はできません。名前を変更したいフォルダに移動してからやってください。');
- return;
- }
- const name = window.prompt('フォルダー名', this.folder.name);
- if (name == null || name == '') return;
- (this as any).api('drive/folders/update', {
- name: name,
- folderId: this.folder.id
- }).then(folder => {
- this.cd(folder);
- });
- },
-
- moveFolder() {
- if (this.folder == null) {
- alert('現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。');
- return;
- }
- (this as any).apis.chooseDriveFolder().then(folder => {
- (this as any).api('drive/folders/update', {
- parentId: folder ? folder.id : null,
- folderId: this.folder.id
- }).then(folder => {
- this.cd(folder);
- });
- });
- },
-
- urlUpload() {
- const url = window.prompt('アップロードしたいファイルのURL');
- if (url == null || url == '') return;
- (this as any).api('drive/files/upload_from_url', {
- url: url,
- folderId: this.folder ? this.folder.id : undefined
- });
- alert('アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。');
- },
-
- onChangeLocalFile() {
- Array.from((this.$refs.file as any).files)
- .forEach(f => (this.$refs.uploader as any).upload(f, this.folder));
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-drive
- background #fff
-
- > nav
- display block
- position sticky
- position -webkit-sticky
- top 0
- z-index 1
- width 100%
- padding 10px 12px
- overflow auto
- white-space nowrap
- font-size 0.9em
- color rgba(0, 0, 0, 0.67)
- -webkit-backdrop-filter blur(12px)
- backdrop-filter blur(12px)
- background-color rgba(#fff, 0.75)
- border-bottom solid 1px rgba(0, 0, 0, 0.13)
-
- > p
- > a
- display inline
- margin 0
- padding 0
- text-decoration none !important
- color inherit
-
- &:last-child
- font-weight bold
-
- > [data-fa]
- margin-right 4px
-
- > span
- margin 0 8px
- opacity 0.5
-
- > .browser
- &.fetching
- opacity 0.5
-
- > .info
- border-bottom solid 1px #eee
-
- &:empty
- display none
-
- > p
- display block
- max-width 500px
- margin 0 auto
- padding 4px 16px
- font-size 10px
- color #777
-
- > .folders
- > .folder
- border-bottom solid 1px #eee
-
- > .files
- > .file
- border-bottom solid 1px #eee
-
- > .more
- display block
- width 100%
- padding 16px
- font-size 16px
- color #555
-
- > .empty
- padding 16px
- text-align center
- color #999
- pointer-events none
-
- > p
- margin 0
-
- > .fetching
- .spinner
- margin 100px auto
- width 40px
- height 40px
- text-align center
-
- animation sk-rotate 2.0s infinite linear
-
- .dot1, .dot2
- width 60%
- height 60%
- display inline-block
- position absolute
- top 0
- background rgba(0, 0, 0, 0.2)
- border-radius 100%
-
- animation sk-bounce 2.0s infinite ease-in-out
-
- .dot2
- top auto
- bottom 0
- animation-delay -1.0s
-
- @keyframes sk-rotate { 100% { transform: rotate(360deg); }}
-
- @keyframes sk-bounce {
- 0%, 100% {
- transform: scale(0.0);
- } 50% {
- transform: scale(1.0);
- }
- }
-
- > .file
- display none
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/follow-button.vue b/src/server/web/app/mobile/views/components/follow-button.vue
deleted file mode 100644
index 43c69d4e02..0000000000
--- a/src/server/web/app/mobile/views/components/follow-button.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-<template>
-<button class="mk-follow-button"
- :class="{ wait: wait, follow: !user.isFollowing, unfollow: user.isFollowing }"
- @click="onClick"
- :disabled="wait"
->
- <template v-if="!wait && user.isFollowing">%fa:minus%</template>
- <template v-if="!wait && !user.isFollowing">%fa:plus%</template>
- <template v-if="wait">%fa:spinner .pulse .fw%</template>
- {{ user.isFollowing ? '%i18n:mobile.tags.mk-follow-button.unfollow%' : '%i18n:mobile.tags.mk-follow-button.follow%' }}
-</button>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: {
- user: {
- type: Object,
- required: true
- }
- },
- data() {
- return {
- wait: false,
- connection: null,
- connectionId: null
- };
- },
- mounted() {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
-
- this.connection.on('follow', this.onFollow);
- this.connection.on('unfollow', this.onUnfollow);
- },
- beforeDestroy() {
- this.connection.off('follow', this.onFollow);
- this.connection.off('unfollow', this.onUnfollow);
- (this as any).os.stream.dispose(this.connectionId);
- },
- methods: {
-
- onFollow(user) {
- if (user.id == this.user.id) {
- this.user.isFollowing = user.isFollowing;
- }
- },
-
- onUnfollow(user) {
- if (user.id == this.user.id) {
- this.user.isFollowing = user.isFollowing;
- }
- },
-
- onClick() {
- this.wait = true;
- if (this.user.isFollowing) {
- (this as any).api('following/delete', {
- userId: this.user.id
- }).then(() => {
- this.user.isFollowing = false;
- }).catch(err => {
- console.error(err);
- }).then(() => {
- this.wait = false;
- });
- } else {
- (this as any).api('following/create', {
- userId: this.user.id
- }).then(() => {
- this.user.isFollowing = true;
- }).catch(err => {
- console.error(err);
- }).then(() => {
- this.wait = false;
- });
- }
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-follow-button
- display block
- user-select none
- cursor pointer
- padding 0 16px
- margin 0
- height inherit
- font-size 16px
- outline none
- border solid 1px $theme-color
- border-radius 4px
-
- *
- pointer-events none
-
- &.follow
- color $theme-color
- background transparent
-
- &:hover
- background rgba($theme-color, 0.1)
-
- &:active
- background rgba($theme-color, 0.2)
-
- &.unfollow
- color $theme-color-foreground
- background $theme-color
-
- &.wait
- cursor wait !important
- opacity 0.7
-
- > [data-fa]
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/friends-maker.vue b/src/server/web/app/mobile/views/components/friends-maker.vue
deleted file mode 100644
index 961a5f568a..0000000000
--- a/src/server/web/app/mobile/views/components/friends-maker.vue
+++ /dev/null
@@ -1,127 +0,0 @@
-<template>
-<div class="mk-friends-maker">
- <p class="title">気になるユーザーをフォロー:</p>
- <div class="users" v-if="!fetching && users.length > 0">
- <mk-user-card v-for="user in users" :key="user.id" :user="user"/>
- </div>
- <p class="empty" v-if="!fetching && users.length == 0">おすすめのユーザーは見つかりませんでした。</p>
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%読み込んでいます<mk-ellipsis/></p>
- <a class="refresh" @click="refresh">もっと見る</a>
- <button class="close" @click="close" title="閉じる">%fa:times%</button>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- data() {
- return {
- users: [],
- fetching: true,
- limit: 6,
- page: 0
- };
- },
- mounted() {
- this.fetch();
- },
- methods: {
- fetch() {
- this.fetching = true;
- this.users = [];
-
- (this as any).api('users/recommendation', {
- limit: this.limit,
- offset: this.limit * this.page
- }).then(users => {
- this.users = users;
- this.fetching = false;
- });
- },
- refresh() {
- if (this.users.length < this.limit) {
- this.page = 0;
- } else {
- this.page++;
- }
- this.fetch();
- },
- close() {
- this.$destroy();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-friends-maker
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- > .title
- margin 0
- padding 8px 16px
- font-size 1em
- font-weight bold
- color #888
-
- > .users
- overflow-x scroll
- -webkit-overflow-scrolling touch
- white-space nowrap
- padding 16px
- background #eee
-
- > .mk-user-card
- &:not(:last-child)
- margin-right 16px
-
- > .empty
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > .fetching
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > [data-fa]
- margin-right 4px
-
- > .refresh
- display block
- margin 0
- padding 8px 16px
- text-align right
- font-size 0.9em
- color #999
-
- > .close
- cursor pointer
- display block
- position absolute
- top 0
- right 0
- z-index 1
- margin 0
- padding 0
- font-size 1.2em
- color #999
- border none
- outline none
- background transparent
-
- &:hover
- color #555
-
- &:active
- color #222
-
- > [data-fa]
- padding 10px
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/index.ts b/src/server/web/app/mobile/views/components/index.ts
deleted file mode 100644
index fb8f65f47d..0000000000
--- a/src/server/web/app/mobile/views/components/index.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import Vue from 'vue';
-
-import ui from './ui.vue';
-import timeline from './timeline.vue';
-import post from './post.vue';
-import posts from './posts.vue';
-import mediaImage from './media-image.vue';
-import mediaVideo from './media-video.vue';
-import drive from './drive.vue';
-import postPreview from './post-preview.vue';
-import subPostContent from './sub-post-content.vue';
-import postCard from './post-card.vue';
-import userCard from './user-card.vue';
-import postDetail from './post-detail.vue';
-import followButton from './follow-button.vue';
-import friendsMaker from './friends-maker.vue';
-import notification from './notification.vue';
-import notifications from './notifications.vue';
-import notificationPreview from './notification-preview.vue';
-import usersList from './users-list.vue';
-import userPreview from './user-preview.vue';
-import userTimeline from './user-timeline.vue';
-import activity from './activity.vue';
-import widgetContainer from './widget-container.vue';
-
-Vue.component('mk-ui', ui);
-Vue.component('mk-timeline', timeline);
-Vue.component('mk-post', post);
-Vue.component('mk-posts', posts);
-Vue.component('mk-media-image', mediaImage);
-Vue.component('mk-media-video', mediaVideo);
-Vue.component('mk-drive', drive);
-Vue.component('mk-post-preview', postPreview);
-Vue.component('mk-sub-post-content', subPostContent);
-Vue.component('mk-post-card', postCard);
-Vue.component('mk-user-card', userCard);
-Vue.component('mk-post-detail', postDetail);
-Vue.component('mk-follow-button', followButton);
-Vue.component('mk-friends-maker', friendsMaker);
-Vue.component('mk-notification', notification);
-Vue.component('mk-notifications', notifications);
-Vue.component('mk-notification-preview', notificationPreview);
-Vue.component('mk-users-list', usersList);
-Vue.component('mk-user-preview', userPreview);
-Vue.component('mk-user-timeline', userTimeline);
-Vue.component('mk-activity', activity);
-Vue.component('mk-widget-container', widgetContainer);
diff --git a/src/server/web/app/mobile/views/components/media-image.vue b/src/server/web/app/mobile/views/components/media-image.vue
deleted file mode 100644
index cfc2134988..0000000000
--- a/src/server/web/app/mobile/views/components/media-image.vue
+++ /dev/null
@@ -1,31 +0,0 @@
-<template>
-<a class="mk-media-image" :href="image.url" target="_blank" :style="style" :title="image.name"></a>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: ['image'],
- computed: {
- style(): any {
- return {
- 'background-color': this.image.properties.avgColor ? `rgb(${this.image.properties.avgColor.join(',')})` : 'transparent',
- 'background-image': `url(${this.image.url}?thumbnail&size=512)`
- };
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-media-image
- display block
- overflow hidden
- width 100%
- height 100%
- background-position center
- background-size cover
- border-radius 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/media-video.vue b/src/server/web/app/mobile/views/components/media-video.vue
deleted file mode 100644
index 68cd48587a..0000000000
--- a/src/server/web/app/mobile/views/components/media-video.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
- <a class="mk-media-video"
- :href="video.url"
- target="_blank"
- :style="imageStyle"
- :title="video.name">
- %fa:R play-circle%
- </a>
-</template>
-
-<script lang="ts">
-import Vue from 'vue'
-export default Vue.extend({
- props: ['video'],
- computed: {
- imageStyle(): any {
- return {
- 'background-image': `url(${this.video.url}?thumbnail&size=512)`
- };
- }
- },})
-</script>
-
-<style lang="stylus" scoped>
-.mk-media-video
- display flex
- justify-content center
- align-items center
-
- font-size 3.5em
- overflow hidden
- background-position center
- background-size cover
- width 100%
- height 100%
-</style>
diff --git a/src/server/web/app/mobile/views/components/notification-preview.vue b/src/server/web/app/mobile/views/components/notification-preview.vue
deleted file mode 100644
index fce9ed82f9..0000000000
--- a/src/server/web/app/mobile/views/components/notification-preview.vue
+++ /dev/null
@@ -1,128 +0,0 @@
-<template>
-<div class="mk-notification-preview" :class="notification.type">
- <template v-if="notification.type == 'reaction'">
- <img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p><mk-reaction-icon :reaction="notification.reaction"/>{{ notification.user.name }}</p>
- <p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%</p>
- </div>
- </template>
-
- <template v-if="notification.type == 'repost'">
- <img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p>%fa:retweet%{{ notification.post.user.name }}</p>
- <p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%</p>
- </div>
- </template>
-
- <template v-if="notification.type == 'quote'">
- <img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p>%fa:quote-left%{{ notification.post.user.name }}</p>
- <p class="post-preview">{{ getPostSummary(notification.post) }}</p>
- </div>
- </template>
-
- <template v-if="notification.type == 'follow'">
- <img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p>%fa:user-plus%{{ notification.user.name }}</p>
- </div>
- </template>
-
- <template v-if="notification.type == 'reply'">
- <img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p>%fa:reply%{{ notification.post.user.name }}</p>
- <p class="post-preview">{{ getPostSummary(notification.post) }}</p>
- </div>
- </template>
-
- <template v-if="notification.type == 'mention'">
- <img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p>%fa:at%{{ notification.post.user.name }}</p>
- <p class="post-preview">{{ getPostSummary(notification.post) }}</p>
- </div>
- </template>
-
- <template v-if="notification.type == 'poll_vote'">
- <img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- <div class="text">
- <p>%fa:chart-pie%{{ notification.user.name }}</p>
- <p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%</p>
- </div>
- </template>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getPostSummary from '../../../../../common/get-post-summary';
-
-export default Vue.extend({
- props: ['notification'],
- data() {
- return {
- getPostSummary
- };
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-notification-preview
- margin 0
- padding 8px
- color #fff
- overflow-wrap break-word
-
- &:after
- content ""
- display block
- clear both
-
- img
- display block
- float left
- min-width 36px
- min-height 36px
- max-width 36px
- max-height 36px
- border-radius 6px
-
- .text
- float right
- width calc(100% - 36px)
- padding-left 8px
-
- p
- margin 0
-
- i, mk-reaction-icon
- margin-right 4px
-
- .post-ref
-
- [data-fa]
- font-size 1em
- font-weight normal
- font-style normal
- display inline-block
- margin-right 3px
-
- &.repost, &.quote
- .text p i
- color #77B255
-
- &.follow
- .text p i
- color #53c7ce
-
- &.reply, &.mention
- .text p i
- color #fff
-
-</style>
-
diff --git a/src/server/web/app/mobile/views/components/notification.vue b/src/server/web/app/mobile/views/components/notification.vue
deleted file mode 100644
index e221fb3ac4..0000000000
--- a/src/server/web/app/mobile/views/components/notification.vue
+++ /dev/null
@@ -1,164 +0,0 @@
-<template>
-<div class="mk-notification">
- <div class="notification reaction" v-if="notification.type == 'reaction'">
- <mk-time :time="notification.createdAt"/>
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="text">
- <p>
- <mk-reaction-icon :reaction="notification.reaction"/>
- <router-link :to="`/@${acct}`">{{ notification.user.name }}</router-link>
- </p>
- <router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
- %fa:quote-left%{{ getPostSummary(notification.post) }}
- %fa:quote-right%
- </router-link>
- </div>
- </div>
-
- <div class="notification repost" v-if="notification.type == 'repost'">
- <mk-time :time="notification.createdAt"/>
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="text">
- <p>
- %fa:retweet%
- <router-link :to="`/@${acct}`">{{ notification.post.user.name }}</router-link>
- </p>
- <router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
- %fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%
- </router-link>
- </div>
- </div>
-
- <template v-if="notification.type == 'quote'">
- <mk-post :post="notification.post"/>
- </template>
-
- <div class="notification follow" v-if="notification.type == 'follow'">
- <mk-time :time="notification.createdAt"/>
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="text">
- <p>
- %fa:user-plus%
- <router-link :to="`/@${acct}`">{{ notification.user.name }}</router-link>
- </p>
- </div>
- </div>
-
- <template v-if="notification.type == 'reply'">
- <mk-post :post="notification.post"/>
- </template>
-
- <template v-if="notification.type == 'mention'">
- <mk-post :post="notification.post"/>
- </template>
-
- <div class="notification poll_vote" v-if="notification.type == 'poll_vote'">
- <mk-time :time="notification.createdAt"/>
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="text">
- <p>
- %fa:chart-pie%
- <router-link :to="`/@${acct}`">{{ notification.user.name }}</router-link>
- </p>
- <router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
- %fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%
- </router-link>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getPostSummary from '../../../../../common/get-post-summary';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['notification'],
- computed: {
- acct() {
- return getAcct(this.notification.user);
- }
- },
- data() {
- return {
- getPostSummary
- };
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-notification
-
- > .notification
- padding 16px
- overflow-wrap break-word
-
- &:after
- content ""
- display block
- clear both
-
- > .mk-time
- display inline
- position absolute
- top 16px
- right 12px
- vertical-align top
- color rgba(0, 0, 0, 0.6)
- font-size 0.9em
-
- > .avatar-anchor
- display block
- float left
-
- img
- min-width 36px
- min-height 36px
- max-width 36px
- max-height 36px
- border-radius 6px
-
- > .text
- float right
- width calc(100% - 36px)
- padding-left 8px
-
- p
- margin 0
-
- i, .mk-reaction-icon
- margin-right 4px
-
- > .post-preview
- color rgba(0, 0, 0, 0.7)
-
- > .post-ref
- color rgba(0, 0, 0, 0.7)
-
- [data-fa]
- font-size 1em
- font-weight normal
- font-style normal
- display inline-block
- margin-right 3px
-
- &.repost
- .text p i
- color #77B255
-
- &.follow
- .text p i
- color #53c7ce
-
-</style>
-
diff --git a/src/server/web/app/mobile/views/components/notifications.vue b/src/server/web/app/mobile/views/components/notifications.vue
deleted file mode 100644
index d68b990dfa..0000000000
--- a/src/server/web/app/mobile/views/components/notifications.vue
+++ /dev/null
@@ -1,168 +0,0 @@
-<template>
-<div class="mk-notifications">
- <div class="notifications" v-if="notifications.length != 0">
- <template v-for="(notification, i) in _notifications">
- <mk-notification :notification="notification" :key="notification.id"/>
- <p class="date" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date">
- <span>%fa:angle-up%{{ notification._datetext }}</span>
- <span>%fa:angle-down%{{ _notifications[i + 1]._datetext }}</span>
- </p>
- </template>
- </div>
- <button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
- <template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>
- {{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-notifications.more%' }}
- </button>
- <p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:mobile.tags.mk-notifications.empty%</p>
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- fetchingMoreNotifications: false,
- notifications: [],
- moreNotifications: false,
- connection: null,
- connectionId: null
- };
- },
- computed: {
- _notifications(): any[] {
- return (this.notifications as any).map(notification => {
- const date = new Date(notification.createdAt).getDate();
- const month = new Date(notification.createdAt).getMonth() + 1;
- notification._date = date;
- notification._datetext = `${month}月 ${date}日`;
- return notification;
- });
- }
- },
- mounted() {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
-
- this.connection.on('notification', this.onNotification);
-
- const max = 10;
-
- (this as any).api('i/notifications', {
- limit: max + 1
- }).then(notifications => {
- if (notifications.length == max + 1) {
- this.moreNotifications = true;
- notifications.pop();
- }
-
- this.notifications = notifications;
- this.fetching = false;
- this.$emit('fetched');
- });
- },
- beforeDestroy() {
- this.connection.off('notification', this.onNotification);
- (this as any).os.stream.dispose(this.connectionId);
- },
- methods: {
- fetchMoreNotifications() {
- this.fetchingMoreNotifications = true;
-
- const max = 30;
-
- (this as any).api('i/notifications', {
- limit: max + 1,
- untilId: this.notifications[this.notifications.length - 1].id
- }).then(notifications => {
- if (notifications.length == max + 1) {
- this.moreNotifications = true;
- notifications.pop();
- } else {
- this.moreNotifications = false;
- }
- this.notifications = this.notifications.concat(notifications);
- this.fetchingMoreNotifications = false;
- });
- },
- onNotification(notification) {
- // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
- this.connection.send({
- type: 'read_notification',
- id: notification.id
- });
-
- this.notifications.unshift(notification);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-notifications
- margin 8px auto
- padding 0
- max-width 500px
- width calc(100% - 16px)
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-
- > .notifications
-
- > .mk-notification
- margin 0 auto
- max-width 500px
- border-bottom solid 1px rgba(0, 0, 0, 0.05)
-
- &:last-child
- border-bottom none
-
- > .date
- display block
- margin 0
- line-height 32px
- text-align center
- font-size 0.8em
- color #aaa
- background #fdfdfd
- border-bottom solid 1px rgba(0, 0, 0, 0.05)
-
- span
- margin 0 16px
-
- i
- margin-right 8px
-
- > .more
- display block
- width 100%
- padding 16px
- color #555
- border-top solid 1px rgba(0, 0, 0, 0.05)
-
- > [data-fa]
- margin-right 4px
-
- > .empty
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > .fetching
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > [data-fa]
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/notify.vue b/src/server/web/app/mobile/views/components/notify.vue
deleted file mode 100644
index 6d4a481dbe..0000000000
--- a/src/server/web/app/mobile/views/components/notify.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-<template>
-<div class="mk-notify">
- <mk-notification-preview :notification="notification"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import * as anime from 'animejs';
-
-export default Vue.extend({
- props: ['notification'],
- mounted() {
- this.$nextTick(() => {
- anime({
- targets: this.$el,
- bottom: '0px',
- duration: 500,
- easing: 'easeOutQuad'
- });
-
- setTimeout(() => {
- anime({
- targets: this.$el,
- bottom: '-64px',
- duration: 500,
- easing: 'easeOutQuad',
- complete: () => this.$destroy()
- });
- }, 6000);
- });
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-notify
- position fixed
- z-index 1024
- bottom -64px
- left 0
- width 100%
- height 64px
- pointer-events none
- -webkit-backdrop-filter blur(2px)
- backdrop-filter blur(2px)
- background-color rgba(#000, 0.5)
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/post-card.vue b/src/server/web/app/mobile/views/components/post-card.vue
deleted file mode 100644
index 10dfd92415..0000000000
--- a/src/server/web/app/mobile/views/components/post-card.vue
+++ /dev/null
@@ -1,89 +0,0 @@
-<template>
-<div class="mk-post-card">
- <a :href="`/@${acct}/${post.id}`">
- <header>
- <img :src="`${acct}?thumbnail&size=64`" alt="avatar"/><h3>{{ post.user.name }}</h3>
- </header>
- <div>
- {{ text }}
- </div>
- <mk-time :time="post.createdAt"/>
- </a>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import summary from '../../../../../common/get-post-summary';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['post'],
- computed: {
- acct() {
- return getAcct(this.post.user);
- },
- text(): string {
- return summary(this.post);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-post-card
- display inline-block
- width 150px
- //height 120px
- font-size 12px
- background #fff
- border-radius 4px
-
- > a
- display block
- color #2c3940
-
- &:hover
- text-decoration none
-
- > header
- > img
- position absolute
- top 8px
- left 8px
- width 28px
- height 28px
- border-radius 6px
-
- > h3
- display inline-block
- overflow hidden
- width calc(100% - 45px)
- margin 8px 0 0 42px
- line-height 28px
- white-space nowrap
- text-overflow ellipsis
- font-size 12px
-
- > div
- padding 2px 8px 8px 8px
- height 60px
- overflow hidden
- white-space normal
-
- &:after
- content ""
- display block
- position absolute
- top 40px
- left 0
- width 100%
- height 20px
- background linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 100%)
-
- > .mk-time
- display inline-block
- padding 8px
- color #aaa
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/post-detail.sub.vue b/src/server/web/app/mobile/views/components/post-detail.sub.vue
deleted file mode 100644
index db7567834a..0000000000
--- a/src/server/web/app/mobile/views/components/post-detail.sub.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<template>
-<div class="root sub">
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="`/@${acct}`">{{ post.user.name }}</router-link>
- <span class="username">@{{ acct }}</span>
- <router-link class="time" :to="`/@${acct}/${post.id}`">
- <mk-time :time="post.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-post-content class="text" :post="post"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['post'],
- computed: {
- acct() {
- return getAcct(this.post.user);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.sub
- padding 8px
- font-size 0.9em
- background #fdfdfd
-
- @media (min-width 500px)
- padding 12px
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 12px 0 0
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 60px)
-
- > header
- display flex
- margin-bottom 4px
- white-space nowrap
-
- > .name
- display block
- margin 0 .5em 0 0
- padding 0
- overflow hidden
- color #607073
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0 .5em 0 0
- color #d1d8da
-
- > .time
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
-</style>
-
diff --git a/src/server/web/app/mobile/views/components/post-detail.vue b/src/server/web/app/mobile/views/components/post-detail.vue
deleted file mode 100644
index f0af1a61aa..0000000000
--- a/src/server/web/app/mobile/views/components/post-detail.vue
+++ /dev/null
@@ -1,447 +0,0 @@
-<template>
-<div class="mk-post-detail">
- <button
- class="more"
- v-if="p.reply && p.reply.replyId && context == null"
- @click="fetchContext"
- :disabled="fetchingContext"
- >
- <template v-if="!contextFetching">%fa:ellipsis-v%</template>
- <template v-if="contextFetching">%fa:spinner .pulse%</template>
- </button>
- <div class="context">
- <x-sub v-for="post in context" :key="post.id" :post="post"/>
- </div>
- <div class="reply-to" v-if="p.reply">
- <x-sub :post="p.reply"/>
- </div>
- <div class="repost" v-if="isRepost">
- <p>
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
- </router-link>
- %fa:retweet%
- <router-link class="name" :to="`/@${acct}`">
- {{ post.user.name }}
- </router-link>
- がRepost
- </p>
- </div>
- <article>
- <header>
- <router-link class="avatar-anchor" :to="`/@${pAcct}`">
- <img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div>
- <router-link class="name" :to="`/@${pAcct}`">{{ p.user.name }}</router-link>
- <span class="username">@{{ pAcct }}</span>
- </div>
- </header>
- <div class="body">
- <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i" :class="$style.text"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
- </div>
- <div class="media" v-if="p.media">
- <mk-media-list :media-list="p.media"/>
- </div>
- <mk-poll v-if="p.poll" :post="p"/>
- <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
- <div class="map" v-if="p.geo" ref="map"></div>
- <div class="repost" v-if="p.repost">
- <mk-post-preview :post="p.repost"/>
- </div>
- </div>
- <router-link class="time" :to="`/@${pAcct}/${p.id}`">
- <mk-time :time="p.createdAt" mode="detail"/>
- </router-link>
- <footer>
- <mk-reactions-viewer :post="p"/>
- <button @click="reply" title="%i18n:mobile.tags.mk-post-detail.reply%">
- %fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
- </button>
- <button @click="repost" title="Repost">
- %fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
- </button>
- <button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-post-detail.reaction%">
- %fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
- </button>
- <button @click="menu" ref="menuButton">
- %fa:ellipsis-h%
- </button>
- </footer>
- </article>
- <div class="replies" v-if="!compact">
- <x-sub v-for="post in replies" :key="post.id" :post="post"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-import MkPostMenu from '../../../common/views/components/post-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './post-detail.sub.vue';
-
-export default Vue.extend({
- components: {
- XSub
- },
- props: {
- post: {
- type: Object,
- required: true
- },
- compact: {
- default: false
- }
- },
- data() {
- return {
- context: [],
- contextFetching: false,
- replies: [],
- };
- },
- computed: {
- acct() {
- return getAcct(this.post.user);
- },
- pAcct() {
- return getAcct(this.p.user);
- },
- isRepost(): boolean {
- return (this.post.repost &&
- this.post.text == null &&
- this.post.mediaIds == null &&
- this.post.poll == null);
- },
- p(): any {
- return this.isRepost ? this.post.repost : this.post;
- },
- reactionsCount(): number {
- return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
- : 0;
- },
- urls(): string[] {
- if (this.p.ast) {
- return this.p.ast
- .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
- .map(t => t.url);
- } else {
- return null;
- }
- }
- },
- mounted() {
- // Get replies
- if (!this.compact) {
- (this as any).api('posts/replies', {
- postId: this.p.id,
- limit: 8
- }).then(replies => {
- this.replies = replies;
- });
- }
-
- // Draw map
- if (this.p.geo) {
- const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
- if (shouldShowMap) {
- (this as any).os.getGoogleMaps().then(maps => {
- const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
- const map = new maps.Map(this.$refs.map, {
- center: uluru,
- zoom: 15
- });
- new maps.Marker({
- position: uluru,
- map: map
- });
- });
- }
- }
- },
- methods: {
- fetchContext() {
- this.contextFetching = true;
-
- // Fetch context
- (this as any).api('posts/context', {
- postId: this.p.replyId
- }).then(context => {
- this.contextFetching = false;
- this.context = context.reverse();
- });
- },
- reply() {
- (this as any).apis.post({
- reply: this.p
- });
- },
- repost() {
- (this as any).apis.post({
- repost: this.p
- });
- },
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- post: this.p,
- compact: true
- });
- },
- menu() {
- (this as any).os.new(MkPostMenu, {
- source: this.$refs.menuButton,
- post: this.p,
- compact: true
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-post-detail
- overflow hidden
- margin 0 auto
- padding 0
- width 100%
- text-align left
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- > .fetching
- padding 64px 0
-
- > .more
- display block
- margin 0
- padding 10px 0
- width 100%
- font-size 1em
- text-align center
- color #999
- cursor pointer
- background #fafafa
- outline none
- border none
- border-bottom solid 1px #eef0f2
- border-radius 6px 6px 0 0
- box-shadow none
-
- &:hover
- background #f6f6f6
-
- &:active
- background #f0f0f0
-
- &:disabled
- color #ccc
-
- > .context
- > *
- border-bottom 1px solid #eef0f2
-
- > .repost
- color #9dbb00
- background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
-
- > p
- margin 0
- padding 16px 32px
-
- .avatar-anchor
- display inline-block
-
- .avatar
- vertical-align bottom
- min-width 28px
- min-height 28px
- max-width 28px
- max-height 28px
- margin 0 8px 0 0
- border-radius 6px
-
- [data-fa]
- margin-right 4px
-
- .name
- font-weight bold
-
- & + article
- padding-top 8px
-
- > .reply-to
- border-bottom 1px solid #eef0f2
-
- > article
- padding 14px 16px 9px 16px
-
- @media (min-width 500px)
- padding 28px 32px 18px 32px
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > header
- display flex
- line-height 1.1
-
- > .avatar-anchor
- display block
- padding 0 .5em 0 0
-
- > .avatar
- display block
- width 54px
- height 54px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- @media (min-width 500px)
- width 60px
- height 60px
-
- > div
-
- > .name
- display inline-block
- margin .4em 0
- color #777
- font-size 16px
- font-weight bold
- text-align left
- text-decoration none
-
- &:hover
- text-decoration underline
-
- > .username
- display block
- text-align left
- margin 0
- color #ccc
-
- > .body
- padding 8px 0
-
- > .repost
- margin 8px 0
-
- > .mk-post-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
-
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
-
- > .map
- width 100%
- height 200px
-
- &:empty
- display none
-
- > .mk-url-preview
- margin-top 8px
-
- > .media
- > img
- display block
- max-width 100%
-
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- > .time
- font-size 16px
- color #c0c0c0
-
- > footer
- font-size 1.2em
-
- > button
- margin 0
- padding 8px
- background transparent
- border none
- box-shadow none
- font-size 1em
- color #ddd
- cursor pointer
-
- &:not(:last-child)
- margin-right 28px
-
- &:hover
- color #666
-
- > .count
- display inline
- margin 0 0 0 8px
- color #999
-
- &.reacted
- color $theme-color
-
- > .replies
- > *
- border-top 1px solid #eef0f2
-
-</style>
-
-<style lang="stylus" module>
-.text
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 16px
- color #717171
-
- @media (min-width 500px)
- font-size 24px
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/post-form.vue b/src/server/web/app/mobile/views/components/post-form.vue
deleted file mode 100644
index 5b78a25710..0000000000
--- a/src/server/web/app/mobile/views/components/post-form.vue
+++ /dev/null
@@ -1,275 +0,0 @@
-<template>
-<div class="mk-post-form">
- <header>
- <button class="cancel" @click="cancel">%fa:times%</button>
- <div>
- <span class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</span>
- <span class="geo" v-if="geo">%fa:map-marker-alt%</span>
- <button class="submit" :disabled="posting" @click="post">{{ reply ? '返信' : '%i18n:mobile.tags.mk-post-form.submit%' }}</button>
- </div>
- </header>
- <div class="form">
- <mk-post-preview v-if="reply" :post="reply"/>
- <textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.post-placeholder%'"></textarea>
- <div class="attaches" v-show="files.length != 0">
- <x-draggable class="files" :list="files" :options="{ animation: 150 }">
- <div class="file" v-for="file in files" :key="file.id">
- <div class="img" :style="`background-image: url(${file.url}?thumbnail&size=128)`" @click="detachMedia(file)"></div>
- </div>
- </x-draggable>
- </div>
- <mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false"/>
- <mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
- <button class="upload" @click="chooseFile">%fa:upload%</button>
- <button class="drive" @click="chooseFileFromDrive">%fa:cloud%</button>
- <button class="kao" @click="kao">%fa:R smile%</button>
- <button class="poll" @click="poll = true">%fa:chart-pie%</button>
- <button class="geo" @click="geo ? removeGeo() : setGeo()">%fa:map-marker-alt%</button>
- <input ref="file" class="file" type="file" accept="image/*" multiple="multiple" @change="onChangeFile"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import * as XDraggable from 'vuedraggable';
-import getKao from '../../../common/scripts/get-kao';
-
-export default Vue.extend({
- components: {
- XDraggable
- },
- props: ['reply'],
- data() {
- return {
- posting: false,
- text: '',
- uploadings: [],
- files: [],
- poll: false,
- geo: null
- };
- },
- mounted() {
- this.$nextTick(() => {
- this.focus();
- });
- },
- methods: {
- focus() {
- (this.$refs.text as any).focus();
- },
- chooseFile() {
- (this.$refs.file as any).click();
- },
- chooseFileFromDrive() {
- (this as any).apis.chooseDriveFile({
- multiple: true
- }).then(files => {
- files.forEach(this.attachMedia);
- });
- },
- attachMedia(driveFile) {
- this.files.push(driveFile);
- this.$emit('change-attached-media', this.files);
- },
- detachMedia(file) {
- this.files = this.files.filter(x => x.id != file.id);
- this.$emit('change-attached-media', this.files);
- },
- onChangeFile() {
- Array.from((this.$refs.file as any).files).forEach(this.upload);
- },
- upload(file) {
- (this.$refs.uploader as any).upload(file);
- },
- onChangeUploadings(uploads) {
- this.$emit('change-uploadings', uploads);
- },
- setGeo() {
- if (navigator.geolocation == null) {
- alert('お使いの端末は位置情報に対応していません');
- return;
- }
-
- navigator.geolocation.getCurrentPosition(pos => {
- this.geo = pos.coords;
- }, err => {
- alert('エラー: ' + err.message);
- }, {
- enableHighAccuracy: true
- });
- },
- removeGeo() {
- this.geo = null;
- },
- clear() {
- this.text = '';
- this.files = [];
- this.poll = false;
- this.$emit('change-attached-media');
- },
- post() {
- this.posting = true;
- const viaMobile = (this as any).os.i.account.clientSettings.disableViaMobile !== true;
- (this as any).api('posts/create', {
- text: this.text == '' ? undefined : this.text,
- mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
- replyId: this.reply ? this.reply.id : undefined,
- poll: this.poll ? (this.$refs.poll as any).get() : undefined,
- geo: this.geo ? {
- coordinates: [this.geo.longitude, this.geo.latitude],
- altitude: this.geo.altitude,
- accuracy: this.geo.accuracy,
- altitudeAccuracy: this.geo.altitudeAccuracy,
- heading: isNaN(this.geo.heading) ? null : this.geo.heading,
- speed: this.geo.speed,
- } : null,
- viaMobile: viaMobile
- }).then(data => {
- this.$emit('post');
- this.$destroy();
- }).catch(err => {
- this.posting = false;
- });
- },
- cancel() {
- this.$emit('cancel');
- this.$destroy();
- },
- kao() {
- this.text += getKao();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-post-form
- max-width 500px
- width calc(100% - 16px)
- margin 8px auto
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-
- > header
- z-index 1
- height 50px
- box-shadow 0 1px 0 0 rgba(0, 0, 0, 0.1)
-
- > .cancel
- padding 0
- width 50px
- line-height 50px
- font-size 24px
- color #555
-
- > div
- position absolute
- top 0
- right 0
- color #657786
-
- > .text-count
- line-height 50px
-
- > .geo
- margin 0 8px
- line-height 50px
-
- > .submit
- margin 8px
- padding 0 16px
- line-height 34px
- vertical-align bottom
- color $theme-color-foreground
- background $theme-color
- border-radius 4px
-
- &:disabled
- opacity 0.7
-
- > .form
- max-width 500px
- margin 0 auto
-
- > .mk-post-preview
- padding 16px
-
- > .attaches
-
- > .files
- display block
- margin 0
- padding 4px
- list-style none
-
- &:after
- content ""
- display block
- clear both
-
- > .file
- display block
- float left
- margin 0
- padding 0
- border solid 4px transparent
-
- > .img
- width 64px
- height 64px
- background-size cover
- background-position center center
-
- > .mk-uploader
- margin 8px 0 0 0
- padding 8px
-
- > .file
- display none
-
- > textarea
- display block
- padding 12px
- margin 0
- width 100%
- max-width 100%
- min-width 100%
- min-height 80px
- font-size 16px
- color #333
- border none
- border-bottom solid 1px #ddd
- border-radius 0
-
- &:disabled
- opacity 0.5
-
- > .upload
- > .drive
- > .kao
- > .poll
- > .geo
- display inline-block
- padding 0
- margin 0
- width 48px
- height 48px
- font-size 20px
- color #657786
- background transparent
- outline none
- border none
- border-radius 0
- box-shadow none
-
-</style>
-
diff --git a/src/server/web/app/mobile/views/components/post-preview.vue b/src/server/web/app/mobile/views/components/post-preview.vue
deleted file mode 100644
index a6141dc8e3..0000000000
--- a/src/server/web/app/mobile/views/components/post-preview.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<template>
-<div class="mk-post-preview">
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="`/@${acct}`">{{ post.user.name }}</router-link>
- <span class="username">@{{ acct }}</span>
- <router-link class="time" :to="`/@${acct}/${post.id}`">
- <mk-time :time="post.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-post-content class="text" :post="post"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['post'],
- computed: {
- acct() {
- return getAcct(this.post.user);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-post-preview
- margin 0
- padding 0
- font-size 0.9em
- background #fff
-
- &:after
- content ""
- display block
- clear both
-
- &:hover
- > .main > footer > button
- color #888
-
- > .avatar-anchor
- display block
- float left
- margin 0 12px 0 0
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- > .main
- float left
- width calc(100% - 60px)
-
- > header
- display flex
- margin-bottom 4px
- white-space nowrap
-
- > .name
- display block
- margin 0 .5em 0 0
- padding 0
- overflow hidden
- color #607073
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0 .5em 0 0
- color #d1d8da
-
- > .time
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/post.sub.vue b/src/server/web/app/mobile/views/components/post.sub.vue
deleted file mode 100644
index adf444a2d6..0000000000
--- a/src/server/web/app/mobile/views/components/post.sub.vue
+++ /dev/null
@@ -1,115 +0,0 @@
-<template>
-<div class="sub">
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="`/@${acct}`">{{ post.user.name }}</router-link>
- <span class="username">@{{ acct }}</span>
- <router-link class="created-at" :to="`/@${acct}/${post.id}`">
- <mk-time :time="post.createdAt"/>
- </router-link>
- </header>
- <div class="body">
- <mk-sub-post-content class="text" :post="post"/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['post'],
- computed: {
- acct() {
- return getAcct(this.post.user);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.sub
- font-size 0.9em
- padding 16px
-
- &:after
- content ""
- display block
- clear both
-
- > .avatar-anchor
- display block
- float left
- margin 0 10px 0 0
-
- @media (min-width 500px)
- margin-right 16px
-
- > .avatar
- display block
- width 44px
- height 44px
- margin 0
- border-radius 8px
- vertical-align bottom
-
- @media (min-width 500px)
- width 52px
- height 52px
-
- > .main
- float left
- width calc(100% - 54px)
-
- @media (min-width 500px)
- width calc(100% - 68px)
-
- > header
- display flex
- margin-bottom 2px
- white-space nowrap
-
- > .name
- display block
- margin 0 0.5em 0 0
- padding 0
- overflow hidden
- color #607073
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0
- color #d1d8da
-
- > .created-at
- margin-left auto
- color #b2b8bb
-
- > .body
-
- > .text
- cursor default
- margin 0
- padding 0
- font-size 1.1em
- color #717171
-
- pre
- max-height 120px
- font-size 80%
-
-</style>
-
diff --git a/src/server/web/app/mobile/views/components/post.vue b/src/server/web/app/mobile/views/components/post.vue
deleted file mode 100644
index a01eb7669e..0000000000
--- a/src/server/web/app/mobile/views/components/post.vue
+++ /dev/null
@@ -1,523 +0,0 @@
-<template>
-<div class="post" :class="{ repost: isRepost }">
- <div class="reply-to" v-if="p.reply">
- <x-sub :post="p.reply"/>
- </div>
- <div class="repost" v-if="isRepost">
- <p>
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- %fa:retweet%
- <span>{{ '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('{')) }}</span>
- <router-link class="name" :to="`/@${acct}`">{{ post.user.name }}</router-link>
- <span>{{ '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1) }}</span>
- </p>
- <mk-time :time="post.createdAt"/>
- </div>
- <article>
- <router-link class="avatar-anchor" :to="`/@${pAcct}`">
- <img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="`/@${pAcct}`">{{ p.user.name }}</router-link>
- <span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
- <span class="username">@{{ pAcct }}</span>
- <div class="info">
- <span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
- <router-link class="created-at" :to="url">
- <mk-time :time="p.createdAt"/>
- </router-link>
- </div>
- </header>
- <div class="body">
- <p class="channel" v-if="p.channel != null"><a target="_blank">{{ p.channel.title }}</a>:</p>
- <div class="text">
- <a class="reply" v-if="p.reply">
- %fa:reply%
- </a>
- <mk-post-html v-if="p.ast" :ast="p.ast" :i="os.i" :class="$style.text"/>
- <a class="rp" v-if="p.repost != null">RP:</a>
- </div>
- <div class="media" v-if="p.media">
- <mk-media-list :media-list="p.media"/>
- </div>
- <mk-poll v-if="p.poll" :post="p" ref="pollViewer"/>
- <div class="tags" v-if="p.tags && p.tags.length > 0">
- <router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
- </div>
- <mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
- <div class="map" v-if="p.geo" ref="map"></div>
- <span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
- <div class="repost" v-if="p.repost">
- <mk-post-preview :post="p.repost"/>
- </div>
- </div>
- <footer>
- <mk-reactions-viewer :post="p" ref="reactionsViewer"/>
- <button @click="reply">
- %fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
- </button>
- <button @click="repost" title="Repost">
- %fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
- </button>
- <button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
- %fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
- </button>
- <button class="menu" @click="menu" ref="menuButton">
- %fa:ellipsis-h%
- </button>
- </footer>
- </div>
- </article>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-import MkPostMenu from '../../../common/views/components/post-menu.vue';
-import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
-import XSub from './post.sub.vue';
-
-export default Vue.extend({
- components: {
- XSub
- },
- props: ['post'],
- data() {
- return {
- connection: null,
- connectionId: null
- };
- },
- computed: {
- acct() {
- return getAcct(this.post.user);
- },
- pAcct() {
- return getAcct(this.p.user);
- },
- isRepost(): boolean {
- return (this.post.repost &&
- this.post.text == null &&
- this.post.mediaIds == null &&
- this.post.poll == null);
- },
- p(): any {
- return this.isRepost ? this.post.repost : this.post;
- },
- reactionsCount(): number {
- return this.p.reactionCounts
- ? Object.keys(this.p.reactionCounts)
- .map(key => this.p.reactionCounts[key])
- .reduce((a, b) => a + b)
- : 0;
- },
- url(): string {
- return `/@${this.pAcct}/${this.p.id}`;
- },
- urls(): string[] {
- if (this.p.ast) {
- return this.p.ast
- .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
- .map(t => t.url);
- } else {
- return null;
- }
- }
- },
- created() {
- if ((this as any).os.isSignedIn) {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
- }
- },
- mounted() {
- this.capture(true);
-
- if ((this as any).os.isSignedIn) {
- this.connection.on('_connected_', this.onStreamConnected);
- }
-
- // Draw map
- if (this.p.geo) {
- const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
- if (shouldShowMap) {
- (this as any).os.getGoogleMaps().then(maps => {
- const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
- const map = new maps.Map(this.$refs.map, {
- center: uluru,
- zoom: 15
- });
- new maps.Marker({
- position: uluru,
- map: map
- });
- });
- }
- }
- },
- beforeDestroy() {
- this.decapture(true);
-
- if ((this as any).os.isSignedIn) {
- this.connection.off('_connected_', this.onStreamConnected);
- (this as any).os.stream.dispose(this.connectionId);
- }
- },
- methods: {
- capture(withHandler = false) {
- if ((this as any).os.isSignedIn) {
- this.connection.send({
- type: 'capture',
- id: this.p.id
- });
- if (withHandler) this.connection.on('post-updated', this.onStreamPostUpdated);
- }
- },
- decapture(withHandler = false) {
- if ((this as any).os.isSignedIn) {
- this.connection.send({
- type: 'decapture',
- id: this.p.id
- });
- if (withHandler) this.connection.off('post-updated', this.onStreamPostUpdated);
- }
- },
- onStreamConnected() {
- this.capture();
- },
- onStreamPostUpdated(data) {
- const post = data.post;
- if (post.id == this.post.id) {
- this.$emit('update:post', post);
- } else if (post.id == this.post.repostId) {
- this.post.repost = post;
- }
- },
- reply() {
- (this as any).apis.post({
- reply: this.p
- });
- },
- repost() {
- (this as any).apis.post({
- repost: this.p
- });
- },
- react() {
- (this as any).os.new(MkReactionPicker, {
- source: this.$refs.reactButton,
- post: this.p,
- compact: true
- });
- },
- menu() {
- (this as any).os.new(MkPostMenu, {
- source: this.$refs.menuButton,
- post: this.p,
- compact: true
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.post
- font-size 12px
- border-bottom solid 1px #eaeaea
-
- &:first-child
- border-radius 8px 8px 0 0
-
- > .repost
- border-radius 8px 8px 0 0
-
- &:last-of-type
- border-bottom none
-
- @media (min-width 350px)
- font-size 14px
-
- @media (min-width 500px)
- font-size 16px
-
- > .repost
- color #9dbb00
- background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
-
- > p
- margin 0
- padding 8px 16px
- line-height 28px
-
- @media (min-width 500px)
- padding 16px
-
- .avatar-anchor
- display inline-block
-
- .avatar
- vertical-align bottom
- width 28px
- height 28px
- margin 0 8px 0 0
- border-radius 6px
-
- [data-fa]
- margin-right 4px
-
- .name
- font-weight bold
-
- > .mk-time
- position absolute
- top 8px
- right 16px
- font-size 0.9em
- line-height 28px
-
- @media (min-width 500px)
- top 16px
-
- & + article
- padding-top 8px
-
- > .reply-to
- background rgba(0, 0, 0, 0.0125)
-
- > .mk-post-preview
- background transparent
-
- > article
- padding 14px 16px 9px 16px
-
- &:after
- content ""
- display block
- clear both
-
- > .avatar-anchor
- display block
- float left
- margin 0 10px 8px 0
- position -webkit-sticky
- position sticky
- top 62px
-
- @media (min-width 500px)
- margin-right 16px
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 6px
- vertical-align bottom
-
- @media (min-width 500px)
- width 58px
- height 58px
- border-radius 8px
-
- > .main
- float left
- width calc(100% - 58px)
-
- @media (min-width 500px)
- width calc(100% - 74px)
-
- > header
- display flex
- align-items center
- white-space nowrap
-
- @media (min-width 500px)
- margin-bottom 2px
-
- > .name
- display block
- margin 0 0.5em 0 0
- padding 0
- overflow hidden
- color #627079
- font-size 1em
- font-weight bold
- text-decoration none
- text-overflow ellipsis
-
- &:hover
- text-decoration underline
-
- > .is-bot
- margin 0 0.5em 0 0
- padding 1px 6px
- font-size 12px
- color #aaa
- border solid 1px #ddd
- border-radius 3px
-
- > .username
- margin 0 0.5em 0 0
- color #ccc
-
- > .info
- margin-left auto
- font-size 0.9em
-
- > .mobile
- margin-right 6px
- color #c0c0c0
-
- > .created-at
- color #c0c0c0
-
- > .body
-
- > .text
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1.1em
- color #717171
-
- >>> .quote
- margin 8px
- padding 6px 12px
- color #aaa
- border-left solid 3px #eee
-
- > .reply
- margin-right 8px
- color #717171
-
- > .rp
- margin-left 4px
- font-style oblique
- color #a0bf46
-
- [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-url-preview
- margin-top 8px
-
- > .channel
- margin 0
-
- > .tags
- margin 4px 0 0 0
-
- > *
- display inline-block
- margin 0 8px 0 0
- padding 2px 8px 2px 16px
- font-size 90%
- color #8d969e
- background #edf0f3
- border-radius 4px
-
- &:before
- content ""
- display block
- position absolute
- top 0
- bottom 0
- left 4px
- width 8px
- height 8px
- margin auto 0
- background #fff
- border-radius 100%
-
- > .media
- > img
- display block
- max-width 100%
-
- > .location
- margin 4px 0
- font-size 12px
- color #ccc
-
- > .map
- width 100%
- height 200px
-
- &:empty
- display none
-
- > .app
- font-size 12px
- color #ccc
-
- > .mk-poll
- font-size 80%
-
- > .repost
- margin 8px 0
-
- > .mk-post-preview
- padding 16px
- border dashed 1px #c0dac6
- border-radius 8px
-
- > footer
- > button
- margin 0
- padding 8px
- background transparent
- border none
- box-shadow none
- font-size 1em
- color #ddd
- cursor pointer
-
- &:not(:last-child)
- margin-right 28px
-
- &:hover
- color #666
-
- > .count
- display inline
- margin 0 0 0 8px
- color #999
-
- &.reacted
- color $theme-color
-
- &.menu
- @media (max-width 350px)
- display none
-
-</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/server/web/app/mobile/views/components/posts.vue b/src/server/web/app/mobile/views/components/posts.vue
deleted file mode 100644
index 4695f1beaa..0000000000
--- a/src/server/web/app/mobile/views/components/posts.vue
+++ /dev/null
@@ -1,111 +0,0 @@
-<template>
-<div class="mk-posts">
- <slot name="head"></slot>
- <slot></slot>
- <template v-for="(post, i) in _posts">
- <mk-post :post="post" :key="post.id" @update:post="onPostUpdated(i, $event)"/>
- <p class="date" v-if="i != posts.length - 1 && post._date != _posts[i + 1]._date">
- <span>%fa:angle-up%{{ post._datetext }}</span>
- <span>%fa:angle-down%{{ _posts[i + 1]._datetext }}</span>
- </p>
- </template>
- <footer>
- <slot name="tail"></slot>
- </footer>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- posts: {
- type: Array,
- default: () => []
- }
- },
- computed: {
- _posts(): any[] {
- return (this.posts as any).map(post => {
- const date = new Date(post.createdAt).getDate();
- const month = new Date(post.createdAt).getMonth() + 1;
- post._date = date;
- post._datetext = `${month}月 ${date}日`;
- return post;
- });
- }
- },
- methods: {
- onPostUpdated(i, post) {
- Vue.set((this as any).posts, i, post);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-posts
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- > .init
- padding 64px 0
- text-align center
- color #999
-
- > [data-fa]
- margin-right 4px
-
- > .empty
- margin 0 auto
- padding 32px
- max-width 400px
- text-align center
- color #999
-
- > [data-fa]
- display block
- margin-bottom 16px
- font-size 3em
- color #ccc
-
- > .date
- display block
- margin 0
- line-height 32px
- text-align center
- font-size 0.9em
- color #aaa
- background #fdfdfd
- border-bottom solid 1px #eaeaea
-
- span
- margin 0 16px
-
- [data-fa]
- margin-right 8px
-
- > footer
- text-align center
- border-top solid 1px #eaeaea
- border-bottom-left-radius 4px
- border-bottom-right-radius 4px
-
- &:empty
- display none
-
- > button
- margin 0
- padding 16px
- width 100%
- color $theme-color
- border-radius 0 0 8px 8px
-
- &:disabled
- opacity 0.7
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/sub-post-content.vue b/src/server/web/app/mobile/views/components/sub-post-content.vue
deleted file mode 100644
index b95883de77..0000000000
--- a/src/server/web/app/mobile/views/components/sub-post-content.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<template>
-<div class="mk-sub-post-content">
- <div class="body">
- <a class="reply" v-if="post.replyId">%fa:reply%</a>
- <mk-post-html v-if="post.ast" :ast="post.ast" :i="os.i"/>
- <a class="rp" v-if="post.repostId">RP: ...</a>
- </div>
- <details v-if="post.media">
- <summary>({{ post.media.length }}個のメディア)</summary>
- <mk-media-list :media-list="post.media"/>
- </details>
- <details v-if="post.poll">
- <summary>%i18n:mobile.tags.mk-sub-post-content.poll%</summary>
- <mk-poll :post="post"/>
- </details>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['post']
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-sub-post-content
- overflow-wrap break-word
-
- > .body
- > .reply
- margin-right 6px
- color #717171
-
- > .rp
- margin-left 4px
- font-style oblique
- color #a0bf46
-
- mk-poll
- font-size 80%
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/timeline.vue b/src/server/web/app/mobile/views/components/timeline.vue
deleted file mode 100644
index 7b5948faf1..0000000000
--- a/src/server/web/app/mobile/views/components/timeline.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<template>
-<div class="mk-timeline">
- <mk-friends-maker v-if="alone"/>
- <mk-posts :posts="posts">
- <div class="init" v-if="fetching">
- %fa:spinner .pulse%%i18n:common.loading%
- </div>
- <div class="empty" v-if="!fetching && posts.length == 0">
- %fa:R comments%
- %i18n:mobile.tags.mk-home-timeline.empty-timeline%
- </div>
- <button v-if="!fetching && existMore" @click="more" :disabled="moreFetching" slot="tail">
- <span v-if="!moreFetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
- <span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
- </button>
- </mk-posts>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-const limit = 10;
-
-export default Vue.extend({
- props: {
- date: {
- type: Date,
- required: false
- }
- },
- data() {
- return {
- fetching: true,
- moreFetching: false,
- posts: [],
- existMore: false,
- connection: null,
- connectionId: null
- };
- },
- computed: {
- alone(): boolean {
- return (this as any).os.i.followingCount == 0;
- }
- },
- mounted() {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
-
- this.connection.on('post', this.onPost);
- this.connection.on('follow', this.onChangeFollowing);
- this.connection.on('unfollow', this.onChangeFollowing);
-
- this.fetch();
- },
- beforeDestroy() {
- this.connection.off('post', this.onPost);
- this.connection.off('follow', this.onChangeFollowing);
- this.connection.off('unfollow', this.onChangeFollowing);
- (this as any).os.stream.dispose(this.connectionId);
- },
- methods: {
- fetch(cb?) {
- this.fetching = true;
- (this as any).api('posts/timeline', {
- limit: limit + 1,
- untilDate: this.date ? (this.date as any).getTime() : undefined
- }).then(posts => {
- if (posts.length == limit + 1) {
- posts.pop();
- this.existMore = true;
- }
- this.posts = posts;
- this.fetching = false;
- this.$emit('loaded');
- if (cb) cb();
- });
- },
- more() {
- this.moreFetching = true;
- (this as any).api('posts/timeline', {
- limit: limit + 1,
- untilId: this.posts[this.posts.length - 1].id
- }).then(posts => {
- if (posts.length == limit + 1) {
- posts.pop();
- this.existMore = true;
- } else {
- this.existMore = false;
- }
- this.posts = this.posts.concat(posts);
- this.moreFetching = false;
- });
- },
- onPost(post) {
- this.posts.unshift(post);
- },
- onChangeFollowing() {
- this.fetch();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-friends-maker
- margin-bottom 8px
-</style>
diff --git a/src/server/web/app/mobile/views/components/ui.header.vue b/src/server/web/app/mobile/views/components/ui.header.vue
deleted file mode 100644
index 2bf47a90a9..0000000000
--- a/src/server/web/app/mobile/views/components/ui.header.vue
+++ /dev/null
@@ -1,242 +0,0 @@
-<template>
-<div class="header">
- <mk-special-message/>
- <div class="main" ref="main">
- <div class="backdrop"></div>
- <p ref="welcomeback" v-if="os.isSignedIn">おかえりなさい、<b>{{ os.i.name }}</b>さん</p>
- <div class="content" ref="mainContainer">
- <button class="nav" @click="$parent.isDrawerOpening = true">%fa:bars%</button>
- <template v-if="hasUnreadNotifications || hasUnreadMessagingMessages || hasGameInvitations">%fa:circle%</template>
- <h1>
- <slot>Misskey</slot>
- </h1>
- <slot name="func"></slot>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import * as anime from 'animejs';
-
-export default Vue.extend({
- props: ['func'],
- data() {
- return {
- hasUnreadNotifications: false,
- hasUnreadMessagingMessages: false,
- hasGameInvitations: false,
- connection: null,
- connectionId: null
- };
- },
- mounted() {
- if ((this as any).os.isSignedIn) {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
-
- this.connection.on('read_all_notifications', this.onReadAllNotifications);
- this.connection.on('unread_notification', this.onUnreadNotification);
- this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
- this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
- this.connection.on('othello_invited', this.onOthelloInvited);
- this.connection.on('othello_no_invites', this.onOthelloNoInvites);
-
- // Fetch count of unread notifications
- (this as any).api('notifications/get_unread_count').then(res => {
- if (res.count > 0) {
- this.hasUnreadNotifications = true;
- }
- });
-
- // Fetch count of unread messaging messages
- (this as any).api('messaging/unread').then(res => {
- if (res.count > 0) {
- this.hasUnreadMessagingMessages = true;
- }
- });
-
- const ago = (new Date().getTime() - new Date((this as any).os.i.account.lastUsedAt).getTime()) / 1000
- const isHisasiburi = ago >= 3600;
- (this as any).os.i.account.lastUsedAt = new Date();
- if (isHisasiburi) {
- (this.$refs.welcomeback as any).style.display = 'block';
- (this.$refs.main as any).style.overflow = 'hidden';
-
- anime({
- targets: this.$refs.welcomeback,
- top: '0',
- opacity: 1,
- delay: 1000,
- duration: 500,
- easing: 'easeOutQuad'
- });
-
- anime({
- targets: this.$refs.mainContainer,
- opacity: 0,
- delay: 1000,
- duration: 500,
- easing: 'easeOutQuad'
- });
-
- setTimeout(() => {
- anime({
- targets: this.$refs.welcomeback,
- top: '-48px',
- opacity: 0,
- duration: 500,
- complete: () => {
- (this.$refs.welcomeback as any).style.display = 'none';
- (this.$refs.main as any).style.overflow = 'initial';
- },
- easing: 'easeInQuad'
- });
-
- anime({
- targets: this.$refs.mainContainer,
- opacity: 1,
- duration: 500,
- easing: 'easeInQuad'
- });
- }, 2500);
- }
- }
- },
- beforeDestroy() {
- if ((this as any).os.isSignedIn) {
- this.connection.off('read_all_notifications', this.onReadAllNotifications);
- this.connection.off('unread_notification', this.onUnreadNotification);
- this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
- this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
- this.connection.off('othello_invited', this.onOthelloInvited);
- this.connection.off('othello_no_invites', this.onOthelloNoInvites);
- (this as any).os.stream.dispose(this.connectionId);
- }
- },
- methods: {
- onReadAllNotifications() {
- this.hasUnreadNotifications = false;
- },
- onUnreadNotification() {
- this.hasUnreadNotifications = true;
- },
- onReadAllMessagingMessages() {
- this.hasUnreadMessagingMessages = false;
- },
- onUnreadMessagingMessage() {
- this.hasUnreadMessagingMessages = true;
- },
- onOthelloInvited() {
- this.hasGameInvitations = true;
- },
- onOthelloNoInvites() {
- this.hasGameInvitations = false;
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.header
- $height = 48px
-
- position fixed
- top 0
- z-index 1024
- width 100%
- box-shadow 0 1px 0 rgba(#000, 0.075)
-
- > .main
- color rgba(#fff, 0.9)
-
- > .backdrop
- position absolute
- top 0
- z-index 1000
- width 100%
- height $height
- -webkit-backdrop-filter blur(12px)
- backdrop-filter blur(12px)
- //background-color rgba(#1b2023, 0.75)
- background-color #1b2023
-
- > p
- display none
- position absolute
- z-index 1002
- top $height
- width 100%
- line-height $height
- margin 0
- text-align center
- color #fff
- opacity 0
-
- > .content
- z-index 1001
-
- > h1
- display block
- margin 0 auto
- padding 0
- width 100%
- max-width calc(100% - 112px)
- text-align center
- font-size 1.1em
- font-weight normal
- line-height $height
- white-space nowrap
- overflow hidden
- text-overflow ellipsis
-
- [data-fa], [data-icon]
- margin-right 4px
-
- > img
- display inline-block
- vertical-align bottom
- width ($height - 16px)
- height ($height - 16px)
- margin 8px
- border-radius 6px
-
- > .nav
- display block
- position absolute
- top 0
- left 0
- padding 0
- width $height
- font-size 1.4em
- line-height $height
- border-right solid 1px rgba(#000, 0.1)
-
- > [data-fa]
- transition all 0.2s ease
-
- > [data-fa].circle
- position absolute
- top 8px
- left 8px
- pointer-events none
- font-size 10px
- color $theme-color
-
- > button:last-child
- display block
- position absolute
- top 0
- right 0
- padding 0
- width $height
- text-align center
- font-size 1.4em
- color inherit
- line-height $height
- border-left solid 1px rgba(#000, 0.1)
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/ui.nav.vue b/src/server/web/app/mobile/views/components/ui.nav.vue
deleted file mode 100644
index a923774a73..0000000000
--- a/src/server/web/app/mobile/views/components/ui.nav.vue
+++ /dev/null
@@ -1,244 +0,0 @@
-<template>
-<div class="nav">
- <transition name="back">
- <div class="backdrop"
- v-if="isOpen"
- @click="$parent.isDrawerOpening = false"
- @touchstart="$parent.isDrawerOpening = false"
- ></div>
- </transition>
- <transition name="nav">
- <div class="body" v-if="isOpen">
- <router-link class="me" v-if="os.isSignedIn" :to="`/@${os.i.username}`">
- <img class="avatar" :src="`${os.i.avatarUrl}?thumbnail&size=128`" alt="avatar"/>
- <p class="name">{{ os.i.name }}</p>
- </router-link>
- <div class="links">
- <ul>
- <li><router-link to="/">%fa:home%%i18n:mobile.tags.mk-ui-nav.home%%fa:angle-right%</router-link></li>
- <li><router-link to="/i/notifications">%fa:R bell%%i18n:mobile.tags.mk-ui-nav.notifications%<template v-if="hasUnreadNotifications">%fa:circle%</template>%fa:angle-right%</router-link></li>
- <li><router-link to="/i/messaging">%fa:R comments%%i18n:mobile.tags.mk-ui-nav.messaging%<template v-if="hasUnreadMessagingMessages">%fa:circle%</template>%fa:angle-right%</router-link></li>
- <li><router-link to="/othello">%fa:gamepad%ゲーム<template v-if="hasGameInvitations">%fa:circle%</template>%fa:angle-right%</router-link></li>
- </ul>
- <ul>
- <li><a :href="chUrl" target="_blank">%fa:tv%%i18n:mobile.tags.mk-ui-nav.ch%%fa:angle-right%</a></li>
- <li><router-link to="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-ui-nav.drive%%fa:angle-right%</router-link></li>
- </ul>
- <ul>
- <li><a @click="search">%fa:search%%i18n:mobile.tags.mk-ui-nav.search%%fa:angle-right%</a></li>
- </ul>
- <ul>
- <li><router-link to="/i/settings">%fa:cog%%i18n:mobile.tags.mk-ui-nav.settings%%fa:angle-right%</router-link></li>
- </ul>
- </div>
- <a :href="aboutUrl"><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
- </div>
- </transition>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import { docsUrl, chUrl, lang } from '../../../config';
-
-export default Vue.extend({
- props: ['isOpen'],
- data() {
- return {
- hasUnreadNotifications: false,
- hasUnreadMessagingMessages: false,
- hasGameInvitations: false,
- connection: null,
- connectionId: null,
- aboutUrl: `${docsUrl}/${lang}/about`,
- chUrl
- };
- },
- mounted() {
- if ((this as any).os.isSignedIn) {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
-
- this.connection.on('read_all_notifications', this.onReadAllNotifications);
- this.connection.on('unread_notification', this.onUnreadNotification);
- this.connection.on('read_all_messaging_messages', this.onReadAllMessagingMessages);
- this.connection.on('unread_messaging_message', this.onUnreadMessagingMessage);
- this.connection.on('othello_invited', this.onOthelloInvited);
- this.connection.on('othello_no_invites', this.onOthelloNoInvites);
-
- // Fetch count of unread notifications
- (this as any).api('notifications/get_unread_count').then(res => {
- if (res.count > 0) {
- this.hasUnreadNotifications = true;
- }
- });
-
- // Fetch count of unread messaging messages
- (this as any).api('messaging/unread').then(res => {
- if (res.count > 0) {
- this.hasUnreadMessagingMessages = true;
- }
- });
- }
- },
- beforeDestroy() {
- if ((this as any).os.isSignedIn) {
- this.connection.off('read_all_notifications', this.onReadAllNotifications);
- this.connection.off('unread_notification', this.onUnreadNotification);
- this.connection.off('read_all_messaging_messages', this.onReadAllMessagingMessages);
- this.connection.off('unread_messaging_message', this.onUnreadMessagingMessage);
- this.connection.off('othello_invited', this.onOthelloInvited);
- this.connection.off('othello_no_invites', this.onOthelloNoInvites);
- (this as any).os.stream.dispose(this.connectionId);
- }
- },
- methods: {
- search() {
- const query = window.prompt('%i18n:mobile.tags.mk-ui-nav.search%');
- if (query == null || query == '') return;
- this.$router.push('/search?q=' + encodeURIComponent(query));
- },
- onReadAllNotifications() {
- this.hasUnreadNotifications = false;
- },
- onUnreadNotification() {
- this.hasUnreadNotifications = true;
- },
- onReadAllMessagingMessages() {
- this.hasUnreadMessagingMessages = false;
- },
- onUnreadMessagingMessage() {
- this.hasUnreadMessagingMessages = true;
- },
- onOthelloInvited() {
- this.hasGameInvitations = true;
- },
- onOthelloNoInvites() {
- this.hasGameInvitations = false;
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.nav
- .backdrop
- position fixed
- top 0
- left 0
- z-index 1025
- width 100%
- height 100%
- background rgba(0, 0, 0, 0.2)
-
- .body
- position fixed
- top 0
- left 0
- z-index 1026
- width 240px
- height 100%
- overflow auto
- -webkit-overflow-scrolling touch
- color #777
- background #fff
-
- .me
- display block
- margin 0
- padding 16px
-
- .avatar
- display inline
- max-width 64px
- border-radius 32px
- vertical-align middle
-
- .name
- display block
- margin 0 16px
- position absolute
- top 0
- left 80px
- padding 0
- width calc(100% - 112px)
- color #777
- line-height 96px
- overflow hidden
- text-overflow ellipsis
- white-space nowrap
-
- ul
- display block
- margin 16px 0
- padding 0
- list-style none
-
- &:first-child
- margin-top 0
-
- li
- display block
- font-size 1em
- line-height 1em
-
- a
- display block
- padding 0 20px
- line-height 3rem
- line-height calc(1rem + 30px)
- color #777
- text-decoration none
-
- > [data-fa]:first-child
- margin-right 0.5em
-
- > [data-fa].circle
- margin-left 6px
- font-size 10px
- color $theme-color
-
- > [data-fa]:last-child
- position absolute
- top 0
- right 0
- padding 0 20px
- font-size 1.2em
- line-height calc(1rem + 30px)
- color #ccc
-
- .about
- margin 0
- padding 1em 0
- text-align center
- font-size 0.8em
- opacity 0.5
-
- a
- color #777
-
-.nav-enter-active,
-.nav-leave-active {
- opacity: 1;
- transform: translateX(0);
- transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.nav-enter,
-.nav-leave-active {
- opacity: 0;
- transform: translateX(-240px);
-}
-
-.back-enter-active,
-.back-leave-active {
- opacity: 1;
- transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1);
-}
-.back-enter,
-.back-leave-active {
- opacity: 0;
-}
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/ui.vue b/src/server/web/app/mobile/views/components/ui.vue
deleted file mode 100644
index 325ce9d40e..0000000000
--- a/src/server/web/app/mobile/views/components/ui.vue
+++ /dev/null
@@ -1,75 +0,0 @@
-<template>
-<div class="mk-ui">
- <x-header>
- <template slot="func"><slot name="func"></slot></template>
- <slot name="header"></slot>
- </x-header>
- <x-nav :is-open="isDrawerOpening"/>
- <div class="content">
- <slot></slot>
- </div>
- <mk-stream-indicator v-if="os.isSignedIn"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import MkNotify from './notify.vue';
-import XHeader from './ui.header.vue';
-import XNav from './ui.nav.vue';
-
-export default Vue.extend({
- components: {
- XHeader,
- XNav
- },
- props: ['title'],
- data() {
- return {
- isDrawerOpening: false,
- connection: null,
- connectionId: null
- };
- },
- mounted() {
- if ((this as any).os.isSignedIn) {
- this.connection = (this as any).os.stream.getConnection();
- this.connectionId = (this as any).os.stream.use();
-
- this.connection.on('notification', this.onNotification);
- }
- },
- beforeDestroy() {
- if ((this as any).os.isSignedIn) {
- this.connection.off('notification', this.onNotification);
- (this as any).os.stream.dispose(this.connectionId);
- }
- },
- methods: {
- onNotification(notification) {
- // TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
- this.connection.send({
- type: 'read_notification',
- id: notification.id
- });
-
- (this as any).os.new(MkNotify, {
- notification
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-ui
- display flex
- flex 1
- flex-direction column
- padding-top 48px
-
- > .content
- display flex
- flex 1
- flex-direction column
-</style>
diff --git a/src/server/web/app/mobile/views/components/user-card.vue b/src/server/web/app/mobile/views/components/user-card.vue
deleted file mode 100644
index ffa1100519..0000000000
--- a/src/server/web/app/mobile/views/components/user-card.vue
+++ /dev/null
@@ -1,69 +0,0 @@
-<template>
-<div class="mk-user-card">
- <header :style="user.bannerUrl ? `background-image: url(${user.bannerUrl}?thumbnail&size=1024)` : ''">
- <a :href="`/@${acct}`">
- <img :src="`${user.avatarUrl}?thumbnail&size=200`" alt="avatar"/>
- </a>
- </header>
- <a class="name" :href="`/@${acct}`" target="_blank">{{ user.name }}</a>
- <p class="username">@{{ acct }}</p>
- <mk-follow-button :user="user"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['user'],
- computed: {
- acct() {
- return getAcct(this.user);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-user-card
- display inline-block
- width 200px
- text-align center
- border-radius 8px
- background #fff
-
- > header
- display block
- height 80px
- background-color #ddd
- background-size cover
- background-position center
- border-radius 8px 8px 0 0
-
- > a
- > img
- position absolute
- top 20px
- left calc(50% - 40px)
- width 80px
- height 80px
- border solid 2px #fff
- border-radius 8px
-
- > .name
- display block
- margin 24px 0 0 0
- font-size 16px
- color #555
-
- > .username
- margin 0
- font-size 15px
- color #ccc
-
- > .mk-follow-button
- display inline-block
- margin 8px 0 16px 0
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/user-preview.vue b/src/server/web/app/mobile/views/components/user-preview.vue
deleted file mode 100644
index e51e4353d3..0000000000
--- a/src/server/web/app/mobile/views/components/user-preview.vue
+++ /dev/null
@@ -1,110 +0,0 @@
-<template>
-<div class="mk-user-preview">
- <router-link class="avatar-anchor" :to="`/@${acct}`">
- <img class="avatar" :src="`${user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- <div class="main">
- <header>
- <router-link class="name" :to="`/@${acct}`">{{ user.name }}</router-link>
- <span class="username">@{{ acct }}</span>
- </header>
- <div class="body">
- <div class="description">{{ user.description }}</div>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['user'],
- computed: {
- acct() {
- return getAcct(this.user);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-user-preview
- margin 0
- padding 16px
- font-size 12px
-
- @media (min-width 350px)
- font-size 14px
-
- @media (min-width 500px)
- font-size 16px
-
- &:after
- content ""
- display block
- clear both
-
- > .avatar-anchor
- display block
- float left
- margin 0 10px 0 0
-
- @media (min-width 500px)
- margin-right 16px
-
- > .avatar
- display block
- width 48px
- height 48px
- margin 0
- border-radius 6px
- vertical-align bottom
-
- @media (min-width 500px)
- width 58px
- height 58px
- border-radius 8px
-
- > .main
- float left
- width calc(100% - 58px)
-
- @media (min-width 500px)
- width calc(100% - 74px)
-
- > header
- @media (min-width 500px)
- margin-bottom 2px
-
- > .name
- display inline
- margin 0
- padding 0
- color #777
- font-size 1em
- font-weight 700
- text-align left
- text-decoration none
-
- &:hover
- text-decoration underline
-
- > .username
- text-align left
- margin 0 0 0 8px
- color #ccc
-
- > .body
-
- > .description
- cursor default
- display block
- margin 0
- padding 0
- overflow-wrap break-word
- font-size 1.1em
- color #717171
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/user-timeline.vue b/src/server/web/app/mobile/views/components/user-timeline.vue
deleted file mode 100644
index bd3e3d0c87..0000000000
--- a/src/server/web/app/mobile/views/components/user-timeline.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<template>
-<div class="mk-user-timeline">
- <mk-posts :posts="posts">
- <div class="init" v-if="fetching">
- %fa:spinner .pulse%%i18n:common.loading%
- </div>
- <div class="empty" v-if="!fetching && posts.length == 0">
- %fa:R comments%
- {{ withMedia ? '%i18n:mobile.tags.mk-user-timeline.no-posts-with-media%' : '%i18n:mobile.tags.mk-user-timeline.no-posts%' }}
- </div>
- <button v-if="!fetching && existMore" @click="more" :disabled="moreFetching" slot="tail">
- <span v-if="!moreFetching">%i18n:mobile.tags.mk-user-timeline.load-more%</span>
- <span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
- </button>
- </mk-posts>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-const limit = 10;
-
-export default Vue.extend({
- props: ['user', 'withMedia'],
- data() {
- return {
- fetching: true,
- posts: [],
- existMore: false,
- moreFetching: false
- };
- },
- mounted() {
- (this as any).api('users/posts', {
- userId: this.user.id,
- withMedia: this.withMedia,
- limit: limit + 1
- }).then(posts => {
- if (posts.length == limit + 1) {
- posts.pop();
- this.existMore = true;
- }
- this.posts = posts;
- this.fetching = false;
- this.$emit('loaded');
- });
- },
- methods: {
- more() {
- this.moreFetching = true;
- (this as any).api('users/posts', {
- userId: this.user.id,
- withMedia: this.withMedia,
- limit: limit + 1,
- untilId: this.posts[this.posts.length - 1].id
- }).then(posts => {
- if (posts.length == limit + 1) {
- posts.pop();
- this.existMore = true;
- } else {
- this.existMore = false;
- }
- this.posts = this.posts.concat(posts);
- this.moreFetching = false;
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-user-timeline
- max-width 600px
- margin 0 auto
-</style>
diff --git a/src/server/web/app/mobile/views/components/users-list.vue b/src/server/web/app/mobile/views/components/users-list.vue
deleted file mode 100644
index b11e4549d6..0000000000
--- a/src/server/web/app/mobile/views/components/users-list.vue
+++ /dev/null
@@ -1,133 +0,0 @@
-<template>
-<div class="mk-users-list">
- <nav>
- <span :data-is-active="mode == 'all'" @click="mode = 'all'">%i18n:mobile.tags.mk-users-list.all%<span>{{ count }}</span></span>
- <span v-if="os.isSignedIn && youKnowCount" :data-is-active="mode == 'iknow'" @click="mode = 'iknow'">%i18n:mobile.tags.mk-users-list.known%<span>{{ youKnowCount }}</span></span>
- </nav>
- <div class="users" v-if="!fetching && users.length != 0">
- <mk-user-preview v-for="u in users" :user="u" :key="u.id"/>
- </div>
- <button class="more" v-if="!fetching && next != null" @click="more" :disabled="moreFetching">
- <span v-if="!moreFetching">%i18n:mobile.tags.mk-users-list.load-more%</span>
- <span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
- </button>
- <p class="no" v-if="!fetching && users.length == 0">
- <slot></slot>
- </p>
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['fetch', 'count', 'youKnowCount'],
- data() {
- return {
- limit: 30,
- mode: 'all',
- fetching: true,
- moreFetching: false,
- users: [],
- next: null
- };
- },
- watch: {
- mode() {
- this._fetch();
- }
- },
- mounted() {
- this._fetch(() => {
- this.$emit('loaded');
- });
- },
- methods: {
- _fetch(cb?) {
- this.fetching = true;
- this.fetch(this.mode == 'iknow', this.limit, null, obj => {
- this.users = obj.users;
- this.next = obj.next;
- this.fetching = false;
- if (cb) cb();
- });
- },
- more() {
- this.moreFetching = true;
- this.fetch(this.mode == 'iknow', this.limit, this.next, obj => {
- this.moreFetching = false;
- this.users = this.users.concat(obj.users);
- this.next = obj.next;
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-users-list
-
- > nav
- display flex
- justify-content center
- margin 0 auto
- max-width 600px
- border-bottom solid 1px rgba(0, 0, 0, 0.2)
-
- > span
- display block
- flex 1 1
- text-align center
- line-height 52px
- font-size 14px
- color #657786
- border-bottom solid 2px transparent
-
- &[data-is-active]
- font-weight bold
- color $theme-color
- border-color $theme-color
-
- > span
- display inline-block
- margin-left 4px
- padding 2px 5px
- font-size 12px
- line-height 1
- color #fff
- background rgba(0, 0, 0, 0.3)
- border-radius 20px
-
- > .users
- margin 8px auto
- max-width 500px
- width calc(100% - 16px)
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-
- > *
- border-bottom solid 1px rgba(0, 0, 0, 0.05)
-
- > .no
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > .fetching
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > [data-fa]
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/components/widget-container.vue b/src/server/web/app/mobile/views/components/widget-container.vue
deleted file mode 100644
index 7319c90849..0000000000
--- a/src/server/web/app/mobile/views/components/widget-container.vue
+++ /dev/null
@@ -1,68 +0,0 @@
-<template>
-<div class="mk-widget-container" :class="{ naked, hideHeader: !showHeader }">
- <header v-if="showHeader">
- <div class="title"><slot name="header"></slot></div>
- <slot name="func"></slot>
- </header>
- <slot></slot>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: {
- showHeader: {
- type: Boolean,
- default: true
- },
- naked: {
- type: Boolean,
- default: false
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-widget-container
- background #eee
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
- overflow hidden
-
- &.hideHeader
- background #fff
-
- &.naked
- background transparent !important
- box-shadow none !important
-
- > header
- > .title
- margin 0
- padding 8px 10px
- font-size 15px
- font-weight normal
- color #465258
- background #fff
- border-radius 8px 8px 0 0
-
- > [data-fa]
- margin-right 6px
-
- &:empty
- display none
-
- > button
- position absolute
- z-index 2
- top 0
- right 0
- padding 0
- width 42px
- height 100%
- font-size 15px
- color #465258
-
-</style>
diff --git a/src/server/web/app/mobile/views/directives/index.ts b/src/server/web/app/mobile/views/directives/index.ts
deleted file mode 100644
index 324e07596d..0000000000
--- a/src/server/web/app/mobile/views/directives/index.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import Vue from 'vue';
-
-import userPreview from './user-preview';
-
-Vue.directive('userPreview', userPreview);
-Vue.directive('user-preview', userPreview);
diff --git a/src/server/web/app/mobile/views/directives/user-preview.ts b/src/server/web/app/mobile/views/directives/user-preview.ts
deleted file mode 100644
index 1a54abc20d..0000000000
--- a/src/server/web/app/mobile/views/directives/user-preview.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-// nope
-export default {};
diff --git a/src/server/web/app/mobile/views/pages/drive.vue b/src/server/web/app/mobile/views/pages/drive.vue
deleted file mode 100644
index 200379f222..0000000000
--- a/src/server/web/app/mobile/views/pages/drive.vue
+++ /dev/null
@@ -1,107 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">
- <template v-if="folder">%fa:R folder-open%{{ folder.name }}</template>
- <template v-if="file"><mk-file-type-icon data-icon :type="file.type"/>{{ file.name }}</template>
- <template v-if="!folder && !file">%fa:cloud%%i18n:mobile.tags.mk-drive-page.drive%</template>
- </span>
- <template slot="func"><button @click="fn">%fa:ellipsis-h%</button></template>
- <mk-drive
- ref="browser"
- :init-folder="initFolder"
- :init-file="initFile"
- :is-naked="true"
- :top="48"
- @begin-fetch="Progress.start()"
- @fetched-mid="Progress.set(0.5)"
- @fetched="Progress.done()"
- @move-root="onMoveRoot"
- @open-folder="onOpenFolder"
- @open-file="onOpenFile"
- />
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-
-export default Vue.extend({
- data() {
- return {
- Progress,
- folder: null,
- file: null,
- initFolder: null,
- initFile: null
- };
- },
- created() {
- this.initFolder = this.$route.params.folder;
- this.initFile = this.$route.params.file;
-
- window.addEventListener('popstate', this.onPopState);
- },
- mounted() {
- document.title = 'Misskey Drive';
- document.documentElement.style.background = '#fff';
- },
- beforeDestroy() {
- window.removeEventListener('popstate', this.onPopState);
- },
- methods: {
- onPopState() {
- if (this.$route.params.folder) {
- (this.$refs as any).browser.cd(this.$route.params.folder, true);
- } else if (this.$route.params.file) {
- (this.$refs as any).browser.cf(this.$route.params.file, true);
- } else {
- (this.$refs as any).browser.goRoot(true);
- }
- },
- fn() {
- (this.$refs as any).browser.openContextMenu();
- },
- onMoveRoot(silent) {
- const title = 'Misskey Drive';
-
- if (!silent) {
- // Rewrite URL
- history.pushState(null, title, '/i/drive');
- }
-
- document.title = title;
-
- this.file = null;
- this.folder = null;
- },
- onOpenFolder(folder, silent) {
- const title = folder.name + ' | Misskey Drive';
-
- if (!silent) {
- // Rewrite URL
- history.pushState(null, title, '/i/drive/folder/' + folder.id);
- }
-
- document.title = title;
-
- this.file = null;
- this.folder = folder;
- },
- onOpenFile(file, silent) {
- const title = file.name + ' | Misskey Drive';
-
- if (!silent) {
- // Rewrite URL
- history.pushState(null, title, '/i/drive/file/' + file.id);
- }
-
- document.title = title;
-
- this.file = file;
- this.folder = null;
- }
- }
-});
-</script>
-
diff --git a/src/server/web/app/mobile/views/pages/followers.vue b/src/server/web/app/mobile/views/pages/followers.vue
deleted file mode 100644
index 8c058eb4e6..0000000000
--- a/src/server/web/app/mobile/views/pages/followers.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<template>
-<mk-ui>
- <template slot="header" v-if="!fetching">
- <img :src="`${user.avatarUrl}?thumbnail&size=64`" alt="">
- {{ '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) }}
- </template>
- <mk-users-list
- v-if="!fetching"
- :fetch="fetchUsers"
- :count="user.followersCount"
- :you-know-count="user.followersYouKnowCount"
- @loaded="onLoaded"
- >
- %i18n:mobile.tags.mk-user-followers.no-users%
- </mk-users-list>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-import parseAcct from '../../../../../common/user/parse-acct';
-
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- user: null
- };
- },
- watch: {
- $route: 'fetch'
- },
- created() {
- this.fetch();
- },
- mounted() {
- document.documentElement.style.background = '#313a42';
- },
- methods: {
- fetch() {
- Progress.start();
- this.fetching = true;
-
- (this as any).api('users/show', parseAcct(this.$route.params.user)).then(user => {
- this.user = user;
- this.fetching = false;
-
- document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey';
- });
- },
- onLoaded() {
- Progress.done();
- },
- fetchUsers(iknow, limit, cursor, cb) {
- (this as any).api('users/followers', {
- userId: this.user.id,
- iknow: iknow,
- limit: limit,
- cursor: cursor ? cursor : undefined
- }).then(cb);
- }
- }
-});
-</script>
diff --git a/src/server/web/app/mobile/views/pages/following.vue b/src/server/web/app/mobile/views/pages/following.vue
deleted file mode 100644
index a73c9d1710..0000000000
--- a/src/server/web/app/mobile/views/pages/following.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<template>
-<mk-ui>
- <template slot="header" v-if="!fetching">
- <img :src="`${user.avatarUrl}?thumbnail&size=64`" alt="">
- {{ '%i18n:mobile.tags.mk-user-following-page.following-of%'.replace('{}', user.name) }}
- </template>
- <mk-users-list
- v-if="!fetching"
- :fetch="fetchUsers"
- :count="user.followingCount"
- :you-know-count="user.followingYouKnowCount"
- @loaded="onLoaded"
- >
- %i18n:mobile.tags.mk-user-following.no-users%
- </mk-users-list>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-import parseAcct from '../../../../../common/user/parse-acct';
-
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- user: null
- };
- },
- watch: {
- $route: 'fetch'
- },
- created() {
- this.fetch();
- },
- mounted() {
- document.documentElement.style.background = '#313a42';
- },
- methods: {
- fetch() {
- Progress.start();
- this.fetching = true;
-
- (this as any).api('users/show', parseAcct(this.$route.params.user)).then(user => {
- this.user = user;
- this.fetching = false;
-
- document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey';
- });
- },
- onLoaded() {
- Progress.done();
- },
- fetchUsers(iknow, limit, cursor, cb) {
- (this as any).api('users/following', {
- userId: this.user.id,
- iknow: iknow,
- limit: limit,
- cursor: cursor ? cursor : undefined
- }).then(cb);
- }
- }
-});
-</script>
diff --git a/src/server/web/app/mobile/views/pages/home.vue b/src/server/web/app/mobile/views/pages/home.vue
deleted file mode 100644
index be9101aa7f..0000000000
--- a/src/server/web/app/mobile/views/pages/home.vue
+++ /dev/null
@@ -1,259 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header" @click="showTl = !showTl">
- <template v-if="showTl">%fa:home%タイムライン</template>
- <template v-else>%fa:home%ウィジェット</template>
- <span style="margin-left:8px">
- <template v-if="showTl">%fa:angle-down%</template>
- <template v-else>%fa:angle-up%</template>
- </span>
- </span>
- <template slot="func">
- <button @click="fn" v-if="showTl">%fa:pencil-alt%</button>
- <button @click="customizing = !customizing" v-else>%fa:cog%</button>
- </template>
- <main>
- <div class="tl">
- <mk-timeline @loaded="onLoaded" v-show="showTl"/>
- </div>
- <div class="widgets" v-show="!showTl">
- <template v-if="customizing">
- <header>
- <select v-model="widgetAdderSelected">
- <option value="profile">プロフィール</option>
- <option value="calendar">カレンダー</option>
- <option value="activity">アクティビティ</option>
- <option value="rss">RSSリーダー</option>
- <option value="photo-stream">フォトストリーム</option>
- <option value="slideshow">スライドショー</option>
- <option value="version">バージョン</option>
- <option value="access-log">アクセスログ</option>
- <option value="server">サーバー情報</option>
- <option value="donation">寄付のお願い</option>
- <option value="nav">ナビゲーション</option>
- <option value="tips">ヒント</option>
- </select>
- <button @click="addWidget">追加</button>
- <p><a @click="hint">カスタマイズのヒント</a></p>
- </header>
- <x-draggable
- :list="widgets"
- :options="{ handle: '.handle', animation: 150 }"
- @sort="onWidgetSort"
- >
- <div v-for="widget in widgets" class="customize-container" :key="widget.id">
- <header>
- <span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
- </header>
- <div @click="widgetFunc(widget.id)">
- <component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" :is-mobile="true"/>
- </div>
- </div>
- </x-draggable>
- </template>
- <template v-else>
- <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>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import * as XDraggable from 'vuedraggable';
-import * as uuid from 'uuid';
-import Progress from '../../../common/scripts/loading';
-import getPostSummary from '../../../../../common/get-post-summary';
-
-export default Vue.extend({
- components: {
- XDraggable
- },
- data() {
- return {
- connection: null,
- connectionId: null,
- unreadCount: 0,
- showTl: true,
- widgets: [],
- customizing: false,
- widgetAdderSelected: null
- };
- },
- created() {
- if ((this as any).os.i.account.clientSettings.mobile_home == null) {
- Vue.set((this as any).os.i.account.clientSettings, 'mobile_home', [{
- name: 'calendar',
- id: 'a', data: {}
- }, {
- name: 'activity',
- id: 'b', data: {}
- }, {
- name: 'rss',
- id: 'c', data: {}
- }, {
- name: 'photo-stream',
- id: 'd', data: {}
- }, {
- name: 'donation',
- id: 'e', data: {}
- }, {
- name: 'nav',
- id: 'f', data: {}
- }, {
- name: 'version',
- id: 'g', data: {}
- }]);
- this.widgets = (this as any).os.i.account.clientSettings.mobile_home;
- this.saveHome();
- } else {
- this.widgets = (this as any).os.i.account.clientSettings.mobile_home;
- }
-
- this.$watch('os.i.account.clientSettings', i => {
- this.widgets = (this as any).os.i.account.clientSettings.mobile_home;
- }, {
- deep: true
- });
- },
- mounted() {
- document.title = 'Misskey';
- document.documentElement.style.background = '#313a42';
-
- this.connection = (this as any).os.stream.getConnection();
- 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);
- },
- methods: {
- fn() {
- (this as any).apis.post();
- },
- onLoaded() {
- Progress.done();
- },
- onStreamPost(post) {
- if (document.hidden && post.userId !== (this as any).os.i.id) {
- this.unreadCount++;
- document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
- }
- },
- onVisibilitychange() {
- if (!document.hidden) {
- this.unreadCount = 0;
- document.title = 'Misskey';
- }
- },
- onHomeUpdated(data) {
- if (data.home) {
- (this as any).os.i.account.clientSettings.mobile_home = data.home;
- this.widgets = data.home;
- } else {
- const w = (this as any).os.i.account.clientSettings.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.account.clientSettings.mobile_home;
- }
- }
- },
- hint() {
- alert('ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。');
- },
- widgetFunc(id) {
- const w = this.$refs[id][0];
- if (w.func) w.func();
- },
- onWidgetSort() {
- this.saveHome();
- },
- addWidget() {
- const widget = {
- name: this.widgetAdderSelected,
- id: uuid(),
- data: {}
- };
-
- this.widgets.unshift(widget);
- this.saveHome();
- },
- removeWidget(widget) {
- this.widgets = this.widgets.filter(w => w.id != widget.id);
- this.saveHome();
- },
- saveHome() {
- (this as any).os.i.account.clientSettings.mobile_home = this.widgets;
- (this as any).api('i/update_mobile_home', {
- home: this.widgets
- });
- },
- warp() {
-
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-main
-
- > .tl
- > .mk-timeline
- max-width 600px
- margin 0 auto
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
- > .widgets
- margin 0 auto
- max-width 500px
-
- @media (min-width 500px)
- padding 8px
-
- > header
- padding 8px
- background #fff
-
- .widget
- margin 8px
-
- .customize-container
- margin 8px
- background #fff
-
- > header
- line-height 32px
- background #eee
-
- > .handle
- padding 0 8px
-
- > .remove
- position absolute
- top 0
- right 0
- padding 0 8px
- line-height 32px
-
- > div
- padding 8px
-
- > *
- pointer-events none
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/index.vue b/src/server/web/app/mobile/views/pages/index.vue
deleted file mode 100644
index 0ea47d913b..0000000000
--- a/src/server/web/app/mobile/views/pages/index.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-<template>
-<component :is="os.isSignedIn ? 'home' : 'welcome'"></component>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Home from './home.vue';
-import Welcome from './welcome.vue';
-
-export default Vue.extend({
- components: {
- Home,
- Welcome
- }
-});
-</script>
diff --git a/src/server/web/app/mobile/views/pages/messaging-room.vue b/src/server/web/app/mobile/views/pages/messaging-room.vue
deleted file mode 100644
index 193c41179c..0000000000
--- a/src/server/web/app/mobile/views/pages/messaging-room.vue
+++ /dev/null
@@ -1,42 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">
- <template v-if="user">%fa:R comments%{{ user.name }}</template>
- <template v-else><mk-ellipsis/></template>
- </span>
- <mk-messaging-room v-if="!fetching" :user="user" :is-naked="true"/>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import parseAcct from '../../../../../common/user/parse-acct';
-
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- user: null
- };
- },
- watch: {
- $route: 'fetch'
- },
- created() {
- document.documentElement.style.background = '#fff';
- this.fetch();
- },
- methods: {
- fetch() {
- this.fetching = true;
- (this as any).api('users/show', parseAcct(this.$route.params.user)).then(user => {
- this.user = user;
- this.fetching = false;
-
- document.title = `%i18n:mobile.tags.mk-messaging-room-page.message%: ${user.name} | Misskey`;
- });
- }
- }
-});
-</script>
-
diff --git a/src/server/web/app/mobile/views/pages/messaging.vue b/src/server/web/app/mobile/views/pages/messaging.vue
deleted file mode 100644
index e92068eda5..0000000000
--- a/src/server/web/app/mobile/views/pages/messaging.vue
+++ /dev/null
@@ -1,23 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:R comments%%i18n:mobile.tags.mk-messaging-page.message%</span>
- <mk-messaging @navigate="navigate" :header-top="48"/>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../common/user/get-acct';
-
-export default Vue.extend({
- mounted() {
- document.title = 'Misskey %i18n:mobile.tags.mk-messaging-page.message%';
- document.documentElement.style.background = '#fff';
- },
- methods: {
- navigate(user) {
- (this as any).$router.push(`/i/messaging/${getAcct(user)}`);
- }
- }
-});
-</script>
diff --git a/src/server/web/app/mobile/views/pages/notifications.vue b/src/server/web/app/mobile/views/pages/notifications.vue
deleted file mode 100644
index 6d45e22a9c..0000000000
--- a/src/server/web/app/mobile/views/pages/notifications.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:R bell%%i18n:mobile.tags.mk-notifications-page.notifications%</span>
- <template slot="func"><button @click="fn">%fa:check%</button></template>
- <mk-notifications @fetched="onFetched"/>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-
-export default Vue.extend({
- mounted() {
- document.title = 'Misskey | %i18n:mobile.tags.mk-notifications-page.notifications%';
- document.documentElement.style.background = '#313a42';
-
- Progress.start();
- },
- methods: {
- fn() {
- const ok = window.confirm('%i18n:mobile.tags.mk-notifications-page.read-all%');
- if (!ok) return;
-
- (this as any).api('notifications/markAsRead_all');
- },
- onFetched() {
- Progress.done();
- }
- }
-});
-</script>
diff --git a/src/server/web/app/mobile/views/pages/othello.vue b/src/server/web/app/mobile/views/pages/othello.vue
deleted file mode 100644
index e04e583c20..0000000000
--- a/src/server/web/app/mobile/views/pages/othello.vue
+++ /dev/null
@@ -1,50 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:gamepad%オセロ</span>
- <mk-othello v-if="!fetching" :init-game="game" @gamed="onGamed"/>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-
-export default Vue.extend({
- data() {
- return {
- fetching: false,
- game: null
- };
- },
- watch: {
- $route: 'fetch'
- },
- created() {
- this.fetch();
- },
- mounted() {
- document.title = 'Misskey オセロ';
- document.documentElement.style.background = '#fff';
- },
- methods: {
- fetch() {
- if (this.$route.params.game == null) return;
-
- Progress.start();
- this.fetching = true;
-
- (this as any).api('othello/games/show', {
- gameId: this.$route.params.game
- }).then(game => {
- this.game = game;
- this.fetching = false;
-
- Progress.done();
- });
- },
- onGamed(game) {
- history.pushState(null, null, '/othello/' + game.id);
- }
- }
-});
-</script>
diff --git a/src/server/web/app/mobile/views/pages/post.vue b/src/server/web/app/mobile/views/pages/post.vue
deleted file mode 100644
index 49a4bfd9dc..0000000000
--- a/src/server/web/app/mobile/views/pages/post.vue
+++ /dev/null
@@ -1,85 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:R sticky-note%%i18n:mobile.tags.mk-post-page.title%</span>
- <main v-if="!fetching">
- <a v-if="post.next" :href="post.next">%fa:angle-up%%i18n:mobile.tags.mk-post-page.next%</a>
- <div>
- <mk-post-detail :post="post"/>
- </div>
- <a v-if="post.prev" :href="post.prev">%fa:angle-down%%i18n:mobile.tags.mk-post-page.prev%</a>
- </main>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- post: null
- };
- },
- watch: {
- $route: 'fetch'
- },
- created() {
- this.fetch();
- },
- mounted() {
- document.title = 'Misskey';
- document.documentElement.style.background = '#313a42';
- },
- methods: {
- fetch() {
- Progress.start();
- this.fetching = true;
-
- (this as any).api('posts/show', {
- postId: this.$route.params.post
- }).then(post => {
- this.post = post;
- this.fetching = false;
-
- Progress.done();
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-main
- text-align center
-
- > div
- margin 8px auto
- padding 0
- max-width 500px
- width calc(100% - 16px)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-
- > a
- display inline-block
-
- &:first-child
- margin-top 8px
-
- @media (min-width 500px)
- margin-top 16px
-
- &:last-child
- margin-bottom 8px
-
- @media (min-width 500px)
- margin-bottom 16px
-
- > [data-fa]
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/profile-setting.vue b/src/server/web/app/mobile/views/pages/profile-setting.vue
deleted file mode 100644
index 15f9bc9b68..0000000000
--- a/src/server/web/app/mobile/views/pages/profile-setting.vue
+++ /dev/null
@@ -1,226 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:user%%i18n:mobile.tags.mk-profile-setting-page.title%</span>
- <div :class="$style.content">
- <p>%fa:info-circle%%i18n:mobile.tags.mk-profile-setting.will-be-published%</p>
- <div :class="$style.form">
- <div :style="os.i.bannerUrl ? `background-image: url(${os.i.bannerUrl}?thumbnail&size=1024)` : ''" @click="setBanner">
- <img :src="`${os.i.avatarUrl}?thumbnail&size=200`" alt="avatar" @click="setAvatar"/>
- </div>
- <label>
- <p>%i18n:mobile.tags.mk-profile-setting.name%</p>
- <input v-model="name" type="text"/>
- </label>
- <label>
- <p>%i18n:mobile.tags.mk-profile-setting.location%</p>
- <input v-model="location" type="text"/>
- </label>
- <label>
- <p>%i18n:mobile.tags.mk-profile-setting.description%</p>
- <textarea v-model="description"></textarea>
- </label>
- <label>
- <p>%i18n:mobile.tags.mk-profile-setting.birthday%</p>
- <input v-model="birthday" type="date"/>
- </label>
- <label>
- <p>%i18n:mobile.tags.mk-profile-setting.avatar%</p>
- <button @click="setAvatar" :disabled="avatarSaving">%i18n:mobile.tags.mk-profile-setting.set-avatar%</button>
- </label>
- <label>
- <p>%i18n:mobile.tags.mk-profile-setting.banner%</p>
- <button @click="setBanner" :disabled="bannerSaving">%i18n:mobile.tags.mk-profile-setting.set-banner%</button>
- </label>
- </div>
- <button :class="$style.save" @click="save" :disabled="saving">%fa:check%%i18n:mobile.tags.mk-profile-setting.save%</button>
- </div>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- data() {
- return {
- name: null,
- location: null,
- description: null,
- birthday: null,
- avatarSaving: false,
- bannerSaving: false,
- saving: false
- };
- },
- created() {
- this.name = (this as any).os.i.name;
- this.location = (this as any).os.i.account.profile.location;
- this.description = (this as any).os.i.description;
- this.birthday = (this as any).os.i.account.profile.birthday;
- },
- mounted() {
- document.title = 'Misskey | %i18n:mobile.tags.mk-profile-setting-page.title%';
- document.documentElement.style.background = '#313a42';
- },
- methods: {
- setAvatar() {
- (this as any).apis.chooseDriveFile({
- multiple: false
- }).then(file => {
- this.avatarSaving = true;
-
- (this as any).api('i/update', {
- avatarId: file.id
- }).then(() => {
- this.avatarSaving = false;
- alert('%i18n:mobile.tags.mk-profile-setting.avatar-saved%');
- });
- });
- },
- setBanner() {
- (this as any).apis.chooseDriveFile({
- multiple: false
- }).then(file => {
- this.bannerSaving = true;
-
- (this as any).api('i/update', {
- bannerId: file.id
- }).then(() => {
- this.bannerSaving = false;
- alert('%i18n:mobile.tags.mk-profile-setting.banner-saved%');
- });
- });
- },
- save() {
- this.saving = true;
-
- (this as any).api('i/update', {
- name: this.name,
- location: this.location || null,
- description: this.description || null,
- birthday: this.birthday || null
- }).then(() => {
- this.saving = false;
- alert('%i18n:mobile.tags.mk-profile-setting.saved%');
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" module>
-@import '~const.styl'
-
-.content
- margin 8px auto
- max-width 500px
- width calc(100% - 16px)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-
- > p
- display block
- margin 0 0 8px 0
- padding 12px 16px
- font-size 14px
- color #79d4e6
- border solid 1px #71afbb
- //color #276f86
- //background #f8ffff
- //border solid 1px #a9d5de
- border-radius 8px
-
- > [data-fa]
- margin-right 6px
-
-.form
- position relative
- background #fff
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
- border-radius 8px
-
- &:before
- content ""
- display block
- position absolute
- bottom -20px
- left calc(50% - 10px)
- border-top solid 10px rgba(0, 0, 0, 0.2)
- border-right solid 10px transparent
- border-bottom solid 10px transparent
- border-left solid 10px transparent
-
- &:after
- content ""
- display block
- position absolute
- bottom -16px
- left calc(50% - 8px)
- border-top solid 8px #fff
- border-right solid 8px transparent
- border-bottom solid 8px transparent
- border-left solid 8px transparent
-
- > div
- height 128px
- background-color #e4e4e4
- background-size cover
- background-position center
- border-radius 8px 8px 0 0
-
- > img
- position absolute
- top 25px
- left calc(50% - 40px)
- width 80px
- height 80px
- border solid 2px #fff
- border-radius 8px
-
- > label
- display block
- margin 0
- padding 16px
- border-bottom solid 1px #eee
-
- &:last-of-type
- border none
-
- > p:first-child
- display block
- margin 0
- padding 0 0 4px 0
- font-weight bold
- color #2f3c42
-
- > input[type="text"]
- > textarea
- display block
- width 100%
- padding 12px
- font-size 16px
- color #192427
- border solid 2px #ddd
- border-radius 4px
-
- > textarea
- min-height 80px
-
-.save
- display block
- margin 8px 0 0 0
- padding 16px
- width 100%
- font-size 16px
- color $theme-color-foreground
- background $theme-color
- border-radius 8px
-
- &:disabled
- opacity 0.7
-
- > [data-fa]
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/search.vue b/src/server/web/app/mobile/views/pages/search.vue
deleted file mode 100644
index cbab504e3c..0000000000
--- a/src/server/web/app/mobile/views/pages/search.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:search% {{ q }}</span>
- <main v-if="!fetching">
- <mk-posts :class="$style.posts" :posts="posts">
- <span v-if="posts.length == 0">{{ '%i18n:mobile.tags.mk-search-posts.empty%'.replace('{}', q) }}</span>
- <button v-if="existMore" @click="more" :disabled="fetching" slot="tail">
- <span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
- <span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
- </button>
- </mk-posts>
- </main>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import Progress from '../../../common/scripts/loading';
-import parse from '../../../common/scripts/parse-search-query';
-
-const limit = 20;
-
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- existMore: false,
- posts: [],
- offset: 0
- };
- },
- watch: {
- $route: 'fetch'
- },
- computed: {
- q(): string {
- return this.$route.query.q;
- }
- },
- mounted() {
- document.title = `%i18n:mobile.tags.mk-search-page.search%: ${this.q} | Misskey`;
- document.documentElement.style.background = '#313a42';
-
- this.fetch();
- },
- methods: {
- fetch() {
- this.fetching = true;
- Progress.start();
-
- (this as any).api('posts/search', Object.assign({
- limit: limit + 1
- }, parse(this.q))).then(posts => {
- if (posts.length == limit + 1) {
- posts.pop();
- this.existMore = true;
- }
- this.posts = posts;
- this.fetching = false;
- Progress.done();
- });
- },
- more() {
- this.offset += limit;
- (this as any).api('posts/search', Object.assign({
- limit: limit + 1,
- offset: this.offset
- }, parse(this.q))).then(posts => {
- if (posts.length == limit + 1) {
- posts.pop();
- } else {
- this.existMore = false;
- }
- this.posts = this.posts.concat(posts);
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" module>
-.posts
- margin 8px auto
- max-width 500px
- width calc(100% - 16px)
- background #fff
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- @media (min-width 500px)
- margin 16px auto
- width calc(100% - 32px)
-</style>
diff --git a/src/server/web/app/mobile/views/pages/selectdrive.vue b/src/server/web/app/mobile/views/pages/selectdrive.vue
deleted file mode 100644
index 3480a0d103..0000000000
--- a/src/server/web/app/mobile/views/pages/selectdrive.vue
+++ /dev/null
@@ -1,96 +0,0 @@
-<template>
-<div class="mk-selectdrive">
- <header>
- <h1>%i18n:mobile.tags.mk-selectdrive-page.select-file%<span class="count" v-if="files.length > 0">({{ files.length }})</span></h1>
- <button class="upload" @click="upload">%fa:upload%</button>
- <button v-if="multiple" class="ok" @click="ok">%fa:check%</button>
- </header>
- <mk-drive ref="browser" select-file :multiple="multiple" is-naked :top="42"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- data() {
- return {
- files: []
- };
- },
- computed: {
- multiple(): boolean {
- const q = (new URL(location.toString())).searchParams;
- return q.get('multiple') == 'true';
- }
- },
- mounted() {
- document.title = '%i18n:desktop.tags.mk-selectdrive-page.title%';
- },
- methods: {
- onSelected(file) {
- this.files = [file];
- this.ok();
- },
- onChangeSelection(files) {
- this.files = files;
- },
- upload() {
- (this.$refs.browser as any).selectLocalFile();
- },
- close() {
- window.close();
- },
- ok() {
- window.opener.cb(this.multiple ? this.files : this.files[0]);
- this.close();
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mk-selectdrive
- width 100%
- height 100%
- background #fff
-
- > header
- position fixed
- top 0
- left 0
- width 100%
- z-index 1000
- background #fff
- box-shadow 0 1px rgba(0, 0, 0, 0.1)
-
- > h1
- margin 0
- padding 0
- text-align center
- line-height 42px
- font-size 1em
- font-weight normal
-
- > .count
- margin-left 4px
- opacity 0.5
-
- > .upload
- position absolute
- top 0
- left 0
- line-height 42px
- width 42px
-
- > .ok
- position absolute
- top 0
- right 0
- line-height 42px
- width 42px
-
- > .mk-drive
- top 42px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/settings.vue b/src/server/web/app/mobile/views/pages/settings.vue
deleted file mode 100644
index a945a21c5c..0000000000
--- a/src/server/web/app/mobile/views/pages/settings.vue
+++ /dev/null
@@ -1,103 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header">%fa:cog%%i18n:mobile.tags.mk-settings-page.settings%</span>
- <div :class="$style.content">
- <p v-html="'%i18n:mobile.tags.mk-settings.signed-in-as%'.replace('{}', '<b>' + os.i.name + '</b>')"></p>
- <ul>
- <li><router-link to="./settings/profile">%fa:user%%i18n:mobile.tags.mk-settings-page.profile%%fa:angle-right%</router-link></li>
- <li><router-link to="./settings/authorized-apps">%fa:puzzle-piece%%i18n:mobile.tags.mk-settings-page.applications%%fa:angle-right%</router-link></li>
- <li><router-link to="./settings/twitter">%fa:B twitter%%i18n:mobile.tags.mk-settings-page.twitter-integration%%fa:angle-right%</router-link></li>
- <li><router-link to="./settings/signin-history">%fa:sign-in-alt%%i18n:mobile.tags.mk-settings-page.signin-history%%fa:angle-right%</router-link></li>
- </ul>
- <ul>
- <li><a @click="signout">%fa:power-off%%i18n:mobile.tags.mk-settings-page.signout%</a></li>
- </ul>
- <p><small>ver {{ version }} ({{ codename }})</small></p>
- </div>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import { version, codename } from '../../../config';
-
-export default Vue.extend({
- data() {
- return {
- version,
- codename
- };
- },
- mounted() {
- document.title = 'Misskey | %i18n:mobile.tags.mk-settings-page.settings%';
- document.documentElement.style.background = '#313a42';
- },
- methods: {
- signout() {
- (this as any).os.signout();
- }
- }
-});
-</script>
-
-<style lang="stylus" module>
-.content
-
- > p
- display block
- margin 24px
- text-align center
- color #cad2da
-
- > ul
- $radius = 8px
-
- display block
- margin 16px auto
- padding 0
- max-width 500px
- width calc(100% - 32px)
- list-style none
- background #fff
- border solid 1px rgba(0, 0, 0, 0.2)
- border-radius $radius
-
- > li
- display block
- border-bottom solid 1px #ddd
-
- &:hover
- background rgba(0, 0, 0, 0.1)
-
- &:first-child
- border-top-left-radius $radius
- border-top-right-radius $radius
-
- &:last-child
- border-bottom-left-radius $radius
- border-bottom-right-radius $radius
- border-bottom none
-
- > a
- $height = 48px
-
- display block
- position relative
- padding 0 16px
- line-height $height
- color #4d635e
-
- > [data-fa]:nth-of-type(1)
- margin-right 4px
-
- > [data-fa]:nth-of-type(2)
- display block
- position absolute
- top 0
- right 8px
- z-index 1
- padding 0 20px
- font-size 1.2em
- line-height $height
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/signup.vue b/src/server/web/app/mobile/views/pages/signup.vue
deleted file mode 100644
index 9dc07a4b86..0000000000
--- a/src/server/web/app/mobile/views/pages/signup.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<template>
-<div class="signup">
- <h1>Misskeyをはじめる</h1>
- <p>いつでも、どこからでもMisskeyを利用できます。もちろん、無料です。</p>
- <div class="form">
- <p>新規登録</p>
- <div>
- <mk-signup/>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- mounted() {
- document.documentElement.style.background = '#293946';
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.signup
- padding 16px
- margin 0 auto
- max-width 500px
-
- h1
- margin 0
- padding 8px
- font-size 1.5em
- font-weight normal
- color #c3c6ca
-
- & + p
- margin 0 0 16px 0
- padding 0 8px 0 8px
- color #949fa9
-
- .form
- background #fff
- border solid 1px rgba(0, 0, 0, 0.2)
- border-radius 8px
- overflow hidden
-
- > p
- margin 0
- padding 12px 20px
- color #555
- background #f5f5f5
- border-bottom solid 1px #ddd
-
- > div
- padding 16px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/user.vue b/src/server/web/app/mobile/views/pages/user.vue
deleted file mode 100644
index 114decb8e4..0000000000
--- a/src/server/web/app/mobile/views/pages/user.vue
+++ /dev/null
@@ -1,247 +0,0 @@
-<template>
-<mk-ui>
- <span slot="header" v-if="!fetching">%fa:user% {{ user.name }}</span>
- <main v-if="!fetching">
- <header>
- <div class="banner" :style="user.bannerUrl ? `background-image: url(${user.bannerUrl}?thumbnail&size=1024)` : ''"></div>
- <div class="body">
- <div class="top">
- <a class="avatar">
- <img :src="`${user.avatarUrl}?thumbnail&size=200`" alt="avatar"/>
- </a>
- <mk-follow-button v-if="os.isSignedIn && os.i.id != user.id" :user="user"/>
- </div>
- <div class="title">
- <h1>{{ user.name }}</h1>
- <span class="username">@{{ acct }}</span>
- <span class="followed" v-if="user.isFollowed">%i18n:mobile.tags.mk-user.follows-you%</span>
- </div>
- <div class="description">{{ user.description }}</div>
- <div class="info">
- <p class="location" v-if="user.host === null && user.account.profile.location">
- %fa:map-marker%{{ user.account.profile.location }}
- </p>
- <p class="birthday" v-if="user.host === null && user.account.profile.birthday">
- %fa:birthday-cake%{{ user.account.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }}歳)
- </p>
- </div>
- <div class="status">
- <a>
- <b>{{ user.postsCount | number }}</b>
- <i>%i18n:mobile.tags.mk-user.posts%</i>
- </a>
- <a :href="`@${acct}/following`">
- <b>{{ user.followingCount | number }}</b>
- <i>%i18n:mobile.tags.mk-user.following%</i>
- </a>
- <a :href="`@${acct}/followers`">
- <b>{{ user.followersCount | number }}</b>
- <i>%i18n:mobile.tags.mk-user.followers%</i>
- </a>
- </div>
- </div>
- </header>
- <nav>
- <div class="nav-container">
- <a :data-is-active=" page == 'home' " @click="page = 'home'">%i18n:mobile.tags.mk-user.overview%</a>
- <a :data-is-active=" page == 'posts' " @click="page = 'posts'">%i18n:mobile.tags.mk-user.timeline%</a>
- <a :data-is-active=" page == 'media' " @click="page = 'media'">%i18n:mobile.tags.mk-user.media%</a>
- </div>
- </nav>
- <div class="body">
- <x-home v-if="page == 'home'" :user="user"/>
- <mk-user-timeline v-if="page == 'posts'" :user="user"/>
- <mk-user-timeline v-if="page == 'media'" :user="user" with-media/>
- </div>
- </main>
-</mk-ui>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import * as age from 's-age';
-import getAcct from '../../../../../common/user/get-acct';
-import getAcct from '../../../../../common/user/parse-acct';
-import Progress from '../../../common/scripts/loading';
-import XHome from './user/home.vue';
-
-export default Vue.extend({
- components: {
- XHome
- },
- data() {
- return {
- fetching: true,
- user: null,
- page: 'home'
- };
- },
- computed: {
- acct() {
- return this.getAcct(this.user);
- },
- age(): number {
- return age(this.user.account.profile.birthday);
- }
- },
- watch: {
- $route: 'fetch'
- },
- created() {
- this.fetch();
- },
- mounted() {
- document.documentElement.style.background = '#313a42';
- },
- methods: {
- fetch() {
- Progress.start();
-
- (this as any).api('users/show', parseAcct(this.$route.params.user)).then(user => {
- this.user = user;
- this.fetching = false;
-
- Progress.done();
- document.title = user.name + ' | Misskey';
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-main
- > header
-
- > .banner
- padding-bottom 33.3%
- background-color #1b1b1b
- background-size cover
- background-position center
-
- > .body
- padding 12px
- margin 0 auto
- max-width 600px
-
- > .top
- &:after
- content ''
- display block
- clear both
-
- > .avatar
- display block
- float left
- width 25%
- height 40px
-
- > img
- display block
- position absolute
- left -2px
- bottom -2px
- width 100%
- border 3px solid #313a42
- border-radius 6px
-
- @media (min-width 500px)
- left -4px
- bottom -4px
- border 4px solid #313a42
- border-radius 12px
-
- > .mk-follow-button
- float right
- height 40px
-
- > .title
- margin 8px 0
-
- > h1
- margin 0
- line-height 22px
- font-size 20px
- color #fff
-
- > .username
- display inline-block
- line-height 20px
- font-size 16px
- font-weight bold
- color #657786
-
- > .followed
- margin-left 8px
- padding 2px 4px
- font-size 12px
- color #657786
- background #f8f8f8
- border-radius 4px
-
- > .description
- margin 8px 0
- color #fff
-
- > .info
- margin 8px 0
-
- > p
- display inline
- margin 0 16px 0 0
- color #a9b9c1
-
- > i
- margin-right 4px
-
- > .status
- > a
- color #657786
-
- &:not(:last-child)
- margin-right 16px
-
- > b
- margin-right 4px
- font-size 16px
- color #fff
-
- > i
- font-size 14px
-
- > nav
- position sticky
- top 48px
- box-shadow 0 4px 4px rgba(0, 0, 0, 0.3)
- background-color #313a42
- z-index 1
- > .nav-container
- display flex
- justify-content center
- margin 0 auto
- max-width 600px
-
- > a
- display block
- flex 1 1
- text-align center
- line-height 52px
- font-size 14px
- text-decoration none
- color #657786
- border-bottom solid 2px transparent
-
- &[data-is-active]
- font-weight bold
- color $theme-color
- border-color $theme-color
-
- > .body
- padding 8px
-
- @media (min-width 500px)
- padding 16px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/user/home.followers-you-know.vue b/src/server/web/app/mobile/views/pages/user/home.followers-you-know.vue
deleted file mode 100644
index 8c84d2dbba..0000000000
--- a/src/server/web/app/mobile/views/pages/user/home.followers-you-know.vue
+++ /dev/null
@@ -1,67 +0,0 @@
-<template>
-<div class="root followers-you-know">
- <p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-followers-you-know.loading%<mk-ellipsis/></p>
- <div v-if="!fetching && users.length > 0">
- <a v-for="user in users" :key="user.id" :href="`/@${getAcct(user)}`">
- <img :src="`${user.avatarUrl}?thumbnail&size=64`" :alt="user.name"/>
- </a>
- </div>
- <p class="empty" v-if="!fetching && users.length == 0">%i18n:mobile.tags.mk-user-overview-followers-you-know.no-users%</p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['user'],
- data() {
- return {
- fetching: true,
- users: []
- };
- },
- methods: {
- getAcct
- },
- mounted() {
- (this as any).api('users/followers', {
- userId: this.user.id,
- iknow: true,
- limit: 30
- }).then(res => {
- this.fetching = false;
- this.users = res.users;
- });
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.followers-you-know
-
- > div
- padding 4px
-
- > a
- display inline-block
- margin 4px
-
- > img
- width 48px
- height 48px
- vertical-align bottom
- border-radius 100%
-
- > .initializing
- > .empty
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > i
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/user/home.friends.vue b/src/server/web/app/mobile/views/pages/user/home.friends.vue
deleted file mode 100644
index 469781abb9..0000000000
--- a/src/server/web/app/mobile/views/pages/user/home.friends.vue
+++ /dev/null
@@ -1,54 +0,0 @@
-<template>
-<div class="root friends">
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-frequently-replied-users.loading%<mk-ellipsis/></p>
- <div v-if="!fetching && users.length > 0">
- <mk-user-card v-for="user in users" :key="user.id" :user="user"/>
- </div>
- <p class="empty" v-if="!fetching && users.length == 0">%i18n:mobile.tags.mk-user-overview-frequently-replied-users.no-users%</p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['user'],
- data() {
- return {
- fetching: true,
- users: []
- };
- },
- mounted() {
- (this as any).api('users/get_frequently_replied_users', {
- userId: this.user.id
- }).then(res => {
- this.users = res.map(x => x.user);
- this.fetching = false;
- });
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.friends
- > div
- overflow-x scroll
- -webkit-overflow-scrolling touch
- white-space nowrap
- padding 8px
-
- > .mk-user-card
- &:not(:last-child)
- margin-right 8px
-
- > .fetching
- > .empty
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > i
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/user/home.photos.vue b/src/server/web/app/mobile/views/pages/user/home.photos.vue
deleted file mode 100644
index f703f8a740..0000000000
--- a/src/server/web/app/mobile/views/pages/user/home.photos.vue
+++ /dev/null
@@ -1,83 +0,0 @@
-<template>
-<div class="root photos">
- <p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-photos.loading%<mk-ellipsis/></p>
- <div class="stream" v-if="!fetching && images.length > 0">
- <a v-for="image in images"
- class="img"
- :style="`background-image: url(${image.media.url}?thumbnail&size=256)`"
- :href="`/@${getAcct(image.post.user)}/${image.post.id}`"
- ></a>
- </div>
- <p class="empty" v-if="!fetching && images.length == 0">%i18n:mobile.tags.mk-user-overview-photos.no-photos%</p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import getAcct from '../../../../../../common/user/get-acct';
-
-export default Vue.extend({
- props: ['user'],
- data() {
- return {
- fetching: true,
- images: []
- };
- },
- methods: {
- getAcct
- },
- mounted() {
- (this as any).api('users/posts', {
- userId: this.user.id,
- withMedia: true,
- limit: 6
- }).then(posts => {
- posts.forEach(post => {
- post.media.forEach(media => {
- if (this.images.length < 9) this.images.push({
- post,
- media
- });
- });
- });
- this.fetching = false;
- });
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.photos
-
- > .stream
- display -webkit-flex
- display -moz-flex
- display -ms-flex
- display flex
- justify-content center
- flex-wrap wrap
- padding 8px
-
- > .img
- flex 1 1 33%
- width 33%
- height 80px
- background-position center center
- background-size cover
- background-clip content-box
- border solid 2px transparent
- border-radius 4px
-
- > .initializing
- > .empty
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > i
- margin-right 4px
-
-</style>
-
diff --git a/src/server/web/app/mobile/views/pages/user/home.posts.vue b/src/server/web/app/mobile/views/pages/user/home.posts.vue
deleted file mode 100644
index 654f7f63e0..0000000000
--- a/src/server/web/app/mobile/views/pages/user/home.posts.vue
+++ /dev/null
@@ -1,57 +0,0 @@
-<template>
-<div class="root posts">
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-posts.loading%<mk-ellipsis/></p>
- <div v-if="!fetching && posts.length > 0">
- <mk-post-card v-for="post in posts" :key="post.id" :post="post"/>
- </div>
- <p class="empty" v-if="!fetching && posts.length == 0">%i18n:mobile.tags.mk-user-overview-posts.no-posts%</p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- props: ['user'],
- data() {
- return {
- fetching: true,
- posts: []
- };
- },
- mounted() {
- (this as any).api('users/posts', {
- userId: this.user.id
- }).then(posts => {
- this.posts = posts;
- this.fetching = false;
- });
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.posts
-
- > div
- overflow-x scroll
- -webkit-overflow-scrolling touch
- white-space nowrap
- padding 8px
-
- > *
- vertical-align top
-
- &:not(:last-child)
- margin-right 8px
-
- > .fetching
- > .empty
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > i
- margin-right 4px
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/user/home.vue b/src/server/web/app/mobile/views/pages/user/home.vue
deleted file mode 100644
index 1afcd1f5ba..0000000000
--- a/src/server/web/app/mobile/views/pages/user/home.vue
+++ /dev/null
@@ -1,94 +0,0 @@
-<template>
-<div class="root home">
- <mk-post-detail v-if="user.pinnedPost" :post="user.pinnedPost" :compact="true"/>
- <section class="recent-posts">
- <h2>%fa:R comments%%i18n:mobile.tags.mk-user-overview.recent-posts%</h2>
- <div>
- <x-posts :user="user"/>
- </div>
- </section>
- <section class="images">
- <h2>%fa:image%%i18n:mobile.tags.mk-user-overview.images%</h2>
- <div>
- <x-photos :user="user"/>
- </div>
- </section>
- <section class="activity">
- <h2>%fa:chart-bar%%i18n:mobile.tags.mk-user-overview.activity%</h2>
- <div>
- <mk-activity :user="user"/>
- </div>
- </section>
- <section class="frequently-replied-users">
- <h2>%fa:users%%i18n:mobile.tags.mk-user-overview.frequently-replied-users%</h2>
- <div>
- <x-friends :user="user"/>
- </div>
- </section>
- <section class="followers-you-know" v-if="os.isSignedIn && os.i.id !== user.id">
- <h2>%fa:users%%i18n:mobile.tags.mk-user-overview.followers-you-know%</h2>
- <div>
- <x-followers-you-know :user="user"/>
- </div>
- </section>
- <p v-if="user.host === null">%i18n:mobile.tags.mk-user-overview.last-used-at%: <b><mk-time :time="user.account.lastUsedAt"/></b></p>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import XPosts from './home.posts.vue';
-import XPhotos from './home.photos.vue';
-import XFriends from './home.friends.vue';
-import XFollowersYouKnow from './home.followers-you-know.vue';
-
-export default Vue.extend({
- components: {
- XPosts,
- XPhotos,
- XFriends,
- XFollowersYouKnow
- },
- props: ['user']
-});
-</script>
-
-<style lang="stylus" scoped>
-.root.home
- max-width 600px
- margin 0 auto
-
- > .mk-post-detail
- margin 0 0 8px 0
-
- > section
- background #eee
- border-radius 8px
- box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
- &:not(:last-child)
- margin-bottom 8px
-
- > h2
- margin 0
- padding 8px 10px
- font-size 15px
- font-weight normal
- color #465258
- background #fff
- border-radius 8px 8px 0 0
-
- > i
- margin-right 6px
-
- > .activity
- > div
- padding 8px
-
- > p
- display block
- margin 16px
- text-align center
- color #cad2da
-
-</style>
diff --git a/src/server/web/app/mobile/views/pages/welcome.vue b/src/server/web/app/mobile/views/pages/welcome.vue
deleted file mode 100644
index 17cdf93065..0000000000
--- a/src/server/web/app/mobile/views/pages/welcome.vue
+++ /dev/null
@@ -1,206 +0,0 @@
-<template>
-<div class="welcome">
- <h1><b>Misskey</b>へようこそ</h1>
- <p>Twitter風ミニブログSNS、Misskeyへようこそ。共有したいことを投稿したり、タイムラインでみんなの投稿を読むこともできます。<br><a href="/signup">アカウントを作成する</a></p>
- <div class="form">
- <p>%fa:lock% ログイン</p>
- <div>
- <form @submit.prevent="onSubmit">
- <input v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" placeholder="ユーザー名" autofocus required @change="onUsernameChange"/>
- <input v-model="password" type="password" placeholder="パスワード" required/>
- <input v-if="user && user.account.twoFactorEnabled" v-model="token" type="number" placeholder="トークン" required/>
- <button type="submit" :disabled="signing">{{ signing ? 'ログインしています' : 'ログイン' }}</button>
- </form>
- <div>
- <a :href="`${apiUrl}/signin/twitter`">Twitterでログイン</a>
- </div>
- </div>
- </div>
- <div class="tl">
- <p>%fa:comments R% タイムラインを見てみる</p>
- <mk-welcome-timeline/>
- </div>
- <div class="users">
- <router-link v-for="user in users" :key="user.id" class="avatar-anchor" :to="`/@${user.username}`">
- <img class="avatar" :src="`${user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
- </router-link>
- </div>
- <footer>
- <small>{{ copyright }}</small>
- </footer>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import { apiUrl, copyright } from '../../../config';
-
-export default Vue.extend({
- data() {
- return {
- signing: false,
- user: null,
- username: '',
- password: '',
- token: '',
- apiUrl,
- copyright,
- users: []
- };
- },
- mounted() {
- (this as any).api('users', {
- sort: '+follower',
- limit: 20
- }).then(users => {
- this.users = users;
- });
- },
- methods: {
- onUsernameChange() {
- (this as any).api('users/show', {
- username: this.username
- }).then(user => {
- this.user = user;
- });
- },
- onSubmit() {
- this.signing = true;
-
- (this as any).api('signin', {
- username: this.username,
- password: this.password,
- token: this.user && this.user.account.twoFactorEnabled ? this.token : undefined
- }).then(() => {
- location.reload();
- }).catch(() => {
- alert('something happened');
- this.signing = false;
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.welcome
- padding 16px
- margin 0 auto
- max-width 500px
-
- h1
- margin 0
- padding 8px
- font-size 1.5em
- font-weight normal
- color #cacac3
-
- & + p
- margin 0 0 16px 0
- padding 0 8px 0 8px
- color #949fa9
-
- .form
- margin-bottom 16px
- background #fff
- border solid 1px rgba(0, 0, 0, 0.2)
- border-radius 8px
- overflow hidden
-
- > p
- margin 0
- padding 12px 20px
- color #555
- background #f5f5f5
- border-bottom solid 1px #ddd
-
- > div
-
- > form
- padding 16px
- border-bottom solid 1px #ddd
-
- input
- display block
- padding 12px
- margin 0 0 16px 0
- width 100%
- font-size 1em
- color rgba(0, 0, 0, 0.7)
- background #fff
- outline none
- border solid 1px #ddd
- border-radius 4px
-
- button
- display block
- width 100%
- padding 10px
- margin 0
- color #333
- font-size 1em
- text-align center
- text-decoration none
- text-shadow 0 1px 0 rgba(255, 255, 255, 0.9)
- background-image linear-gradient(#fafafa, #eaeaea)
- border 1px solid #ddd
- border-bottom-color #cecece
- border-radius 4px
-
- &:active
- background-color #767676
- background-image none
- border-color #444
- box-shadow 0 1px 3px rgba(0, 0, 0, 0.075), inset 0 0 5px rgba(0, 0, 0, 0.2)
-
- > div
- padding 16px
- text-align center
-
- > .tl
- background #fff
- border solid 1px rgba(0, 0, 0, 0.2)
- border-radius 8px
- overflow hidden
-
- > p
- margin 0
- padding 12px 20px
- color #555
- background #f5f5f5
- border-bottom solid 1px #ddd
-
- > .mk-welcome-timeline
- max-height 300px
- overflow auto
-
- > .users
- margin 12px 0 0 0
-
- > *
- display inline-block
- margin 4px
-
- > *
- display inline-block
- width 38px
- height 38px
- vertical-align top
- border-radius 6px
-
- > footer
- text-align center
- color #fff
-
- > small
- display block
- margin 16px 0 0 0
- opacity 0.7
-
-</style>
-
-<style lang="stylus">
-html
-body
- background linear-gradient(to bottom, #1e1d65, #bd6659)
-</style>
diff --git a/src/server/web/app/mobile/views/widgets/activity.vue b/src/server/web/app/mobile/views/widgets/activity.vue
deleted file mode 100644
index 48dcafb3ed..0000000000
--- a/src/server/web/app/mobile/views/widgets/activity.vue
+++ /dev/null
@@ -1,32 +0,0 @@
-<template>
-<div class="mkw-activity">
- <mk-widget-container :show-header="!props.compact">
- <template slot="header">%fa:chart-bar%アクティビティ</template>
- <div :class="$style.body">
- <mk-activity :user="os.i"/>
- </div>
- </mk-widget-container>
-</div>
-</template>
-
-<script lang="ts">
-import define from '../../../common/define-widget';
-
-export default define({
- name: 'activity',
- props: () => ({
- compact: false
- })
-}).extend({
- methods: {
- func() {
- this.props.compact = !this.props.compact;
- }
- }
-});
-</script>
-
-<style lang="stylus" module>
-.body
- padding 8px
-</style>
diff --git a/src/server/web/app/mobile/views/widgets/index.ts b/src/server/web/app/mobile/views/widgets/index.ts
deleted file mode 100644
index 4de912b64c..0000000000
--- a/src/server/web/app/mobile/views/widgets/index.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-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/server/web/app/mobile/views/widgets/profile.vue b/src/server/web/app/mobile/views/widgets/profile.vue
deleted file mode 100644
index f1d283e45a..0000000000
--- a/src/server/web/app/mobile/views/widgets/profile.vue
+++ /dev/null
@@ -1,62 +0,0 @@
-<template>
-<div class="mkw-profile">
- <mk-widget-container>
- <div :class="$style.banner"
- :style="os.i.bannerUrl ? `background-image: url(${os.i.bannerUrl}?thumbnail&size=256)` : ''"
- ></div>
- <img :class="$style.avatar"
- :src="`${os.i.avatarUrl}?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>