Compare commits

...

102 Commits

Author SHA1 Message Date
cb0673b1ec 10.51.0 2018-11-15 06:26:15 +09:00
cd018db945 Update src/client/app/admin/views/index.vue 2018-11-15 06:23:40 +09:00
50fe67b99b [Client] Improve admin panel 2018-11-15 06:21:31 +09:00
1dba82aae5 [API] Add /instances 2018-11-15 06:21:13 +09:00
17c6d64750 [Client] Prevent cache locale file 2018-11-15 05:20:25 +09:00
b4c04efa23 Improve usability 2018-11-15 05:00:30 +09:00
152dd74abf 10.50.0 2018-11-15 04:26:33 +09:00
0985f7f609 [Client] Fix bugs 2018-11-15 04:24:40 +09:00
56d571c0f0 Moderator system
Closes #2357
2018-11-15 04:15:42 +09:00
dc9a19b9c7 [Client] Add missing icon 2018-11-15 03:17:48 +09:00
88a2c7715a [Client] Add missing icon 2018-11-15 03:14:52 +09:00
2fa8cb1b73 10.49.7 2018-11-15 01:46:01 +09:00
5f8a66fdb9 🎨 2018-11-15 01:45:13 +09:00
57320a94f9 [Client] Add missing icon 2018-11-15 01:43:26 +09:00
89f045d624 🎨 2018-11-15 01:43:06 +09:00
1a77dea7ed [Client] Fix icon 2018-11-15 01:09:50 +09:00
d063d59a91 [Client] Improve UI 2018-11-15 00:01:49 +09:00
90429b787c 10.49.6 2018-11-14 20:40:21 +09:00
7a2ef04ec3 [Client] Improve UI 2018-11-14 20:36:15 +09:00
76a9ea8d3d 10.49.5 2018-11-14 20:27:12 +09:00
0a05a2d060 🎨 2018-11-14 20:23:51 +09:00
a7e2ee3b0c Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-14 20:21:45 +09:00
40efa90dd5 🎨 2018-11-14 20:21:35 +09:00
4ca0a22bfc Fix: default order of users/notes (#3234) 2018-11-14 20:18:47 +09:00
20a943b193 🎨 2018-11-14 20:17:12 +09:00
552df8737d [Client] Add missing icon 2018-11-14 18:07:38 +09:00
860f622d79 10.49.4 2018-11-14 18:04:30 +09:00
e76bf5707a [Client] Fix icons 2018-11-14 18:03:38 +09:00
bf37a72f59 🎨 2018-11-14 18:00:23 +09:00
840ad75830 [Client] Better thumbnail generation 2018-11-14 18:00:15 +09:00
4c7dd7228f 10.49.3 2018-11-14 16:39:27 +09:00
46a51addad ServiceWorkerはメンテナンスされていないのでとりあえず無効化 2018-11-14 16:39:04 +09:00
0a5fe37025 New Crowdin translations (#3205)
* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)
2018-11-14 16:35:55 +09:00
00bb403497 Fix #3228 (#3230)
* Update index.ts

* fix sw.js path

* Revert "Update index.ts"

This reverts commit e0b1d4239f16b65512f5dbdd93c8765f708b5282.
2018-11-14 16:35:00 +09:00
11afa8140c [Client] Improve alert component 2018-11-14 16:30:58 +09:00
850396e9da [Client] Add missing icons 2018-11-14 14:57:59 +09:00
5ee75be49e 🎨 2018-11-14 14:47:18 +09:00
879116a20c Update vue-sweetalert2 requirement from 1.5.7 to 1.5.9 (#3229)
Updates the requirements on [vue-sweetalert2](https://github.com/avil13/vue-sweetalert2) to permit the latest version.
- [Release notes](https://github.com/avil13/vue-sweetalert2/releases)
- [Commits](https://github.com/avil13/vue-sweetalert2/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-14 13:27:37 +09:00
e509b1f488 Update github-bot.ts 2018-11-14 05:33:48 +09:00
468ff7037f 10.49.2 2018-11-14 03:07:55 +09:00
df23504ccf [Client] Fix #3227 2018-11-14 03:05:13 +09:00
66e3cb8eda Update src/client/app/init.ts 2018-11-14 02:04:24 +09:00
6ddd2389dc [Client] Add missing icons 2018-11-14 01:45:15 +09:00
402efb8c50 [Client] Fix imports 2018-11-14 01:40:29 +09:00
7b6eae0ce4 Fix error 2018-11-14 01:32:38 +09:00
26ce9725ce [Client] Fix #3009 2018-11-14 01:31:36 +09:00
ebfaa18f12 Create setup.fr.md (#3225) 2018-11-14 00:15:33 +09:00
cc81d41a05 [Client] Some optimizations 2018-11-13 23:33:30 +09:00
212176ee5c Use terser instead of uglifyjs 2018-11-13 23:10:51 +09:00
a63ec05e41 [Client] Some optimizations 2018-11-13 23:06:31 +09:00
0dcb527bf3 [Client] Fix bug 2018-11-13 22:45:28 +09:00
54710f17fc [Client] Some performance optimizations 2018-11-13 22:43:09 +09:00
e58a6593c0 [Client] Fix bug 2018-11-13 22:42:31 +09:00
62132570e1 [Client] Split components to reduce bundle size 2018-11-13 20:21:52 +09:00
9f0b8ba2f8 Fix: notes/create hangs when rejected (#3221) 2018-11-13 19:34:09 +09:00
adbe0fbcd1 10.49.1 2018-11-13 16:33:21 +09:00
7896242f57 [Client] Fix #3213 2018-11-13 16:19:46 +09:00
4a6722b9e9 Merge branch 'master' into develop 2018-11-13 15:01:52 +09:00
7c9fb5228b Improve usability 2018-11-13 15:01:05 +09:00
81805b01cc sort user timeline by date (#3210) 2018-11-13 01:17:59 +09:00
50824a7245 10.49.0 2018-11-13 01:12:27 +09:00
6f2953f3a7 [Client] Clear cached locale data when shouldFlush is true 2018-11-13 01:11:36 +09:00
dd3f007582 [Client] Improve post-form widget 2018-11-13 01:04:15 +09:00
a4b2b093fc 🎨 2018-11-13 00:21:49 +09:00
0fbf56219f [Client] Emoji picker
Closes #3130
2018-11-13 00:12:55 +09:00
0acacf7a8e 10.48.1 2018-11-12 06:12:45 +09:00
c84500d914 Clean up 2018-11-12 06:12:22 +09:00
a9cfbda858 10.48.0 2018-11-12 05:48:09 +09:00
33e79e4bb8 [Client] Split some components to reduce bundle size 2018-11-12 05:35:09 +09:00
fab389e624 [Client] Stop generate scripts for each languages
Resolve #3172
2018-11-12 05:03:12 +09:00
b1b02d0e32 [Client] Enable code splitting
And some optimizations
2018-11-12 04:09:02 +09:00
0b40194d31 🎨 2018-11-12 01:20:26 +09:00
c2038bec73 Improve streaming 2018-11-12 00:31:09 +09:00
8674d55c8e Hotfix typo 2018-11-11 21:34:50 +09:00
c7c0c9e79d 10.47.0 2018-11-11 21:24:10 +09:00
2dff48167c 🎨 2018-11-11 21:18:24 +09:00
71d42f64dc [Client] Implement word mute
Closes #1739
2018-11-11 21:17:51 +09:00
1b4072610a [Client] Fix i18n 2018-11-11 19:15:08 +09:00
3826a820bb [Client] Fix i18n
Closes #3192
2018-11-11 19:13:10 +09:00
625eb376ae Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-11 19:10:24 +09:00
72cbab6514 [Clinet] Fix i18n 2018-11-11 19:10:15 +09:00
c7a7059e26 New Crowdin translations (#3186)
* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Catalan)

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

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Dutch)

* New translations ja-JP.yml (Norwegian)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (English)
2018-11-11 18:52:23 +09:00
550593b208 Update @types/mongodb requirement from 3.1.12 to 3.1.14 (#3193)
Updates the requirements on [@types/mongodb](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-11 18:30:38 +09:00
f76255fa63 Update @types/webpack requirement from 4.4.17 to 4.4.18 (#3194)
Updates the requirements on [@types/webpack](https://github.com/DefinitelyTyped/DefinitelyTyped) to permit the latest version.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2018-11-11 18:30:27 +09:00
15a2881083 [API] Fix #3203 2018-11-11 18:26:09 +09:00
37bfb79123 Update setup.en.md (#3202)
初期状態のDebianやUbuntuだとpythonがないとインストールできなかったため(ConoHa, Vultr, DigitalOceanで検証済み)
2018-11-11 14:47:53 +09:00
b62203b1f1 Check MongoDB version (#3185)
* Check MongoDB version

* Fix bug
2018-11-11 14:27:00 +09:00
16136c252a fix self host detection (#3201) 2018-11-11 13:11:16 +09:00
75864a5125 Fix #3190 2018-11-11 13:08:48 +09:00
a59f53e6da Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-11 12:43:52 +09:00
2ceaccf9ab Fix chart engine 2018-11-11 12:43:35 +09:00
036d46c459 show self host in unicode (#3200) 2018-11-11 12:35:30 +09:00
5d3d78a73e Better text 2018-11-11 02:44:54 +09:00
6012e98ae6 Improve streaming API 2018-11-11 02:22:34 +09:00
9c0e990568 Better chart generation 2018-11-10 23:25:09 +09:00
6167ed4c9f Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-10 17:10:29 +09:00
988d5405c3 Clean up: Remove unused dependencies 2018-11-10 17:10:22 +09:00
ad0ea2fab2 Update config.yml (#3195) 2018-11-10 10:42:12 +09:00
d62c67208f [Client] Show domain in admin page 2018-11-10 04:28:56 +09:00
2da1432e52 Update CONTRIBUTING.md 2018-11-10 00:47:36 +09:00
69eefc1425 [Client] Fix bug 2018-11-10 00:30:58 +09:00
27bdb26202 Fix #3187 2018-11-10 00:28:50 +09:00
209 changed files with 2752 additions and 1922 deletions

View File

@ -117,8 +117,7 @@ jobs:
command: |
if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
then
curl -LSs 'https://github.com/stedolan/jq/releases/download/jq-1.6/jq-linux64' > jq
chmod +x jq
apk update && apk add jq
docker tag misskey/misskey misskey/misskey:$(cat package.json | jq -r .version)
docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
docker push misskey/misskey

View File

@ -11,6 +11,9 @@ Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
## Internationalization (i18n)
Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
## Documentation
* Documents for contributors are located in `/docs`.
* Documents for instance admins are located in `/docs`.

View File

@ -69,7 +69,7 @@ Build misskey with the following:
`npm run build`
If you're on Debian, you will need to install the `build-essential` package.
If you're on Debian, you will need to install the `build-essential`, `python` package.
If you're still encountering errors about some modules, use node-gyp:

126
docs/setup.fr.md Normal file
View File

@ -0,0 +1,126 @@
Guide d'installation et de configuration de Misskey
================================================================
Nous vous remerçions de l'intrêt que vous manifestez pour l'installation de votre propre instance Misskey !
Ce guide décrit les étapes à suivre afin d'installer et de configurer une instance Misskey.
[La version en japonnais est également disponible sur - 日本語版もあります](./setup.ja.md)
----------------------------------------------------------------
*1.* Création de l'utilisateur Misskey
----------------------------------------------------------------
Lancer misskey en tant qu'utilisateur est une mauvaise idée, nous avons besoin de créer un utilisateur dédié.
Sur Debian, à titre d'exemple :
```
adduser --disabled-password --disabled-login misskey
```
*2.* Installation des dépendances
----------------------------------------------------------------
Installez les paquets suivants :
#### Dépendences :package:
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
##### Optionnels
* [Redis](https://redis.io/)
* Redis est optionnel mais nous vous recommandons vivement de l'installer
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche
*3.* Paramètrage de MongoDB
----------------------------------------------------------------
En mode root :
1. `mongo` Accédez au shell de mango
2. `use misskey` Utilisez la base de données misskey
3. `db.users.save( {dummy:"dummy"} )` Write dummy data to initialize the db.
4. `db.createUser( { user: "misskey", pwd: "<password>", roles: [ { role: "readWrite", db: "misskey" } ] } )` Créez l'utilisateur misskey.
5. `exit` Vous avez terminé !
*4.* Installation de Misskey
----------------------------------------------------------------
1. `su - misskey` Basculez vers l'utilisateur misskey.
2. `git clone -b master git://github.com/syuilo/misskey.git` Clonez la branche master du dépôt misskey.
3. `cd misskey` Accédez au dossier misskey.
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Télécharge la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
5. `npm install` Installez les dépendances de misskey.
*(optionnel)* Génération des clés VAPID
----------------------------------------------------------------
Si vous désirez activer ServiceWorker, vous devez générer les clés VAPID :
Unless you have set your global node_modules location elsewhere, vous devez lancer ceci en mode root.
``` shell
npm install web-push -g
web-push generate-vapid-keys
```
*5.* Création du fichier de configuration
----------------------------------------------------------------
1. `cp .config/example.yml .config/default.yml` Copiez le fichier `.config/example.yml` et renommez-le `default.yml`.
2. Editez le fichier `default.yml`
*6.* Construction de Misskey
----------------------------------------------------------------
Construisez Misskey comme ceci :
`npm run build`
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential`, `python`.
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
1. `npm install -g node-gyp`
2. `node-gyp configure`
3. `node-gyp build`
4. `npm run build`
*7.* C'est tout.
----------------------------------------------------------------
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
### Lancement conventionnel
Lancez tout simplement `npm start`. Bonne chance et amusez-vous bien !
### Démarrage avec systemd
1. Créez une service systemd sur : `/etc/systemd/system/misskey.service`
2. Editez-le puis copiez et coller ceci dans le fichier :
```
[Unit]
Description=Misskey daemon
[Service]
Type=simple
User=misskey
ExecStart=/usr/bin/npm start
WorkingDirectory=/home/misskey/misskey
TimeoutSec=60
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=misskey
Restart=always
[Install]
WantedBy=multi-user.target
```
3. `systemctl daemon-reload ; systemctl enable misskey` Redémarre systemd et active le service misskey.
4. `systemctl start misskey` Démarre le service misskey.
Vous pouvez vérifier si le service a démarré en utilisant la commande `systemctl status misskey`.
### Méthode de mise à jour vers la plus récente version de Misskey
1. `git fetch`
2. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)`
3. `npm install`
4. `npm run build`
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.
----------------------------------------------------------------
Si vous rencontrez des difficultés ou avez d'autres questions, n'hésitez pas à nous contacter !

View File

@ -5,6 +5,7 @@
import * as gulp from 'gulp';
import * as gutil from 'gulp-util';
import * as ts from 'gulp-typescript';
const yaml = require('gulp-yaml');
const sourcemaps = require('gulp-sourcemaps');
import tslint from 'gulp-tslint';
const cssnano = require('gulp-cssnano');
@ -39,6 +40,7 @@ gulp.task('build', [
'build:ts',
'build:copy',
'build:client',
'locales',
'doc'
]);
@ -57,16 +59,7 @@ gulp.task('build:copy:views', () =>
gulp.src('./src/server/web/views/**/*').pipe(gulp.dest('./built/server/web/views'))
);
// 互換性のため
gulp.task('build:copy:lang', () =>
gulp.src(['./built/client/assets/*.*-*.js'])
.pipe(rename(path => {
path.basename = path.basename.replace(/\-(.*)$/, '');
}))
.pipe(gulp.dest('./built/client/assets/'))
);
gulp.task('build:copy', ['build:copy:views', 'build:copy:lang'], () =>
gulp.task('build:copy', ['build:copy:views'], () =>
gulp.src([
'./build/Release/crypto_key.node',
'./src/const.json',
@ -201,6 +194,12 @@ gulp.task('build:client:pug', [
.pipe(gulp.dest('./built/client/app/'))
);
gulp.task('locales', () =>
gulp.src('./locales/*.yml')
.pipe(yaml({ schema: 'DEFAULT_SAFE_SCHEMA' }))
.pipe(gulp.dest('./built/client/assets/locales/'))
);
gulp.task('doc', () =>
gulp.src('./src/docs/**/*.styl')
.pipe(stylus())

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Diese Abstimmung löschen"
common/views/components/reaction-picker.vue:
choose-reaction: "Wähle eine Reaktion aus"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Benutzername"
password: "Passwort"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "Folge ich"
follow: "Folgen"
request-pending: "Ausstehend"
follow-processing: "フォロー処理中"
follow-request: "Follower-Anfragen"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Discard the poll"
common/views/components/reaction-picker.vue:
choose-reaction: "Send a reaction"
common/views/components/emoji-picker.vue:
custom-emoji: "Custom Emoji"
people: "People"
animals-and-nature: "Animals & Nature"
food-and-drink: "Food & drink"
activity: "Activity"
travel-and-places: "Travel & Places"
objects: "Objects"
symbols: "Symbols"
flags: "Flags"
common/views/components/signin.vue:
username: "Username"
password: "Password"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "The content is NSFW"
click-to-show: "Click to show"
desktop/views/components/follow-button.vue:
following: "Following"
follow: "Follow"
request-pending: "Pending follow request"
follow-processing: "Processing follow"
follow-request: "Follow request"
desktop/views/components/followers-window.vue:
followers: "{}'s followers"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "Blocking"
no-muted-users: "No muted users"
no-blocked-users: "No blocked users"
word-mute: "Word mute"
muted-words: "Muted keywords"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "Save"
common/views/components/password-settings.vue:
reset: "Change password"
enter-current-password: "Enter the current password"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "Instance"
instance-name: "Instance name"
instance-description: "Instance description"
host: "Host"
banner-url: "Banner image URL"
languages: "Language of this instance"
languages-desc: "You can add more than one, separated by spaces."
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "Following"
followers: "Followers"
is-bot: "This account is a Bot"
years-old: " years old"
years-old: "{age} years old"
year: "/"
month: "/"
day: "-"
@ -1160,11 +1169,11 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "The content is NSFW"
click-to-show: "Click to show"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "Following"
follow: "Follow"
request-pending: "Pending follow request"
follow-processing: "Processing follow"
request-pending: "Pending"
follow-processing: "Processing"
follow-request: "Follow request"
mobile/views/components/friends-maker.vue:
title: "Let's follow them"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "Unmute"
block: "Block"
unblock: "Unblock"
years-old: "{age} years old"
mobile/views/pages/user/home.vue:
recent-notes: "Recent notes"
images: "Images"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Cancelar la encuesta"
common/views/components/reaction-picker.vue:
choose-reaction: "Escoge una reacción"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Usuario"
password: "Contraseña"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "Este contenido no es apropiado para ver en el trabajo"
click-to-show: "Click para mostrar"
desktop/views/components/follow-button.vue:
following: "Siguiendo"
follow: "Sigue"
request-pending: "Pendiente de aprobación"
follow-processing: "フォロー処理中"
follow-request: "Solicitud de seguir"
desktop/views/components/followers-window.vue:
followers: "{} seguidores"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -71,7 +71,7 @@ common:
friday: "Vendredi"
saturday: "Samedi"
reactions:
like: "J'aime"
like: "Bien"
love: "Adore"
laugh: "Rire"
hmm: "Hmm … ?"
@ -85,8 +85,8 @@ common:
public: "Public"
home: "Principal"
home-desc: "Publier sur le fil principal uniquement"
followers: "Abonnés·es"
followers-desc: "Publier à vos abonnés·es uniquement"
followers: "Abonné·e·s"
followers-desc: "Publier à vos abonné·e·s uniquement"
specified: "Direct"
specified-desc: "Publier uniquement aux utilisateurs·rices mentionnés·es"
private: "Privé"
@ -99,7 +99,7 @@ common:
f: "En attente de vos écrits"
search: "Recherche"
delete: "Supprimer"
loading: "Chargement"
loading: "Chargement en cours"
ok: "OK"
update-available-title: "Mise à jour disponible"
update-available: "Une nouvelle version de Misskey est disponible ({newer}, version actuelle: {current}). Veuillez recharger la page pour appliquer la mise à jour."
@ -117,8 +117,8 @@ common:
this-setting-is-this-device-only: "Uniquement sur cet appareil"
use-os-default-emojis: "Utiliser les émojis standards du système"
do-not-use-in-production: 'Il sagit dune version de développement. Ne pas utiliser dans un environnement de production.'
is-remote-user: "Ces informations utilisateur ont été copiées."
is-remote-post: "Ceci est une publication distante"
is-remote-user: "Ces informations appartiennent à un·e utilisateur·rice distant·e."
is-remote-post: "Ceci est une publication distante."
view-on-remote: "Consulter le profil complet"
error:
title: 'Une erreur est survenue'
@ -128,12 +128,12 @@ common:
my-turn: "Cest votre tour"
opponent-turn: "Tour de ladversaire"
turn-of: "Tour de {name}"
past-turn-of: "{name}のターン"
past-turn-of: "Tour de {name}"
won: "{name} a gagné"
black: "Noirs"
white: "Blancs"
total: "Total"
this-turn: "{count}ターン目"
this-turn: "Tour {count}"
widgets:
analog-clock: "Horloge analogique"
profile: "Profil"
@ -151,7 +151,7 @@ common:
notifications: "Notifications"
users: "Utilisateur·rice·s"
polls: "Sondages"
post-form: "Formulaire de publication"
post-form: "Champs de publication"
server: "Info sur le serveur"
donation: "Dons"
nav: "Navigation"
@ -160,13 +160,13 @@ common:
dev: "Échec lors de la création de lapplication. Veuillez réessayer."
ai-chan-kawaii: "Ai-Chan est mignone !"
auth/views/form.vue:
share-access: "<i>{name}</i>があなたのアカウントにアクセスすることを許可しますか?"
share-access: "Désirez-vous autoriser <i>{name}</i> à avoir accès à votre compte?"
permission-ask: "Cette application nécessite les autorisations suivantes :"
account-read: "Afficher les informations du compte :"
account-write: "Modifications des informations du compte :"
note-write: "Publier."
like-write: "Réagir aux publications."
following-write: "S'abonner et se désabonner."
following-write: "Sabonner et se désabonner."
drive-read: "Lire votre Drive"
drive-write: "Téléverser/supprimer des fichiers dans votre Drive."
notification-read: "Lire vos notifications."
@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Annuler ce sondage"
common/views/components/reaction-picker.vue:
choose-reaction: "Choisissez votre réaction"
common/views/components/emoji-picker.vue:
custom-emoji: "Émoji personnalisé"
people: "Personnes"
animals-and-nature: "Animaux et nature"
food-and-drink: "Nourriture et boisson"
activity: "Activités"
travel-and-places: "Lieux et voyages"
objects: "Objets"
symbols: "Symboles"
flags: "Drapeaux"
common/views/components/signin.vue:
username: "Nom d'utilisateur·rice"
password: "Mot de passe"
@ -491,8 +501,8 @@ common/views/pages/follow.vue:
following: "Suit"
follow: "Suivre"
request-pending: "Demande d'abonnement en attente"
follow-processing: "En cours dabonnement"
follow-request: "Demande d'abonnement"
follow-processing: "Demande en attente"
follow-request: "Demande dabonnement"
desktop:
banner-crop-title: "Découpez la partie qui apparaitra comme bannière"
banner: "Bannière"
@ -519,7 +529,7 @@ desktop/views/components/calendar.vue:
next: "Mois prochain"
go: "Cliquez pour naviguer"
desktop/views/components/choose-file-from-drive-window.vue:
chosen-files: "{count}ファイル選択中"
chosen-files: "{count} fichier·s sélectionné·s"
upload: "Téléverser des fichiers à partir de votre ordinateur"
cancel: "Annuler"
ok: "OK"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher"
desktop/views/components/follow-button.vue:
following: "Abonné·e"
follow: "Suivre"
request-pending: "En attente d'approbation"
follow-processing: "Continuer labonnement"
follow-request: "Demande d'abonnement"
desktop/views/components/followers-window.vue:
followers: "{} abonné·e·s"
desktop/views/components/followers.vue:
@ -687,8 +691,8 @@ desktop/views/components/renote-form.vue:
desktop/views/components/renote-form-window.vue:
title: "Êtes vous sûr de vouloir renote cette note?"
desktop/views/pages/user-following-or-followers.vue:
following: "{user}のフォロー"
followers: "{user}のフォロワー"
following: "{user} suit"
followers: "Abonné·e·s de {user}"
desktop/views/components/settings-window.vue:
settings: "Paramètres"
desktop/views/components/settings.vue:
@ -717,7 +721,7 @@ desktop/views/components/settings.vue:
api-via-stream-desc: "この設定をオンにすると、websocket接続を経由してAPIリクエストが行われます(パフォーマンス向上が期待できます)。オフにすると、ネイティブの fetch APIが利用されます。この設定はこのデバイスのみ有効です。"
deck-nav: "デッキ内ナビゲーション"
deck-nav-desc: "デッキを使用しているとき、ナビゲーションが発生する際にページ遷移を行わずに一時的なカラムで受けるようにします。"
deck-default: "デッキをデフォルトのUIにする"
deck-default: "Utiliser le Deck comme IU par défaut"
display: "Affichage et design"
customize: "Personnaliser l'Accueil"
wallpaper: "Arrière plan"
@ -737,7 +741,7 @@ desktop/views/components/settings.vue:
show-renoted-my-notes: "Afficher mes republications dans les fils"
show-local-renotes: "Afficher les partages locaux sur les fils"
show-maps: "Afficher la carte"
deck-column-align: "デッキのカラムの位置"
deck-column-align: "Alignement des colonnes du Deck"
deck-column-align-center: "Centrer"
deck-column-align-left: "À gauche"
sound: "Son"
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "En cours blocage"
no-muted-users: "Aucun utilisateur·rice nest mis·e en sourdine"
no-blocked-users: "Aucun utilisateur·rice nest bloqué·e"
word-mute: "Filtre de mots"
muted-words: "Mots masqués"
muted-words-description: "Description des mots mis en sourdine"
save: "Enregistrer"
common/views/components/password-settings.vue:
reset: "Modifier le mot de passe"
enter-current-password: "Entrez votre mot de passe actuel"
@ -870,7 +878,7 @@ desktop/views/components/ui.header.account.vue:
dark: "Fall in dark"
desktop/views/components/ui.header.nav.vue:
home: "Accueil"
deck: "デッキ"
deck: "Deck"
game: "Jeux"
desktop/views/components/ui.header.notifications.vue:
title: "Notifications"
@ -920,30 +928,31 @@ admin/views/instance.vue:
instance: "Instance"
instance-name: "Nom de linstance"
instance-description: "Description de linstance"
host: "Hôte"
banner-url: "Url de limage de la bannière"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
languages: "Langue de linstance"
languages-desc: "Vous pouvez en définir plus dune, séparées par des espaces."
maintainer-config: "Informations de ladministrateur"
maintainer-name: "Nom de ladministrateur"
maintainer-email: "Contact administratif"
drive-config: "Paramètres du lecteur"
cache-remote-files: "Mettre en cache des fichiers distants"
cache-remote-files-desc: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。そのためサーバーのストレージを節約できますが、プライバシー設定で直リンクを無効にしているユーザーにはファイルが見えなくなったり、サムネイルが生成されないので通信量が増加します。通常はこの設定をオンにしておくことをおすすめします。"
local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量"
remote-drive-capacity-mb: "リモートユーザーひとりあたりのドライブ容量"
local-drive-capacity-mb: "Volume du lecteur par utilisateur"
remote-drive-capacity-mb: "Volume du lecteur par utilisateur distant"
mb: "en mégaoctets"
recaptcha-config: "Paramètres de reCAPTCHA"
recaptcha-info: "reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。"
recaptcha-info: "Si activé, un jeton reCAPTCHA est requis. Vous pouvez en obtenir un sur https://www.google.com/recaptcha/intro/"
enable-recaptcha: "Activation de reCAPTCHA"
recaptcha-site-key: "Clé reCAPTCHA du site"
recaptcha-secret-key: "Clé secrète reCAPTCHA"
twitter-integration-config: "Paramètres de connexion à Twitter"
twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
twitter-integration-info: "LURL callback est définit sur /api/tw/cb"
enable-twitter-integration: "Activer la connection à Twitter"
twitter-integration-consumer-key: "Clé du consommateur"
twitter-integration-consumer-secret: "Secret du consommateur"
github-integration-config: "GitHub連携の設定"
github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
github-integration-config: "Paramètres dauthentification GitHub"
github-integration-info: "LURL callback est définit sur /api/gh/cb"
enable-github-integration: "Activer lauthentification avec Github"
github-integration-client-id: "ID client"
github-integration-client-secret: "Secret client"
@ -951,7 +960,7 @@ admin/views/instance.vue:
proxy-account-info: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。"
proxy-account-username: "Nom dutilisateur du compte proxy"
proxy-account-username-desc: "Spécifiez le nom dutilisateur du compte utilisé comme proxy."
proxy-account-warn: "アカウントは自動で作られないため、そのユーザー名のアカウントを予め作成しておく必要があります。"
proxy-account-warn: "Avant dentammer cette action, vous devez au préalable avoir créé un compte avec ce nom dutilisateur."
max-note-text-length: "Nombre maximal de caractères pour les messages"
disable-registration: "Désactiver les inscriptions"
disable-local-timeline: "Désactiver lheure locale"
@ -1024,7 +1033,7 @@ admin/views/announcements.vue:
text: "Contenu"
saved: "Sauvegardé"
_remove:
are-you-sure: "「$1」を削除しますか"
are-you-sure: "Supprimer « %1$s»?"
removed: "Supprimé"
admin/views/hashtags.vue:
hided-tags: "Tags cachés"
@ -1054,11 +1063,11 @@ desktop/views/pages/selectdrive.vue:
upload: "Téléverser des fichiers à partir de votre ordinateur"
desktop/views/pages/search.vue:
not-available: "La fonction de recherche est désactivée dans les paramètres de linstance."
not-found: "「{q}」に関する投稿は見つかりませんでした。"
not-found: "Aucune publication trouvée pour « {q} »."
desktop/views/pages/share.vue:
share-with: "Partager avec {name}"
desktop/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
no-posts-found: "Aucune publication contenant « {q} » na été trouvée."
desktop/views/pages/user-list.users.vue:
users: "Utilisateurs"
add-user: "Ajouter un utilisateur"
@ -1066,7 +1075,7 @@ desktop/views/pages/user-list.users.vue:
desktop/views/pages/user/user.followers-you-know.vue:
title: "Abonné·e·s que vous connaissez"
loading: "Chargement en cours"
no-users: "Pas d'utilisateurs"
no-users: "Aucun abonné connu"
desktop/views/pages/user/user.friends.vue:
title: "Mentions fréquentes"
loading: "Chargement en cours"
@ -1078,8 +1087,8 @@ desktop/views/pages/user/user.photos.vue:
desktop/views/pages/user/user.profile.vue:
follows-you: "Vous suit"
stalk: "Traquer"
stalking: "ストーキングしています"
unstalk: "ストーク解除"
stalking: "Entrain de poursuivre"
unstalk: "Cesser la poursuite"
mute: "Mettre en sourdine"
muted: "Muting"
unmute: "Enlever la sourdine"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "Suit"
followers: "Abonné·e·s"
is-bot: "Ce compte est un Bot"
years-old: "ans dâge"
years-old: "{age} ans"
year: "Année"
month: "/"
day: "-"
@ -1160,12 +1169,12 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "Le contenu est NSFW"
click-to-show: "Cliquer pour afficher"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "Abonné·e"
follow: "Suivre"
request-pending: "En attente d'approbation"
follow-processing: "En cours dabonnement"
follow-request: "Demande d'abonnement"
follow: "Sabonner"
request-pending: "Demande en attente"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "Abonnez-vous aux utilisateurs"
empty: "Impossible de trouver des utilisateurs·trices à recommander."
@ -1218,7 +1227,7 @@ mobile/views/components/ui.header.vue:
mobile/views/components/ui.nav.vue:
timeline: "Fil d'actualité"
notifications: "Notifications"
follow-requests: "Demandes d'abonnement"
follow-requests: "Demandes dabonnement"
search: "Rechercher"
favorites: "Favoris"
user-lists: "Listes"
@ -1244,7 +1253,7 @@ mobile/views/pages/signup.vue:
mobile/views/pages/followers.vue:
followers-of: "Abonné·e·s de {name}"
mobile/views/pages/following.vue:
following-of: "{name}のフォロー"
following-of: "Abonné·e·s de {name}"
mobile/views/pages/home.vue:
home: "Accueil"
local: "Local"
@ -1253,7 +1262,7 @@ mobile/views/pages/home.vue:
mentions: "Mentions"
messages: "Messages"
mobile/views/pages/tag.vue:
no-posts-found: "ハッシュタグ「{q}」が付けられた投稿は見つかりませんでした。"
no-posts-found: "Aucune publication ayant pour hashtag « {q} » na été trouvée."
mobile/views/pages/welcome.vue:
signup: "S'enregistrer"
mobile/views/pages/widgets.vue:
@ -1266,8 +1275,8 @@ mobile/views/pages/widgets/activity.vue:
mobile/views/pages/share.vue:
share-with: "Partager avec {name}"
mobile/views/pages/received-follow-requests.vue:
title: "Demandes d'abonnement"
accept: "Approuver"
title: "Demandes dabonnement"
accept: "Accepter"
reject: "Refuser"
mobile/views/pages/note.vue:
title: "Post"
@ -1280,7 +1289,7 @@ mobile/views/pages/games/reversi.vue:
reversi: "Reversi"
mobile/views/pages/search.vue:
search: "Chercher"
not-found: "「{q}」に関する投稿は見つかりませんでした。"
not-found: "Aucune publication trouvée pour « {q} »."
mobile/views/pages/selectdrive.vue:
select-file: "Choisissez un fichier"
mobile/views/pages/settings.vue:
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "Enlever la sourdine"
block: "Bloquer"
unblock: "Débloquer"
years-old: "{age} ans"
mobile/views/pages/user/home.vue:
recent-notes: "Notes récentes"
images: "Images"
@ -1361,7 +1371,7 @@ mobile/views/pages/user/home.vue:
followers-you-know: "Abonné·e·s que vous connaissez"
last-used-at: "Dernière connexion il y a"
mobile/views/pages/user/home.followers-you-know.vue:
no-users: "Pas d'utilisateurs"
no-users: "Aucun utilisateur connu"
mobile/views/pages/user/home.friends.vue:
no-users: "Pass d'utilisateurs"
mobile/views/pages/user/home.notes.vue:
@ -1391,7 +1401,7 @@ deck:
deck/deck.tl-column.vue:
is-media-only: "Les publications médias uniquement"
is-media-view: "Vue média"
edit: "オプション"
edit: "Option"
deck/deck.user-column.vue:
posts: "Notes"
following: "Suit"
@ -1438,7 +1448,7 @@ dev/views/new-app.vue:
app-desc: "Brève description introductive à votre application."
app-desc-ex: "p. ex) Misskey pour iOS"
callback-url: "LUrl de callback (facultatif)"
callback-url-desc: "ユーザーが認証フォームで認証した際にリダイレクトするURLを設定できます。"
callback-url-desc: "Vous pouvez définir lURL de redirection lorsque lutilisateur sest authentifié via formulaire dauthentification."
authority: "Autorisations "
authority-desc: "Sont accessibles via lAPI, uniquement les fonctionnalités demandées ici."
authority-warning: "アプリ作成後も変更できますが、新たな権限を付与する場合、その時点で関連付けられているユーザーキーはすべて無効になります。"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -379,6 +379,17 @@ common/views/components/poll-editor.vue:
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -657,13 +668,6 @@ desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
@ -935,6 +939,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
@ -1026,6 +1034,7 @@ admin/views/index.vue:
dashboard: "ダッシュボード"
instance: "インスタンス"
emoji: "カスタム絵文字"
moderators: "モデレーター"
users: "ユーザー"
update: "更新"
announcements: "お知らせ"
@ -1045,6 +1054,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1124,6 +1134,12 @@ admin/views/users.vue:
unverify: "公式アカウントを解除する"
unverified: "公式アカウントを解除しました"
admin/views/moderators.vue:
add-moderator:
title: "モデレーターの登録"
add: "登録"
added: "モデレーターを登録しました"
admin/views/emoji.vue:
add-emoji:
title: "絵文字の登録"
@ -1237,7 +1253,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1320,7 +1336,7 @@ mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1543,6 +1559,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートをほかそ"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクション、どれにするんや?"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "ちょっと見せられへんわ"
click-to-show: "クリックして見せるで"
desktop/views/components/follow-button.vue:
following: "フォローしとる"
follow: "フォロー"
request-pending: "フォローの許し待っとる"
follow-processing: "今フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしとるユーザーはおらんで"
no-blocked-users: "ブロックしとるユーザーはおらんで"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワード変える"
enter-current-password: "今のパスワードを入れてや"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotや"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,12 +1169,12 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "ちょっと見せられへんわ"
click-to-show: "押してみ、見せたるわ"
mobile/views/components/follow-button.vue:
following: "フォローしとる"
common/views/components/follow-button.vue:
following: "フォロー"
follow: "フォロー"
request-pending: "フォローの許し待っとる"
follow-processing: "フォロー処理やっとる‥"
follow-request: "フォロー許してくれや!言うてみる"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "おもろそうやな"
empty: "おすすめのユーザーはおらん。"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロックやめたる"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近儲かりまっか?"
images: "画像"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Deze peiling vernietigen"
common/views/components/reaction-picker.vue:
choose-reaction: "Kies een reactie"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Gebruikersnaam"
password: "Wachtwoord"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "Volgen"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "Volgers van {}"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,9 +1169,9 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "Volgen"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "Recente notities"
images: "Afbeeldingen"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Brukernavn"
password: "Passord"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "Innholdet er NSFW"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "Følger"
follow: "Følg"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "Følger"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,9 +1169,9 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "Innholdet er NSFW"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
following: "Følger"
follow: "Følg"
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "Nylige innlegg"
images: "Bilder"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "Usuń tę ankietę"
common/views/components/reaction-picker.vue:
choose-reaction: "Wybierz reakcję"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "Nazwa użytkownika"
password: "Hasło"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "To jest zawartość NSFW"
click-to-show: "Naciśnij aby wyświetlić"
desktop/views/components/follow-button.vue:
following: "Śledzisz"
follow: "Śledź"
request-pending: "Oczekiwanie na pozwolenie"
follow-processing: "フォロー処理中"
follow-request: "Poproś o śledzenie"
desktop/views/components/followers-window.vue:
followers: "Śledzący"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "Śledzeni"
followers: "Śledzący"
is-bot: "To konto jest botem"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,12 +1169,12 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "To jest zawartość NSFW"
click-to-show: "Naciśnij aby wyświetlić"
mobile/views/components/follow-button.vue:
following: "Śledzisz"
follow: "Śledź"
request-pending: "Oczekiwanie na pozwolenie"
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "Poproś o śledzenie"
follow-request: "フォロー申請"
mobile/views/components/friends-maker.vue:
title: "Zacznij śledzić ludzi takich jak Ty"
empty: "Nie znaleziono podobnych użytkowników."
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "Ostatnie wpisy"
images: "Zdjęcia"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "Notas recentes"
images: "Imagens"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -344,6 +344,16 @@ common/views/components/poll-editor.vue:
destroy: "アンケートを破棄"
common/views/components/reaction-picker.vue:
choose-reaction: "リアクションを選択"
common/views/components/emoji-picker.vue:
custom-emoji: "カスタム絵文字"
people: "人"
animals-and-nature: "動物&自然"
food-and-drink: "食べ物&飲み物"
activity: "アクティビティ"
travel-and-places: "場所"
objects: "物"
symbols: "記号"
flags: "旗"
common/views/components/signin.vue:
username: "ユーザー名"
password: "パスワード"
@ -588,12 +598,6 @@ desktop/views/components/media-image.vue:
desktop/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
desktop/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
follow-processing: "フォロー処理中"
follow-request: "フォロー申請"
desktop/views/components/followers-window.vue:
followers: "{} のフォロワー"
desktop/views/components/followers.vue:
@ -829,6 +833,10 @@ common/views/components/mute-and-block.vue:
block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません"
no-blocked-users: "ブロックしているユーザーはいません"
word-mute: "ワードミュート"
muted-words: "ミュートされたキーワード"
muted-words-description: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
save: "保存"
common/views/components/password-settings.vue:
reset: "パスワードを変更する"
enter-current-password: "現在のパスワードを入力してください"
@ -920,6 +928,7 @@ admin/views/instance.vue:
instance: "インスタンス"
instance-name: "インスタンス名"
instance-description: "インスタンスの紹介"
host: "ホスト"
banner-url: "バナー画像URL"
languages: "インスタンスの対象言語"
languages-desc: "スペースで区切って複数設定できます。"
@ -1093,7 +1102,7 @@ desktop/views/pages/user/user.header.vue:
following: "フォロー"
followers: "フォロワー"
is-bot: "このアカウントはBotです"
years-old: "歳"
years-old: "{age}歳"
year: "年"
month: "月"
day: "日"
@ -1160,7 +1169,7 @@ mobile/views/components/media-image.vue:
mobile/views/components/media-video.vue:
sensitive: "閲覧注意"
click-to-show: "クリックして表示"
mobile/views/components/follow-button.vue:
common/views/components/follow-button.vue:
following: "フォロー中"
follow: "フォロー"
request-pending: "フォロー許可待ち"
@ -1351,6 +1360,7 @@ mobile/views/pages/user.vue:
unmute: "ミュート解除"
block: "ブロック"
unblock: "ブロック解除"
years-old: "{age}歳"
mobile/views/pages/user/home.vue:
recent-notes: "最近の投稿"
images: "画像"

View File

@ -1,8 +1,8 @@
{
"name": "misskey",
"author": "syuilo <i@syuilo.com>",
"version": "10.46.2",
"clientVersion": "1.0.11698",
"version": "10.51.0",
"clientVersion": "2.0.11800",
"codename": "nighthike",
"main": "./built/index.js",
"private": true,
@ -60,7 +60,7 @@
"@types/minio": "7.0.1",
"@types/mkdirp": "0.5.2",
"@types/mocha": "5.2.5",
"@types/mongodb": "3.1.12",
"@types/mongodb": "3.1.14",
"@types/ms": "0.7.30",
"@types/node": "10.12.2",
"@types/oauth": "0.9.1",
@ -75,13 +75,12 @@
"@types/seedrandom": "2.4.27",
"@types/sharp": "0.21.0",
"@types/showdown": "1.7.5",
"@types/single-line-log": "1.1.0",
"@types/speakeasy": "2.0.3",
"@types/systeminformation": "3.23.0",
"@types/tinycolor2": "1.4.1",
"@types/tmp": "0.0.33",
"@types/uuid": "3.4.4",
"@types/webpack": "4.4.17",
"@types/webpack": "4.4.18",
"@types/webpack-stream": "3.2.10",
"@types/websocket": "0.0.40",
"@types/ws": "6.0.1",
@ -100,6 +99,7 @@
"commander": "2.19.0",
"crc-32": "1.2.0",
"css-loader": "1.0.1",
"cssnano": "4.1.7",
"dateformat": "3.0.3",
"debug": "4.1.0",
"deep-equal": "1.0.1",
@ -129,6 +129,7 @@
"gulp-typescript": "4.0.2",
"gulp-uglify": "3.0.1",
"gulp-util": "3.0.8",
"gulp-yaml": "2.0.2",
"hard-source-webpack-plugin": "0.12.0",
"html-minifier": "3.5.21",
"http-signature": "1.2.0",
@ -152,12 +153,11 @@
"koa-slow": "2.1.0",
"koa-views": "6.1.4",
"loader-utils": "1.1.0",
"mecab-async": "0.1.2",
"merge-options": "1.0.1",
"minio": "7.0.1",
"mkdirp": "0.5.1",
"mocha": "5.2.0",
"moji": "0.5.1",
"moment": "2.22.2",
"mongodb": "3.1.8",
"monk": "6.0.6",
"ms": "2.1.1",
@ -169,12 +169,14 @@
"os-utils": "0.0.14",
"parse5": "5.1.0",
"portscanner": "2.2.0",
"postcss-loader": "3.0.0",
"progress-bar-webpack-plugin": "1.11.0",
"promise-limit": "2.7.0",
"promise-sequential": "1.1.1",
"pug": "2.0.3",
"punycode": "2.1.1",
"qrcode": "1.3.2",
"randomcolor": "0.5.3",
"ratelimiter": "3.2.0",
"recaptcha-promise": "0.1.3",
"reconnecting-websocket": "4.1.10",
@ -185,12 +187,10 @@
"rimraf": "2.6.2",
"rndstr": "1.0.0",
"s-age": "1.1.2",
"sass-loader": "7.1.0",
"seedrandom": "2.4.4",
"sharp": "0.21.0",
"showdown": "1.8.7",
"showdown-highlightjs-extension": "0.1.2",
"single-line-log": "1.1.2",
"speakeasy": "2.0.0",
"stringz": "1.0.0",
"style-loader": "0.23.1",
@ -199,6 +199,7 @@
"summaly": "2.2.0",
"systeminformation": "3.47.0",
"syuilo-password-strength": "0.0.1",
"terser-webpack-plugin": "1.1.0",
"textarea-caret": "3.1.0",
"tinycolor2": "1.4.1",
"tmp": "0.0.33",
@ -218,10 +219,10 @@
"vue-i18n": "8.3.1",
"vue-js-modal": "1.3.26",
"vue-loader": "15.4.2",
"vue-marquee-text-component": "1.1.0",
"vue-router": "3.0.1",
"vue-style-loader": "4.1.2",
"vue-svg-inline-loader": "1.2.1",
"vue-sweetalert2": "1.5.7",
"vue-template-compiler": "2.5.17",
"vuedraggable": "2.16.0",
"vuewordcloud": "18.7.11",

View File

@ -2,12 +2,15 @@
* チャートエンジン
*/
import * as moment from 'moment';
const nestedProperty = require('nested-property');
import autobind from 'autobind-decorator';
import * as mongo from 'mongodb';
import db from '../db/mongodb';
import { ICollection } from 'monk';
const utc = moment.utc;
export type Obj = { [key: string]: any };
export type Partial<T> = {
@ -87,12 +90,12 @@ export default abstract class Chart<T> {
@autobind
private getCurrentDate(): [number, number, number, number] {
const now = new Date();
const now = moment().utc();
const y = now.getFullYear();
const m = now.getMonth();
const d = now.getDate();
const h = now.getHours();
const y = now.year();
const m = now.month();
const d = now.date();
const h = now.hour();
return [y, m, d, h];
}
@ -114,15 +117,15 @@ export default abstract class Chart<T> {
const [y, m, d, h] = this.getCurrentDate();
const current =
span == 'day' ? new Date(y, m, d) :
span == 'hour' ? new Date(y, m, d, h) :
span == 'day' ? utc([y, m, d]) :
span == 'hour' ? utc([y, m, d, h]) :
null;
// 現在(今日または今のHour)のログ
const currentLog = await this.collection.findOne({
group: group,
span: span,
date: current
date: current.toDate()
});
// ログがあればそれを返して終了
@ -158,7 +161,7 @@ export default abstract class Chart<T> {
log = await this.collection.insert({
group: group,
span: span,
date: current,
date: current.toDate(),
data: data
});
} catch (e) {
@ -225,8 +228,8 @@ export default abstract class Chart<T> {
const [y, m, d, h] = this.getCurrentDate();
const gt =
span == 'day' ? new Date(y, m, d - range) :
span == 'hour' ? new Date(y, m, d, h - range) :
span == 'day' ? utc([y, m, d]).subtract(range, 'days') :
span == 'hour' ? utc([y, m, d, h]).subtract(range, 'hours') :
null;
// ログ取得
@ -234,7 +237,7 @@ export default abstract class Chart<T> {
group: group,
span: span,
date: {
$gt: gt
$gte: gt.toDate()
}
}, {
sort: {
@ -264,22 +267,45 @@ export default abstract class Chart<T> {
if (recentLog) {
logs = [recentLog];
}
// 要求された範囲の最も古い箇所に位置するログが存在しなかったら
} else if (!utc(logs[logs.length - 1].date).isSame(gt)) {
// 要求された範囲の最も古い箇所時点での最も新しいログを持ってきて末尾に追加する
// (隙間埋めできないため)
const outdatedLog = await this.collection.findOne({
group: group,
span: span,
date: {
$lt: gt.toDate()
}
}, {
sort: {
date: -1
},
fields: {
_id: 0
}
});
if (outdatedLog) {
logs.push(outdatedLog);
}
}
// 整形
for (let i = (range - 1); i >= 0; i--) {
const current =
span == 'day' ? new Date(y, m, d - i) :
span == 'hour' ? new Date(y, m, d, h - i) :
span == 'day' ? utc([y, m, d]).subtract(i, 'days') :
span == 'hour' ? utc([y, m, d, h]).subtract(i, 'hours') :
null;
const log = logs.find(l => l.date.getTime() == current.getTime());
const log = logs.find(l => utc(l.date).isSame(current));
if (log) {
promisedChart.unshift(Promise.resolve(log.data));
} else {
// 隙間埋め
const latest = logs.find(l => l.date.getTime() < current.getTime());
const latest = logs.find(l => utc(l.date).isBefore(current));
promisedChart.unshift(this.getTemplate(false, latest ? latest.data : null));
}
}

View File

@ -41,22 +41,22 @@ export default Vue.extend({
methods: {
add() {
this.announcements.push({
this.announcements.unshift({
title: '',
text: ''
});
},
remove(i) {
this.$swal({
this.$root.alert({
type: 'warning',
text: this.$t('_remove.are-you-sure').replace('$1', this.announcements.find((_, j) => j == i).title),
showCancelButton: true
}).then(res => {
if (!res.value) return;
if (!res) return;
this.announcements = this.announcements.filter((_, j) => j !== i);
this.save(true);
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('_remove.removed')
});
@ -68,13 +68,13 @@ export default Vue.extend({
broadcasts: this.announcements
}).then(() => {
if (!silent) {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});
}
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});

View File

@ -3,17 +3,17 @@
<table>
<thead>
<tr>
<th><fa icon="exchange-alt"/> In/Out</th>
<th><fa :icon="faExchangeAlt"/> In/Out</th>
<th><fa :icon="faBolt"/> Activity</th>
<th><fa icon="server"/> Host</th>
<th><fa icon="bolt"/> Activity</th>
<th><fa icon="user"/> Actor</th>
</tr>
</thead>
<tbody>
<tr v-for="log in logs" :key="log.id">
<td :class="log.direction">{{ log.direction == 'in' ? '<' : '>' }} {{ log.direction }}</td>
<td>{{ log.host }}</td>
<td>{{ log.activity }}</td>
<td>{{ log.host }}</td>
<td>@{{ log.actor }}</td>
</tr>
</tbody>
@ -23,12 +23,14 @@
<script lang="ts">
import Vue from 'vue';
import { faBolt, faExchangeAlt } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
data() {
return {
logs: [],
connection: null
connection: null,
faBolt, faExchangeAlt
};
},

View File

@ -37,7 +37,7 @@
</div>
<div>
<div>
<div><fa icon="database"/></div>
<div><fa :icon="faDatabase"/></div>
<div>
<span>{{ $t('drive') }}</span>
<b>{{ stats.driveUsageLocal | bytes }}</b>
@ -83,9 +83,11 @@ import i18n from '../../i18n';
import XCpuMemory from "./cpu-memory.vue";
import XCharts from "./charts.vue";
import XApLog from "./ap-log.vue";
import { faDatabase } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/dashboard.vue'),
components: {
XCpuMemory,
XCharts,
@ -96,7 +98,8 @@ export default Vue.extend({
return {
stats: null,
connection: null,
meta: null
meta: null,
faDatabase
};
},

View File

@ -23,7 +23,7 @@
</ui-card>
<ui-card>
<div slot="title"><fa :icon="['far', 'grin']"/> {{ $t('emojis.title') }}</div>
<div slot="title"><fa :icon="faGrin"/> {{ $t('emojis.title') }}</div>
<section v-for="emoji in emojis">
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
<ui-horizon-group inputs>
@ -50,6 +50,7 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { faGrin } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/emoji.vue'),
@ -58,7 +59,8 @@ export default Vue.extend({
name: '',
url: '',
aliases: '',
emojis: []
emojis: [],
faGrin
};
},
@ -73,13 +75,13 @@ export default Vue.extend({
url: this.url,
aliases: this.aliases.split(' ').filter(x => x.length > 0)
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('add-emoji.added')
});
this.fetchEmojis();
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});
@ -101,12 +103,12 @@ export default Vue.extend({
url: emoji.url,
aliases: emoji.aliases.split(' ').filter(x => x.length > 0)
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('updated')
});
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});
@ -114,23 +116,23 @@ export default Vue.extend({
},
removeEmoji(emoji) {
this.$swal({
this.$root.alert({
type: 'warning',
text: this.$t('remove-emoji.are-you-sure').replace('$1', emoji.name),
showCancelButton: true
}).then(res => {
if (!res.value) return;
if (!res) return;
this.$root.api('admin/emoji/remove', {
id: emoji.id
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('remove-emoji.removed')
});
this.fetchEmojis();
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});

View File

@ -20,8 +20,9 @@
<ul>
<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>{{ $t('dashboard') }}</li>
<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>{{ $t('instance') }}</li>
<li @click="nav('moderators')" :class="{ active: page == 'moderators' }"><fa :icon="faHeadset" fixed-width/>{{ $t('moderators') }}</li>
<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>{{ $t('users') }}</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="['far', 'grin']" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="faGrin" fixed-width/>{{ $t('emoji') }}</li>
<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>{{ $t('announcements') }}</li>
<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>{{ $t('hashtags') }}</li>
@ -29,21 +30,27 @@
<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">{{ $t('update') }}</li> -->
</ul>
<div class="back-to-misskey">
<a href="/"><fa icon="arrow-left"/> {{ $t('back-to-misskey') }}</a>
<a href="/"><fa :icon="faArrowLeft"/> {{ $t('back-to-misskey') }}</a>
</div>
<div class="version">
<small>Misskey {{ version }}</small>
</div>
</nav>
<main>
<div v-if="page == 'dashboard'"><x-dashboard/></div>
<div v-if="page == 'instance'"><x-instance/></div>
<div v-if="page == 'users'"><x-users/></div>
<div v-if="page == 'emoji'"><x-emoji/></div>
<div v-if="page == 'announcements'"><x-announcements/></div>
<div v-if="page == 'hashtags'"><x-hashtags/></div>
<div v-if="page == 'drive'"></div>
<div v-if="page == 'update'"></div>
<marquee-text v-if="instances.length > 0" class="instances" :repeat="10" :duration="10">
<span v-for="instance in instances" class="instance"><b :style="{ background: instance.bg }">{{ instance.host }}</b>{{ instance.notesCount | number }}</span>
</marquee-text>
<div class="page">
<div v-if="page == 'dashboard'"><x-dashboard/></div>
<div v-if="page == 'instance'"><x-instance/></div>
<div v-if="page == 'moderators'"><x-moderators/></div>
<div v-if="page == 'users'"><x-users/></div>
<div v-if="page == 'emoji'"><x-emoji/></div>
<div v-if="page == 'announcements'"><x-announcements/></div>
<div v-if="page == 'hashtags'"><x-hashtags/></div>
<div v-if="page == 'drive'"></div>
<div v-if="page == 'update'"></div>
</div>
</main>
</div>
</template>
@ -54,10 +61,15 @@ import i18n from '../../i18n';
import { version } from '../../config';
import XDashboard from "./dashboard.vue";
import XInstance from "./instance.vue";
import XModerators from "./moderators.vue";
import XEmoji from "./emoji.vue";
import XAnnouncements from "./announcements.vue";
import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue";
import { faHeadset, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { faGrin } from '@fortawesome/free-regular-svg-icons';
import MarqueeText from 'vue-marquee-text-component';
import randomColor from 'randomcolor';
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
@ -68,10 +80,12 @@ export default Vue.extend({
components: {
XDashboard,
XInstance,
XModerators,
XEmoji,
XAnnouncements,
XHashtags,
XUsers
XUsers,
MarqueeText
},
provide: {
isMobile
@ -81,9 +95,24 @@ export default Vue.extend({
page: 'dashboard',
version,
isMobile,
navOpend: !isMobile
navOpend: !isMobile,
instances: [],
faGrin,
faArrowLeft,
faHeadset
};
},
created() {
this.$root.api('instances').then(instances => {
instances.forEach(i => {
i.bg = randomColor({
seed: i.host,
luminosity: 'dark'
});
});
this.instances = instances;
});
},
methods: {
nav(page: string) {
this.page = page;
@ -92,7 +121,7 @@ export default Vue.extend({
});
</script>
<style lang="stylus">
<style lang="stylus" scoped>
.mk-admin
$headerHeight = 48px
@ -253,7 +282,23 @@ export default Vue.extend({
> main
width 100%
padding 0 0 0 250px
max-width 1300px
> .instances
padding 8px
background rgba(0, 0, 0, 0.7)
color #fff
font-size 14px
>>> .instance
margin 0 10px
> b
padding 0px 6px
margin-right 4px
border-radius 4px
> .page
max-width 1300px
&.isMobile
> main

View File

@ -3,13 +3,14 @@
<ui-card>
<div slot="title"><fa icon="cog"/> {{ $t('instance') }}</div>
<section class="fit-top fit-bottom">
<ui-input :value="host" readonly>{{ $t('host') }}</ui-input>
<ui-input v-model="name">{{ $t('instance-name') }}</ui-input>
<ui-textarea v-model="description">{{ $t('instance-description') }}</ui-textarea>
<ui-input v-model="bannerUrl"><i slot="icon"><fa icon="link"/></i>{{ $t('banner-url') }}</ui-input>
<ui-input v-model="languages"><i slot="icon"><fa icon="language"/></i>{{ $t('languages') }}<span slot="desc">{{ $t('languages-desc') }}</span></ui-input>
</section>
<section class="fit-bottom">
<header><fa icon="headset"/> {{ $t('maintainer-config') }}</header>
<header><fa :icon="faHeadset"/> {{ $t('maintainer-config') }}</header>
<ui-input v-model="maintainerName">{{ $t('maintainer-name') }}</ui-input>
<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="['far', 'envelope']"/></i>{{ $t('maintainer-email') }}</ui-input>
</section>
@ -23,14 +24,14 @@
<ui-input v-model="remoteDriveCapacityMb" type="number" :disabled="!cacheRemoteFiles">{{ $t('remote-drive-capacity-mb') }}<span slot="suffix">MB</span><span slot="desc">{{ $t('mb') }}</span></ui-input>
</section>
<section class="fit-bottom">
<header><fa icon="shield-alt"/> {{ $t('recaptcha-config') }}</header>
<header><fa :icon="faShieldAlt"/> {{ $t('recaptcha-config') }}</header>
<ui-switch v-model="enableRecaptcha">{{ $t('enable-recaptcha') }}</ui-switch>
<ui-info>{{ $t('recaptcha-info') }}</ui-info>
<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>{{ $t('recaptcha-site-key') }}</ui-input>
<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>{{ $t('recaptcha-secret-key') }}</ui-input>
</section>
<section>
<header><fa icon="ghost"/> {{ $t('proxy-account-config') }}</header>
<header><fa :icon="faGhost"/> {{ $t('proxy-account-config') }}</header>
<ui-info>{{ $t('proxy-account-info') }}</ui-info>
<ui-input v-model="proxyAccount"><span slot="prefix">@</span>{{ $t('proxy-account-username') }}<span slot="desc">{{ $t('proxy-account-username-desc') }}</span></ui-input>
<ui-info warn>{{ $t('proxy-account-warn') }}</ui-info>
@ -81,11 +82,16 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import { host } from '../../config';
import { toUnicode } from 'punycode';
import { faHeadset, faShieldAlt, faGhost } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('admin/views/instance.vue'),
data() {
return {
host: toUnicode(host),
maintainerName: null,
maintainerEmail: null,
disableRegistration: false,
@ -109,6 +115,7 @@ export default Vue.extend({
githubClientSecret: null,
proxyAccount: null,
inviteCode: null,
faHeadset, faShieldAlt, faGhost
};
},
@ -142,7 +149,7 @@ export default Vue.extend({
this.$root.api('admin/invite').then(x => {
this.inviteCode = x.code;
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});
@ -174,12 +181,12 @@ export default Vue.extend({
githubClientId: this.githubClientId,
githubClientSecret: this.githubClientSecret,
}).then(() => {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});
}).catch(e => {
this.$swal({
this.$root.alert({
type: 'error',
text: e
});

View File

@ -0,0 +1,61 @@
<template>
<div class="jnhmugbb">
<ui-card>
<div slot="title"><fa icon="plus"/> {{ $t('add-moderator.title') }}</div>
<section class="fit-top">
<ui-input v-model="username" type="text">
<span slot="prefix">@</span>
</ui-input>
<ui-button @click="add" :disabled="adding">{{ $t('add-moderator.add') }}</ui-button>
</section>
</ui-card>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../i18n';
import parseAcct from "../../../../misc/acct/parse";
export default Vue.extend({
i18n: i18n('admin/views/moderators.vue'),
data() {
return {
username: '',
adding: false
};
},
methods: {
async add() {
this.adding = true;
const process = async () => {
const user = await this.$root.api('users/show', parseAcct(this.username));
await this.$root.api('admin/moderators/add', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('add-moderator.added')
});
};
await process().catch(e => {
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.adding = false;
},
}
});
</script>
<style lang="stylus" scoped>
.jnhmugbb
@media (min-width 500px)
padding 16px
</style>

View File

@ -49,6 +49,7 @@ import parseAcct from "../../../../misc/acct/parse";
export default Vue.extend({
i18n: i18n('admin/views/users.vue'),
data() {
return {
verifyUsername: null,
@ -67,13 +68,19 @@ export default Vue.extend({
this.verifying = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.verifyUsername));
await this.$root.os.api('admin/verify-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('verified') });
const user = await this.$root.api('users/show', parseAcct(this.verifyUsername));
await this.$root.api('admin/verify-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('verified')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.verifying = false;
@ -83,13 +90,19 @@ export default Vue.extend({
this.unverifying = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.unverifyUsername));
await this.$root.os.api('admin/unverify-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('unverified') });
const user = await this.$root.api('users/show', parseAcct(this.unverifyUsername));
await this.$root.api('admin/unverify-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('unverified')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.unverifying = false;
@ -99,13 +112,19 @@ export default Vue.extend({
this.suspending = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.suspendUsername));
await this.$root.os.api('admin/suspend-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('suspended') });
const user = await this.$root.api('users/show', parseAcct(this.suspendUsername));
await this.$root.api('admin/suspend-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('suspended')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.suspending = false;
@ -115,13 +134,19 @@ export default Vue.extend({
this.unsuspending = true;
const process = async () => {
const user = await this.$root.os.api('users/show', parseAcct(this.unsuspendUsername));
await this.$root.os.api('admin/unsuspend-user', { userId: user.id });
//this.$root.os.apis.dialog({ text: this.$t('unsuspended') });
const user = await this.$root.api('users/show', parseAcct(this.unsuspendUsername));
await this.$root.api('admin/unsuspend-user', { userId: user.id });
this.$root.alert({
type: 'success',
text: this.$t('unsuspended')
});
};
await process().catch(e => {
//this.$root.os.apis.dialog({ text: `Failed: ${e}` });
this.$root.alert({
type: 'error',
text: e.toString()
});
});
this.unsuspending = false;

View File

@ -123,29 +123,3 @@ pre
[data-icon]
display inline-block
.swal2-container
z-index 10000 !important
&.swal2-shown
background-color rgba(0, 0, 0, 0.5) !important
.swal2-popup
background var(--face) !important
.swal2-content
color var(--text) !important
.swal2-confirm
background-color var(--primary) !important
border-left-color var(--primary) !important
border-right-color var(--primary) !important
color var(--primaryForeground) !important
&:hover
background-image none !important
background-color var(--primaryDarken5) !important
&:active
background-image none !important
background-color var(--primaryDarken5) !important

View File

@ -9,14 +9,11 @@ import './style.styl';
import init from '../init';
import Index from './views/index.vue';
import * as config from '../config';
/**
* init
*/
init(launch => {
document.title = `${config.name} | %i18n:common.application-authorization%`;
// Init router
const router = new VueRouter({
mode: 'history',

View File

@ -3,15 +3,9 @@
* (ENTRY POINT)
*/
/**
* ドメインに基づいて適切なスクリプトを読み込みます。
* ユーザーの言語およびモバイル端末か否かも考慮します。
* webpackは介さないためrequireやimportは使えません。
*/
'use strict';
(function() {
(async function() {
// キャッシュ削除要求があれば従う
if (localStorage.getItem('shouldFlush') == 'true') {
refresh();
@ -49,6 +43,9 @@
if (`${url.pathname}/`.startsWith('/admin/')) app = 'admin';
//#endregion
// Script version
const ver = localStorage.getItem('v') || VERSION;
//#region Detect the user language
let lang = null;
@ -67,8 +64,18 @@
langs.includes(settings.device.lang)) {
lang = settings.device.lang;
}
window.lang = lang;
//#endregion
let locale = localStorage.getItem('locale');
if (locale == null) {
const locale = await fetch(`/assets/locales/${lang}.json?ver=${ver}`)
.then(response => response.json());
localStorage.setItem('locale', JSON.stringify(locale));
}
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
const isMobile = /mobile|iphone|ipad|android/.test(ua);
@ -94,9 +101,6 @@
app = isMobile ? 'mobile' : 'desktop';
}
// Script version
const ver = localStorage.getItem('v') || VERSION;
// Get salt query
const salt = localStorage.getItem('salt')
? `?salt=${localStorage.getItem('salt')}`
@ -106,7 +110,7 @@
// 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}.js${salt}`);
script.setAttribute('src', `/assets/${app}.${ver}.js${salt}`);
script.setAttribute('async', 'true');
script.setAttribute('defer', 'true');
head.appendChild(script);
@ -142,6 +146,8 @@
function refresh() {
localStorage.setItem('shouldFlush', 'false');
localStorage.removeItem('locale');
// Random
localStorage.setItem('salt', Math.random().toString().substr(2, 8));

View File

@ -66,7 +66,7 @@ export default function<T extends object>(data: {
this.bakeProps();
(this as any).api('i/update_widget', {
this.$root.api('i/update_widget', {
id: this.id,
data: this.props
});

View File

@ -22,9 +22,9 @@ export default async function($root: any, force = false, silent = false) {
}
if (!silent) {
$root.$dialog({
title: '%i18n:common.update-available-title%',
text: '%i18n:common.update-available%'.replace('{newer}', newer).replace('{current}', current)
$root.alert({
title: $root.$t('@.update-available-title'),
text: $root.$t('@.update-available', { newer, current })
});
}

View File

@ -4,12 +4,9 @@ export default ($root: any) => {
require('fuckadblock');
function adBlockDetected() {
$root.$dialog({
title: '%fa:exclamation-triangle%%i18n:common.adblock.detected%',
text: '%i18n:common.adblock.warning%',
actins: [{
text: 'OK'
}]
$root.alert({
title: $root.$t('@.adblock.detected'),
text: $root.$t('@.adblock.warning')
});
}

View File

@ -1,8 +1,10 @@
const crypto = require('crypto');
// スクリプトサイズがデカい
//const crypto = require('crypto');
export default (data: ArrayBuffer) => {
const buf = new Buffer(data);
const hash = crypto.createHash("md5");
hash.update(buf);
return hash.digest("hex");
};
//const buf = new Buffer(data);
//const hash = crypto.createHash("md5");
//hash.update(buf);
//return hash.digest("hex");
return '';
};

View File

@ -1,8 +1,8 @@
import parse from '../../../../mfm/parse';
import { sum } from '../../../../prelude/array';
import shouldMuteNote from './should-mute-note';
import MkNoteMenu from '../views/components/note-menu.vue';
import MkReactionPicker from '../views/components/reaction-picker.vue';
import Ok from '../views/components/ok.vue';
function focus(el, fn) {
const target = fn(el);
@ -22,7 +22,8 @@ type Opts = {
export default (opts: Opts = {}) => ({
data() {
return {
showContent: false
showContent: false,
hideThisNote: false
};
},
@ -86,6 +87,10 @@ export default (opts: Opts = {}) => ({
}
},
created() {
this.hideThisNote = shouldMuteNote(this.$store.state.i, this.$store.state.settings, this.appearNote);
},
methods: {
reply(viaKeyboard = false) {
this.$root.$post({
@ -136,7 +141,10 @@ export default (opts: Opts = {}) => ({
this.$root.api('notes/favorites/create', {
noteId: this.appearNote.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
});
},

View File

@ -0,0 +1,28 @@
export default function(me, settings, note) {
const isMyNote = note.userId == me.id;
const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
return true;
}
}
if (settings.showRenotedMyNotes === false) {
if (isPureRenote && (note.renote.userId == me.id)) {
return true;
}
}
if (settings.showLocalRenotes === false) {
if (isPureRenote && (note.renote.user.host == null)) {
return true;
}
}
if (!isMyNote && note.text && settings.mutedWords.some(q => !q.some(word => !note.text.includes(word)))) {
return true;
}
return false;
}

View File

@ -8,11 +8,12 @@
<script lang="ts">
import Vue from 'vue';
import { host } from '../../../config';
import { toUnicode } from 'punycode';
export default Vue.extend({
props: ['user', 'detail'],
data() {
return {
host
host: toUnicode(host)
};
}
});

View File

@ -0,0 +1,197 @@
<template>
<div class="felqjxyj" :class="{ splash }">
<div class="bg" ref="bg" @click="onBgClick"></div>
<div class="main" ref="main">
<div class="icon" :class="type"><fa :icon="icon"/></div>
<header v-if="title" v-html="title"></header>
<div class="body" v-if="text" v-html="text"></div>
<ui-horizon-group no-grow class="buttons" v-if="!splash">
<ui-button @click="ok" primary autofocus>OK</ui-button>
<ui-button @click="cancel" v-if="showCancelButton">Cancel</ui-button>
</ui-horizon-group>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
import { faTimesCircle, faQuestionCircle } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
props: {
type: {
type: String,
required: false,
default: 'info'
},
title: {
type: String,
required: false
},
text: {
type: String,
required: false
},
showCancelButton: {
type: Boolean,
default: false
},
splash: {
type: Boolean,
default: false
}
},
computed: {
icon(): any {
switch (this.type) {
case 'success': return 'check';
case 'error': return faTimesCircle;
case 'warning': return 'exclamation-triangle';
case 'info': return 'info-circle';
case 'question': return faQuestionCircle;
}
}
},
mounted() {
this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto';
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 100,
easing: 'linear'
});
anime({
targets: this.$refs.main,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
if (this.splash) {
setTimeout(() => {
this.close();
}, 1000);
}
});
},
methods: {
ok() {
this.$emit('ok');
this.close();
},
cancel() {
this.$emit('cancel');
this.close();
},
close() {
(this.$refs.bg as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
(this.$refs.main as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.main,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [0, 0.5, 0.5, 1],
complete: () => this.destroyDom()
});
},
onBgClick() {
this.cancel();
}
}
});
</script>
<style lang="stylus" scoped>
.felqjxyj
display flex
align-items center
justify-content center
position fixed
z-index 30000
top 0
left 0
width 100%
height 100%
&.splash
&, *
pointer-events none !important
> .main
min-width 0
width initial
> .bg
display block
position fixed
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
pointer-events none
> .main
display block
position fixed
margin auto
padding 32px
min-width 320px
max-width 480px
width calc(100% - 32px)
text-align center
background var(--face)
border-radius 8px
color var(--faceText)
opacity 0
> .icon
font-size 32px
&.success
color #37ec92
&.error
color #ec4137
&.warning
color #ecb637
> *
display block
margin 0 auto
> header
margin 16px 0 8px 0
font-weight bold
font-size 20px
& + .body
margin-top 8px
> .body
margin 16px 0
> .buttons
margin-top 16px
</style>

View File

@ -0,0 +1,202 @@
<template>
<div class="prlncendiewqqkrevzeruhndoakghvtx">
<header>
<button v-for="category in categories"
:title="category.text"
@click="go(category.ref)"
:class="{ active: category.isActive }"
>
<fa :icon="category.icon" fixed-width/>
</button>
</header>
<div class="emojis" ref="emojis" @scroll.passive="onScroll">
<section v-for="category in categories" :ref="category.ref">
<header><fa :icon="category.icon" fixed-width/> {{ category.text }}</header>
<div v-if="category.name">
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === category.name)"
:title="emoji[0]"
@click="chosen(emoji[1].char)"
>
<mk-emoji :emoji="emoji[1].char"/>
</button>
</div>
<div v-else>
<button v-for="emoji in customEmojis"
:title="emoji.name"
@click="chosen(`:${emoji.name}:`)"
>
<img :src="emoji.url" :alt="emoji.name"/>
</button>
</div>
</section>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import { lib } from 'emojilib';
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice } from '@fortawesome/free-solid-svg-icons';
import { faHeart, faFlag } from '@fortawesome/free-regular-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/components/emoji-picker.vue'),
data() {
return {
lib,
customEmojis: [],
categories: [{
ref: 'customEmojiSection',
text: this.$t('custom-emoji'),
icon: faAsterisk,
isActive: true
}, {
name: 'people',
ref: 'peopleSection',
text: this.$t('people'),
icon: ['far', 'laugh'],
isActive: false
}, {
name: 'animals_and_nature',
ref: 'animalsAndNatureSection',
text: this.$t('animals-and-nature'),
icon: faLeaf,
isActive: false
}, {
name: 'food_and_drink',
ref: 'foodAndDrinkSection',
text: this.$t('food-and-drink'),
icon: faUtensils,
isActive: false
}, {
name: 'activity',
ref: 'activitySection',
text: this.$t('activity'),
icon: faFutbol,
isActive: false
}, {
name: 'travel_and_places',
ref: 'travelAndPlacesSection',
text: this.$t('travel-and-places'),
icon: faCity,
isActive: false
}, {
name: 'objects',
ref: 'objectsSection',
text: this.$t('objects'),
icon: faDice,
isActive: false
}, {
name: 'symbols',
ref: 'symbolsSection',
text: this.$t('symbols'),
icon: faHeart,
isActive: false
}, {
name: 'flags',
ref: 'flagsSection',
text: this.$t('flags'),
icon: faFlag,
isActive: false
}]
}
},
created() {
this.customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
},
methods: {
go(ref) {
this.$refs.emojis.scrollTop = this.$refs[ref][0].offsetTop;
},
onScroll(e) {
const section = this.categories.forEach(x => {
const top = e.target.scrollTop;
const el = this.$refs[x.ref][0];
x.isActive = el.offsetTop <= top && el.offsetTop + el.offsetHeight > top;
});
},
chosen(emoji) {
this.$emit('chosen', emoji);
}
}
});
</script>
<style lang="stylus" scoped>
.prlncendiewqqkrevzeruhndoakghvtx
width 350px
background var(--face)
> header
display flex
> button
flex 1
padding 10px 0
font-size 16px
color var(--text)
transition color 0.2s ease
&:hover
color var(--textHighlighted)
transition color 0s
&.active
color var(--primary)
transition color 0s
> .emojis
height 300px
overflow-y auto
overflow-x hidden
> section
> header
position sticky
top 0
left 0
z-index 1
padding 8px
background var(--faceHeader)
color var(--text)
font-size 12px
> div
display grid
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
gap 4px
padding 8px
> button
padding 0
width 100%
&:before
content ''
display block
width 1px
height 0
padding-bottom 100%
&:hover
> *
transform scale(1.2)
transition transform 0s
> *
position absolute
top 0
left 0
width 100%
height 100%
font-size 28px
transition transform 0.2s ease
pointer-events none
</style>

View File

@ -7,7 +7,8 @@
<script lang="ts">
import Vue from 'vue';
import { lib } from 'emojilib';
// スクリプトサイズがデカい
//import { lib } from 'emojilib';
export default Vue.extend({
props: {
@ -21,7 +22,7 @@ export default Vue.extend({
},
customEmojis: {
required: false,
default: []
default: () => []
}
},
@ -50,10 +51,10 @@ export default Vue.extend({
this.customEmoji = customEmoji;
this.url = customEmoji.url;
} else {
const emoji = lib[this.name];
if (emoji) {
this.char = emoji.char;
}
//const emoji = lib[this.name];
//if (emoji) {
// this.char = emoji.char;
//}
}
} else {
this.char = this.emoji;

View File

@ -0,0 +1,184 @@
<template>
<button class="wfliddvnhxvyusikowhxozkyxyenqxqr"
:class="{ wait, block, mini, active: isFollowing || hasPendingFollowRequestFromYou }"
@click="onClick"
:disabled="wait"
>
<template v-if="!wait">
<fa :icon="iconAndText[0]"/> <template v-if="!mini">{{ iconAndText[1] }}</template>
</template>
<template v-else><fa icon="spinner" pulse fixed-width/></template>
</button>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('common/views/components/follow-button.vue'),
props: {
user: {
type: Object,
required: true
},
block: {
type: Boolean,
required: false,
default: false
},
mini: {
type: Boolean,
required: false,
default: false
}
},
data() {
return {
isFollowing: this.user.isFollowing,
hasPendingFollowRequestFromYou: this.user.hasPendingFollowRequestFromYou,
wait: false,
connection: null
};
},
computed: {
iconAndText(): any[] {
return (
(this.hasPendingFollowRequestFromYou && this.user.isLocked) ? ['hourglass-half', this.$t('request-pending')] :
(this.hasPendingFollowRequestFromYou && !this.user.isLocked) ? ['hourglass-start', this.$t('follow-processing')] :
(this.isFollowing) ? ['minus', this.$t('following')] :
(!this.isFollowing && this.user.isLocked) ? ['plus', this.$t('follow-request')] :
(!this.isFollowing && !this.user.isLocked) ? ['plus', this.$t('follow')] :
[]
);
}
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onFollowChange(user) {
if (user.id == this.user.id) {
this.isFollowing = user.isFollowing;
this.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
}
},
async onClick() {
this.wait = true;
try {
if (this.isFollowing) {
await this.$root.api('following/delete', {
userId: this.user.id
});
} else {
if (this.hasPendingFollowRequestFromYou) {
await this.$root.api('following/requests/cancel', {
userId: this.user.id
});
} else if (this.user.isLocked) {
await this.$root.api('following/create', {
userId: this.user.id
});
this.hasPendingFollowRequestFromYou = true;
} else {
await this.$root.api('following/create', {
userId: this.user.id
});
this.hasPendingFollowRequestFromYou = true;
}
}
} catch (e) {
console.error(e);
} finally {
this.wait = false;
}
}
}
});
</script>
<style lang="stylus" scoped>
.wfliddvnhxvyusikowhxozkyxyenqxqr
display block
user-select none
cursor pointer
padding 0 16px
margin 0
min-width 100px
line-height 36px
font-size 14px
font-weight bold
color var(--primary)
background transparent
outline none
border solid 1px var(--primary)
border-radius 36px
&.mini
padding 0
min-width 0
width 32px
height 32px
font-size 16px
border-radius 4px
line-height 32px
&:focus
&:after
border-radius 8px
&.block
width 100%
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid var(--primaryAlpha03)
border-radius 36px
&:hover
background var(--primaryAlpha01)
&:active
background var(--primaryAlpha02)
&.active
color var(--primaryForeground)
background var(--primary)
&:hover
background var(--primaryLighten10)
border-color var(--primaryLighten10)
&:active
background var(--primaryDarken10)
border-color var(--primaryDarken10)
&.wait
cursor wait !important
opacity 0.7
*
pointer-events none
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="mk-github-setting">
<p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-github`" target="_blank">{{ $t('detail') }}</a></p>
<p>{{ $t('description') }}</p>
<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">{{ $t('connected-to') }}: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
<p>
<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? this.$t('reconnect') : this.$t('connect') }}</a>
@ -14,15 +14,14 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, docsUrl } from '../../../config';
import { apiUrl } from '../../../config';
export default Vue.extend({
i18n: i18n('common/views/components/github-setting.vue'),
data() {
return {
form: null,
apiUrl,
docsUrl
apiUrl
};
},
mounted() {

View File

@ -1,13 +1,8 @@
import Vue from 'vue';
import muteAndBlock from './mute-and-block.vue';
import followButton from './follow-button.vue';
import error from './error.vue';
import apiSettings from './api-settings.vue';
import passwordSettings from './password-settings.vue';
import driveSettings from './drive-settings.vue';
import profileEditor from './profile-editor.vue';
import noteSkeleton from './note-skeleton.vue';
import theme from './theme.vue';
import instance from './instance.vue';
import cwButton from './cw-button.vue';
import tagCloud from './tag-cloud.vue';
@ -27,13 +22,10 @@ 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 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 githubSetting from './github-setting.vue';
@ -53,14 +45,9 @@ import uiInfo from './ui/info.vue';
import formButton from './ui/form/button.vue';
import formRadio from './ui/form/radio.vue';
Vue.component('mk-mute-and-block', muteAndBlock);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-error', error);
Vue.component('mk-api-settings', apiSettings);
Vue.component('mk-password-settings', passwordSettings);
Vue.component('mk-drive-settings', driveSettings);
Vue.component('mk-profile-editor', profileEditor);
Vue.component('mk-note-skeleton', noteSkeleton);
Vue.component('mk-theme', theme);
Vue.component('mk-instance', instance);
Vue.component('mk-cw-button', cwButton);
Vue.component('mk-tag-cloud', tagCloud);
@ -80,13 +67,10 @@ 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-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-github-setting', githubSetting);

View File

@ -8,7 +8,7 @@
<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>{{ $t('empty') }}</p>
<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>{{ $t('no-history') }}</p>
<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('@.loading') : $t('@.load-more') }}
</button>
<template v-for="(message, i) in _messages">
<x-message :message="message" :key="message.id"/>
@ -20,7 +20,7 @@
<footer>
<transition name="fade">
<div class="new-message" v-show="showIndicator">
<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>{{ $t('new-message') }}</button>
<button @click="onIndicatorClick"><i><fa :icon="faArrowCircleDown"/></i>{{ $t('new-message') }}</button>
</div>
</transition>
<x-form :user="user" ref="form"/>
@ -34,6 +34,7 @@ import i18n from '../../../i18n';
import XMessage from './messaging-room.message.vue';
import XForm from './messaging-room.form.vue';
import { url } from '../../../config';
import { faArrowCircleDown } from '@fortawesome/free-solid-svg-icons';
export default Vue.extend({
i18n: i18n('common/views/components/messaging-room.vue'),
@ -52,7 +53,8 @@ export default Vue.extend({
existMoreMessages: false,
connection: null,
showIndicator: false,
timer: null
timer: null,
faArrowCircleDown
};
},

View File

@ -45,7 +45,7 @@
</template>
</div>
<p class="no-history" v-if="!fetching && messages.length == 0">{{ $t('no-history') }}</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
</div>
</template>

View File

@ -21,6 +21,14 @@
</div>
</div>
</section>
<section>
<header>{{ $t('word-mute') }}</header>
<ui-textarea v-model="mutedWords">
{{ $t('muted-words') }}<span slot="desc">{{ $t('muted-words-description') }}</span>
</ui-textarea>
<ui-button @click="save">{{ $t('save') }}</ui-button>
</section>
</ui-card>
</template>
@ -30,16 +38,27 @@ import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('common/views/components/mute-and-block.vue'),
data() {
return {
muteFetching: true,
blockFetching: true,
mute: [],
block: []
block: [],
mutedWords: ''
};
},
computed: {
_mutedWords: {
get() { return this.$store.state.settings.mutedWords; },
set(value) { this.$store.dispatch('settings/set', { key: 'mutedWords', value }); }
},
},
mounted() {
this.mutedWords = this._mutedWords.map(words => words.join(' ')).join('\n');
this.$root.api('mute/list').then(mute => {
this.mute = mute.map(x => x.mutee);
this.muteFetching = false;
@ -49,6 +68,12 @@ export default Vue.extend({
this.block = blocking.map(x => x.blockee);
this.blockFetching = false;
});
},
methods: {
save() {
this._mutedWords = this.mutedWords.split('\n').map(line => line.split(' '));
}
}
});
</script>

View File

@ -9,7 +9,6 @@ import Vue from 'vue';
import i18n from '../../../i18n';
import { url } from '../../../config';
import copyToClipboard from '../../../common/scripts/copy-to-clipboard';
import Ok from './ok.vue';
import { concat, intersperse } from '../../../../../prelude/array';
export default Vue.extend({
@ -56,7 +55,7 @@ export default Vue.extend({
}
] : []
], [
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin ? [{
this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [{
icon: ['far', 'trash-alt'],
text: this.$t('delete'),
action: this.del
@ -79,7 +78,10 @@ export default Vue.extend({
this.$root.api('i/pin', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
this.destroyDom();
});
},
@ -93,11 +95,18 @@ export default Vue.extend({
},
del() {
if (!window.confirm(this.$t('delete-confirm'))) return;
this.$root.api('notes/delete', {
noteId: this.note.id
}).then(() => {
this.destroyDom();
this.$root.alert({
type: 'warning',
text: this.$t('delete-confirm'),
showCancelButton: true
}).then(res => {
if (!res) return;
this.$root.api('notes/delete', {
noteId: this.note.id
}).then(() => {
this.destroyDom();
});
});
},
@ -105,7 +114,10 @@ export default Vue.extend({
this.$root.api('notes/favorites/create', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
this.destroyDom();
});
},
@ -114,7 +126,10 @@ export default Vue.extend({
this.$root.api('notes/favorites/delete', {
noteId: this.note.id
}).then(() => {
this.$root.new(Ok);
this.$root.alert({
type: 'success',
splash: true
});
this.destroyDom();
});
},

View File

@ -1,175 +0,0 @@
<template>
<div class="yvbkymdqeusiqucuuloahhiqflzinufs">
<div class="bg" ref="bg"></div>
<div class="body" ref="body">
<div class="icon">
<div class="circle left"></div>
<span class="check tip"></span>
<span class="check long"></span>
<div class="ring"></div>
<div class="fix"></div>
<div class="circle right"></div>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
export default Vue.extend({
mounted() {
this.$nextTick(() => {
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 300,
easing: 'linear'
});
anime({
targets: this.$refs.body,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
});
setTimeout(() => {
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
anime({
targets: this.$refs.body,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [0.5, 0, 1, 0.5],
complete: () => this.destroyDom()
});
}, 1250);
}
});
</script>
<style lang="stylus" scoped>
.yvbkymdqeusiqucuuloahhiqflzinufs
pointer-events none
> .bg
display block
position fixed
z-index 10000
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
> .body
position fixed
z-index 10000
top 0
right 0
left 0
bottom 0
margin auto
width 150px
height 150px
background var(--face)
border-radius 8px
opacity 0
> .icon
display flex
justify-content center
position absolute
top 0
right 0
left 0
bottom 0
width 5em
height 5em
margin auto
border .25em solid transparent
border-radius 50%
line-height 5em
cursor default
box-sizing content-box
user-select none
zoom normal
border-color #a5dc86
> .circle
position absolute
width 3.75em
height 7.5em
transform rotate(45deg)
border-radius 50%
background var(--face)
&.left
top -.4375em
left -2.0635em
transform rotate(-45deg)
transform-origin 3.75em 3.75em
border-radius 7.5em 0 0 7.5em
&.right
top -.6875em
left 1.875em
transform rotate(-45deg)
transform-origin 0 3.75em
border-radius 0 7.5em 7.5em 0
animation swal2-rotate-success-circular-line 4.25s ease-in
> .check
display block
position absolute
height .3125em
border-radius .125em
background-color #a5dc86
z-index 2
&.tip
top 2.875em
left .875em
width 1.5625em
transform rotate(45deg)
animation swal2-animate-success-line-tip .75s
&.long
top 2.375em
right .5em
width 2.9375em
transform rotate(-45deg)
animation swal2-animate-success-line-long .75s
> .fix
position absolute
top .5em
left 1.625em
width .4375em
height 5.625em
transform rotate(-45deg)
z-index 1
background var(--face)
> .ring
position absolute
top -.25em
left -.25em
width 100%
height 100%
border .25em solid rgba(165,220,134,.3)
border-radius 50%
z-index 2
box-sizing content-box
</style>

View File

@ -25,12 +25,9 @@ export default Vue.extend({
type: 'password'
}).then(newPassword2 => {
if (newPassword !== newPassword2) {
this.$dialog({
this.$root.alert({
title: null,
text: this.$t('not-match'),
actions: [{
text: 'OK'
}]
text: this.$t('not-match')
});
return;
}

View File

@ -73,12 +73,13 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, host } from '../../../config';
import { toUnicode } from 'punycode';
export default Vue.extend({
i18n: i18n('common/views/components/profile-editor.vue'),
data() {
return {
host,
host: toUnicode(host),
name: null,
username: null,
location: null,
@ -192,7 +193,7 @@ export default Vue.extend({
this.$store.state.i.bannerUrl = i.bannerUrl;
if (notify) {
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});
@ -222,6 +223,5 @@ export default Vue.extend({
width 72px
height 72px
margin auto
box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
</style>

View File

@ -21,6 +21,7 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, host } from '../../../config';
import { toUnicode } from 'punycode';
export default Vue.extend({
i18n: i18n('common/views/components/signin.vue'),
@ -39,7 +40,7 @@ export default Vue.extend({
password: '',
token: '',
apiUrl,
host
host: toUnicode(host)
};
},
methods: {

View File

@ -10,7 +10,7 @@
<span>{{ $t('username') }}</span>
<span slot="prefix">@</span>
<span slot="suffix">@{{ host }}</span>
<p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> {{ $t('checking') }}</p>
<p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner" pulse fixed-width/> {{ $t('checking') }}</p>
<p slot="desc" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> {{ $t('available') }}</p>
<p slot="desc" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('unavailable') }}</p>
<p slot="desc" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> {{ $t('error') }}</p>
@ -46,12 +46,13 @@ import Vue from 'vue';
import i18n from '../../../i18n';
const getPasswordStrength = require('syuilo-password-strength');
import { host, url } from '../../../config';
import { toUnicode } from 'punycode';
export default Vue.extend({
i18n: i18n('common/views/components/signup.vue'),
data() {
return {
host,
host: toUnicode(host),
username: '',
password: '',
retypedPassword: '',

View File

@ -1,11 +1,11 @@
<template>
<div class="mk-stream-indicator">
<p v-if="stream.state == 'initializing'">
<fa icon="spinner .pulse"/>
<fa icon="spinner" pulse/>
<span>{{ $t('connecting') }}<mk-ellipsis/></span>
</p>
<p v-if="stream.state == 'reconnecting'">
<fa icon="spinner .pulse"/>
<fa icon="spinner" pulse/>
<span>{{ $t('reconnecting') }}<mk-ellipsis/></span>
</p>
<p v-if="stream.state == 'connected'">

View File

@ -1,6 +1,6 @@
<template>
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
<div v-else>
<vue-word-cloud

View File

@ -1,7 +1,7 @@
<template>
<div class="nicnklzforebnpfgasiypmpdaaglujqm">
<label>
<span>{{ $t('light-theme') }}</span>
<span><fa :icon="faSun"/> {{ $t('light-theme') }}</span>
<ui-select v-model="light" :placeholder="$t('light-theme')">
<optgroup :label="$t('light-themes')">
<option v-for="x in lightThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@ -13,7 +13,7 @@
</label>
<label>
<span>{{ $t('dark-theme') }}</span>
<span><fa :icon="faMoon"/> {{ $t('dark-theme') }}</span>
<ui-select v-model="dark" :placeholder="$t('dark-theme')">
<optgroup :label="$t('dark-themes')">
<option v-for="x in darkThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
@ -104,6 +104,7 @@ import { Chrome } from 'vue-color';
import * as uuid from 'uuid';
import * as tinycolor from 'tinycolor2';
import * as JSON5 from 'json5';
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
// 後方互換性のため
function convertOldThemedefinition(t) {
@ -135,7 +136,8 @@ export default Vue.extend({
myThemeDesc: '',
myThemePrimary: lightTheme.vars.primary,
myThemeSecondary: lightTheme.vars.secondary,
myThemeText: lightTheme.vars.text
myThemeText: lightTheme.vars.text,
faMoon, faSun
};
},
@ -221,7 +223,7 @@ export default Vue.extend({
try {
theme = JSON5.parse(code);
} catch (e) {
this.$swal({
this.$root.alert({
type: 'error',
text: this.$t('invalid-theme')
});
@ -234,7 +236,7 @@ export default Vue.extend({
}
if (theme.id == null) {
this.$swal({
this.$root.alert({
type: 'error',
text: this.$t('invalid-theme')
});
@ -242,7 +244,7 @@ export default Vue.extend({
}
if (this.$store.state.device.themes.some(t => t.id == theme.id)) {
this.$swal({
this.$root.alert({
type: 'info',
text: this.$t('already-installed')
});
@ -254,7 +256,7 @@ export default Vue.extend({
key: 'themes', value: themes
});
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('installed').replace('{}', theme.name)
});
@ -267,7 +269,7 @@ export default Vue.extend({
key: 'themes', value: themes
});
this.$swal({
this.$root.alert({
type: 'info',
text: this.$t('uninstalled').replace('{}', theme.name)
});
@ -304,7 +306,7 @@ export default Vue.extend({
const theme = this.myTheme;
if (theme.name == null || theme.name.trim() == '') {
this.$swal({
this.$root.alert({
type: 'warning',
text: this.$t('theme-name-required')
});
@ -318,7 +320,7 @@ export default Vue.extend({
key: 'themes', value: themes
});
this.$swal({
this.$root.alert({
type: 'success',
text: this.$t('saved')
});

View File

@ -1,49 +0,0 @@
<template>
<time class="mk-time">
{{ hh }}:{{ mm }}:{{ ss }}
</time>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: {
time: {
type: [Date, String],
required: true
}
},
data() {
return {
tickId: null,
hh: null,
mm: null,
ss: null
};
},
computed: {
_time(): Date {
return typeof this.time == 'string' ? new Date(this.time) : this.time;
}
},
created() {
this.tick();
this.tickId = setInterval(this.tick, 1000);
},
destroyed() {
clearInterval(this.tickId);
},
methods: {
tick() {
const now = new Date().getTime();
const start = this._time.getTime();
const ago = Math.floor((now - start) / 1000);
this.hh = Math.floor(ago / (60 * 60)).toString().padStart(2, '0');
this.mm = Math.floor(ago / 60).toString().padStart(2, '0');
this.ss = (ago % 60).toString().padStart(2, '0');
}
}
});
</script>

View File

@ -1,6 +1,6 @@
<template>
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>{{ $t('empty') }}</p>
<!-- トランジションを有効にするとなぜかメモリリークする -->
<transition-group v-else tag="div" name="chart">

View File

@ -1,6 +1,6 @@
<template>
<div class="mk-twitter-setting">
<p>{{ $t('description') }}<a :href="`${docsUrl}/link-to-twitter`" target="_blank">{{ $t('detail') }}</a></p>
<p>{{ $t('description') }}</p>
<p class="account" v-if="$store.state.i.twitter" :title="`Twitter ID: ${$store.state.i.twitter.userId}`">{{ $t('connected-to') }}: <a :href="`https://twitter.com/${$store.state.i.twitter.screenName}`" target="_blank">@{{ $store.state.i.twitter.screenName }}</a></p>
<p>
<a :href="`${apiUrl}/connect/twitter`" target="_blank" @click.prevent="connect">{{ $store.state.i.twitter ? this.$t('reconnect') : this.$t('connect') }}</a>
@ -14,15 +14,14 @@
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
import { apiUrl, docsUrl } from '../../../config';
import { apiUrl } from '../../../config';
export default Vue.extend({
i18n: i18n('common/views/components/twitter-setting.vue'),
data() {
return {
form: null,
apiUrl,
docsUrl
apiUrl
};
},
mounted() {

View File

@ -38,12 +38,24 @@ export default Vue.extend({
type: Boolean,
required: false,
default: false
}
},
autofocus: {
type: Boolean,
required: false,
default: false
},
},
data() {
return {
styl: 'fill'
};
},
mounted() {
if (this.autofocus) {
this.$nextTick(() => {
this.$el.focus();
});
}
}
});
</script>
@ -57,6 +69,7 @@ export default Vue.extend({
text-align center
font-weight normal
font-size 16px
line-height 24px
border none
border-radius 6px
outline none
@ -85,6 +98,7 @@ export default Vue.extend({
&.inline
display inline-block
width auto
min-width 100px
&.primary
font-weight bold

View File

@ -1,5 +1,5 @@
<template>
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze" :class="{ inputs }">
<div class="vnxwkwuf" :class="{ inputs, noGrow }">
<slot></slot>
</div>
</template>
@ -15,21 +15,27 @@ export default Vue.extend({
type: Boolean,
required: false,
default: false
},
noGrow: {
type: Boolean,
required: false,
default: false
}
}
});
</script>
<style lang="stylus" scoped>
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
display flex
.vnxwkwuf
&.inputs
margin 32px 0
> *
flex 1
&:not(.noGrow)
display flex
&:not(:last-child)
margin-right 16px
> *
flex 1
> *:not(:last-child)
margin-right 16px
</style>

View File

@ -141,6 +141,7 @@ root(fill)
> .desc
margin 6px 0
font-size 13px
opacity 0.7
*
margin 0

View File

@ -3,7 +3,7 @@
<ol v-if="uploads.length > 0">
<li v-for="ctx in uploads" :key="ctx.id">
<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
<p class="name"><fa icon="spinner" pulse/>{{ ctx.name }}</p>
<p class="status">
<span class="initing" v-if="ctx.progress == undefined">{{ $t('waiting') }}<mk-ellipsis/></span>
<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
@ -57,17 +57,11 @@ export default Vue.extend({
return;
}
// Upload if the file didn't exist yet
const buf = new Uint8Array(e.target.result);
let bin = '';
// We use for-of loop instead of apply() to avoid RangeError
// SEE: https://stackoverflow.com/questions/9267899/arraybuffer-to-base64-encoded-string
for (const byte of buf) bin += String.fromCharCode(byte);
const ctx = {
id: id,
name: file.name || 'untitled',
progress: undefined,
img: 'data:*/*;base64,' + btoa(bin)
img: window.URL.createObjectURL(file)
};
this.uploads.push(ctx);

View File

@ -1,5 +1,4 @@
import * as getCaretCoordinates from 'textarea-caret';
import MkAutocomplete from '../components/autocomplete.vue';
import { toASCII } from 'punycode';
export default {
@ -123,7 +122,7 @@ class Autocomplete {
/**
* サジェストを提示します。
*/
private open(type, q) {
private async open(type, q) {
if (type != this.currentType) {
this.close();
}
@ -143,6 +142,8 @@ class Autocomplete {
this.suggestion.y = y;
this.suggestion.q = q;
} else {
const MkAutocomplete = await import('../components/autocomplete.vue').then(m => m.default);
// サジェスト要素作成
this.suggestion = new MkAutocomplete({
parent: this.vm,

View File

@ -25,7 +25,7 @@
<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
<template v-else><fa icon="spinner" pulse fixed-width/></template>
</button>
</div>
</template>

View File

@ -3,9 +3,15 @@
<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
<template slot="header"><fa icon="camera"/>{{ $t('title') }}</template>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div :class="$style.stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
<div v-for="image in images"
:class="$style.img"
:style="`background-image: url(${image.thumbnailUrl || image.url})`"
draggable="true"
@dragstart="onDragstart(image, $event)"
@dragend="onDragend"
></div>
</div>
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
</mk-widget-container>
@ -31,6 +37,7 @@ export default define({
connection: null
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
@ -44,9 +51,11 @@ export default define({
this.fetching = false;
});
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onDriveFileCreated(file) {
if (/^image\/.+$/.test(file.type)) {
@ -54,6 +63,7 @@ export default define({
if (this.images.length > 9) this.images.pop();
}
},
func() {
if (this.props.design == 2) {
this.props.design = 0;
@ -62,7 +72,16 @@ export default define({
}
this.save();
}
},
onDragstart(file, e) {
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('mk_drive_file', JSON.stringify(file));
},
onDragend(e) {
this.browser.isDragSource = false;
},
}
});
</script>

View File

@ -5,7 +5,7 @@
<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<div class="feed" v-else>
<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
</div>

View File

@ -2,7 +2,7 @@
<div class="memory">
<x-pie class="pie" :value="usage"/>
<div>
<p><fa icon="flask"/>Memory</p>
<p><fa icon="memory"/>Memory</p>
<p>Total: {{ total | bytes(1) }}</p>
<p>Used: {{ used | bytes(1) }}</p>
<p>Free: {{ free | bytes(1) }}</p>

View File

@ -4,7 +4,7 @@
<template slot="header"><fa icon="server"/>{{ $t('title') }}</template>
<button slot="func" @click="toggle" :title="$t('toggle')"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-if="!fetching">
<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>

View File

@ -1,6 +1,4 @@
declare const _LANG_: string;
declare const _LANGS_: string;
declare const _LOCALE_: { [key: string]: any };
declare const _LANGS_: string[];
declare const _THEME_COLOR_: string;
declare const _COPYRIGHT_: string;
declare const _VERSION_: string;
@ -15,9 +13,9 @@ export const hostname = address.hostname;
export const url = address.origin;
export const apiUrl = url + '/api';
export const wsUrl = url.replace('http://', 'ws://').replace('https://', 'wss://') + '/streaming';
export const lang = _LANG_;
export const lang = window.lang;
export const langs = _LANGS_;
export const locale = _LOCALE_;
export const locale = JSON.parse(localStorage.getItem('locale'));
export const themeColor = _THEME_COLOR_;
export const copyright = _COPYRIGHT_;
export const version = _VERSION_;

View File

@ -8,12 +8,9 @@ export default ($root: any) => {
const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
if (!regex.test(file.name) ) {
$root.$dialog({
$root.alert({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return reject('invalid-filetype');
}
@ -90,12 +87,9 @@ export default ($root: any) => {
value: i.avatarUrl
});
$root.$dialog({
$root.alert({
title: '%fa:info-circle% %i18n:desktop.avatar-updated%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return i;

View File

@ -10,10 +10,7 @@ export default ($root: any) => {
if (!regex.test(file.name) ) {
$root.dialog({
title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return reject('invalid-filetype');
}
@ -90,12 +87,9 @@ export default ($root: any) => {
value: i.bannerUrl
});
$root.$dialog({
$root.alert({
title: '%fa:info-circle% %i18n:desktop.banner-updated%',
text: null,
actions: [{
text: '%i18n:common.got-it%'
}]
text: null
});
return i;

View File

@ -34,7 +34,6 @@ import PostFormWindow from './views/components/post-form-window.vue';
import RenoteFormWindow from './views/components/renote-form-window.vue';
import MkChooseFileFromDriveWindow from './views/components/choose-file-from-drive-window.vue';
import MkChooseFolderFromDriveWindow from './views/components/choose-folder-from-drive-window.vue';
import Dialog from './views/components/dialog.vue';
import InputDialog from './views/components/input-dialog.vue';
import Notification from './views/components/ui-notification.vue';
@ -114,21 +113,6 @@ init(async (launch) => {
});
},
$dialog(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};
const d = this.$root.new(Dialog, {
title: o.title,
text: o.text,
modal: o.modal,
buttons: o.actions
});
d.$once('clicked', id => {
res(id);
});
});
},
$input(opts) {
return new Promise<string>((res, rej) => {
const o = opts || {};

View File

@ -4,7 +4,7 @@
<template slot="header"><fa icon="chart-bar"/>{{ $t('title') }}</template>
<button slot="func" :title="$t('toggle')" @click="toggle"><fa icon="sort"/></button>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
<template v-else>
<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
<x-chart v-show="view == 1" :data="[].concat(activity)"/>

View File

@ -5,7 +5,7 @@
<span :class="$style.count" v-if="multiple && files.length > 0">({{ $t('chosen-files', { count: files.length }) }})</span>
</span>
<mk-drive
<x-drive
ref="browser"
:class="$style.browser"
:multiple="multiple"
@ -25,6 +25,9 @@ import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/choose-file-from-drive-window.vue'),
components: {
XDrive: () => import('./drive.vue').then(m => m.default),
},
props: {
multiple: {
default: false

View File

@ -4,7 +4,7 @@
<span :class="$style.title">{{ $t('choose-prompt') }}</span>
</span>
<mk-drive
<x-drive
ref="browser"
:class="$style.browser"
:multiple="false"
@ -21,6 +21,9 @@ import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/choose-folder-from-drive-window.vue'),
components: {
XDrive: () => import('./drive.vue').then(m => m.default),
},
methods: {
ok() {
this.$emit('selected', (this.$refs.browser as any).folder);

View File

@ -1,168 +0,0 @@
<template>
<div class="mk-dialog">
<div class="bg" ref="bg" @click="onBgClick"></div>
<div class="main" ref="main">
<header v-html="title" :class="$style.header"></header>
<div class="body" v-html="text"></div>
<div class="buttons">
<button v-for="button in buttons" @click="click(button)">{{ button.text }}</button>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import * as anime from 'animejs';
export default Vue.extend({
props: {
title: {
type: String,
required: false
},
text: {
type: String,
required: true
},
buttons: {
type: Array,
default: () => {
return [{
text: 'OK'
}];
}
},
modal: {
type: Boolean,
default: false
}
},
mounted() {
this.$nextTick(() => {
(this.$refs.bg as any).style.pointerEvents = 'auto';
anime({
targets: this.$refs.bg,
opacity: 1,
duration: 100,
easing: 'linear'
});
anime({
targets: this.$refs.main,
opacity: 1,
scale: [1.2, 1],
duration: 300,
easing: [0, 0.5, 0.5, 1]
});
});
},
methods: {
click(button) {
this.$emit('clicked', button.id);
this.close();
},
close() {
(this.$refs.bg as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.bg,
opacity: 0,
duration: 300,
easing: 'linear'
});
(this.$refs.main as any).style.pointerEvents = 'none';
anime({
targets: this.$refs.main,
opacity: 0,
scale: 0.8,
duration: 300,
easing: [ 0.5, -0.5, 1, 0.5 ],
complete: () => this.destroyDom()
});
},
onBgClick() {
if (!this.modal) {
this.close();
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-dialog
> .bg
display block
position fixed
z-index 8192
top 0
left 0
width 100%
height 100%
background rgba(#000, 0.7)
opacity 0
pointer-events none
> .main
display block
position fixed
z-index 8192
top 20%
left 0
right 0
margin 0 auto 0 auto
padding 32px 42px
width 480px
background #fff
opacity 0
> .body
margin 1em 0
color #888
> .buttons
> button
display inline-block
float right
margin 0
padding 10px 10px
font-size 1.1em
font-weight normal
text-decoration none
color #888
background transparent
outline none
border none
border-radius 0
cursor pointer
transition color 0.1s ease
i
margin 0 0.375em
&:hover
color var(--primary)
&:active
color var(--primaryDarken10)
transition color 0s ease
</style>
<style lang="stylus" module>
.header
margin 1em 0
color var(--primary)
// color #43A4EC
font-weight bold
&:empty
display none
> i
margin-right 0.5em
</style>

View File

@ -4,7 +4,7 @@
<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> {{ $t('used') }}</p>
<span :class="$style.title"><fa icon="cloud"/>{{ $t('@.drive') }}</span>
</template>
<mk-drive :class="$style.browser" multiple :init-folder="folder" ref="browser"/>
<x-drive :class="$style.browser" multiple :init-folder="folder" ref="browser"/>
</mk-window>
</template>
@ -15,6 +15,9 @@ import { url } from '../../../config';
export default Vue.extend({
i18n: i18n('desktop/views/components/drive-window.vue'),
components: {
XDrive: () => import('./drive.vue').then(m => m.default),
},
props: ['folder'],
data() {
return {

View File

@ -170,12 +170,9 @@ export default Vue.extend({
copyUrl() {
copyToClipboard(this.file.url);
this.$dialog({
this.$root.alert({
title: this.$t('contextmenu.copied'),
text: this.$t('contextmenu.copied-url-to-clipboard'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('contextmenu.copied-url-to-clipboard')
});
},

View File

@ -155,12 +155,9 @@ export default Vue.extend({
}).catch(err => {
switch (err) {
case 'detected-circular-definition':
this.$dialog({
this.$root.alert({
title: this.$t('unable-to-process'),
text: this.$t('circular-reference-detected'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('circular-reference-detected')
});
break;
default:

View File

@ -313,16 +313,13 @@ export default Vue.extend({
}).catch(err => {
switch (err) {
case 'detected-circular-definition':
this.$dialog({
this.$root.alert({
title: this.$t('unable-to-process'),
text: this.$t('circular-reference-detected'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('circular-reference-detected')
});
break;
default:
alert(`%i18n:@unhandled-error% ${err}`);
alert(this.$t('unhandled-error'));
}
});
}
@ -343,12 +340,9 @@ export default Vue.extend({
folderId: this.folder ? this.folder.id : undefined
});
this.$dialog({
this.$root.alert({
title: this.$t('url-upload-requested'),
text: this.$t('may-take-time'),
actions: [{
text: this.$t('@.ok')
}]
text: this.$t('may-take-time')
});
});
},

View File

@ -0,0 +1,84 @@
<template>
<div class="gcafiosrssbtbnbzqupfmglvzgiaipyv">
<x-picker @chosen="chosen"/>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import contains from '../../../common/scripts/contains';
export default Vue.extend({
components: {
XPicker: () => import('../../../common/views/components/emoji-picker.vue').then(m => m.default)
},
props: {
x: {
type: Number,
required: true
},
y: {
type: Number,
required: true
}
},
mounted() {
this.$nextTick(() => {
const width = this.$el.offsetWidth;
const height = this.$el.offsetHeight;
let x = this.x;
let y = this.y;
if (x + width - window.pageXOffset > window.innerWidth) {
x = window.innerWidth - width + window.pageXOffset;
}
if (y + height - window.pageYOffset > window.innerHeight) {
y = window.innerHeight - height + window.pageYOffset;
}
this.$el.style.left = x + 'px';
this.$el.style.top = y + 'px';
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.addEventListener('mousedown', this.onMousedown);
});
});
},
methods: {
onMousedown(e) {
e.preventDefault();
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
return false;
},
chosen(emoji) {
this.$emit('chosen', emoji);
this.close();
},
close() {
Array.from(document.querySelectorAll('body *')).forEach(el => {
el.removeEventListener('mousedown', this.onMousedown);
});
this.$emit('closed');
this.destroyDom();
}
}
});
</script>
<style lang="stylus" scoped>
.gcafiosrssbtbnbzqupfmglvzgiaipyv
position fixed
top 0
left 0
z-index 3000
box-shadow 0 2px 12px 0 rgba(0, 0, 0, 0.3)
</style>

View File

@ -1,157 +0,0 @@
<template>
<button class="mk-follow-button"
:class="{ wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou, big: size == 'big' }"
@click="onClick"
:disabled="wait"
>
<template v-if="!wait">
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> {{ $t('request-pending') }}</template></template>
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> {{ $t('follow-processing') }}</template></template>
<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> {{ $t('following') }}</template></template>
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow-request') }}</template></template>
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow') }}</template></template>
</template>
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
</button>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/follow-button.vue'),
props: {
user: {
type: Object,
required: true
},
size: {
type: String,
default: 'compact'
}
},
data() {
return {
u: this.user,
wait: false,
connection: null
};
},
mounted() {
this.connection = this.$root.stream.useSharedConnection('main');
this.connection.on('follow', this.onFollowChange);
this.connection.on('unfollow', this.onFollowChange);
},
beforeDestroy() {
this.connection.dispose();
},
methods: {
onFollowChange(user) {
if (user.id == this.u.id) {
this.u.isFollowing = user.isFollowing;
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
this.$forceUpdate();
}
},
async onClick() {
this.wait = true;
try {
if (this.u.isFollowing) {
this.u = await this.$root.api('following/delete', {
userId: this.u.id
});
} else {
if (this.u.hasPendingFollowRequestFromYou) {
this.u = await this.$root.api('following/requests/cancel', {
userId: this.u.id
});
} else if (this.u.isLocked) {
this.u = await this.$root.api('following/create', {
userId: this.u.id
});
} else {
this.u = await this.$root.api('following/create', {
userId: this.user.id
});
}
}
} catch (e) {
console.error(e);
} finally {
this.wait = false;
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-follow-button
display block
cursor pointer
padding 0
margin 0
width 32px
height 32px
font-size 1em
outline none
border-radius 4px
*
pointer-events none
&:focus
&:after
content ""
pointer-events none
position absolute
top -5px
right -5px
bottom -5px
left -5px
border 2px solid var(--primaryAlpha03)
border-radius 8px
&:not(.active)
color var(--primary)
border solid 1px var(--primary)
&:hover
background var(--primaryAlpha03)
&:active
background var(--primaryAlpha05)
&.active
color var(--primaryForeground)
background var(--primary)
border solid 1px var(--primary)
&:not(:disabled)
font-weight bold
&:hover:not(:disabled)
background var(--primaryLighten5)
border-color var(--primaryLighten5)
&:active:not(:disabled)
background var(--primaryDarken5)
border-color var(--primaryDarken5)
&.wait
cursor wait !important
opacity 0.7
&.big
width 100%
height 38px
line-height 38px
</style>

View File

@ -11,7 +11,7 @@
</div>
</div>
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('empty') }}</p>
<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
<p class="fetching" v-if="fetching"><fa icon="spinner" pulse fixed-width/>{{ $t('fetching') }}<mk-ellipsis/></p>
<a class="refresh" @click="refresh">{{ $t('refresh') }}</a>
<button class="close" @click="destroyDom()" :title="$t('title')"><fa icon="times"/></button>
</div>

View File

@ -13,7 +13,7 @@ import { url } from '../../../config';
export default Vue.extend({
i18n: i18n('desktop/views/components/game-window.vue'),
components: {
XReversi: () => import('../../../common/views/components/games/reversi/reversi.vue')
XReversi: () => import('../../../common/views/components/games/reversi/reversi.vue').then(m => m.default)
},
data() {
return {

View File

@ -186,12 +186,9 @@ export default Vue.extend({
methods: {
hint() {
this.$dialog({
this.$root.alert({
title: this.$t('@.customization-tips.title'),
text: this.$t('@.customization-tips.paragraph'),
actions: [{
text: this.$t('@.customization-tips.gotit')
}]
text: this.$t('@.customization-tips.paragraph')
});
},

View File

@ -14,11 +14,8 @@ import mediaVideo from './media-video.vue';
import notifications from './notifications.vue';
import noteForm from './post-form.vue';
import renoteForm from './renote-form.vue';
import followButton from './follow-button.vue';
import notePreview from './note-preview.vue';
import drive from './drive.vue';
import noteDetail from './note-detail.vue';
import settings from './settings.vue';
import calendar from './calendar.vue';
import activity from './activity.vue';
import friendsMaker from './friends-maker.vue';
@ -40,11 +37,8 @@ Vue.component('mk-media-video', mediaVideo);
Vue.component('mk-notifications', notifications);
Vue.component('mk-post-form', noteForm);
Vue.component('mk-renote-form', renoteForm);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-note-preview', notePreview);
Vue.component('mk-drive', drive);
Vue.component('mk-note-detail', noteDetail);
Vue.component('mk-settings', settings);
Vue.component('mk-calendar', calendar);
Vue.component('mk-activity', activity);
Vue.component('mk-friends-maker', friendsMaker);

View File

@ -1,7 +1,7 @@
<template>
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="comments"/>{{ $t('title') }} {{ user | userName }}</span>
<mk-messaging-room :user="user" :class="$style.content"/>
<x-messaging-room :user="user" :class="$style.content"/>
</mk-window>
</template>
@ -13,6 +13,9 @@ import getAcct from '../../../../../misc/acct/render';
export default Vue.extend({
i18n: i18n('desktop/views/components/messaging-room-window.vue'),
components: {
XMessagingRoom: () => import('../../../common/views/components/messaging-room.vue').then(m => m.default)
},
props: ['user'],
computed: {
popout(): string {

View File

@ -1,7 +1,7 @@
<template>
<mk-window ref="window" width="500px" height="560px" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="comments"/>{{ $t('title') }}</span>
<mk-messaging :class="$style.content" @navigate="navigate"/>
<x-messaging :class="$style.content" @navigate="navigate"/>
</mk-window>
</template>
@ -12,6 +12,9 @@ import MkMessagingRoomWindow from './messaging-room-window.vue';
export default Vue.extend({
i18n: i18n('desktop/views/components/messaging-window.vue'),
components: {
XMessaging: () => import('../../../common/views/components/messaging.vue').then(m => m.default)
},
methods: {
navigate(user) {
this.$root.new(MkMessagingRoomWindow, {

View File

@ -8,7 +8,7 @@
:disabled="conversationFetching"
>
<template v-if="!conversationFetching"><fa icon="ellipsis-v"/></template>
<template v-if="conversationFetching"><fa icon="spinner .pulse"/></template>
<template v-if="conversationFetching"><fa icon="spinner" pulse/></template>
</button>
<div class="conversation">
<x-sub v-for="note in conversation" :key="note.id" :note="note"/>

View File

@ -2,7 +2,7 @@
<div
class="note"
:class="{ mini }"
v-show="appearNote.deletedAt == null"
v-show="appearNote.deletedAt == null && !hideThisNote"
:tabindex="appearNote.deletedAt == null ? '-1' : null"
v-hotkey="keymap"
:title="title"

View File

@ -26,7 +26,7 @@
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('@.load-more') }}</template>
<template v-if="moreFetching"><fa icon="spinner .pulse" fixed-width/></template>
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
</button>
</footer>
</div>
@ -36,7 +36,7 @@
import Vue from 'vue';
import i18n from '../../../i18n';
import * as config from '../../../config';
import shouldMuteNote from '../../../common/scripts/should-mute-note';
import XNote from './note.vue';
const displayLimit = 30;
@ -119,28 +119,8 @@ export default Vue.extend({
},
prepend(note, silent = false) {
//#region 弾く
const isMyNote = note.userId == this.$store.state.i.id;
const isPureRenote = note.renoteId != null && note.text == null && note.fileIds.length == 0 && note.poll == null;
if (this.$store.state.settings.showMyRenotes === false) {
if (isMyNote && isPureRenote) {
return;
}
}
if (this.$store.state.settings.showRenotedMyNotes === false) {
if (isPureRenote && (note.renote.userId == this.$store.state.i.id)) {
return;
}
}
if (this.$store.state.settings.showLocalRenotes === false) {
if (isPureRenote && (note.renote.user.host == null)) {
return;
}
}
//#endregion
// 弾く
if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
if (document.hidden || !this.isScrollTop()) {

View File

@ -105,7 +105,7 @@
</component>
</div>
<button class="more" :class="{ fetching: fetchingMoreNotifications }" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
<template v-if="fetchingMoreNotifications"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
</button>
<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
</div>

View File

@ -15,28 +15,33 @@
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
</div>
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
<textarea :class="{ with: (files.length != 0 || poll) }"
ref="text" v-model="text" :disabled="posting"
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
v-autocomplete="'text'"
></textarea>
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
<p class="remain">{{ 4 - files.length }}/4</p>
<div class="textarea">
<textarea :class="{ with: (files.length != 0 || poll) }"
ref="text" v-model="text" :disabled="posting"
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
v-autocomplete="'text'"
></textarea>
<button class="emoji" @click="emoji" ref="emoji">
<fa :icon="['far', 'laugh']"/>
</button>
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
<x-draggable :list="files" :options="{ animation: 150 }">
<div v-for="file in files" :key="file.id">
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
</div>
</x-draggable>
<p class="remain">{{ 4 - files.length }}/4</p>
</div>
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
</div>
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
</div>
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
<button class="kao" :title="$t('insert-a-kao')" @click="kao"><fa :icon="['far', 'smile']"/></button>
<button class="poll" :title="$t('create-poll')" @click="poll = !poll"><fa icon="chart-pie"/></button>
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa icon="eye-slash"/></button>
<button class="cw" :title="$t('hide-contents')" @click="useCw = !useCw"><fa :icon="['far', 'eye-slash']"/></button>
<button class="geo" :title="$t('attach-location-information')" @click="geo ? removeGeo() : setGeo()"><fa icon="map-marker-alt"/></button>
<button class="visibility" :title="$t('visibility')" @click="setVisibility" ref="visibilityButton">
<span v-if="visibility === 'public'"><fa icon="globe"/></span>
@ -377,6 +382,19 @@ export default Vue.extend({
this.visibleUsers = erase(user, this.visibleUsers);
},
async emoji() {
const Picker = await import('./emoji-picker-dialog.vue').then(m => m.default);
const button = this.$refs.emoji;
const rect = button.getBoundingClientRect();
const vm = this.$root.new(Picker, {
x: button.offsetWidth + rect.left + window.pageXOffset,
y: rect.top + window.pageYOffset
});
vm.$once('chosen', emoji => {
insertTextAtCursor(this.$refs.text, emoji);
});
},
post() {
this.posting = true;
@ -469,7 +487,7 @@ export default Vue.extend({
> .content
> input
> textarea
> .textarea > textarea
display block
width 100%
padding 12px
@ -498,27 +516,108 @@ export default Vue.extend({
> input
margin-bottom 8px
> textarea
margin 0
max-width 100%
min-width 100%
min-height 84px
> .textarea
> .emoji
position absolute
top 0
right 0
padding 10px
font-size 18px
color var(--text)
opacity 0.5
&:hover
& + *
& + * + *
border-color var(--primaryAlpha02)
transition border-color .1s ease
&:hover
color var(--textHighlighted)
opacity 1
&:focus
& + *
& + * + *
border-color var(--primaryAlpha05)
transition border-color 0s ease
&:active
color var(--primary)
opacity 1
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 4px 4px 0 0
> textarea
margin 0
max-width 100%
min-width 100%
min-height 84px
&:hover
& + * + *
& + * + * + *
border-color var(--primaryAlpha02)
transition border-color .1s ease
&:focus
& + * + *
& + * + * + *
border-color var(--primaryAlpha05)
transition border-color 0s ease
& + .emoji
opacity 0.7
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 4px 4px 0 0
> .files
margin 0
padding 0
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 0
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color var(--primaryAlpha04)
> div
padding 4px
&:after
content ""
display block
clear both
> div
float left
border solid 4px transparent
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .mk-poll-editor
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
> .visibleUsers
margin-bottom 8px
@ -541,66 +640,6 @@ export default Vue.extend({
margin-right 8px
white-space nowrap
> .files
margin 0
padding 0
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
&.with
border-bottom solid 1px var(--primaryAlpha01) !important
border-radius 0
> .remain
display block
position absolute
top 8px
right 8px
margin 0
padding 0
color var(--primaryAlpha04)
> div
padding 4px
&:after
content ""
display block
clear both
> div
float left
border solid 4px transparent
cursor move
&:hover > .remove
display block
> .img
width 64px
height 64px
background-size cover
background-position center center
> .remove
display none
position absolute
top -6px
right -6px
width 16px
height 16px
cursor pointer
> .mk-poll-editor
background var(--desktopPostFormTextareaBg)
border solid 1px var(--primaryAlpha01)
border-top none
border-radius 0 0 4px 4px
transition border-color .3s ease
> .mk-uploader
margin 8px 0 0 0
padding 8px

View File

@ -1,15 +1,21 @@
<template>
<mk-window ref="window" is-modal width="700px" height="550px" @closed="destroyDom">
<span slot="header" :class="$style.header"><fa icon="cog"/>{{ $t('settings') }}</span>
<mk-settings :initial-page="initialPage" @done="close"/>
<x-settings :initial-page="initialPage" @done="close"/>
</mk-window>
</template>
<script lang="ts">
import Vue from 'vue';
import i18n from '../../../i18n';
export default Vue.extend({
i18n: i18n('desktop/views/components/settings-window.vue'),
components: {
XSettings: () => import('./settings.vue').then(m => m.default)
},
props: {
initialPage: {
type: String,

View File

@ -15,7 +15,7 @@
</div>
<div class="pages">
<div class="profile" v-show="page == 'profile'">
<mk-profile-editor/>
<x-profile-editor/>
<ui-card>
<div slot="title"><fa :icon="['fab', 'twitter']"/> {{ $t('twitter') }}</div>
@ -36,7 +36,7 @@
<div slot="title"><fa icon="palette"/> {{ $t('theme') }}</div>
<section>
<mk-theme/>
<x-theme/>
</section>
</ui-card>
@ -194,7 +194,7 @@
</ui-card>
<div class="drive" v-if="page == 'drive'">
<mk-drive-settings/>
<x-drive-settings/>
</div>
<ui-card class="hashtags" v-show="page == 'hashtags'">
@ -205,7 +205,7 @@
</ui-card>
<div class="muteAndBlock" v-show="page == 'muteAndBlock'">
<mk-mute-and-block/>
<x-mute-and-block/>
</div>
<ui-card class="apps" v-show="page == 'apps'">
@ -218,7 +218,7 @@
<ui-card class="password" v-show="page == 'security'">
<div slot="title"><fa icon="unlock-alt"/> {{ $t('password') }}</div>
<section>
<mk-password-settings/>
<x-password-settings/>
</section>
</ui-card>
@ -237,7 +237,7 @@
</ui-card>
<div class="api" v-show="page == 'api'">
<mk-api-settings/>
<x-api-settings/>
</div>
<ui-card class="other" v-show="page == 'other'">
@ -301,7 +301,13 @@ export default Vue.extend({
X2fa,
XApps,
XSignins,
XTags
XTags,
XTheme: () => import('../../../common/views/components/theme.vue').then(m => m.default),
XDriveSettings: () => import('../../../common/views/components/drive-settings.vue').then(m => m.default),
XMuteAndBlock: () => import('../../../common/views/components/mute-and-block.vue').then(m => m.default),
XPasswordSettings: () => import('../../../common/views/components/password-settings.vue').then(m => m.default),
XProfileEditor: () => import('../../../common/views/components/profile-editor.vue').then(m => m.default),
XApiSettings: () => import('../../../common/views/components/api-settings.vue').then(m => m.default),
},
props: {
initialPage: {
@ -543,12 +549,12 @@ export default Vue.extend({
this.checkingForUpdate = false;
this.latestVersion = newer;
if (newer == null) {
this.$dialog({
this.$root.alert({
title: this.$t('no-updates'),
text: this.$t('no-updates-desc')
});
} else {
this.$dialog({
this.$root.alert({
title: this.$t('update-available'),
text: this.$t('update-available-desc')
});
@ -557,7 +563,7 @@ export default Vue.extend({
},
clean() {
localStorage.clear();
this.$dialog({
this.$root.alert({
title: this.$t('cache-cleared'),
text: this.$t('cache-cleared-desc')
});

View File

@ -58,7 +58,7 @@
<i><fa icon="angle-right"/></i>
</p>
</li>
<li v-if="$store.state.i.isAdmin">
<li v-if="$store.state.i.isAdmin || $store.state.i.isModerator">
<a href="/admin">
<i><fa icon="terminal"/></i>
<span>{{ $t('admin') }}</span>

View File

@ -2,7 +2,7 @@
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
<div class="banner" :style="bannerStyle"></div>
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<mk-follow-button :user="user" class="follow"/>
<mk-follow-button :user="user" class="follow" mini/>
<div class="body">
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
<span class="username">@{{ user | acct }}</span>

Some files were not shown because too many files have changed in this diff Show More