summaryrefslogtreecommitdiff
path: root/src/client
diff options
context:
space:
mode:
authortamaina <tamaina@hotmail.co.jp>2020-06-03 13:30:17 +0900
committerGitHub <noreply@github.com>2020-06-03 13:30:17 +0900
commit111eb43fd93d0a496da054c36ec84c6066c1c434 (patch)
tree8db1683b69ef571567a84e285f5eb27e14645166 /src/client
parentfix(server): Fix #6433 (diff)
downloadmisskey-111eb43fd93d0a496da054c36ec84c6066c1c434.tar.gz
misskey-111eb43fd93d0a496da054c36ec84c6066c1c434.tar.bz2
misskey-111eb43fd93d0a496da054c36ec84c6066c1c434.zip
feat(client): 投稿フォームのボタンの説明を表示するように (#6408)
* Add title attr with buttons on the post form * fix * tooltip * missing ; * remove title attr * fix bug * Update reactions-viewer.details.vue * help wip * ok! * i18n Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Diffstat (limited to 'src/client')
-rw-r--r--src/client/components/cw-button.vue2
-rw-r--r--src/client/components/link.vue4
-rw-r--r--src/client/components/post-form.vue18
-rw-r--r--src/client/components/reactions-viewer.details.vue104
-rw-r--r--src/client/components/reactions-viewer.reaction.vue5
-rw-r--r--src/client/components/ui/tooltip.vue96
-rw-r--r--src/client/components/url.vue4
-rw-r--r--src/client/directives/index.ts2
-rw-r--r--src/client/directives/tooltip.ts62
-rw-r--r--src/client/scripts/compose-notification.ts2
-rw-r--r--src/client/scripts/is-device-touch.ts4
11 files changed, 200 insertions, 103 deletions
diff --git a/src/client/components/cw-button.vue b/src/client/components/cw-button.vue
index 07a44d970f..16a9b84f62 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 4c709375d3..7a364d0986 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 cdb61f51d5..ee6148a355 100644
--- a/src/client/components/post-form.vue
+++ b/src/client/components/post-form.vue
@@ -9,13 +9,13 @@
<button v-if="!fixed" class="cancel _button" @click="cancel"><fa :icon="faTimes"/></button>
<div>
<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
- <button class="_button visibility" @click="setVisibility" ref="visibilityButton">
+ <button class="_button visibility" @click="setVisibility" ref="visibilityButton" v-tooltip="$t('visibility')">
<span v-if="visibility === 'public'"><fa :icon="faGlobe"/></span>
<span v-if="visibility === 'home'"><fa :icon="faHome"/></span>
<span v-if="visibility === 'followers'"><fa :icon="faUnlock"/></span>
<span v-if="visibility === 'specified'"><fa :icon="faEnvelope"/></span>
</button>
- <button class="_button localOnly" v-if="visibility !== 'specified'" @click="localOnly = !localOnly" :class="{ active: localOnly }"><fa :icon="faBiohazard"/></button>
+ <button class="_button localOnly" v-if="visibility !== 'specified'" @click="localOnly = !localOnly" :class="{ active: localOnly }" v-tooltip="$t('_visibility.localOnly')"><fa :icon="faBiohazard"/></button>
<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<fa :icon="reply ? faReply : renote ? faQuoteRight : faPaperPlane"/></button>
</div>
</header>
@@ -26,7 +26,7 @@
<div v-if="visibility === 'specified'" class="to-specified">
<span style="margin-right: 8px;">{{ $t('recipient') }}</span>
<div class="visibleUsers">
- <span v-for="u in visibleUsers">
+ <span v-for="u in visibleUsers" :key="u.id">
<mk-acct :user="u"/>
<button class="_button" @click="removeVisibleUser(u)"><fa :icon="faTimes"/></button>
</span>
@@ -39,11 +39,11 @@
<x-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="onPollUpdate()"/>
<x-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
<footer>
- <button class="_button" @click="chooseFileFrom"><fa :icon="faPhotoVideo"/></button>
- <button class="_button" @click="poll = !poll" :class="{ active: poll }"><fa :icon="faPollH"/></button>
- <button class="_button" @click="useCw = !useCw" :class="{ active: useCw }"><fa :icon="faEyeSlash"/></button>
- <button class="_button" @click="insertMention"><fa :icon="faAt"/></button>
- <button class="_button" @click="insertEmoji"><fa :icon="faLaughSquint"/></button>
+ <button class="_button" @click="chooseFileFrom" v-tooltip="$t('attachFile')"><fa :icon="faPhotoVideo"/></button>
+ <button class="_button" @click="poll = !poll" :class="{ active: poll }" v-tooltip="$t('poll')"><fa :icon="faPollH"/></button>
+ <button class="_button" @click="useCw = !useCw" :class="{ active: useCw }" v-tooltip="$t('useCw')"><fa :icon="faEyeSlash"/></button>
+ <button class="_button" @click="insertMention" v-tooltip="$t('mention')"><fa :icon="faAt"/></button>
+ <button class="_button" @click="insertEmoji" v-tooltip="$t('emoji')"><fa :icon="faLaughSquint"/></button>
</footer>
<input ref="file" class="file _button" type="file" multiple="multiple" @change="onChangeFile"/>
</div>
@@ -576,7 +576,7 @@ export default Vue.extend({
insertTextAtCursor(this.$refs.text, emoji);
vm.close();
});
- }
+ },
}
});
</script>
diff --git a/src/client/components/reactions-viewer.details.vue b/src/client/components/reactions-viewer.details.vue
index 67c8b261be..96d1408fc1 100644
--- a/src/client/components/reactions-viewer.details.vue
+++ b/src/client/components/reactions-viewer.details.vue
@@ -1,27 +1,29 @@
<template>
-<transition name="zoom-in-top">
- <div class="buebdbiu" ref="popover" v-if="show">
- <template v-if="users.length <= 10">
- <b v-for="u in users" :key="u.id" style="margin-right: 12px;">
- <mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
- <mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
- </b>
- </template>
- <template v-if="10 < users.length">
- <b v-for="u in users" :key="u.id" style="margin-right: 12px;">
- <mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
- <mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
- </b>
- <span slot="omitted">+{{ count - 10 }}</span>
- </template>
- </div>
-</transition>
+<mk-tooltip :source="source" ref="tooltip">
+ <template v-if="users.length <= 10">
+ <b v-for="u in users" :key="u.id" style="margin-right: 12px;">
+ <mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
+ <mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
+ </b>
+ </template>
+ <template v-if="10 < users.length">
+ <b v-for="u in users" :key="u.id" style="margin-right: 12px;">
+ <mk-avatar :user="u" style="width: 24px; height: 24px; margin-right: 2px;"/>
+ <mk-user-name :user="u" :nowrap="false" style="line-height: 24px;"/>
+ </b>
+ <span slot="omitted">+{{ count - 10 }}</span>
+ </template>
+</mk-tooltip>
</template>
<script lang="ts">
import Vue from 'vue';
+import MkTooltip from './ui/tooltip.vue';
export default Vue.extend({
+ components: {
+ MkTooltip
+ },
props: {
reaction: {
type: String,
@@ -39,77 +41,11 @@ export default Vue.extend({
required: true,
}
},
- data() {
- return {
- show: false
- };
- },
- mounted() {
- this.show = true;
-
- this.$nextTick(() => {
- const popover = this.$refs.popover as any;
- if (this.source == null) {
- this.destroyDom();
- return;
- }
- const rect = this.source.getBoundingClientRect();
-
- const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
- const y = rect.top + window.pageYOffset + this.source.offsetHeight;
- popover.style.left = (x - 28) + 'px';
- popover.style.top = (y + 16) + 'px';
- });
- }
methods: {
close() {
- this.show = false;
- setTimeout(this.destroyDom, 300);
+ this.$refs.tooltip.close();
}
}
})
</script>
-
-<style lang="scss" scoped>
-.buebdbiu {
- z-index: 10000;
- display: block;
- position: absolute;
- max-width: 240px;
- font-size: 0.8em;
- padding: 6px 8px;
- background: var(--panel);
- text-align: center;
- border-radius: 4px;
- box-shadow: 0 2px 8px rgba(0,0,0,0.25);
- pointer-events: none;
- transform-origin: center -16px;
-
- &:before {
- content: "";
- pointer-events: none;
- display: block;
- position: absolute;
- top: -28px;
- left: 12px;
- border-top: solid 14px transparent;
- border-right: solid 14px transparent;
- border-bottom: solid 14px rgba(0,0,0,0.1);
- border-left: solid 14px transparent;
- }
-
- &:after {
- content: "";
- pointer-events: none;
- display: block;
- position: absolute;
- top: -27px;
- left: 12px;
- border-top: solid 14px transparent;
- border-right: solid 14px transparent;
- border-bottom: solid 14px var(--panel);
- border-left: solid 14px transparent;
- }
-}
-</style>
diff --git a/src/client/components/reactions-viewer.reaction.vue b/src/client/components/reactions-viewer.reaction.vue
index 33911dedb8..6b72f2e105 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 0000000000..b7a56708b7
--- /dev/null
+++ b/src/client/components/ui/tooltip.vue
@@ -0,0 +1,96 @@
+<template>
+<transition name="zoom-in-top" appear>
+ <div class="buebdbiu" v-if="show">
+ <slot>{{ text }}</slot>
+ </div>
+</transition>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+ props: {
+ source: {
+ required: true,
+ },
+ text: {
+ type: String,
+ required: false
+ }
+ },
+
+ data() {
+ return {
+ show: false
+ };
+ },
+
+ mounted() {
+ this.show = true;
+
+ this.$nextTick(() => {
+ if (this.source == null) {
+ this.destroyDom();
+ return;
+ }
+ const rect = this.source.getBoundingClientRect();
+
+ const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
+ const y = rect.top + window.pageYOffset + this.source.offsetHeight;
+ this.$el.style.left = (x - 28) + 'px';
+ this.$el.style.top = (y + 16) + 'px';
+ });
+ },
+
+ methods: {
+ close() {
+ this.show = false;
+ setTimeout(this.destroyDom, 300);
+ }
+ }
+})
+</script>
+
+<style lang="scss" scoped>
+.buebdbiu {
+ z-index: 11000;
+ display: block;
+ position: absolute;
+ max-width: 240px;
+ font-size: 0.8em;
+ padding: 6px 8px;
+ background: var(--panel);
+ text-align: center;
+ border-radius: 4px;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25);
+ pointer-events: none;
+ transform-origin: center -16px;
+
+ &:before {
+ content: "";
+ pointer-events: none;
+ display: block;
+ position: absolute;
+ top: -28px;
+ left: 12px;
+ border-top: solid 14px transparent;
+ border-right: solid 14px transparent;
+ border-bottom: solid 14px rgba(0,0,0,0.1);
+ border-left: solid 14px transparent;
+ }
+
+ &:after {
+ content: "";
+ pointer-events: none;
+ display: block;
+ position: absolute;
+ top: -27px;
+ left: 12px;
+ border-top: solid 14px transparent;
+ border-right: solid 14px transparent;
+ border-bottom: solid 14px var(--panel);
+ border-left: solid 14px transparent;
+ }
+}
+</style>
diff --git a/src/client/components/url.vue b/src/client/components/url.vue
index 4dd23a50ed..0a5a5bc508 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 64d33c0ff3..8cd5ed464d 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 0000000000..28d22fc024
--- /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 c3281955e4..1552d45e4e 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<string> => 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 9f439ae4fd..3f0bfefed2 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;