From cf33e483f7e6f40e8cbbbc0118a7df70bdaf651f Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 29 Mar 2018 20:32:18 +0900 Subject: 整理した MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/app/animation.styl | 12 + src/client/app/app.styl | 128 ++++ src/client/app/app.vue | 3 + src/client/app/auth/assets/logo.svg | 7 + src/client/app/auth/script.ts | 25 + src/client/app/auth/style.styl | 15 + src/client/app/auth/views/form.vue | 141 ++++ src/client/app/auth/views/index.vue | 149 ++++ src/client/app/base.pug | 38 + src/client/app/boot.js | 120 ++++ src/client/app/ch/script.ts | 15 + src/client/app/ch/style.styl | 10 + src/client/app/ch/tags/channel.tag | 403 +++++++++++ src/client/app/ch/tags/header.tag | 20 + src/client/app/ch/tags/index.tag | 37 + src/client/app/ch/tags/index.ts | 3 + src/client/app/common/define-widget.ts | 79 +++ src/client/app/common/mios.ts | 578 +++++++++++++++ src/client/app/common/scripts/check-for-update.ts | 33 + .../app/common/scripts/compose-notification.ts | 67 ++ src/client/app/common/scripts/contains.ts | 8 + src/client/app/common/scripts/copy-to-clipboard.ts | 13 + src/client/app/common/scripts/date-stringify.ts | 13 + src/client/app/common/scripts/fuck-ad-block.ts | 21 + src/client/app/common/scripts/gcd.ts | 2 + src/client/app/common/scripts/get-kao.ts | 5 + src/client/app/common/scripts/get-median.ts | 11 + src/client/app/common/scripts/loading.ts | 21 + .../app/common/scripts/parse-search-query.ts | 53 ++ src/client/app/common/scripts/streaming/channel.ts | 13 + src/client/app/common/scripts/streaming/drive.ts | 34 + src/client/app/common/scripts/streaming/home.ts | 57 ++ .../common/scripts/streaming/messaging-index.ts | 34 + .../app/common/scripts/streaming/messaging.ts | 20 + .../app/common/scripts/streaming/othello-game.ts | 11 + src/client/app/common/scripts/streaming/othello.ts | 31 + .../app/common/scripts/streaming/requests.ts | 30 + src/client/app/common/scripts/streaming/server.ts | 30 + .../app/common/scripts/streaming/stream-manager.ts | 108 +++ src/client/app/common/scripts/streaming/stream.ts | 137 ++++ .../app/common/views/components/autocomplete.vue | 306 ++++++++ .../components/connect-failed.troubleshooter.vue | 137 ++++ .../app/common/views/components/connect-failed.vue | 106 +++ .../app/common/views/components/ellipsis.vue | 26 + .../app/common/views/components/file-type-icon.vue | 17 + src/client/app/common/views/components/forkit.vue | 42 ++ src/client/app/common/views/components/index.ts | 51 ++ .../app/common/views/components/media-list.vue | 57 ++ .../views/components/messaging-room.form.vue | 305 ++++++++ .../views/components/messaging-room.message.vue | 263 +++++++ .../app/common/views/components/messaging-room.vue | 377 ++++++++++ .../app/common/views/components/messaging.vue | 463 ++++++++++++ src/client/app/common/views/components/nav.vue | 41 ++ .../app/common/views/components/othello.game.vue | 324 +++++++++ .../common/views/components/othello.gameroom.vue | 42 ++ .../app/common/views/components/othello.room.vue | 297 ++++++++ src/client/app/common/views/components/othello.vue | 311 +++++++++ .../app/common/views/components/poll-editor.vue | 142 ++++ src/client/app/common/views/components/poll.vue | 124 ++++ .../app/common/views/components/post-html.ts | 137 ++++ .../app/common/views/components/post-menu.vue | 141 ++++ .../app/common/views/components/reaction-icon.vue | 28 + .../common/views/components/reaction-picker.vue | 191 +++++ .../common/views/components/reactions-viewer.vue | 49 ++ src/client/app/common/views/components/signin.vue | 142 ++++ src/client/app/common/views/components/signup.vue | 287 ++++++++ .../common/views/components/special-message.vue | 42 ++ .../common/views/components/stream-indicator.vue | 86 +++ src/client/app/common/views/components/switch.vue | 190 +++++ src/client/app/common/views/components/time.vue | 76 ++ src/client/app/common/views/components/timer.vue | 49 ++ .../common/views/components/twitter-setting.vue | 66 ++ .../app/common/views/components/uploader.vue | 212 ++++++ .../app/common/views/components/url-preview.vue | 142 ++++ src/client/app/common/views/components/url.vue | 66 ++ .../common/views/components/welcome-timeline.vue | 118 ++++ .../app/common/views/directives/autocomplete.ts | 194 ++++++ src/client/app/common/views/directives/index.ts | 5 + src/client/app/common/views/filters/bytes.ts | 8 + src/client/app/common/views/filters/index.ts | 2 + src/client/app/common/views/filters/number.ts | 5 + src/client/app/common/views/widgets/access-log.vue | 90 +++ src/client/app/common/views/widgets/broadcast.vue | 161 +++++ src/client/app/common/views/widgets/calendar.vue | 201 ++++++ src/client/app/common/views/widgets/donation.vue | 58 ++ src/client/app/common/views/widgets/index.ts | 25 + src/client/app/common/views/widgets/nav.vue | 31 + .../app/common/views/widgets/photo-stream.vue | 104 +++ src/client/app/common/views/widgets/rss.vue | 93 +++ .../app/common/views/widgets/server.cpu-memory.vue | 127 ++++ src/client/app/common/views/widgets/server.cpu.vue | 68 ++ .../app/common/views/widgets/server.disk.vue | 76 ++ .../app/common/views/widgets/server.info.vue | 25 + .../app/common/views/widgets/server.memory.vue | 76 ++ src/client/app/common/views/widgets/server.pie.vue | 61 ++ .../app/common/views/widgets/server.uptimes.vue | 46 ++ src/client/app/common/views/widgets/server.vue | 93 +++ src/client/app/common/views/widgets/slideshow.vue | 159 +++++ src/client/app/common/views/widgets/tips.vue | 108 +++ src/client/app/common/views/widgets/version.vue | 29 + src/client/app/config.ts | 37 + src/client/app/desktop/api/choose-drive-file.ts | 30 + src/client/app/desktop/api/choose-drive-folder.ts | 17 + src/client/app/desktop/api/contextmenu.ts | 16 + src/client/app/desktop/api/dialog.ts | 19 + src/client/app/desktop/api/input.ts | 20 + src/client/app/desktop/api/notify.ts | 10 + src/client/app/desktop/api/post.ts | 21 + src/client/app/desktop/api/update-avatar.ts | 98 +++ src/client/app/desktop/api/update-banner.ts | 98 +++ src/client/app/desktop/assets/grid.svg | 150 ++++ .../app/desktop/assets/header-logo-white.svg | 25 + src/client/app/desktop/assets/header-logo.svg | 25 + src/client/app/desktop/assets/index.jpg | Bin 0 -> 410409 bytes src/client/app/desktop/assets/remove.png | Bin 0 -> 3115 bytes src/client/app/desktop/script.ts | 167 +++++ src/client/app/desktop/style.styl | 50 ++ src/client/app/desktop/ui.styl | 125 ++++ .../desktop/views/components/activity.calendar.vue | 66 ++ .../desktop/views/components/activity.chart.vue | 103 +++ .../app/desktop/views/components/activity.vue | 116 ++++ .../app/desktop/views/components/analog-clock.vue | 108 +++ .../app/desktop/views/components/calendar.vue | 252 +++++++ .../components/choose-file-from-drive-window.vue | 180 +++++ .../components/choose-folder-from-drive-window.vue | 114 +++ .../desktop/views/components/context-menu.menu.vue | 121 ++++ .../app/desktop/views/components/context-menu.vue | 74 ++ .../app/desktop/views/components/crop-window.vue | 178 +++++ src/client/app/desktop/views/components/dialog.vue | 170 +++++ .../app/desktop/views/components/drive-window.vue | 56 ++ .../app/desktop/views/components/drive.file.vue | 317 +++++++++ .../app/desktop/views/components/drive.folder.vue | 267 +++++++ .../desktop/views/components/drive.nav-folder.vue | 113 +++ src/client/app/desktop/views/components/drive.vue | 773 +++++++++++++++++++++ .../app/desktop/views/components/ellipsis-icon.vue | 37 + .../app/desktop/views/components/follow-button.vue | 164 +++++ .../desktop/views/components/followers-window.vue | 26 + .../app/desktop/views/components/followers.vue | 26 + .../desktop/views/components/following-window.vue | 26 + .../app/desktop/views/components/following.vue | 26 + .../app/desktop/views/components/friends-maker.vue | 171 +++++ .../app/desktop/views/components/game-window.vue | 37 + src/client/app/desktop/views/components/home.vue | 357 ++++++++++ src/client/app/desktop/views/components/index.ts | 61 ++ .../app/desktop/views/components/input-dialog.vue | 180 +++++ .../views/components/media-image-dialog.vue | 69 ++ .../app/desktop/views/components/media-image.vue | 63 ++ .../views/components/media-video-dialog.vue | 70 ++ .../app/desktop/views/components/media-video.vue | 67 ++ .../app/desktop/views/components/mentions.vue | 125 ++++ .../views/components/messaging-room-window.vue | 32 + .../desktop/views/components/messaging-window.vue | 32 + .../app/desktop/views/components/notifications.vue | 317 +++++++++ .../desktop/views/components/post-detail.sub.vue | 126 ++++ .../app/desktop/views/components/post-detail.vue | 433 ++++++++++++ .../desktop/views/components/post-form-window.vue | 76 ++ .../app/desktop/views/components/post-form.vue | 536 ++++++++++++++ .../app/desktop/views/components/post-preview.vue | 103 +++ .../desktop/views/components/posts.post.sub.vue | 112 +++ .../app/desktop/views/components/posts.post.vue | 582 ++++++++++++++++ src/client/app/desktop/views/components/posts.vue | 89 +++ .../desktop/views/components/progress-dialog.vue | 95 +++ .../views/components/repost-form-window.vue | 42 ++ .../app/desktop/views/components/repost-form.vue | 131 ++++ .../desktop/views/components/settings-window.vue | 24 + .../app/desktop/views/components/settings.2fa.vue | 80 +++ .../app/desktop/views/components/settings.api.vue | 40 ++ .../app/desktop/views/components/settings.apps.vue | 39 ++ .../desktop/views/components/settings.drive.vue | 35 + .../app/desktop/views/components/settings.mute.vue | 35 + .../desktop/views/components/settings.password.vue | 47 ++ .../desktop/views/components/settings.profile.vue | 87 +++ .../desktop/views/components/settings.signins.vue | 98 +++ .../app/desktop/views/components/settings.vue | 419 +++++++++++ .../desktop/views/components/sub-post-content.vue | 56 ++ .../app/desktop/views/components/taskmanager.vue | 219 ++++++ .../app/desktop/views/components/timeline.vue | 156 +++++ .../desktop/views/components/ui-notification.vue | 61 ++ .../desktop/views/components/ui.header.account.vue | 225 ++++++ .../desktop/views/components/ui.header.clock.vue | 109 +++ .../app/desktop/views/components/ui.header.nav.vue | 175 +++++ .../views/components/ui.header.notifications.vue | 158 +++++ .../desktop/views/components/ui.header.post.vue | 54 ++ .../desktop/views/components/ui.header.search.vue | 70 ++ .../app/desktop/views/components/ui.header.vue | 172 +++++ src/client/app/desktop/views/components/ui.vue | 37 + .../app/desktop/views/components/user-preview.vue | 173 +++++ .../desktop/views/components/users-list.item.vue | 107 +++ .../app/desktop/views/components/users-list.vue | 143 ++++ .../desktop/views/components/widget-container.vue | 85 +++ src/client/app/desktop/views/components/window.vue | 635 +++++++++++++++++ src/client/app/desktop/views/directives/index.ts | 6 + .../app/desktop/views/directives/user-preview.ts | 72 ++ src/client/app/desktop/views/pages/drive.vue | 52 ++ .../app/desktop/views/pages/home-customize.vue | 12 + src/client/app/desktop/views/pages/home.vue | 62 ++ src/client/app/desktop/views/pages/index.vue | 16 + .../app/desktop/views/pages/messaging-room.vue | 54 ++ src/client/app/desktop/views/pages/othello.vue | 50 ++ src/client/app/desktop/views/pages/post.vue | 67 ++ src/client/app/desktop/views/pages/search.vue | 138 ++++ src/client/app/desktop/views/pages/selectdrive.vue | 177 +++++ .../views/pages/user/user.followers-you-know.vue | 84 +++ .../app/desktop/views/pages/user/user.friends.vue | 124 ++++ .../app/desktop/views/pages/user/user.header.vue | 196 ++++++ .../app/desktop/views/pages/user/user.home.vue | 103 +++ .../app/desktop/views/pages/user/user.photos.vue | 88 +++ .../app/desktop/views/pages/user/user.profile.vue | 138 ++++ .../app/desktop/views/pages/user/user.timeline.vue | 139 ++++ src/client/app/desktop/views/pages/user/user.vue | 53 ++ src/client/app/desktop/views/pages/welcome.vue | 319 +++++++++ src/client/app/desktop/views/widgets/activity.vue | 31 + .../desktop/views/widgets/channel.channel.form.vue | 67 ++ .../desktop/views/widgets/channel.channel.post.vue | 71 ++ .../app/desktop/views/widgets/channel.channel.vue | 106 +++ src/client/app/desktop/views/widgets/channel.vue | 107 +++ src/client/app/desktop/views/widgets/index.ts | 23 + src/client/app/desktop/views/widgets/messaging.vue | 59 ++ .../app/desktop/views/widgets/notifications.vue | 70 ++ src/client/app/desktop/views/widgets/polls.vue | 129 ++++ src/client/app/desktop/views/widgets/post-form.vue | 111 +++ src/client/app/desktop/views/widgets/profile.vue | 125 ++++ .../app/desktop/views/widgets/timemachine.vue | 28 + src/client/app/desktop/views/widgets/trends.vue | 135 ++++ src/client/app/desktop/views/widgets/users.vue | 172 +++++ src/client/app/dev/script.ts | 44 ++ src/client/app/dev/style.styl | 10 + src/client/app/dev/views/app.vue | 39 ++ src/client/app/dev/views/apps.vue | 37 + src/client/app/dev/views/index.vue | 10 + src/client/app/dev/views/new-app.vue | 105 +++ src/client/app/dev/views/ui.vue | 20 + src/client/app/init.css | 66 ++ src/client/app/init.ts | 172 +++++ src/client/app/mobile/api/choose-drive-file.ts | 18 + src/client/app/mobile/api/choose-drive-folder.ts | 17 + src/client/app/mobile/api/dialog.ts | 5 + src/client/app/mobile/api/input.ts | 8 + src/client/app/mobile/api/notify.ts | 3 + src/client/app/mobile/api/post.ts | 43 ++ src/client/app/mobile/script.ts | 84 +++ src/client/app/mobile/style.styl | 15 + .../app/mobile/views/components/activity.vue | 62 ++ .../mobile/views/components/drive-file-chooser.vue | 98 +++ .../views/components/drive-folder-chooser.vue | 78 +++ .../mobile/views/components/drive.file-detail.vue | 295 ++++++++ .../app/mobile/views/components/drive.file.vue | 171 +++++ .../app/mobile/views/components/drive.folder.vue | 58 ++ src/client/app/mobile/views/components/drive.vue | 581 ++++++++++++++++ .../app/mobile/views/components/follow-button.vue | 123 ++++ .../app/mobile/views/components/friends-maker.vue | 127 ++++ src/client/app/mobile/views/components/index.ts | 47 ++ .../app/mobile/views/components/media-image.vue | 31 + .../app/mobile/views/components/media-video.vue | 36 + .../views/components/notification-preview.vue | 128 ++++ .../app/mobile/views/components/notification.vue | 164 +++++ .../app/mobile/views/components/notifications.vue | 168 +++++ src/client/app/mobile/views/components/notify.vue | 49 ++ .../app/mobile/views/components/post-card.vue | 89 +++ .../mobile/views/components/post-detail.sub.vue | 109 +++ .../app/mobile/views/components/post-detail.vue | 447 ++++++++++++ .../app/mobile/views/components/post-form.vue | 275 ++++++++ .../app/mobile/views/components/post-preview.vue | 106 +++ .../app/mobile/views/components/post.sub.vue | 115 +++ src/client/app/mobile/views/components/post.vue | 523 ++++++++++++++ src/client/app/mobile/views/components/posts.vue | 111 +++ .../mobile/views/components/sub-post-content.vue | 43 ++ .../app/mobile/views/components/timeline.vue | 109 +++ .../app/mobile/views/components/ui.header.vue | 242 +++++++ src/client/app/mobile/views/components/ui.nav.vue | 244 +++++++ src/client/app/mobile/views/components/ui.vue | 75 ++ .../app/mobile/views/components/user-card.vue | 69 ++ .../app/mobile/views/components/user-preview.vue | 110 +++ .../app/mobile/views/components/user-timeline.vue | 76 ++ .../app/mobile/views/components/users-list.vue | 133 ++++ .../mobile/views/components/widget-container.vue | 68 ++ src/client/app/mobile/views/directives/index.ts | 6 + .../app/mobile/views/directives/user-preview.ts | 2 + src/client/app/mobile/views/pages/drive.vue | 107 +++ src/client/app/mobile/views/pages/followers.vue | 65 ++ src/client/app/mobile/views/pages/following.vue | 65 ++ src/client/app/mobile/views/pages/home.vue | 259 +++++++ src/client/app/mobile/views/pages/index.vue | 16 + .../app/mobile/views/pages/messaging-room.vue | 42 ++ src/client/app/mobile/views/pages/messaging.vue | 23 + .../app/mobile/views/pages/notifications.vue | 32 + src/client/app/mobile/views/pages/othello.vue | 50 ++ src/client/app/mobile/views/pages/post.vue | 85 +++ .../app/mobile/views/pages/profile-setting.vue | 226 ++++++ src/client/app/mobile/views/pages/search.vue | 93 +++ src/client/app/mobile/views/pages/selectdrive.vue | 96 +++ src/client/app/mobile/views/pages/settings.vue | 103 +++ src/client/app/mobile/views/pages/signup.vue | 57 ++ src/client/app/mobile/views/pages/user.vue | 247 +++++++ .../views/pages/user/home.followers-you-know.vue | 67 ++ .../app/mobile/views/pages/user/home.friends.vue | 54 ++ .../app/mobile/views/pages/user/home.photos.vue | 83 +++ .../app/mobile/views/pages/user/home.posts.vue | 57 ++ src/client/app/mobile/views/pages/user/home.vue | 94 +++ src/client/app/mobile/views/pages/welcome.vue | 206 ++++++ src/client/app/mobile/views/widgets/activity.vue | 32 + src/client/app/mobile/views/widgets/index.ts | 7 + src/client/app/mobile/views/widgets/profile.vue | 62 ++ src/client/app/reset.styl | 32 + src/client/app/safe.js | 31 + src/client/app/stats/style.styl | 10 + src/client/app/stats/tags/index.tag | 209 ++++++ src/client/app/stats/tags/index.ts | 1 + src/client/app/status/style.styl | 10 + src/client/app/status/tags/index.tag | 201 ++++++ src/client/app/status/tags/index.ts | 1 + src/client/app/sw.js | 71 ++ src/client/app/tsconfig.json | 23 + src/client/app/v.d.ts | 4 + src/client/assets/404.js | 21 + src/client/assets/code-highlight.css | 93 +++ src/client/assets/error.jpg | Bin 0 -> 56865 bytes src/client/assets/favicon.ico | Bin 0 -> 360414 bytes src/client/assets/label.svg | 6 + src/client/assets/manifest.json | 7 + src/client/assets/message.mp3 | Bin 0 -> 4584 bytes src/client/assets/othello-put-me.mp3 | Bin 0 -> 15672 bytes src/client/assets/othello-put-you.mp3 | Bin 0 -> 26121 bytes src/client/assets/post.mp3 | Bin 0 -> 2506 bytes src/client/assets/reactions/angry.png | Bin 0 -> 5875 bytes src/client/assets/reactions/confused.png | Bin 0 -> 7255 bytes src/client/assets/reactions/congrats.png | Bin 0 -> 10643 bytes src/client/assets/reactions/hmm.png | Bin 0 -> 6628 bytes src/client/assets/reactions/laugh.png | Bin 0 -> 7921 bytes src/client/assets/reactions/like.png | Bin 0 -> 4835 bytes src/client/assets/reactions/love.png | Bin 0 -> 3342 bytes src/client/assets/reactions/pudding.png | Bin 0 -> 7652 bytes src/client/assets/reactions/surprise.png | Bin 0 -> 4698 bytes src/client/assets/recover.html | 36 + src/client/assets/title.svg | 25 + src/client/assets/unread.svg | 7 + src/client/assets/welcome-bg.svg | 579 +++++++++++++++ src/client/assets/welcome-fg.svg | 380 ++++++++++ src/client/const.styl | 4 + src/client/docs/about.en.pug | 3 + src/client/docs/about.ja.pug | 3 + src/client/docs/api.ja.pug | 103 +++ src/client/docs/api/endpoints/posts/create.yaml | 53 ++ src/client/docs/api/endpoints/posts/timeline.yaml | 32 + src/client/docs/api/endpoints/style.styl | 21 + src/client/docs/api/endpoints/view.pug | 32 + src/client/docs/api/entities/drive-file.yaml | 73 ++ src/client/docs/api/entities/post.yaml | 168 +++++ src/client/docs/api/entities/style.styl | 1 + src/client/docs/api/entities/user.yaml | 173 +++++ src/client/docs/api/entities/view.pug | 20 + src/client/docs/api/gulpfile.ts | 188 +++++ src/client/docs/api/mixins.pug | 37 + src/client/docs/api/style.styl | 11 + src/client/docs/gulpfile.ts | 77 ++ src/client/docs/index.en.pug | 3 + src/client/docs/index.ja.pug | 3 + src/client/docs/layout.pug | 41 ++ src/client/docs/license.en.pug | 17 + src/client/docs/license.ja.pug | 17 + src/client/docs/mute.ja.pug | 13 + src/client/docs/search.ja.pug | 120 ++++ src/client/docs/server.ts | 21 + src/client/docs/style.styl | 120 ++++ src/client/docs/tou.ja.pug | 3 + src/client/docs/ui.styl | 19 + src/client/docs/vars.ts | 64 ++ src/client/element.scss | 12 + src/client/style.styl | 37 + 369 files changed, 36011 insertions(+) create mode 100644 src/client/app/animation.styl create mode 100644 src/client/app/app.styl create mode 100644 src/client/app/app.vue create mode 100644 src/client/app/auth/assets/logo.svg create mode 100644 src/client/app/auth/script.ts create mode 100644 src/client/app/auth/style.styl create mode 100644 src/client/app/auth/views/form.vue create mode 100644 src/client/app/auth/views/index.vue create mode 100644 src/client/app/base.pug create mode 100644 src/client/app/boot.js create mode 100644 src/client/app/ch/script.ts create mode 100644 src/client/app/ch/style.styl create mode 100644 src/client/app/ch/tags/channel.tag create mode 100644 src/client/app/ch/tags/header.tag create mode 100644 src/client/app/ch/tags/index.tag create mode 100644 src/client/app/ch/tags/index.ts create mode 100644 src/client/app/common/define-widget.ts create mode 100644 src/client/app/common/mios.ts create mode 100644 src/client/app/common/scripts/check-for-update.ts create mode 100644 src/client/app/common/scripts/compose-notification.ts create mode 100644 src/client/app/common/scripts/contains.ts create mode 100644 src/client/app/common/scripts/copy-to-clipboard.ts create mode 100644 src/client/app/common/scripts/date-stringify.ts create mode 100644 src/client/app/common/scripts/fuck-ad-block.ts create mode 100644 src/client/app/common/scripts/gcd.ts create mode 100644 src/client/app/common/scripts/get-kao.ts create mode 100644 src/client/app/common/scripts/get-median.ts create mode 100644 src/client/app/common/scripts/loading.ts create mode 100644 src/client/app/common/scripts/parse-search-query.ts create mode 100644 src/client/app/common/scripts/streaming/channel.ts create mode 100644 src/client/app/common/scripts/streaming/drive.ts create mode 100644 src/client/app/common/scripts/streaming/home.ts create mode 100644 src/client/app/common/scripts/streaming/messaging-index.ts create mode 100644 src/client/app/common/scripts/streaming/messaging.ts create mode 100644 src/client/app/common/scripts/streaming/othello-game.ts create mode 100644 src/client/app/common/scripts/streaming/othello.ts create mode 100644 src/client/app/common/scripts/streaming/requests.ts create mode 100644 src/client/app/common/scripts/streaming/server.ts create mode 100644 src/client/app/common/scripts/streaming/stream-manager.ts create mode 100644 src/client/app/common/scripts/streaming/stream.ts create mode 100644 src/client/app/common/views/components/autocomplete.vue create mode 100644 src/client/app/common/views/components/connect-failed.troubleshooter.vue create mode 100644 src/client/app/common/views/components/connect-failed.vue create mode 100644 src/client/app/common/views/components/ellipsis.vue create mode 100644 src/client/app/common/views/components/file-type-icon.vue create mode 100644 src/client/app/common/views/components/forkit.vue create mode 100644 src/client/app/common/views/components/index.ts create mode 100644 src/client/app/common/views/components/media-list.vue create mode 100644 src/client/app/common/views/components/messaging-room.form.vue create mode 100644 src/client/app/common/views/components/messaging-room.message.vue create mode 100644 src/client/app/common/views/components/messaging-room.vue create mode 100644 src/client/app/common/views/components/messaging.vue create mode 100644 src/client/app/common/views/components/nav.vue create mode 100644 src/client/app/common/views/components/othello.game.vue create mode 100644 src/client/app/common/views/components/othello.gameroom.vue create mode 100644 src/client/app/common/views/components/othello.room.vue create mode 100644 src/client/app/common/views/components/othello.vue create mode 100644 src/client/app/common/views/components/poll-editor.vue create mode 100644 src/client/app/common/views/components/poll.vue create mode 100644 src/client/app/common/views/components/post-html.ts create mode 100644 src/client/app/common/views/components/post-menu.vue create mode 100644 src/client/app/common/views/components/reaction-icon.vue create mode 100644 src/client/app/common/views/components/reaction-picker.vue create mode 100644 src/client/app/common/views/components/reactions-viewer.vue create mode 100644 src/client/app/common/views/components/signin.vue create mode 100644 src/client/app/common/views/components/signup.vue create mode 100644 src/client/app/common/views/components/special-message.vue create mode 100644 src/client/app/common/views/components/stream-indicator.vue create mode 100644 src/client/app/common/views/components/switch.vue create mode 100644 src/client/app/common/views/components/time.vue create mode 100644 src/client/app/common/views/components/timer.vue create mode 100644 src/client/app/common/views/components/twitter-setting.vue create mode 100644 src/client/app/common/views/components/uploader.vue create mode 100644 src/client/app/common/views/components/url-preview.vue create mode 100644 src/client/app/common/views/components/url.vue create mode 100644 src/client/app/common/views/components/welcome-timeline.vue create mode 100644 src/client/app/common/views/directives/autocomplete.ts create mode 100644 src/client/app/common/views/directives/index.ts create mode 100644 src/client/app/common/views/filters/bytes.ts create mode 100644 src/client/app/common/views/filters/index.ts create mode 100644 src/client/app/common/views/filters/number.ts create mode 100644 src/client/app/common/views/widgets/access-log.vue create mode 100644 src/client/app/common/views/widgets/broadcast.vue create mode 100644 src/client/app/common/views/widgets/calendar.vue create mode 100644 src/client/app/common/views/widgets/donation.vue create mode 100644 src/client/app/common/views/widgets/index.ts create mode 100644 src/client/app/common/views/widgets/nav.vue create mode 100644 src/client/app/common/views/widgets/photo-stream.vue create mode 100644 src/client/app/common/views/widgets/rss.vue create mode 100644 src/client/app/common/views/widgets/server.cpu-memory.vue create mode 100644 src/client/app/common/views/widgets/server.cpu.vue create mode 100644 src/client/app/common/views/widgets/server.disk.vue create mode 100644 src/client/app/common/views/widgets/server.info.vue create mode 100644 src/client/app/common/views/widgets/server.memory.vue create mode 100644 src/client/app/common/views/widgets/server.pie.vue create mode 100644 src/client/app/common/views/widgets/server.uptimes.vue create mode 100644 src/client/app/common/views/widgets/server.vue create mode 100644 src/client/app/common/views/widgets/slideshow.vue create mode 100644 src/client/app/common/views/widgets/tips.vue create mode 100644 src/client/app/common/views/widgets/version.vue create mode 100644 src/client/app/config.ts create mode 100644 src/client/app/desktop/api/choose-drive-file.ts create mode 100644 src/client/app/desktop/api/choose-drive-folder.ts create mode 100644 src/client/app/desktop/api/contextmenu.ts create mode 100644 src/client/app/desktop/api/dialog.ts create mode 100644 src/client/app/desktop/api/input.ts create mode 100644 src/client/app/desktop/api/notify.ts create mode 100644 src/client/app/desktop/api/post.ts create mode 100644 src/client/app/desktop/api/update-avatar.ts create mode 100644 src/client/app/desktop/api/update-banner.ts create mode 100644 src/client/app/desktop/assets/grid.svg create mode 100644 src/client/app/desktop/assets/header-logo-white.svg create mode 100644 src/client/app/desktop/assets/header-logo.svg create mode 100644 src/client/app/desktop/assets/index.jpg create mode 100644 src/client/app/desktop/assets/remove.png create mode 100644 src/client/app/desktop/script.ts create mode 100644 src/client/app/desktop/style.styl create mode 100644 src/client/app/desktop/ui.styl create mode 100644 src/client/app/desktop/views/components/activity.calendar.vue create mode 100644 src/client/app/desktop/views/components/activity.chart.vue create mode 100644 src/client/app/desktop/views/components/activity.vue create mode 100644 src/client/app/desktop/views/components/analog-clock.vue create mode 100644 src/client/app/desktop/views/components/calendar.vue create mode 100644 src/client/app/desktop/views/components/choose-file-from-drive-window.vue create mode 100644 src/client/app/desktop/views/components/choose-folder-from-drive-window.vue create mode 100644 src/client/app/desktop/views/components/context-menu.menu.vue create mode 100644 src/client/app/desktop/views/components/context-menu.vue create mode 100644 src/client/app/desktop/views/components/crop-window.vue create mode 100644 src/client/app/desktop/views/components/dialog.vue create mode 100644 src/client/app/desktop/views/components/drive-window.vue create mode 100644 src/client/app/desktop/views/components/drive.file.vue create mode 100644 src/client/app/desktop/views/components/drive.folder.vue create mode 100644 src/client/app/desktop/views/components/drive.nav-folder.vue create mode 100644 src/client/app/desktop/views/components/drive.vue create mode 100644 src/client/app/desktop/views/components/ellipsis-icon.vue create mode 100644 src/client/app/desktop/views/components/follow-button.vue create mode 100644 src/client/app/desktop/views/components/followers-window.vue create mode 100644 src/client/app/desktop/views/components/followers.vue create mode 100644 src/client/app/desktop/views/components/following-window.vue create mode 100644 src/client/app/desktop/views/components/following.vue create mode 100644 src/client/app/desktop/views/components/friends-maker.vue create mode 100644 src/client/app/desktop/views/components/game-window.vue create mode 100644 src/client/app/desktop/views/components/home.vue create mode 100644 src/client/app/desktop/views/components/index.ts create mode 100644 src/client/app/desktop/views/components/input-dialog.vue create mode 100644 src/client/app/desktop/views/components/media-image-dialog.vue create mode 100644 src/client/app/desktop/views/components/media-image.vue create mode 100644 src/client/app/desktop/views/components/media-video-dialog.vue create mode 100644 src/client/app/desktop/views/components/media-video.vue create mode 100644 src/client/app/desktop/views/components/mentions.vue create mode 100644 src/client/app/desktop/views/components/messaging-room-window.vue create mode 100644 src/client/app/desktop/views/components/messaging-window.vue create mode 100644 src/client/app/desktop/views/components/notifications.vue create mode 100644 src/client/app/desktop/views/components/post-detail.sub.vue create mode 100644 src/client/app/desktop/views/components/post-detail.vue create mode 100644 src/client/app/desktop/views/components/post-form-window.vue create mode 100644 src/client/app/desktop/views/components/post-form.vue create mode 100644 src/client/app/desktop/views/components/post-preview.vue create mode 100644 src/client/app/desktop/views/components/posts.post.sub.vue create mode 100644 src/client/app/desktop/views/components/posts.post.vue create mode 100644 src/client/app/desktop/views/components/posts.vue create mode 100644 src/client/app/desktop/views/components/progress-dialog.vue create mode 100644 src/client/app/desktop/views/components/repost-form-window.vue create mode 100644 src/client/app/desktop/views/components/repost-form.vue create mode 100644 src/client/app/desktop/views/components/settings-window.vue create mode 100644 src/client/app/desktop/views/components/settings.2fa.vue create mode 100644 src/client/app/desktop/views/components/settings.api.vue create mode 100644 src/client/app/desktop/views/components/settings.apps.vue create mode 100644 src/client/app/desktop/views/components/settings.drive.vue create mode 100644 src/client/app/desktop/views/components/settings.mute.vue create mode 100644 src/client/app/desktop/views/components/settings.password.vue create mode 100644 src/client/app/desktop/views/components/settings.profile.vue create mode 100644 src/client/app/desktop/views/components/settings.signins.vue create mode 100644 src/client/app/desktop/views/components/settings.vue create mode 100644 src/client/app/desktop/views/components/sub-post-content.vue create mode 100644 src/client/app/desktop/views/components/taskmanager.vue create mode 100644 src/client/app/desktop/views/components/timeline.vue create mode 100644 src/client/app/desktop/views/components/ui-notification.vue create mode 100644 src/client/app/desktop/views/components/ui.header.account.vue create mode 100644 src/client/app/desktop/views/components/ui.header.clock.vue create mode 100644 src/client/app/desktop/views/components/ui.header.nav.vue create mode 100644 src/client/app/desktop/views/components/ui.header.notifications.vue create mode 100644 src/client/app/desktop/views/components/ui.header.post.vue create mode 100644 src/client/app/desktop/views/components/ui.header.search.vue create mode 100644 src/client/app/desktop/views/components/ui.header.vue create mode 100644 src/client/app/desktop/views/components/ui.vue create mode 100644 src/client/app/desktop/views/components/user-preview.vue create mode 100644 src/client/app/desktop/views/components/users-list.item.vue create mode 100644 src/client/app/desktop/views/components/users-list.vue create mode 100644 src/client/app/desktop/views/components/widget-container.vue create mode 100644 src/client/app/desktop/views/components/window.vue create mode 100644 src/client/app/desktop/views/directives/index.ts create mode 100644 src/client/app/desktop/views/directives/user-preview.ts create mode 100644 src/client/app/desktop/views/pages/drive.vue create mode 100644 src/client/app/desktop/views/pages/home-customize.vue create mode 100644 src/client/app/desktop/views/pages/home.vue create mode 100644 src/client/app/desktop/views/pages/index.vue create mode 100644 src/client/app/desktop/views/pages/messaging-room.vue create mode 100644 src/client/app/desktop/views/pages/othello.vue create mode 100644 src/client/app/desktop/views/pages/post.vue create mode 100644 src/client/app/desktop/views/pages/search.vue create mode 100644 src/client/app/desktop/views/pages/selectdrive.vue create mode 100644 src/client/app/desktop/views/pages/user/user.followers-you-know.vue create mode 100644 src/client/app/desktop/views/pages/user/user.friends.vue create mode 100644 src/client/app/desktop/views/pages/user/user.header.vue create mode 100644 src/client/app/desktop/views/pages/user/user.home.vue create mode 100644 src/client/app/desktop/views/pages/user/user.photos.vue create mode 100644 src/client/app/desktop/views/pages/user/user.profile.vue create mode 100644 src/client/app/desktop/views/pages/user/user.timeline.vue create mode 100644 src/client/app/desktop/views/pages/user/user.vue create mode 100644 src/client/app/desktop/views/pages/welcome.vue create mode 100644 src/client/app/desktop/views/widgets/activity.vue create mode 100644 src/client/app/desktop/views/widgets/channel.channel.form.vue create mode 100644 src/client/app/desktop/views/widgets/channel.channel.post.vue create mode 100644 src/client/app/desktop/views/widgets/channel.channel.vue create mode 100644 src/client/app/desktop/views/widgets/channel.vue create mode 100644 src/client/app/desktop/views/widgets/index.ts create mode 100644 src/client/app/desktop/views/widgets/messaging.vue create mode 100644 src/client/app/desktop/views/widgets/notifications.vue create mode 100644 src/client/app/desktop/views/widgets/polls.vue create mode 100644 src/client/app/desktop/views/widgets/post-form.vue create mode 100644 src/client/app/desktop/views/widgets/profile.vue create mode 100644 src/client/app/desktop/views/widgets/timemachine.vue create mode 100644 src/client/app/desktop/views/widgets/trends.vue create mode 100644 src/client/app/desktop/views/widgets/users.vue create mode 100644 src/client/app/dev/script.ts create mode 100644 src/client/app/dev/style.styl create mode 100644 src/client/app/dev/views/app.vue create mode 100644 src/client/app/dev/views/apps.vue create mode 100644 src/client/app/dev/views/index.vue create mode 100644 src/client/app/dev/views/new-app.vue create mode 100644 src/client/app/dev/views/ui.vue create mode 100644 src/client/app/init.css create mode 100644 src/client/app/init.ts create mode 100644 src/client/app/mobile/api/choose-drive-file.ts create mode 100644 src/client/app/mobile/api/choose-drive-folder.ts create mode 100644 src/client/app/mobile/api/dialog.ts create mode 100644 src/client/app/mobile/api/input.ts create mode 100644 src/client/app/mobile/api/notify.ts create mode 100644 src/client/app/mobile/api/post.ts create mode 100644 src/client/app/mobile/script.ts create mode 100644 src/client/app/mobile/style.styl create mode 100644 src/client/app/mobile/views/components/activity.vue create mode 100644 src/client/app/mobile/views/components/drive-file-chooser.vue create mode 100644 src/client/app/mobile/views/components/drive-folder-chooser.vue create mode 100644 src/client/app/mobile/views/components/drive.file-detail.vue create mode 100644 src/client/app/mobile/views/components/drive.file.vue create mode 100644 src/client/app/mobile/views/components/drive.folder.vue create mode 100644 src/client/app/mobile/views/components/drive.vue create mode 100644 src/client/app/mobile/views/components/follow-button.vue create mode 100644 src/client/app/mobile/views/components/friends-maker.vue create mode 100644 src/client/app/mobile/views/components/index.ts create mode 100644 src/client/app/mobile/views/components/media-image.vue create mode 100644 src/client/app/mobile/views/components/media-video.vue create mode 100644 src/client/app/mobile/views/components/notification-preview.vue create mode 100644 src/client/app/mobile/views/components/notification.vue create mode 100644 src/client/app/mobile/views/components/notifications.vue create mode 100644 src/client/app/mobile/views/components/notify.vue create mode 100644 src/client/app/mobile/views/components/post-card.vue create mode 100644 src/client/app/mobile/views/components/post-detail.sub.vue create mode 100644 src/client/app/mobile/views/components/post-detail.vue create mode 100644 src/client/app/mobile/views/components/post-form.vue create mode 100644 src/client/app/mobile/views/components/post-preview.vue create mode 100644 src/client/app/mobile/views/components/post.sub.vue create mode 100644 src/client/app/mobile/views/components/post.vue create mode 100644 src/client/app/mobile/views/components/posts.vue create mode 100644 src/client/app/mobile/views/components/sub-post-content.vue create mode 100644 src/client/app/mobile/views/components/timeline.vue create mode 100644 src/client/app/mobile/views/components/ui.header.vue create mode 100644 src/client/app/mobile/views/components/ui.nav.vue create mode 100644 src/client/app/mobile/views/components/ui.vue create mode 100644 src/client/app/mobile/views/components/user-card.vue create mode 100644 src/client/app/mobile/views/components/user-preview.vue create mode 100644 src/client/app/mobile/views/components/user-timeline.vue create mode 100644 src/client/app/mobile/views/components/users-list.vue create mode 100644 src/client/app/mobile/views/components/widget-container.vue create mode 100644 src/client/app/mobile/views/directives/index.ts create mode 100644 src/client/app/mobile/views/directives/user-preview.ts create mode 100644 src/client/app/mobile/views/pages/drive.vue create mode 100644 src/client/app/mobile/views/pages/followers.vue create mode 100644 src/client/app/mobile/views/pages/following.vue create mode 100644 src/client/app/mobile/views/pages/home.vue create mode 100644 src/client/app/mobile/views/pages/index.vue create mode 100644 src/client/app/mobile/views/pages/messaging-room.vue create mode 100644 src/client/app/mobile/views/pages/messaging.vue create mode 100644 src/client/app/mobile/views/pages/notifications.vue create mode 100644 src/client/app/mobile/views/pages/othello.vue create mode 100644 src/client/app/mobile/views/pages/post.vue create mode 100644 src/client/app/mobile/views/pages/profile-setting.vue create mode 100644 src/client/app/mobile/views/pages/search.vue create mode 100644 src/client/app/mobile/views/pages/selectdrive.vue create mode 100644 src/client/app/mobile/views/pages/settings.vue create mode 100644 src/client/app/mobile/views/pages/signup.vue create mode 100644 src/client/app/mobile/views/pages/user.vue create mode 100644 src/client/app/mobile/views/pages/user/home.followers-you-know.vue create mode 100644 src/client/app/mobile/views/pages/user/home.friends.vue create mode 100644 src/client/app/mobile/views/pages/user/home.photos.vue create mode 100644 src/client/app/mobile/views/pages/user/home.posts.vue create mode 100644 src/client/app/mobile/views/pages/user/home.vue create mode 100644 src/client/app/mobile/views/pages/welcome.vue create mode 100644 src/client/app/mobile/views/widgets/activity.vue create mode 100644 src/client/app/mobile/views/widgets/index.ts create mode 100644 src/client/app/mobile/views/widgets/profile.vue create mode 100644 src/client/app/reset.styl create mode 100644 src/client/app/safe.js create mode 100644 src/client/app/stats/style.styl create mode 100644 src/client/app/stats/tags/index.tag create mode 100644 src/client/app/stats/tags/index.ts create mode 100644 src/client/app/status/style.styl create mode 100644 src/client/app/status/tags/index.tag create mode 100644 src/client/app/status/tags/index.ts create mode 100644 src/client/app/sw.js create mode 100644 src/client/app/tsconfig.json create mode 100644 src/client/app/v.d.ts create mode 100644 src/client/assets/404.js create mode 100644 src/client/assets/code-highlight.css create mode 100644 src/client/assets/error.jpg create mode 100644 src/client/assets/favicon.ico create mode 100644 src/client/assets/label.svg create mode 100644 src/client/assets/manifest.json create mode 100644 src/client/assets/message.mp3 create mode 100644 src/client/assets/othello-put-me.mp3 create mode 100644 src/client/assets/othello-put-you.mp3 create mode 100644 src/client/assets/post.mp3 create mode 100644 src/client/assets/reactions/angry.png create mode 100644 src/client/assets/reactions/confused.png create mode 100644 src/client/assets/reactions/congrats.png create mode 100644 src/client/assets/reactions/hmm.png create mode 100644 src/client/assets/reactions/laugh.png create mode 100644 src/client/assets/reactions/like.png create mode 100644 src/client/assets/reactions/love.png create mode 100644 src/client/assets/reactions/pudding.png create mode 100644 src/client/assets/reactions/surprise.png create mode 100644 src/client/assets/recover.html create mode 100644 src/client/assets/title.svg create mode 100644 src/client/assets/unread.svg create mode 100644 src/client/assets/welcome-bg.svg create mode 100644 src/client/assets/welcome-fg.svg create mode 100644 src/client/const.styl create mode 100644 src/client/docs/about.en.pug create mode 100644 src/client/docs/about.ja.pug create mode 100644 src/client/docs/api.ja.pug create mode 100644 src/client/docs/api/endpoints/posts/create.yaml create mode 100644 src/client/docs/api/endpoints/posts/timeline.yaml create mode 100644 src/client/docs/api/endpoints/style.styl create mode 100644 src/client/docs/api/endpoints/view.pug create mode 100644 src/client/docs/api/entities/drive-file.yaml create mode 100644 src/client/docs/api/entities/post.yaml create mode 100644 src/client/docs/api/entities/style.styl create mode 100644 src/client/docs/api/entities/user.yaml create mode 100644 src/client/docs/api/entities/view.pug create mode 100644 src/client/docs/api/gulpfile.ts create mode 100644 src/client/docs/api/mixins.pug create mode 100644 src/client/docs/api/style.styl create mode 100644 src/client/docs/gulpfile.ts create mode 100644 src/client/docs/index.en.pug create mode 100644 src/client/docs/index.ja.pug create mode 100644 src/client/docs/layout.pug create mode 100644 src/client/docs/license.en.pug create mode 100644 src/client/docs/license.ja.pug create mode 100644 src/client/docs/mute.ja.pug create mode 100644 src/client/docs/search.ja.pug create mode 100644 src/client/docs/server.ts create mode 100644 src/client/docs/style.styl create mode 100644 src/client/docs/tou.ja.pug create mode 100644 src/client/docs/ui.styl create mode 100644 src/client/docs/vars.ts create mode 100644 src/client/element.scss create mode 100644 src/client/style.styl (limited to 'src/client') diff --git a/src/client/app/animation.styl b/src/client/app/animation.styl new file mode 100644 index 0000000000..8f121b313b --- /dev/null +++ b/src/client/app/animation.styl @@ -0,0 +1,12 @@ +.zoom-in-top-enter-active, +.zoom-in-top-leave-active { + opacity: 1; + transform: scaleY(1); + transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1); + transform-origin: center top; +} +.zoom-in-top-enter, +.zoom-in-top-leave-active { + opacity: 0; + transform: scaleY(0); +} diff --git a/src/client/app/app.styl b/src/client/app/app.styl new file mode 100644 index 0000000000..431b9daa65 --- /dev/null +++ b/src/client/app/app.styl @@ -0,0 +1,128 @@ +@import "../style" +@import "../animation" + +html + &.progress + &, * + cursor progress !important + +body + overflow-wrap break-word + +#error + padding 32px + color #fff + + hr + border solid 1px #fff + +#nprogress + pointer-events none + + position absolute + z-index 65536 + + .bar + background $theme-color + + position fixed + z-index 65537 + top 0 + left 0 + + width 100% + height 2px + + /* Fancy blur effect */ + .peg + display block + position absolute + right 0px + width 100px + height 100% + box-shadow 0 0 10px $theme-color, 0 0 5px $theme-color + opacity 1 + + transform rotate(3deg) translate(0px, -4px) + +#wait + display block + position fixed + z-index 65537 + top 15px + right 15px + + &:before + content "" + display block + width 18px + height 18px + box-sizing border-box + + border solid 2px transparent + border-top-color $theme-color + border-left-color $theme-color + border-radius 50% + + animation progress-spinner 400ms linear infinite + + @keyframes progress-spinner + 0% + transform rotate(0deg) + 100% + transform rotate(360deg) + +code + font-family Consolas, 'Courier New', Courier, Monaco, monospace + + .comment + opacity 0.5 + + .string + color #e96900 + + .regexp + color #e9003f + + .keyword + color #2973b7 + + &.true + &.false + &.null + &.nil + &.undefined + color #ae81ff + + .symbol + color #42b983 + + .number + .nan + color #ae81ff + + .var:not(.keyword) + font-weight bold + font-style italic + //text-decoration underline + + .method + font-style italic + color #8964c1 + + .property + color #a71d5d + + .label + color #e9003f + +pre + display block + + > code + display block + overflow auto + tab-size 2 + +[data-fa] + display inline-block diff --git a/src/client/app/app.vue b/src/client/app/app.vue new file mode 100644 index 0000000000..7a46e7dea0 --- /dev/null +++ b/src/client/app/app.vue @@ -0,0 +1,3 @@ + diff --git a/src/client/app/auth/assets/logo.svg b/src/client/app/auth/assets/logo.svg new file mode 100644 index 0000000000..19b8a2737e --- /dev/null +++ b/src/client/app/auth/assets/logo.svg @@ -0,0 +1,7 @@ + + + + + diff --git a/src/client/app/auth/script.ts b/src/client/app/auth/script.ts new file mode 100644 index 0000000000..31c758ebc2 --- /dev/null +++ b/src/client/app/auth/script.ts @@ -0,0 +1,25 @@ +/** + * Authorize Form + */ + +// Style +import './style.styl'; + +import init from '../init'; + +import Index from './views/index.vue'; + +/** + * init + */ +init(async (launch) => { + document.title = 'Misskey | アプリの連携'; + + // Launch the app + const [app] = launch(); + + // Routing + app.$router.addRoutes([ + { path: '/:token', component: Index }, + ]); +}); diff --git a/src/client/app/auth/style.styl b/src/client/app/auth/style.styl new file mode 100644 index 0000000000..bd25e1b572 --- /dev/null +++ b/src/client/app/auth/style.styl @@ -0,0 +1,15 @@ +@import "../app" +@import "../reset" + +html + background #eee + + @media (max-width 600px) + background #fff + +body + margin 0 + padding 32px 0 + + @media (max-width 600px) + padding 0 diff --git a/src/client/app/auth/views/form.vue b/src/client/app/auth/views/form.vue new file mode 100644 index 0000000000..9d9e8cdb1b --- /dev/null +++ b/src/client/app/auth/views/form.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/client/app/auth/views/index.vue b/src/client/app/auth/views/index.vue new file mode 100644 index 0000000000..e1e1b265e1 --- /dev/null +++ b/src/client/app/auth/views/index.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/client/app/base.pug b/src/client/app/base.pug new file mode 100644 index 0000000000..32a95a6c99 --- /dev/null +++ b/src/client/app/base.pug @@ -0,0 +1,38 @@ +doctype html + +!= '\n\n' + +html + + head + meta(charset='utf-8') + meta(name='application-name' content='Misskey') + meta(name='theme-color' content=themeColor) + meta(name='referrer' content='origin') + link(rel='manifest' href='/manifest.json') + + title Misskey + + style + include ./../../../built/client/assets/init.css + script + include ./../../../built/client/assets/boot.js + + script + include ./../../../built/client/assets/safe.js + + //- FontAwesome style + style #{facss} + + //- highlight.js style + style #{hljscss} + + body + noscript: p + | JavaScriptを有効にしてください + br + | Please turn on your JavaScript + div#ini: p + span . + span . + span . diff --git a/src/client/app/boot.js b/src/client/app/boot.js new file mode 100644 index 0000000000..0846e4bd55 --- /dev/null +++ b/src/client/app/boot.js @@ -0,0 +1,120 @@ +/** + * MISSKEY BOOT LOADER + * (ENTRY POINT) + */ + +/** + * ドメインに基づいて適切なスクリプトを読み込みます。 + * ユーザーの言語およびモバイル端末か否かも考慮します。 + * webpackは介さないためrequireやimportは使えません。 + */ + +'use strict'; + +// Chromeで確認したことなのですが、constやletを用いたとしても +// グローバルなスコープで定数/変数を定義するとwindowのプロパティ +// としてそれがアクセスできるようになる訳ではありませんが、普通に +// コンソールから定数/変数名を入力するとアクセスできてしまいます。 +// ブロック内に入れてスコープを非グローバル化するとそれが防げます +// (Chrome以外のブラウザでは検証していません) +{ + // Get the current url information + const url = new URL(location.href); + + //#region Detect app name + let app = null; + + if (url.pathname == '/docs') app = 'docs'; + if (url.pathname == '/dev') app = 'dev'; + if (url.pathname == '/auth') app = 'auth'; + //#endregion + + // Detect the user language + // Note: The default language is English + let lang = navigator.language.split('-')[0]; + if (!/^(en|ja)$/.test(lang)) lang = 'en'; + if (localStorage.getItem('lang')) lang = localStorage.getItem('lang'); + if (ENV != 'production') lang = 'ja'; + + // Detect the user agent + const ua = navigator.userAgent.toLowerCase(); + const isMobile = /mobile|iphone|ipad|android/.test(ua); + + // Get the element + const head = document.getElementsByTagName('head')[0]; + + // If mobile, insert the viewport meta tag + if (isMobile) { + const meta = document.createElement('meta'); + meta.setAttribute('name', 'viewport'); + meta.setAttribute('content', + 'width=device-width,' + + 'initial-scale=1,' + + 'minimum-scale=1,' + + 'maximum-scale=1,' + + 'user-scalable=no'); + head.appendChild(meta); + } + + // Switch desktop or mobile version + if (app == null) { + app = isMobile ? 'mobile' : 'desktop'; + } + + // Script version + const ver = localStorage.getItem('v') || VERSION; + + // Whether in debug mode + const isDebug = localStorage.getItem('debug') == 'true'; + + // Whether use raw version script + const raw = (localStorage.getItem('useRawScript') == 'true' && isDebug) + || ENV != 'production'; + + // Load an app script + // Note: 'async' make it possible to load the script asyncly. + // 'defer' make it possible to run the script when the dom loaded. + const script = document.createElement('script'); + script.setAttribute('src', `/assets/${app}.${ver}.${lang}.${raw ? 'raw' : 'min'}.js`); + script.setAttribute('async', 'true'); + script.setAttribute('defer', 'true'); + head.appendChild(script); + + // 1秒経ってもスクリプトがロードされない場合はバージョンが古くて + // 404になっているせいかもしれないので、バージョンを確認して古ければ更新する + // + // 読み込まれたスクリプトからこのタイマーを解除できるように、 + // グローバルにタイマーIDを代入しておく + window.mkBootTimer = window.setTimeout(async () => { + // Fetch meta + const res = await fetch(API + '/meta', { + method: 'POST', + cache: 'no-cache' + }); + + // Parse + const meta = await res.json(); + + // Compare versions + if (meta.version != ver) { + alert( + 'Misskeyの新しいバージョンがあります。ページを再度読み込みします。' + + '\n\n' + + 'New version of Misskey available. The page will be reloaded.'); + + // Clear cache (serive worker) + try { + navigator.serviceWorker.controller.postMessage('clear'); + + navigator.serviceWorker.getRegistrations().then(registrations => { + registrations.forEach(registration => registration.unregister()); + }); + } catch (e) { + console.error(e); + } + + // Force reload + location.reload(true); + } + }, 1000); +} diff --git a/src/client/app/ch/script.ts b/src/client/app/ch/script.ts new file mode 100644 index 0000000000..4c6b6dfd1b --- /dev/null +++ b/src/client/app/ch/script.ts @@ -0,0 +1,15 @@ +/** + * Channels + */ + +// Style +import './style.styl'; + +require('./tags'); +import init from '../init'; + +/** + * init + */ +init(() => { +}); diff --git a/src/client/app/ch/style.styl b/src/client/app/ch/style.styl new file mode 100644 index 0000000000..21ca648cbe --- /dev/null +++ b/src/client/app/ch/style.styl @@ -0,0 +1,10 @@ +@import "../app" + +html + padding 8px + background #efefef + +#wait + top auto + bottom 15px + left 15px diff --git a/src/client/app/ch/tags/channel.tag b/src/client/app/ch/tags/channel.tag new file mode 100644 index 0000000000..2abfb106a5 --- /dev/null +++ b/src/client/app/ch/tags/channel.tag @@ -0,0 +1,403 @@ + + +
+
+

{ channel.title }

+ +
+

このチャンネルをウォッチしています ウォッチ解除

+

このチャンネルをウォッチする

+
+ + + +
+

読み込み中

+
+

まだ投稿がありません

+ +
+
+
+ +
+

参加するにはログインまたは新規登録してください

+
+
+
+ Misskey ver { _VERSION_ } (葵 aoi) +
+
+ + +
+ + +
+ { post.index }: + { post.user.name } + + + ID:{ acct } +
+
+ >>{ post.reply.index } + { post.text } +
+ +
+
+ + +
+ + +

>>{ reply.index } ({ reply.user.name }): [x]

+ +
+ + + +
+ +
    +
  1. { name }
  2. +
+ + + +
+ + + + + + + + + + diff --git a/src/client/app/ch/tags/header.tag b/src/client/app/ch/tags/header.tag new file mode 100644 index 0000000000..901123d63b --- /dev/null +++ b/src/client/app/ch/tags/header.tag @@ -0,0 +1,20 @@ + +
+ Index | Misskey +
+
+ ログイン(新規登録) + { I.username } +
+ + +
diff --git a/src/client/app/ch/tags/index.tag b/src/client/app/ch/tags/index.tag new file mode 100644 index 0000000000..88df2ec45d --- /dev/null +++ b/src/client/app/ch/tags/index.tag @@ -0,0 +1,37 @@ + + +
+ +
+ + + +
diff --git a/src/client/app/ch/tags/index.ts b/src/client/app/ch/tags/index.ts new file mode 100644 index 0000000000..12ffdaeb84 --- /dev/null +++ b/src/client/app/ch/tags/index.ts @@ -0,0 +1,3 @@ +require('./index.tag'); +require('./channel.tag'); +require('./header.tag'); diff --git a/src/client/app/common/define-widget.ts b/src/client/app/common/define-widget.ts new file mode 100644 index 0000000000..27db59b5ee --- /dev/null +++ b/src/client/app/common/define-widget.ts @@ -0,0 +1,79 @@ +import Vue from 'vue'; + +export default function(data: { + name: string; + props?: () => T; +}) { + return Vue.extend({ + props: { + widget: { + type: Object + }, + isMobile: { + type: Boolean, + default: false + }, + isCustomizeMode: { + type: Boolean, + default: false + } + }, + computed: { + id(): string { + return this.widget.id; + } + }, + data() { + return { + props: data.props ? data.props() : {} as T, + bakedOldProps: null, + preventSave: false + }; + }, + created() { + if (this.props) { + Object.keys(this.props).forEach(prop => { + if (this.widget.data.hasOwnProperty(prop)) { + this.props[prop] = this.widget.data[prop]; + } + }); + } + + this.bakeProps(); + + this.$watch('props', newProps => { + if (this.preventSave) { + this.preventSave = false; + this.bakeProps(); + return; + } + if (this.bakedOldProps == JSON.stringify(newProps)) return; + + this.bakeProps(); + + if (this.isMobile) { + (this as any).api('i/update_mobile_home', { + id: this.id, + data: newProps + }).then(() => { + (this as any).os.i.account.clientSettings.mobile_home.find(w => w.id == this.id).data = newProps; + }); + } else { + (this as any).api('i/update_home', { + id: this.id, + data: newProps + }).then(() => { + (this as any).os.i.account.clientSettings.home.find(w => w.id == this.id).data = newProps; + }); + } + }, { + deep: true + }); + }, + methods: { + bakeProps() { + this.bakedOldProps = JSON.stringify(this.props); + } + } + }); +} diff --git a/src/client/app/common/mios.ts b/src/client/app/common/mios.ts new file mode 100644 index 0000000000..bcb8b60678 --- /dev/null +++ b/src/client/app/common/mios.ts @@ -0,0 +1,578 @@ +import Vue from 'vue'; +import { EventEmitter } from 'eventemitter3'; +import * as merge from 'object-assign-deep'; +import * as uuid from 'uuid'; + +import { hostname, apiUrl, swPublickey, version, lang, googleMapsApiKey } from '../config'; +import Progress from './scripts/loading'; +import Connection from './scripts/streaming/stream'; +import { HomeStreamManager } from './scripts/streaming/home'; +import { DriveStreamManager } from './scripts/streaming/drive'; +import { ServerStreamManager } from './scripts/streaming/server'; +import { RequestsStreamManager } from './scripts/streaming/requests'; +import { MessagingIndexStreamManager } from './scripts/streaming/messaging-index'; +import { OthelloStreamManager } from './scripts/streaming/othello'; + +import Err from '../common/views/components/connect-failed.vue'; + +//#region api requests +let spinner = null; +let pending = 0; +//#endregion + +export type API = { + chooseDriveFile: (opts: { + title?: string; + currentFolder?: any; + multiple?: boolean; + }) => Promise; + + chooseDriveFolder: (opts: { + title?: string; + currentFolder?: any; + }) => Promise; + + dialog: (opts: { + title: string; + text: string; + actions?: Array<{ + text: string; + id?: string; + }>; + }) => Promise; + + input: (opts: { + title: string; + placeholder?: string; + default?: string; + }) => Promise; + + post: (opts?: { + reply?: any; + repost?: any; + }) => void; + + notify: (message: string) => void; +}; + +/** + * Misskey Operating System + */ +export default class MiOS extends EventEmitter { + /** + * Misskeyの /meta で取得できるメタ情報 + */ + private meta: { + data: { [x: string]: any }; + chachedAt: Date; + }; + + private isMetaFetching = false; + + public app: Vue; + + public new(vm, props) { + const w = new vm({ + parent: this.app, + propsData: props + }).$mount(); + document.body.appendChild(w.$el); + } + + /** + * A signing user + */ + public i: { [x: string]: any }; + + /** + * Whether signed in + */ + public get isSignedIn() { + return this.i != null; + } + + /** + * Whether is debug mode + */ + public get debug() { + return localStorage.getItem('debug') == 'true'; + } + + /** + * Whether enable sounds + */ + public get isEnableSounds() { + return localStorage.getItem('enableSounds') == 'true'; + } + + public apis: API; + + /** + * A connection manager of home stream + */ + public stream: HomeStreamManager; + + /** + * Connection managers + */ + public streams: { + driveStream: DriveStreamManager; + serverStream: ServerStreamManager; + requestsStream: RequestsStreamManager; + messagingIndexStream: MessagingIndexStreamManager; + othelloStream: OthelloStreamManager; + } = { + driveStream: null, + serverStream: null, + requestsStream: null, + messagingIndexStream: null, + othelloStream: null + }; + + /** + * A registration of service worker + */ + private swRegistration: ServiceWorkerRegistration = null; + + /** + * Whether should register ServiceWorker + */ + private shouldRegisterSw: boolean; + + /** + * ウィンドウシステム + */ + public windows = new WindowSystem(); + + /** + * MiOSインスタンスを作成します + * @param shouldRegisterSw ServiceWorkerを登録するかどうか + */ + constructor(shouldRegisterSw = false) { + super(); + + this.shouldRegisterSw = shouldRegisterSw; + + //#region BIND + this.log = this.log.bind(this); + this.logInfo = this.logInfo.bind(this); + this.logWarn = this.logWarn.bind(this); + this.logError = this.logError.bind(this); + this.init = this.init.bind(this); + this.api = this.api.bind(this); + this.getMeta = this.getMeta.bind(this); + this.registerSw = this.registerSw.bind(this); + //#endregion + + if (this.debug) { + (window as any).os = this; + } + } + + private googleMapsIniting = false; + + public getGoogleMaps() { + return new Promise((res, rej) => { + if ((window as any).google && (window as any).google.maps) { + res((window as any).google.maps); + } else { + this.once('init-google-maps', () => { + res((window as any).google.maps); + }); + + //#region load google maps api + if (!this.googleMapsIniting) { + this.googleMapsIniting = true; + (window as any).initGoogleMaps = () => { + this.emit('init-google-maps'); + }; + const head = document.getElementsByTagName('head')[0]; + const script = document.createElement('script'); + script.setAttribute('src', `https://maps.googleapis.com/maps/api/js?key=${googleMapsApiKey}&callback=initGoogleMaps`); + script.setAttribute('async', 'true'); + script.setAttribute('defer', 'true'); + head.appendChild(script); + } + //#endregion + } + }); + } + + public log(...args) { + if (!this.debug) return; + console.log.apply(null, args); + } + + public logInfo(...args) { + if (!this.debug) return; + console.info.apply(null, args); + } + + public logWarn(...args) { + if (!this.debug) return; + console.warn.apply(null, args); + } + + public logError(...args) { + if (!this.debug) return; + console.error.apply(null, args); + } + + public signout() { + localStorage.removeItem('me'); + document.cookie = `i=; domain=.${hostname}; expires=Thu, 01 Jan 1970 00:00:01 GMT;`; + location.href = '/'; + } + + /** + * Initialize MiOS (boot) + * @param callback A function that call when initialized + */ + public async init(callback) { + //#region Init stream managers + this.streams.serverStream = new ServerStreamManager(this); + this.streams.requestsStream = new RequestsStreamManager(this); + + this.once('signedin', () => { + // Init home stream manager + this.stream = new HomeStreamManager(this, this.i); + + // Init other stream manager + this.streams.driveStream = new DriveStreamManager(this, this.i); + this.streams.messagingIndexStream = new MessagingIndexStreamManager(this, this.i); + this.streams.othelloStream = new OthelloStreamManager(this, this.i); + }); + //#endregion + + // ユーザーをフェッチしてコールバックする + const fetchme = (token, cb) => { + let me = null; + + // Return when not signed in + if (token == null) { + return done(); + } + + // Fetch user + fetch(`${apiUrl}/i`, { + method: 'POST', + body: JSON.stringify({ + i: token + }) + }) + // When success + .then(res => { + // When failed to authenticate user + if (res.status !== 200) { + return this.signout(); + } + + // Parse response + res.json().then(i => { + me = i; + me.account.token = token; + done(); + }); + }) + // When failure + .catch(() => { + // Render the error screen + document.body.innerHTML = '
'; + new Vue({ + render: createEl => createEl(Err) + }).$mount('#err'); + + Progress.done(); + }); + + function done() { + if (cb) cb(me); + } + }; + + // フェッチが完了したとき + const fetched = me => { + if (me) { + // デフォルトの設定をマージ + me.account.clientSettings = Object.assign({ + fetchOnScroll: true, + showMaps: true, + showPostFormOnTopOfTl: false, + gradientWindowHeader: false + }, me.account.clientSettings); + + // ローカルストレージにキャッシュ + localStorage.setItem('me', JSON.stringify(me)); + } + + this.i = me; + + this.emit('signedin'); + + // Finish init + callback(); + + //#region Post + + // Init service worker + if (this.shouldRegisterSw) this.registerSw(); + + //#endregion + }; + + // Get cached account data + const cachedMe = JSON.parse(localStorage.getItem('me')); + + // キャッシュがあったとき + if (cachedMe) { + // とりあえずキャッシュされたデータでお茶を濁して(?)おいて、 + fetched(cachedMe); + + // 後から新鮮なデータをフェッチ + fetchme(cachedMe.account.token, freshData => { + merge(cachedMe, freshData); + }); + } else { + // Get token from cookie + const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1]; + + fetchme(i, fetched); + } + } + + /** + * Register service worker + */ + private registerSw() { + // Check whether service worker and push manager supported + const isSwSupported = + ('serviceWorker' in navigator) && ('PushManager' in window); + + // Reject when browser not service worker supported + if (!isSwSupported) return; + + // Reject when not signed in to Misskey + if (!this.isSignedIn) return; + + // When service worker activated + navigator.serviceWorker.ready.then(registration => { + this.log('[sw] ready: ', registration); + + this.swRegistration = registration; + + // Options of pushManager.subscribe + // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters + const opts = { + // A boolean indicating that the returned push subscription + // will only be used for messages whose effect is made visible to the user. + userVisibleOnly: true, + + // A public key your push server will use to send + // messages to client apps via a push server. + applicationServerKey: urlBase64ToUint8Array(swPublickey) + }; + + // Subscribe push notification + this.swRegistration.pushManager.subscribe(opts).then(subscription => { + this.log('[sw] Subscribe OK:', subscription); + + function encode(buffer: ArrayBuffer) { + return btoa(String.fromCharCode.apply(null, new Uint8Array(buffer))); + } + + // Register + this.api('sw/register', { + endpoint: subscription.endpoint, + auth: encode(subscription.getKey('auth')), + publickey: encode(subscription.getKey('p256dh')) + }); + }) + // When subscribe failed + .catch(async (err: Error) => { + this.logError('[sw] Subscribe Error:', err); + + // 通知が許可されていなかったとき + if (err.name == 'NotAllowedError') { + this.logError('[sw] Subscribe failed due to notification not allowed'); + return; + } + + // 違うapplicationServerKey (または gcm_sender_id)のサブスクリプションが + // 既に存在していることが原因でエラーになった可能性があるので、 + // そのサブスクリプションを解除しておく + const subscription = await this.swRegistration.pushManager.getSubscription(); + if (subscription) subscription.unsubscribe(); + }); + }); + + // Whether use raw version script + const raw = (localStorage.getItem('useRawScript') == 'true' && this.debug) + || process.env.NODE_ENV != 'production'; + + // The path of service worker script + const sw = `/sw.${version}.${lang}.${raw ? 'raw' : 'min'}.js`; + + // Register service worker + navigator.serviceWorker.register(sw).then(registration => { + // 登録成功 + this.logInfo('[sw] Registration successful with scope: ', registration.scope); + }).catch(err => { + // 登録失敗 :( + this.logError('[sw] Registration failed: ', err); + }); + } + + public requests = []; + + /** + * Misskey APIにリクエストします + * @param endpoint エンドポイント名 + * @param data パラメータ + */ + public api(endpoint: string, data: { [x: string]: any } = {}): Promise<{ [x: string]: any }> { + if (++pending === 1) { + spinner = document.createElement('div'); + spinner.setAttribute('id', 'wait'); + document.body.appendChild(spinner); + } + + // Append a credential + if (this.isSignedIn) (data as any).i = this.i.account.token; + + // TODO + //const viaStream = localStorage.getItem('enableExperimental') == 'true'; + + return new Promise((resolve, reject) => { + /*if (viaStream) { + const stream = this.stream.borrow(); + const id = Math.random().toString(); + stream.once(`api-res:${id}`, res => { + resolve(res); + }); + stream.send({ + type: 'api', + id, + endpoint, + data + }); + } else {*/ + const req = { + id: uuid(), + date: new Date(), + name: endpoint, + data, + res: null, + status: null + }; + + if (this.debug) { + this.requests.push(req); + } + + // Send request + fetch(endpoint.indexOf('://') > -1 ? endpoint : `${apiUrl}/${endpoint}`, { + method: 'POST', + body: JSON.stringify(data), + credentials: endpoint === 'signin' ? 'include' : 'omit', + cache: 'no-cache' + }).then(async (res) => { + if (--pending === 0) spinner.parentNode.removeChild(spinner); + + const body = res.status === 204 ? null : await res.json(); + + if (this.debug) { + req.status = res.status; + req.res = body; + } + + if (res.status === 200) { + resolve(body); + } else if (res.status === 204) { + resolve(); + } else { + reject(body.error); + } + }).catch(reject); + /*}*/ + }); + } + + /** + * Misskeyのメタ情報を取得します + * @param force キャッシュを無視するか否か + */ + public getMeta(force = false) { + return new Promise<{ [x: string]: any }>(async (res, rej) => { + if (this.isMetaFetching) { + this.once('_meta_fetched_', () => { + res(this.meta.data); + }); + return; + } + + const expire = 1000 * 60; // 1min + + // forceが有効, meta情報を保持していない or 期限切れ + if (force || this.meta == null || Date.now() - this.meta.chachedAt.getTime() > expire) { + this.isMetaFetching = true; + const meta = await this.api('meta'); + this.meta = { + data: meta, + chachedAt: new Date() + }; + this.isMetaFetching = false; + this.emit('_meta_fetched_'); + res(meta); + } else { + res(this.meta.data); + } + }); + } + + public connections: Connection[] = []; + + public registerStreamConnection(connection: Connection) { + this.connections.push(connection); + } + + public unregisterStreamConnection(connection: Connection) { + this.connections = this.connections.filter(c => c != connection); + } +} + +class WindowSystem extends EventEmitter { + public windows = new Set(); + + public add(window) { + this.windows.add(window); + this.emit('added', window); + } + + public remove(window) { + this.windows.delete(window); + this.emit('removed', window); + } + + public getAll() { + return this.windows; + } +} + +/** + * Convert the URL safe base64 string to a Uint8Array + * @param base64String base64 string + */ +function urlBase64ToUint8Array(base64String: string): Uint8Array { + const padding = '='.repeat((4 - base64String.length % 4) % 4); + const base64 = (base64String + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; +} diff --git a/src/client/app/common/scripts/check-for-update.ts b/src/client/app/common/scripts/check-for-update.ts new file mode 100644 index 0000000000..81c1eb9812 --- /dev/null +++ b/src/client/app/common/scripts/check-for-update.ts @@ -0,0 +1,33 @@ +import MiOS from '../mios'; +import { version as current } from '../../config'; + +export default async function(mios: MiOS, force = false, silent = false) { + const meta = await mios.getMeta(force); + const newer = meta.version; + + if (newer != current) { + localStorage.setItem('should-refresh', 'true'); + localStorage.setItem('v', newer); + + // Clear cache (serive worker) + try { + if (navigator.serviceWorker.controller) { + navigator.serviceWorker.controller.postMessage('clear'); + } + + navigator.serviceWorker.getRegistrations().then(registrations => { + registrations.forEach(registration => registration.unregister()); + }); + } catch (e) { + console.error(e); + } + + if (!silent) { + alert('%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)); + } + + return newer; + } else { + return null; + } +} diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts new file mode 100644 index 0000000000..273579cbc6 --- /dev/null +++ b/src/client/app/common/scripts/compose-notification.ts @@ -0,0 +1,67 @@ +import getPostSummary from '../../../../common/get-post-summary'; +import getReactionEmoji from '../../../../common/get-reaction-emoji'; + +type Notification = { + title: string; + body: string; + icon: string; + onclick?: any; +}; + +// TODO: i18n + +export default function(type, data): Notification { + switch (type) { + case 'drive_file_created': + return { + title: 'ファイルがアップロードされました', + body: data.name, + icon: data.url + '?thumbnail&size=64' + }; + + case 'mention': + return { + title: `${data.user.name}さんから:`, + body: getPostSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'reply': + return { + title: `${data.user.name}さんから返信:`, + body: getPostSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'quote': + return { + title: `${data.user.name}さんが引用:`, + body: getPostSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'reaction': + return { + title: `${data.user.name}: ${getReactionEmoji(data.reaction)}:`, + body: getPostSummary(data.post), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'unread_messaging_message': + return { + title: `${data.user.name}さんからメッセージ:`, + body: data.text, // TODO: getMessagingMessageSummary(data), + icon: data.user.avatarUrl + '?thumbnail&size=64' + }; + + case 'othello_invited': + return { + title: '対局への招待があります', + body: `${data.parent.name}さんから`, + icon: data.parent.avatarUrl + '?thumbnail&size=64' + }; + + default: + return null; + } +} diff --git a/src/client/app/common/scripts/contains.ts b/src/client/app/common/scripts/contains.ts new file mode 100644 index 0000000000..a5071b3f25 --- /dev/null +++ b/src/client/app/common/scripts/contains.ts @@ -0,0 +1,8 @@ +export default (parent, child) => { + let node = child.parentNode; + while (node) { + if (node == parent) return true; + node = node.parentNode; + } + return false; +}; diff --git a/src/client/app/common/scripts/copy-to-clipboard.ts b/src/client/app/common/scripts/copy-to-clipboard.ts new file mode 100644 index 0000000000..3d2741f8d7 --- /dev/null +++ b/src/client/app/common/scripts/copy-to-clipboard.ts @@ -0,0 +1,13 @@ +/** + * Clipboardに値をコピー(TODO: 文字列以外も対応) + */ +export default val => { + const form = document.createElement('textarea'); + form.textContent = val; + document.body.appendChild(form); + form.select(); + const result = document.execCommand('copy'); + document.body.removeChild(form); + + return result; +}; diff --git a/src/client/app/common/scripts/date-stringify.ts b/src/client/app/common/scripts/date-stringify.ts new file mode 100644 index 0000000000..e51de8833d --- /dev/null +++ b/src/client/app/common/scripts/date-stringify.ts @@ -0,0 +1,13 @@ +export default date => { + if (typeof date == 'string') date = new Date(date); + return ( + date.getFullYear() + '年' + + (date.getMonth() + 1) + '月' + + date.getDate() + '日' + + ' ' + + date.getHours() + '時' + + date.getMinutes() + '分' + + ' ' + + `(${['日', '月', '火', '水', '木', '金', '土'][date.getDay()]})` + ); +}; diff --git a/src/client/app/common/scripts/fuck-ad-block.ts b/src/client/app/common/scripts/fuck-ad-block.ts new file mode 100644 index 0000000000..9bcf7deeff --- /dev/null +++ b/src/client/app/common/scripts/fuck-ad-block.ts @@ -0,0 +1,21 @@ +require('fuckadblock'); + +declare const fuckAdBlock: any; + +export default (os) => { + function adBlockDetected() { + os.apis.dialog({ + title: '%fa:exclamation-triangle%広告ブロッカーを無効にしてください', + text: 'Misskeyは広告を掲載していませんが、広告をブロックする機能が有効だと一部の機能が利用できなかったり、不具合が発生する場合があります。', + actins: [{ + text: 'OK' + }] + }); + } + + if (fuckAdBlock === undefined) { + adBlockDetected(); + } else { + fuckAdBlock.onDetected(adBlockDetected); + } +}; diff --git a/src/client/app/common/scripts/gcd.ts b/src/client/app/common/scripts/gcd.ts new file mode 100644 index 0000000000..9a19f9da66 --- /dev/null +++ b/src/client/app/common/scripts/gcd.ts @@ -0,0 +1,2 @@ +const gcd = (a, b) => !b ? a : gcd(b, a % b); +export default gcd; diff --git a/src/client/app/common/scripts/get-kao.ts b/src/client/app/common/scripts/get-kao.ts new file mode 100644 index 0000000000..2168c5be88 --- /dev/null +++ b/src/client/app/common/scripts/get-kao.ts @@ -0,0 +1,5 @@ +export default () => [ + '(=^・・^=)', + 'v(‘ω’)v', + '🐡( \'-\' 🐡 )フグパンチ!!!!' +][Math.floor(Math.random() * 3)]; diff --git a/src/client/app/common/scripts/get-median.ts b/src/client/app/common/scripts/get-median.ts new file mode 100644 index 0000000000..91a415d5b2 --- /dev/null +++ b/src/client/app/common/scripts/get-median.ts @@ -0,0 +1,11 @@ +/** + * 中央値を求めます + * @param samples サンプル + */ +export default function(samples) { + if (!samples.length) return 0; + const numbers = samples.slice(0).sort((a, b) => a - b); + const middle = Math.floor(numbers.length / 2); + const isEven = numbers.length % 2 === 0; + return isEven ? (numbers[middle] + numbers[middle - 1]) / 2 : numbers[middle]; +} diff --git a/src/client/app/common/scripts/loading.ts b/src/client/app/common/scripts/loading.ts new file mode 100644 index 0000000000..c48e626648 --- /dev/null +++ b/src/client/app/common/scripts/loading.ts @@ -0,0 +1,21 @@ +const NProgress = require('nprogress'); +NProgress.configure({ + trickleSpeed: 500, + showSpinner: false +}); + +const root = document.getElementsByTagName('html')[0]; + +export default { + start: () => { + root.classList.add('progress'); + NProgress.start(); + }, + done: () => { + root.classList.remove('progress'); + NProgress.done(); + }, + set: val => { + NProgress.set(val); + } +}; diff --git a/src/client/app/common/scripts/parse-search-query.ts b/src/client/app/common/scripts/parse-search-query.ts new file mode 100644 index 0000000000..4f09d2b93f --- /dev/null +++ b/src/client/app/common/scripts/parse-search-query.ts @@ -0,0 +1,53 @@ +export default function(qs: string) { + const q = { + text: '' + }; + + qs.split(' ').forEach(x => { + if (/^([a-z_]+?):(.+?)$/.test(x)) { + const [key, value] = x.split(':'); + switch (key) { + case 'user': + q['includeUserUsernames'] = value.split(','); + break; + case 'exclude_user': + q['excludeUserUsernames'] = value.split(','); + break; + case 'follow': + q['following'] = value == 'null' ? null : value == 'true'; + break; + case 'reply': + q['reply'] = value == 'null' ? null : value == 'true'; + break; + case 'repost': + q['repost'] = value == 'null' ? null : value == 'true'; + break; + case 'media': + q['media'] = value == 'null' ? null : value == 'true'; + break; + case 'poll': + q['poll'] = value == 'null' ? null : value == 'true'; + break; + case 'until': + case 'since': + // YYYY-MM-DD + if (/^[0-9]+\-[0-9]+\-[0-9]+$/) { + const [yyyy, mm, dd] = value.split('-'); + q[`${key}_date`] = (new Date(parseInt(yyyy, 10), parseInt(mm, 10) - 1, parseInt(dd, 10))).getTime(); + } + break; + default: + q[key] = value; + break; + } + } else { + q.text += x + ' '; + } + }); + + if (q.text) { + q.text = q.text.trim(); + } + + return q; +} diff --git a/src/client/app/common/scripts/streaming/channel.ts b/src/client/app/common/scripts/streaming/channel.ts new file mode 100644 index 0000000000..cab5f4edb4 --- /dev/null +++ b/src/client/app/common/scripts/streaming/channel.ts @@ -0,0 +1,13 @@ +import Stream from './stream'; +import MiOS from '../../mios'; + +/** + * Channel stream connection + */ +export default class Connection extends Stream { + constructor(os: MiOS, channelId) { + super(os, 'channel', { + channel: channelId + }); + } +} diff --git a/src/client/app/common/scripts/streaming/drive.ts b/src/client/app/common/scripts/streaming/drive.ts new file mode 100644 index 0000000000..f11573685e --- /dev/null +++ b/src/client/app/common/scripts/streaming/drive.ts @@ -0,0 +1,34 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../mios'; + +/** + * Drive stream connection + */ +export class DriveStream extends Stream { + constructor(os: MiOS, me) { + super(os, 'drive', { + i: me.account.token + }); + } +} + +export class DriveStreamManager extends StreamManager { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new DriveStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/home.ts b/src/client/app/common/scripts/streaming/home.ts new file mode 100644 index 0000000000..c198619400 --- /dev/null +++ b/src/client/app/common/scripts/streaming/home.ts @@ -0,0 +1,57 @@ +import * as merge from 'object-assign-deep'; + +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../mios'; + +/** + * Home stream connection + */ +export class HomeStream extends Stream { + constructor(os: MiOS, me) { + super(os, '', { + i: me.account.token + }); + + // 最終利用日時を更新するため定期的にaliveメッセージを送信 + setInterval(() => { + this.send({ type: 'alive' }); + me.account.lastUsedAt = new Date(); + }, 1000 * 60); + + // 自分の情報が更新されたとき + this.on('i_updated', i => { + if (os.debug) { + console.log('I updated:', i); + } + merge(me, i); + }); + + // トークンが再生成されたとき + // このままではAPIが利用できないので強制的にサインアウトさせる + this.on('my_token_regenerated', () => { + alert('%i18n:common.my-token-regenerated%'); + os.signout(); + }); + } +} + +export class HomeStreamManager extends StreamManager { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new HomeStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/messaging-index.ts b/src/client/app/common/scripts/streaming/messaging-index.ts new file mode 100644 index 0000000000..24f0ce0c9f --- /dev/null +++ b/src/client/app/common/scripts/streaming/messaging-index.ts @@ -0,0 +1,34 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../mios'; + +/** + * Messaging index stream connection + */ +export class MessagingIndexStream extends Stream { + constructor(os: MiOS, me) { + super(os, 'messaging-index', { + i: me.account.token + }); + } +} + +export class MessagingIndexStreamManager extends StreamManager { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new MessagingIndexStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/messaging.ts b/src/client/app/common/scripts/streaming/messaging.ts new file mode 100644 index 0000000000..4c593deb31 --- /dev/null +++ b/src/client/app/common/scripts/streaming/messaging.ts @@ -0,0 +1,20 @@ +import Stream from './stream'; +import MiOS from '../../mios'; + +/** + * Messaging stream connection + */ +export class MessagingStream extends Stream { + constructor(os: MiOS, me, otherparty) { + super(os, 'messaging', { + i: me.account.token, + otherparty + }); + + (this as any).on('_connected_', () => { + this.send({ + i: me.account.token + }); + }); + } +} diff --git a/src/client/app/common/scripts/streaming/othello-game.ts b/src/client/app/common/scripts/streaming/othello-game.ts new file mode 100644 index 0000000000..f34ef35147 --- /dev/null +++ b/src/client/app/common/scripts/streaming/othello-game.ts @@ -0,0 +1,11 @@ +import Stream from './stream'; +import MiOS from '../../mios'; + +export class OthelloGameStream extends Stream { + constructor(os: MiOS, me, game) { + super(os, 'othello-game', { + i: me ? me.account.token : null, + game: game.id + }); + } +} diff --git a/src/client/app/common/scripts/streaming/othello.ts b/src/client/app/common/scripts/streaming/othello.ts new file mode 100644 index 0000000000..8c6f4b9c3c --- /dev/null +++ b/src/client/app/common/scripts/streaming/othello.ts @@ -0,0 +1,31 @@ +import StreamManager from './stream-manager'; +import Stream from './stream'; +import MiOS from '../../mios'; + +export class OthelloStream extends Stream { + constructor(os: MiOS, me) { + super(os, 'othello', { + i: me.account.token + }); + } +} + +export class OthelloStreamManager extends StreamManager { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new OthelloStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/requests.ts b/src/client/app/common/scripts/streaming/requests.ts new file mode 100644 index 0000000000..5bec30143f --- /dev/null +++ b/src/client/app/common/scripts/streaming/requests.ts @@ -0,0 +1,30 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../mios'; + +/** + * Requests stream connection + */ +export class RequestsStream extends Stream { + constructor(os: MiOS) { + super(os, 'requests'); + } +} + +export class RequestsStreamManager extends StreamManager { + private os: MiOS; + + constructor(os: MiOS) { + super(); + + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new RequestsStream(this.os); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/server.ts b/src/client/app/common/scripts/streaming/server.ts new file mode 100644 index 0000000000..3d35ef4d9d --- /dev/null +++ b/src/client/app/common/scripts/streaming/server.ts @@ -0,0 +1,30 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../mios'; + +/** + * Server stream connection + */ +export class ServerStream extends Stream { + constructor(os: MiOS) { + super(os, 'server'); + } +} + +export class ServerStreamManager extends StreamManager { + private os: MiOS; + + constructor(os: MiOS) { + super(); + + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new ServerStream(this.os); + } + + return this.connection; + } +} diff --git a/src/client/app/common/scripts/streaming/stream-manager.ts b/src/client/app/common/scripts/streaming/stream-manager.ts new file mode 100644 index 0000000000..568b8b0372 --- /dev/null +++ b/src/client/app/common/scripts/streaming/stream-manager.ts @@ -0,0 +1,108 @@ +import { EventEmitter } from 'eventemitter3'; +import * as uuid from 'uuid'; +import Connection from './stream'; + +/** + * ストリーム接続を管理するクラス + * 複数の場所から同じストリームを利用する際、接続をまとめたりする + */ +export default abstract class StreamManager extends EventEmitter { + private _connection: T = null; + + private disposeTimerId: any; + + /** + * コネクションを必要としているユーザー + */ + private users = []; + + protected set connection(connection: T) { + this._connection = connection; + + if (this._connection == null) { + this.emit('disconnected'); + } else { + this.emit('connected', this._connection); + + this._connection.on('_connected_', () => { + this.emit('_connected_'); + }); + + this._connection.on('_disconnected_', () => { + this.emit('_disconnected_'); + }); + + this._connection.user = 'Managed'; + } + } + + protected get connection() { + return this._connection; + } + + /** + * コネクションを持っているか否か + */ + public get hasConnection() { + return this._connection != null; + } + + public get state(): string { + if (!this.hasConnection) return 'no-connection'; + return this._connection.state; + } + + /** + * コネクションを要求します + */ + public abstract getConnection(): T; + + /** + * 現在接続しているコネクションを取得します + */ + public borrow() { + return this._connection; + } + + /** + * コネクションを要求するためのユーザーIDを発行します + */ + public use() { + // タイマー解除 + if (this.disposeTimerId) { + clearTimeout(this.disposeTimerId); + this.disposeTimerId = null; + } + + // ユーザーID生成 + const userId = uuid(); + + this.users.push(userId); + + this._connection.user = `Managed (${ this.users.length })`; + + return userId; + } + + /** + * コネクションを利用し終わってもう必要ないことを通知します + * @param userId use で発行したユーザーID + */ + public dispose(userId) { + this.users = this.users.filter(id => id != userId); + + this._connection.user = `Managed (${ this.users.length })`; + + // 誰もコネクションの利用者がいなくなったら + if (this.users.length == 0) { + // また直ぐに再利用される可能性があるので、一定時間待ち、 + // 新たな利用者が現れなければコネクションを切断する + this.disposeTimerId = setTimeout(() => { + this.disposeTimerId = null; + + this.connection.close(); + this.connection = null; + }, 3000); + } + } +} diff --git a/src/client/app/common/scripts/streaming/stream.ts b/src/client/app/common/scripts/streaming/stream.ts new file mode 100644 index 0000000000..3912186ad3 --- /dev/null +++ b/src/client/app/common/scripts/streaming/stream.ts @@ -0,0 +1,137 @@ +import { EventEmitter } from 'eventemitter3'; +import * as uuid from 'uuid'; +import * as ReconnectingWebsocket from 'reconnecting-websocket'; +import { wsUrl } from '../../../config'; +import MiOS from '../../mios'; + +/** + * Misskey stream connection + */ +export default class Connection extends EventEmitter { + public state: string; + private buffer: any[]; + public socket: ReconnectingWebsocket; + public name: string; + public connectedAt: Date; + public user: string = null; + public in: number = 0; + public out: number = 0; + public inout: Array<{ + type: 'in' | 'out', + at: Date, + data: string + }> = []; + public id: string; + public isSuspended = false; + private os: MiOS; + + constructor(os: MiOS, endpoint, params?) { + super(); + + //#region BIND + this.onOpen = this.onOpen.bind(this); + this.onClose = this.onClose.bind(this); + this.onMessage = this.onMessage.bind(this); + this.send = this.send.bind(this); + this.close = this.close.bind(this); + //#endregion + + this.id = uuid(); + this.os = os; + this.name = endpoint; + this.state = 'initializing'; + this.buffer = []; + + const query = params + ? Object.keys(params) + .map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])) + .join('&') + : null; + + this.socket = new ReconnectingWebsocket(`${wsUrl}/${endpoint}${query ? '?' + query : ''}`); + this.socket.addEventListener('open', this.onOpen); + this.socket.addEventListener('close', this.onClose); + this.socket.addEventListener('message', this.onMessage); + + // Register this connection for debugging + this.os.registerStreamConnection(this); + } + + /** + * Callback of when open connection + */ + private onOpen() { + this.state = 'connected'; + this.emit('_connected_'); + + this.connectedAt = new Date(); + + // バッファーを処理 + const _buffer = [].concat(this.buffer); // Shallow copy + this.buffer = []; // Clear buffer + _buffer.forEach(data => { + this.send(data); // Resend each buffered messages + + if (this.os.debug) { + this.out++; + this.inout.push({ type: 'out', at: new Date(), data }); + } + }); + } + + /** + * Callback of when close connection + */ + private onClose() { + this.state = 'reconnecting'; + this.emit('_disconnected_'); + } + + /** + * Callback of when received a message from connection + */ + private onMessage(message) { + if (this.isSuspended) return; + + if (this.os.debug) { + this.in++; + this.inout.push({ type: 'in', at: new Date(), data: message.data }); + } + + try { + const msg = JSON.parse(message.data); + if (msg.type) this.emit(msg.type, msg.body); + } catch (e) { + // noop + } + } + + /** + * Send a message to connection + */ + public send(data) { + if (this.isSuspended) return; + + // まだ接続が確立されていなかったらバッファリングして次に接続した時に送信する + if (this.state != 'connected') { + this.buffer.push(data); + return; + } + + if (this.os.debug) { + this.out++; + this.inout.push({ type: 'out', at: new Date(), data }); + } + + this.socket.send(JSON.stringify(data)); + } + + /** + * Close this connection + */ + public close() { + this.os.unregisterStreamConnection(this); + this.socket.removeEventListener('open', this.onOpen); + this.socket.removeEventListener('message', this.onMessage); + } +} diff --git a/src/client/app/common/views/components/autocomplete.vue b/src/client/app/common/views/components/autocomplete.vue new file mode 100644 index 0000000000..79bd2ba023 --- /dev/null +++ b/src/client/app/common/views/components/autocomplete.vue @@ -0,0 +1,306 @@ + + + + + diff --git a/src/client/app/common/views/components/connect-failed.troubleshooter.vue b/src/client/app/common/views/components/connect-failed.troubleshooter.vue new file mode 100644 index 0000000000..cadbd36ba4 --- /dev/null +++ b/src/client/app/common/views/components/connect-failed.troubleshooter.vue @@ -0,0 +1,137 @@ + + + + + diff --git a/src/client/app/common/views/components/connect-failed.vue b/src/client/app/common/views/components/connect-failed.vue new file mode 100644 index 0000000000..185250dbd8 --- /dev/null +++ b/src/client/app/common/views/components/connect-failed.vue @@ -0,0 +1,106 @@ + + + + + + diff --git a/src/client/app/common/views/components/ellipsis.vue b/src/client/app/common/views/components/ellipsis.vue new file mode 100644 index 0000000000..07349902de --- /dev/null +++ b/src/client/app/common/views/components/ellipsis.vue @@ -0,0 +1,26 @@ + + + diff --git a/src/client/app/common/views/components/file-type-icon.vue b/src/client/app/common/views/components/file-type-icon.vue new file mode 100644 index 0000000000..b7e868d1f7 --- /dev/null +++ b/src/client/app/common/views/components/file-type-icon.vue @@ -0,0 +1,17 @@ + + + diff --git a/src/client/app/common/views/components/forkit.vue b/src/client/app/common/views/components/forkit.vue new file mode 100644 index 0000000000..6f334b965a --- /dev/null +++ b/src/client/app/common/views/components/forkit.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts new file mode 100644 index 0000000000..b58ba37ecb --- /dev/null +++ b/src/client/app/common/views/components/index.ts @@ -0,0 +1,51 @@ +import Vue from 'vue'; + +import signin from './signin.vue'; +import signup from './signup.vue'; +import forkit from './forkit.vue'; +import nav from './nav.vue'; +import postHtml from './post-html'; +import poll from './poll.vue'; +import pollEditor from './poll-editor.vue'; +import reactionIcon from './reaction-icon.vue'; +import reactionsViewer from './reactions-viewer.vue'; +import time from './time.vue'; +import timer from './timer.vue'; +import mediaList from './media-list.vue'; +import uploader from './uploader.vue'; +import specialMessage from './special-message.vue'; +import streamIndicator from './stream-indicator.vue'; +import ellipsis from './ellipsis.vue'; +import messaging from './messaging.vue'; +import messagingRoom from './messaging-room.vue'; +import urlPreview from './url-preview.vue'; +import twitterSetting from './twitter-setting.vue'; +import fileTypeIcon from './file-type-icon.vue'; +import Switch from './switch.vue'; +import Othello from './othello.vue'; +import welcomeTimeline from './welcome-timeline.vue'; + +Vue.component('mk-signin', signin); +Vue.component('mk-signup', signup); +Vue.component('mk-forkit', forkit); +Vue.component('mk-nav', nav); +Vue.component('mk-post-html', postHtml); +Vue.component('mk-poll', poll); +Vue.component('mk-poll-editor', pollEditor); +Vue.component('mk-reaction-icon', reactionIcon); +Vue.component('mk-reactions-viewer', reactionsViewer); +Vue.component('mk-time', time); +Vue.component('mk-timer', timer); +Vue.component('mk-media-list', mediaList); +Vue.component('mk-uploader', uploader); +Vue.component('mk-special-message', specialMessage); +Vue.component('mk-stream-indicator', streamIndicator); +Vue.component('mk-ellipsis', ellipsis); +Vue.component('mk-messaging', messaging); +Vue.component('mk-messaging-room', messagingRoom); +Vue.component('mk-url-preview', urlPreview); +Vue.component('mk-twitter-setting', twitterSetting); +Vue.component('mk-file-type-icon', fileTypeIcon); +Vue.component('mk-switch', Switch); +Vue.component('mk-othello', Othello); +Vue.component('mk-welcome-timeline', welcomeTimeline); diff --git a/src/client/app/common/views/components/media-list.vue b/src/client/app/common/views/components/media-list.vue new file mode 100644 index 0000000000..64172ad0b4 --- /dev/null +++ b/src/client/app/common/views/components/media-list.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/src/client/app/common/views/components/messaging-room.form.vue b/src/client/app/common/views/components/messaging-room.form.vue new file mode 100644 index 0000000000..704f2016d8 --- /dev/null +++ b/src/client/app/common/views/components/messaging-room.form.vue @@ -0,0 +1,305 @@ + + + + + diff --git a/src/client/app/common/views/components/messaging-room.message.vue b/src/client/app/common/views/components/messaging-room.message.vue new file mode 100644 index 0000000000..94f87fd709 --- /dev/null +++ b/src/client/app/common/views/components/messaging-room.message.vue @@ -0,0 +1,263 @@ + + + + + diff --git a/src/client/app/common/views/components/messaging-room.vue b/src/client/app/common/views/components/messaging-room.vue new file mode 100644 index 0000000000..d30c64d74a --- /dev/null +++ b/src/client/app/common/views/components/messaging-room.vue @@ -0,0 +1,377 @@ + + + + + diff --git a/src/client/app/common/views/components/messaging.vue b/src/client/app/common/views/components/messaging.vue new file mode 100644 index 0000000000..8317c3738a --- /dev/null +++ b/src/client/app/common/views/components/messaging.vue @@ -0,0 +1,463 @@ + + + + + diff --git a/src/client/app/common/views/components/nav.vue b/src/client/app/common/views/components/nav.vue new file mode 100644 index 0000000000..8ce75d3529 --- /dev/null +++ b/src/client/app/common/views/components/nav.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/client/app/common/views/components/othello.game.vue b/src/client/app/common/views/components/othello.game.vue new file mode 100644 index 0000000000..f08742ad10 --- /dev/null +++ b/src/client/app/common/views/components/othello.game.vue @@ -0,0 +1,324 @@ + + + + + diff --git a/src/client/app/common/views/components/othello.gameroom.vue b/src/client/app/common/views/components/othello.gameroom.vue new file mode 100644 index 0000000000..dba9ccd16d --- /dev/null +++ b/src/client/app/common/views/components/othello.gameroom.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/client/app/common/views/components/othello.room.vue b/src/client/app/common/views/components/othello.room.vue new file mode 100644 index 0000000000..a32be6b74f --- /dev/null +++ b/src/client/app/common/views/components/othello.room.vue @@ -0,0 +1,297 @@ + + + + + + + + + diff --git a/src/client/app/common/views/components/othello.vue b/src/client/app/common/views/components/othello.vue new file mode 100644 index 0000000000..8f7d9dfd6a --- /dev/null +++ b/src/client/app/common/views/components/othello.vue @@ -0,0 +1,311 @@ + + + + + diff --git a/src/client/app/common/views/components/poll-editor.vue b/src/client/app/common/views/components/poll-editor.vue new file mode 100644 index 0000000000..47d901d7b1 --- /dev/null +++ b/src/client/app/common/views/components/poll-editor.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/src/client/app/common/views/components/poll.vue b/src/client/app/common/views/components/poll.vue new file mode 100644 index 0000000000..711d89720e --- /dev/null +++ b/src/client/app/common/views/components/poll.vue @@ -0,0 +1,124 @@ + + + + + diff --git a/src/client/app/common/views/components/post-html.ts b/src/client/app/common/views/components/post-html.ts new file mode 100644 index 0000000000..98da86617d --- /dev/null +++ b/src/client/app/common/views/components/post-html.ts @@ -0,0 +1,137 @@ +import Vue from 'vue'; +import * as emojilib from 'emojilib'; +import getAcct from '../../../../../common/user/get-acct'; +import { url } from '../../../config'; +import MkUrl from './url.vue'; + +const flatten = list => list.reduce( + (a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [] +); + +export default Vue.component('mk-post-html', { + props: { + ast: { + type: Array, + required: true + }, + shouldBreak: { + type: Boolean, + default: true + }, + i: { + type: Object, + default: null + } + }, + render(createElement) { + const els = flatten((this as any).ast.map(token => { + switch (token.type) { + case 'text': + const text = token.content.replace(/(\r\n|\n|\r)/g, '\n'); + + if ((this as any).shouldBreak) { + const x = text.split('\n') + .map(t => t == '' ? [createElement('br')] : [createElement('span', t), createElement('br')]); + x[x.length - 1].pop(); + return x; + } else { + return createElement('span', text.replace(/\n/g, ' ')); + } + + case 'bold': + return createElement('strong', token.bold); + + case 'url': + return createElement(MkUrl, { + props: { + url: token.content, + target: '_blank' + } + }); + + case 'link': + return createElement('a', { + attrs: { + class: 'link', + href: token.url, + target: '_blank', + title: token.url + } + }, token.title); + + case 'mention': + return (createElement as any)('a', { + attrs: { + href: `${url}/@${getAcct(token)}`, + target: '_blank', + dataIsMe: (this as any).i && getAcct((this as any).i) == getAcct(token) + }, + directives: [{ + name: 'user-preview', + value: token.content + }] + }, token.content); + + case 'hashtag': + return createElement('a', { + attrs: { + href: `${url}/search?q=${token.content}`, + target: '_blank' + } + }, token.content); + + case 'code': + return createElement('pre', [ + createElement('code', { + domProps: { + innerHTML: token.html + } + }) + ]); + + case 'inline-code': + return createElement('code', token.html); + + case 'quote': + const text2 = token.quote.replace(/(\r\n|\n|\r)/g, '\n'); + + if ((this as any).shouldBreak) { + const x = text2.split('\n') + .map(t => [createElement('span', t), createElement('br')]); + x[x.length - 1].pop(); + return createElement('div', { + attrs: { + class: 'quote' + } + }, x); + } else { + return createElement('span', { + attrs: { + class: 'quote' + } + }, text2.replace(/\n/g, ' ')); + } + + case 'emoji': + const emoji = emojilib.lib[token.emoji]; + return createElement('span', emoji ? emoji.char : token.content); + + default: + console.log('unknown ast type:', token.type); + } + })); + + const _els = []; + els.forEach((el, i) => { + if (el.tag == 'br') { + if (els[i - 1].tag != 'div') { + _els.push(el); + } + } else { + _els.push(el); + } + }); + + return createElement('span', _els); + } +}); diff --git a/src/client/app/common/views/components/post-menu.vue b/src/client/app/common/views/components/post-menu.vue new file mode 100644 index 0000000000..35116db7e2 --- /dev/null +++ b/src/client/app/common/views/components/post-menu.vue @@ -0,0 +1,141 @@ + + + + + diff --git a/src/client/app/common/views/components/reaction-icon.vue b/src/client/app/common/views/components/reaction-icon.vue new file mode 100644 index 0000000000..7d24f4f9e9 --- /dev/null +++ b/src/client/app/common/views/components/reaction-icon.vue @@ -0,0 +1,28 @@ + + + + + diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue new file mode 100644 index 0000000000..bcb6b2b965 --- /dev/null +++ b/src/client/app/common/views/components/reaction-picker.vue @@ -0,0 +1,191 @@ + + + + + diff --git a/src/client/app/common/views/components/reactions-viewer.vue b/src/client/app/common/views/components/reactions-viewer.vue new file mode 100644 index 0000000000..246451008f --- /dev/null +++ b/src/client/app/common/views/components/reactions-viewer.vue @@ -0,0 +1,49 @@ + + + + + diff --git a/src/client/app/common/views/components/signin.vue b/src/client/app/common/views/components/signin.vue new file mode 100644 index 0000000000..17154e6b31 --- /dev/null +++ b/src/client/app/common/views/components/signin.vue @@ -0,0 +1,142 @@ + + + + + diff --git a/src/client/app/common/views/components/signup.vue b/src/client/app/common/views/components/signup.vue new file mode 100644 index 0000000000..e77d849ade --- /dev/null +++ b/src/client/app/common/views/components/signup.vue @@ -0,0 +1,287 @@ + + + + + diff --git a/src/client/app/common/views/components/special-message.vue b/src/client/app/common/views/components/special-message.vue new file mode 100644 index 0000000000..2fd4d6515e --- /dev/null +++ b/src/client/app/common/views/components/special-message.vue @@ -0,0 +1,42 @@ + + + + + diff --git a/src/client/app/common/views/components/stream-indicator.vue b/src/client/app/common/views/components/stream-indicator.vue new file mode 100644 index 0000000000..1f18fa76ed --- /dev/null +++ b/src/client/app/common/views/components/stream-indicator.vue @@ -0,0 +1,86 @@ + + + + + diff --git a/src/client/app/common/views/components/switch.vue b/src/client/app/common/views/components/switch.vue new file mode 100644 index 0000000000..19a4adc3de --- /dev/null +++ b/src/client/app/common/views/components/switch.vue @@ -0,0 +1,190 @@ + + + + + diff --git a/src/client/app/common/views/components/time.vue b/src/client/app/common/views/components/time.vue new file mode 100644 index 0000000000..6e0d2b0dcb --- /dev/null +++ b/src/client/app/common/views/components/time.vue @@ -0,0 +1,76 @@ + + + diff --git a/src/client/app/common/views/components/timer.vue b/src/client/app/common/views/components/timer.vue new file mode 100644 index 0000000000..a3c4f01b77 --- /dev/null +++ b/src/client/app/common/views/components/timer.vue @@ -0,0 +1,49 @@ + + + diff --git a/src/client/app/common/views/components/twitter-setting.vue b/src/client/app/common/views/components/twitter-setting.vue new file mode 100644 index 0000000000..082d2b435d --- /dev/null +++ b/src/client/app/common/views/components/twitter-setting.vue @@ -0,0 +1,66 @@ + + + + + diff --git a/src/client/app/common/views/components/uploader.vue b/src/client/app/common/views/components/uploader.vue new file mode 100644 index 0000000000..c74a1edb41 --- /dev/null +++ b/src/client/app/common/views/components/uploader.vue @@ -0,0 +1,212 @@ + + + + + diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue new file mode 100644 index 0000000000..e91e510550 --- /dev/null +++ b/src/client/app/common/views/components/url-preview.vue @@ -0,0 +1,142 @@ +