diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 38827ea35..c97aad482 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -45,6 +45,7 @@ loadMore: "もっと見る"
youGotNewFollower: "フォローされました"
receiveFollowRequest: "フォローリクエストされました"
followRequestAccepted: "フォローが承認されました"
+mention: "メンション"
mentions: "あなた宛て"
directNotes: "ダイレクト投稿"
importAndExport: "インポートとエクスポート"
@@ -104,6 +105,7 @@ suspendConfirm: "凍結しますか?"
unsuspendConfirm: "解凍しますか?"
selectList: "リストを選択"
customEmojis: "カスタム絵文字"
+emoji: "絵文字"
emojiName: "絵文字名"
emojiUrl: "絵文字画像URL"
addEmoji: "絵文字を追加"
@@ -510,6 +512,9 @@ serviceworkerInfo: "プッシュ通知を行うには有効する必要があり
deletedNote: "削除された投稿"
invisibleNote: "非公開の投稿"
enableInfiniteScroll: "自動でもっと見る"
+visibility: "公開範囲"
+poll: "アンケート"
+useCw: "内容を隠す"
_theme:
explore: "テーマを探す"
@@ -648,7 +653,6 @@ _cw:
show: "もっと見る"
chars: "{count}文字"
files: "{count}ファイル"
- poll: "アンケート"
_poll:
noOnlyOneChoice: "選択肢は最低2つ必要です"
@@ -1119,3 +1123,4 @@ _notification:
youReceivedFollowRequest: "フォローリクエストが来ました"
yourFollowRequestAccepted: "フォローリクエストが承認されました"
youWereInvitedToGroup: "グループに招待されました"
+
diff --git a/src/client/components/cw-button.vue b/src/client/components/cw-button.vue
index 07a44d970..16a9b84f6 100644
--- a/src/client/components/cw-button.vue
+++ b/src/client/components/cw-button.vue
@@ -27,7 +27,7 @@ export default Vue.extend({
return concat([
this.note.text ? [this.$t('_cw.chars', { count: length(this.note.text) })] : [],
this.note.files && this.note.files.length !== 0 ? [this.$t('_cw.files', { count: this.note.files.length }) ] : [],
- this.note.poll != null ? [this.$t('_cw.poll')] : []
+ this.note.poll != null ? [this.$t('poll')] : []
] as string[][]).join(' / ');
}
},
diff --git a/src/client/components/link.vue b/src/client/components/link.vue
index 4c709375d..7a364d098 100644
--- a/src/client/components/link.vue
+++ b/src/client/components/link.vue
@@ -62,13 +62,13 @@ export default Vue.extend({
}
},
onMouseover() {
- if (isDeviceTouch()) return;
+ if (isDeviceTouch) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
- if (isDeviceTouch()) return;
+ if (isDeviceTouch) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
diff --git a/src/client/components/post-form.vue b/src/client/components/post-form.vue
index cdb61f51d..ee6148a35 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -9,13 +9,13 @@
{{ $t('recipient') }}
-
+
@@ -39,11 +39,11 @@
@@ -576,7 +576,7 @@ export default Vue.extend({
insertTextAtCursor(this.$refs.text, emoji);
vm.close();
});
- }
+ },
}
});
diff --git a/src/client/components/reactions-viewer.details.vue b/src/client/components/reactions-viewer.details.vue
index 67c8b261b..96d1408fc 100644
--- a/src/client/components/reactions-viewer.details.vue
+++ b/src/client/components/reactions-viewer.details.vue
@@ -1,27 +1,29 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- +{{ count - 10 }}
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ +{{ count - 10 }}
+
+
-
-
diff --git a/src/client/components/reactions-viewer.reaction.vue b/src/client/components/reactions-viewer.reaction.vue
index 33911dedb..6b72f2e10 100644
--- a/src/client/components/reactions-viewer.reaction.vue
+++ b/src/client/components/reactions-viewer.reaction.vue
@@ -4,8 +4,10 @@
:class="{ reacted: note.myReaction == reaction, canToggle }"
@click="toggleReaction(reaction)"
v-if="count > 0"
+ @touchstart="onMouseover"
@mouseover="onMouseover"
@mouseleave="onMouseleave"
+ @touchend="onMouseleave"
ref="reaction"
v-particle
>
@@ -90,16 +92,17 @@ export default Vue.extend({
}
},
onMouseover() {
+ if (this.isHovering) return;
this.isHovering = true;
this.detailsTimeoutId = setTimeout(this.openDetails, 300);
},
onMouseleave() {
+ if (!this.isHovering) return;
this.isHovering = false;
clearTimeout(this.detailsTimeoutId);
this.closeDetails();
},
openDetails() {
- if (this.$root.isMobile) return;
this.$root.api('notes/reactions', {
noteId: this.note.id,
type: this.reaction,
diff --git a/src/client/components/ui/tooltip.vue b/src/client/components/ui/tooltip.vue
new file mode 100644
index 000000000..b7a56708b
--- /dev/null
+++ b/src/client/components/ui/tooltip.vue
@@ -0,0 +1,96 @@
+
+
+
+ {{ text }}
+
+
+
+
+
+
+
diff --git a/src/client/components/url.vue b/src/client/components/url.vue
index 4dd23a50e..0a5a5bc50 100644
--- a/src/client/components/url.vue
+++ b/src/client/components/url.vue
@@ -93,13 +93,13 @@ export default Vue.extend({
}
},
onMouseover() {
- if (isDeviceTouch()) return;
+ if (isDeviceTouch) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.showTimer = setTimeout(this.showPreview, 500);
},
onMouseleave() {
- if (isDeviceTouch()) return;
+ if (isDeviceTouch) return;
clearTimeout(this.showTimer);
clearTimeout(this.hideTimer);
this.hideTimer = setTimeout(this.closePreview, 500);
diff --git a/src/client/directives/index.ts b/src/client/directives/index.ts
index 64d33c0ff..8cd5ed464 100644
--- a/src/client/directives/index.ts
+++ b/src/client/directives/index.ts
@@ -4,9 +4,11 @@ import userPreview from './user-preview';
import autocomplete from './autocomplete';
import size from './size';
import particle from './particle';
+import tooltip from './tooltip';
Vue.directive('autocomplete', autocomplete);
Vue.directive('userPreview', userPreview);
Vue.directive('user-preview', userPreview);
Vue.directive('size', size);
Vue.directive('particle', particle);
+Vue.directive('tooltip', tooltip);
diff --git a/src/client/directives/tooltip.ts b/src/client/directives/tooltip.ts
new file mode 100644
index 000000000..28d22fc02
--- /dev/null
+++ b/src/client/directives/tooltip.ts
@@ -0,0 +1,62 @@
+import MkTooltip from '../components/ui/tooltip.vue';
+import { isDeviceTouch } from '../scripts/is-device-touch';
+
+const start = isDeviceTouch ? 'touchstart' : 'mouseover';
+const end = isDeviceTouch ? 'touchend' : 'mouseleave';
+
+export default {
+ bind(el: HTMLElement, binding, vn) {
+ const self = (el as any)._tooltipDirective_ = {} as any;
+
+ self.text = binding.value as string;
+ self.tag = null;
+ self.showTimer = null;
+ self.hideTimer = null;
+ self.checkTimer = null;
+
+ self.close = () => {
+ if (self.tag) {
+ clearInterval(self.checkTimer);
+ self.tag.close();
+ self.tag = null;
+ }
+ };
+
+ const show = e => {
+ if (!document.body.contains(el)) return;
+ if (self.tag) return;
+
+ self.tag = new MkTooltip({
+ parent: vn.context,
+ propsData: {
+ text: self.text,
+ source: el
+ }
+ }).$mount();
+
+ document.body.appendChild(self.tag.$el);
+ };
+
+ el.addEventListener(start, () => {
+ clearTimeout(self.showTimer);
+ clearTimeout(self.hideTimer);
+ self.showTimer = setTimeout(show, 300);
+ });
+
+ el.addEventListener(end, () => {
+ clearTimeout(self.showTimer);
+ clearTimeout(self.hideTimer);
+ self.hideTimer = setTimeout(self.close, 300);
+ });
+
+ el.addEventListener('click', () => {
+ clearTimeout(self.showTimer);
+ self.close();
+ });
+ },
+
+ unbind(el, binding, vn) {
+ const self = el._tooltipDirective_;
+ clearInterval(self.checkTimer);
+ },
+};
diff --git a/src/client/scripts/compose-notification.ts b/src/client/scripts/compose-notification.ts
index c3281955e..1552d45e4 100644
--- a/src/client/scripts/compose-notification.ts
+++ b/src/client/scripts/compose-notification.ts
@@ -5,7 +5,7 @@ import { clientDb, get, bulkGet } from '../db';
const getTranslation = (text: string): Promise
=> get(text, clientDb.i18n);
export default async function(type, data): Promise<[string, NotificationOptions]> {
- const contexts = ['deletedNote', 'invisibleNote', 'withNFiles', '_cw.poll'];
+ const contexts = ['deletedNote', 'invisibleNote', 'withNFiles', 'poll'];
const locale = Object.fromEntries(await bulkGet(contexts, clientDb.i18n) as [string, string][]);
switch (type) {
diff --git a/src/client/scripts/is-device-touch.ts b/src/client/scripts/is-device-touch.ts
index 9f439ae4f..3f0bfefed 100644
--- a/src/client/scripts/is-device-touch.ts
+++ b/src/client/scripts/is-device-touch.ts
@@ -1,3 +1 @@
-export function isDeviceTouch(): boolean {
- return 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
-}
+export const isDeviceTouch = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
diff --git a/src/misc/get-note-summary.ts b/src/misc/get-note-summary.ts
index c23306ab1..7db8bca3e 100644
--- a/src/misc/get-note-summary.ts
+++ b/src/misc/get-note-summary.ts
@@ -27,7 +27,7 @@ const summarize = (note: any, locale: any): string => {
// 投票が添付されているとき
if (note.poll) {
- summary += ` (${locale._cw?.poll || locale['_cw.poll']})`;
+ summary += ` (${locale['poll']})`;
}
// 返信のとき