diff options
| author | syuilo <Syuilotan@yahoo.co.jp> | 2022-06-26 03:12:58 +0900 |
|---|---|---|
| committer | syuilo <Syuilotan@yahoo.co.jp> | 2022-06-26 03:12:58 +0900 |
| commit | 5e95a1f7af841f10646133ad0cc155a2c5cea9fd (patch) | |
| tree | 6aacbbdca450e7a0ae54e7040147395c934e787b /packages/client/src/components | |
| parent | enhance(client): tweak control panel dashboard (diff) | |
| download | sharkey-5e95a1f7af841f10646133ad0cc155a2c5cea9fd.tar.gz sharkey-5e95a1f7af841f10646133ad0cc155a2c5cea9fd.tar.bz2 sharkey-5e95a1f7af841f10646133ad0cc155a2c5cea9fd.zip | |
refactor(client): extract interval logic to a composable function
あと`onUnmounted`を`onMounted`内で呼んでいたりしたのを修正したりとか
Diffstat (limited to 'packages/client/src/components')
| -rw-r--r-- | packages/client/src/components/form/input.vue | 73 | ||||
| -rw-r--r-- | packages/client/src/components/form/range.vue | 24 | ||||
| -rw-r--r-- | packages/client/src/components/form/select.vue | 57 | ||||
| -rw-r--r-- | packages/client/src/components/global/time.vue | 18 | ||||
| -rw-r--r-- | packages/client/src/components/mini-chart.vue | 8 | ||||
| -rw-r--r-- | packages/client/src/components/notification.vue | 17 | ||||
| -rw-r--r-- | packages/client/src/components/notifications.vue | 10 | ||||
| -rw-r--r-- | packages/client/src/components/poll.vue | 14 | ||||
| -rw-r--r-- | packages/client/src/components/sparkle.vue | 17 |
9 files changed, 126 insertions, 112 deletions
diff --git a/packages/client/src/components/form/input.vue b/packages/client/src/components/form/input.vue index 7165671af3..5065e28892 100644 --- a/packages/client/src/components/form/input.vue +++ b/packages/client/src/components/form/input.vue @@ -3,7 +3,8 @@ <div class="label" @click="focus"><slot name="label"></slot></div> <div class="input" :class="{ inline, disabled, focused }"> <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div> - <input ref="inputEl" + <input + ref="inputEl" v-model="v" v-adaptive-border :type="type" @@ -34,8 +35,9 @@ <script lang="ts"> import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs } from 'vue'; -import MkButton from '@/components/ui/button.vue'; import { debounce } from 'throttle-debounce'; +import MkButton from '@/components/ui/button.vue'; +import { useInterval } from '@/scripts/use-interval'; export default defineComponent({ components: { @@ -44,45 +46,45 @@ export default defineComponent({ props: { modelValue: { - required: true + required: true, }, type: { type: String, - required: false + required: false, }, required: { type: Boolean, - required: false + required: false, }, readonly: { type: Boolean, - required: false + required: false, }, disabled: { type: Boolean, - required: false + required: false, }, pattern: { type: String, - required: false + required: false, }, placeholder: { type: String, - required: false + required: false, }, autofocus: { type: Boolean, required: false, - default: false + default: false, }, autocomplete: { - required: false + required: false, }, spellcheck: { - required: false + required: false, }, step: { - required: false + required: false, }, datalist: { type: Array, @@ -91,17 +93,17 @@ export default defineComponent({ inline: { type: Boolean, required: false, - default: false + default: false, }, debounce: { type: Boolean, required: false, - default: false + default: false, }, manualSave: { type: Boolean, required: false, - default: false + default: false, }, }, @@ -134,7 +136,7 @@ export default defineComponent({ const updated = () => { changed.value = false; - if (type?.value === 'number') { + if (type.value === 'number') { context.emit('update:modelValue', parseFloat(v.value)); } else { context.emit('update:modelValue', v.value); @@ -159,30 +161,29 @@ export default defineComponent({ invalid.value = inputEl.value.validity.badInput; }); + // このコンポーネントが作成された時、非表示状態である場合がある + // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する + useInterval(() => { + if (prefixEl.value) { + if (prefixEl.value.offsetWidth) { + inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; + } + } + if (suffixEl.value) { + if (suffixEl.value.offsetWidth) { + inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px'; + } + } + }, 100, { + immediate: true, + afterMounted: true, + }); + onMounted(() => { nextTick(() => { if (autofocus.value) { focus(); } - - // このコンポーネントが作成された時、非表示状態である場合がある - // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する - const clock = window.setInterval(() => { - if (prefixEl.value) { - if (prefixEl.value.offsetWidth) { - inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; - } - } - if (suffixEl.value) { - if (suffixEl.value.offsetWidth) { - inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px'; - } - } - }, 100); - - onUnmounted(() => { - window.clearInterval(clock); - }); }); }); diff --git a/packages/client/src/components/form/range.vue b/packages/client/src/components/form/range.vue index 9bf7651119..221ad029a7 100644 --- a/packages/client/src/components/form/range.vue +++ b/packages/client/src/components/form/range.vue @@ -24,31 +24,31 @@ export default defineComponent({ modelValue: { type: Number, required: false, - default: 0 + default: 0, }, disabled: { type: Boolean, required: false, - default: false + default: false, }, min: { type: Number, required: false, - default: 0 + default: 0, }, max: { type: Number, required: false, - default: 100 + default: 100, }, step: { type: Number, required: false, - default: 1 + default: 1, }, autofocus: { type: Boolean, - required: false + required: false, }, textConverter: { type: Function, @@ -90,14 +90,18 @@ export default defineComponent({ } }; watch([steppedValue, containerEl], calcThumbPosition); + + let ro: ResizeObserver | undefined; + onMounted(() => { - const ro = new ResizeObserver((entries, observer) => { + ro = new ResizeObserver((entries, observer) => { calcThumbPosition(); }); ro.observe(containerEl.value); - onUnmounted(() => { - ro.disconnect(); - }); + }); + + onUnmounted(() => { + if (ro) ro.disconnect(); }); const steps = computed(() => { diff --git a/packages/client/src/components/form/select.vue b/packages/client/src/components/form/select.vue index 87196027a8..7f5f8784b6 100644 --- a/packages/client/src/components/form/select.vue +++ b/packages/client/src/components/form/select.vue @@ -3,7 +3,8 @@ <div class="label" @click="focus"><slot name="label"></slot></div> <div ref="container" class="input" :class="{ inline, disabled, focused }" @click.prevent="onClick"> <div ref="prefixEl" class="prefix"><slot name="prefix"></slot></div> - <select ref="inputEl" + <select + ref="inputEl" v-model="v" v-adaptive-border class="select" @@ -29,6 +30,7 @@ import { defineComponent, onMounted, onUnmounted, nextTick, ref, watch, computed, toRefs, VNode } from 'vue'; import MkButton from '@/components/ui/button.vue'; import * as os from '@/os'; +import { useInterval } from '@/scripts/use-interval'; export default defineComponent({ components: { @@ -37,38 +39,38 @@ export default defineComponent({ props: { modelValue: { - required: true + required: true, }, required: { type: Boolean, - required: false + required: false, }, readonly: { type: Boolean, - required: false + required: false, }, disabled: { type: Boolean, - required: false + required: false, }, placeholder: { type: String, - required: false + required: false, }, autofocus: { type: Boolean, required: false, - default: false + default: false, }, inline: { type: Boolean, required: false, - default: false + default: false, }, manualSave: { type: Boolean, required: false, - default: false + default: false, }, }, @@ -109,30 +111,29 @@ export default defineComponent({ invalid.value = inputEl.value.validity.badInput; }); + // このコンポーネントが作成された時、非表示状態である場合がある + // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する + useInterval(() => { + if (prefixEl.value) { + if (prefixEl.value.offsetWidth) { + inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; + } + } + if (suffixEl.value) { + if (suffixEl.value.offsetWidth) { + inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px'; + } + } + }, 100, { + immediate: true, + afterMounted: true, + }); + onMounted(() => { nextTick(() => { if (autofocus.value) { focus(); } - - // このコンポーネントが作成された時、非表示状態である場合がある - // 非表示状態だと要素の幅などは0になってしまうので、定期的に計算する - const clock = window.setInterval(() => { - if (prefixEl.value) { - if (prefixEl.value.offsetWidth) { - inputEl.value.style.paddingLeft = prefixEl.value.offsetWidth + 'px'; - } - } - if (suffixEl.value) { - if (suffixEl.value.offsetWidth) { - inputEl.value.style.paddingRight = suffixEl.value.offsetWidth + 'px'; - } - } - }, 100); - - onUnmounted(() => { - window.clearInterval(clock); - }); }); }); diff --git a/packages/client/src/components/global/time.vue b/packages/client/src/components/global/time.vue index a7f142f961..801490225b 100644 --- a/packages/client/src/components/global/time.vue +++ b/packages/client/src/components/global/time.vue @@ -24,14 +24,14 @@ let now = $ref(new Date()); const relative = $computed(() => { const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/; return ( - ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) : - ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) : - ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) : - ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) : - ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) : - ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) : - ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) : - ago >= -1 ? i18n.ts._ago.justNow : + ago >= 31536000 ? i18n.t('_ago.yearsAgo', { n: Math.round(ago / 31536000).toString() }) : + ago >= 2592000 ? i18n.t('_ago.monthsAgo', { n: Math.round(ago / 2592000).toString() }) : + ago >= 604800 ? i18n.t('_ago.weeksAgo', { n: Math.round(ago / 604800).toString() }) : + ago >= 86400 ? i18n.t('_ago.daysAgo', { n: Math.round(ago / 86400).toString() }) : + ago >= 3600 ? i18n.t('_ago.hoursAgo', { n: Math.round(ago / 3600).toString() }) : + ago >= 60 ? i18n.t('_ago.minutesAgo', { n: (~~(ago / 60)).toString() }) : + ago >= 10 ? i18n.t('_ago.secondsAgo', { n: (~~(ago % 60)).toString() }) : + ago >= -1 ? i18n.ts._ago.justNow : i18n.ts._ago.future); }); @@ -50,7 +50,7 @@ if (props.mode === 'relative' || props.mode === 'detail') { tickId = window.requestAnimationFrame(tick); onUnmounted(() => { - window.clearTimeout(tickId); + window.cancelAnimationFrame(tickId); }); } </script> diff --git a/packages/client/src/components/mini-chart.vue b/packages/client/src/components/mini-chart.vue index 5e842b1975..c64ce163f9 100644 --- a/packages/client/src/components/mini-chart.vue +++ b/packages/client/src/components/mini-chart.vue @@ -29,6 +29,7 @@ import { onUnmounted, watch } from 'vue'; import { v4 as uuid } from 'uuid'; import tinycolor from 'tinycolor2'; +import { useInterval } from '@/scripts/use-interval'; const props = defineProps<{ src: number[]; @@ -65,9 +66,8 @@ function draw(): void { watch(() => props.src, draw, { immediate: true }); // Vueが何故かWatchを発動させない場合があるので -clock = window.setInterval(draw, 1000); - -onUnmounted(() => { - window.clearInterval(clock); +useInterval(draw, 1000, { + immediate: false, + afterMounted: true, }); </script> diff --git a/packages/client/src/components/notification.vue b/packages/client/src/components/notification.vue index cbfd809f37..26fbeecb68 100644 --- a/packages/client/src/components/notification.vue +++ b/packages/client/src/components/notification.vue @@ -112,9 +112,12 @@ export default defineComponent({ const elRef = ref<HTMLElement>(null); const reactionRef = ref(null); + let readObserver: IntersectionObserver | undefined; + let connection; + onMounted(() => { if (!props.notification.isRead) { - const readObserver = new IntersectionObserver((entries, observer) => { + readObserver = new IntersectionObserver((entries, observer) => { if (!entries.some(entry => entry.isIntersecting)) return; stream.send('readNotification', { id: props.notification.id, @@ -124,19 +127,19 @@ export default defineComponent({ readObserver.observe(elRef.value); - const connection = stream.useChannel('main'); + connection = stream.useChannel('main'); connection.on('readAllNotifications', () => readObserver.disconnect()); watch(props.notification.isRead, () => { readObserver.disconnect(); }); - - onUnmounted(() => { - readObserver.disconnect(); - connection.dispose(); - }); } }); + + onUnmounted(() => { + if (readObserver) readObserver.disconnect(); + if (connection) connection.dispose(); + }); const followRequestDone = ref(false); const groupInviteDone = ref(false); diff --git a/packages/client/src/components/notifications.vue b/packages/client/src/components/notifications.vue index 8eb569c369..eb19ad488c 100644 --- a/packages/client/src/components/notifications.vue +++ b/packages/client/src/components/notifications.vue @@ -60,8 +60,10 @@ const onNotification = (notification) => { } }; +let connection; + onMounted(() => { - const connection = stream.useChannel('main'); + connection = stream.useChannel('main'); connection.on('notification', onNotification); connection.on('readAllNotifications', () => { if (pagingComponent.value) { @@ -87,10 +89,10 @@ onMounted(() => { } } }); +}); - onUnmounted(() => { - connection.dispose(); - }); +onUnmounted(() => { + if (connection) connection.dispose(); }); </script> diff --git a/packages/client/src/components/poll.vue b/packages/client/src/components/poll.vue index d9ef5970cb..35f87325d8 100644 --- a/packages/client/src/components/poll.vue +++ b/packages/client/src/components/poll.vue @@ -27,18 +27,19 @@ import { sum } from '@/scripts/array'; import { pleaseLogin } from '@/scripts/please-login'; import * as os from '@/os'; import { i18n } from '@/i18n'; +import { useInterval } from '@/scripts/use-interval'; export default defineComponent({ props: { note: { type: Object, - required: true + required: true, }, readOnly: { type: Boolean, required: false, default: false, - } + }, }, setup(props) { @@ -54,7 +55,7 @@ export default defineComponent({ s: Math.floor(remaining.value % 60), m: Math.floor(remaining.value / 60) % 60, h: Math.floor(remaining.value / 3600) % 24, - d: Math.floor(remaining.value / 86400) + d: Math.floor(remaining.value / 86400), })); const showResult = ref(props.readOnly || isVoted.value); @@ -68,10 +69,9 @@ export default defineComponent({ } }; - tick(); - const intevalId = window.setInterval(tick, 3000); - onUnmounted(() => { - window.clearInterval(intevalId); + useInterval(tick, 3000, { + immediate: true, + afterMounted: false, }); } diff --git a/packages/client/src/components/sparkle.vue b/packages/client/src/components/sparkle.vue index f52e5a3f9b..b52dbe31c4 100644 --- a/packages/client/src/components/sparkle.vue +++ b/packages/client/src/components/sparkle.vue @@ -33,7 +33,8 @@ </svg> --> <svg v-for="particle in particles" :key="particle.id" :width="width" :height="height" :viewBox="`0 0 ${width} ${height}`" xmlns="http://www.w3.org/2000/svg"> - <path style="transform-origin: center; transform-box: fill-box;" + <path + style="transform-origin: center; transform-box: fill-box;" :transform="`translate(${particle.x} ${particle.y})`" :fill="particle.color" d="M29.427,2.011C29.721,0.83 30.782,0 32,0C33.218,0 34.279,0.83 34.573,2.011L39.455,21.646C39.629,22.347 39.991,22.987 40.502,23.498C41.013,24.009 41.653,24.371 42.354,24.545L61.989,29.427C63.17,29.721 64,30.782 64,32C64,33.218 63.17,34.279 61.989,34.573L42.354,39.455C41.653,39.629 41.013,39.991 40.502,40.502C39.991,41.013 39.629,41.653 39.455,42.354L34.573,61.989C34.279,63.17 33.218,64 32,64C30.782,64 29.721,63.17 29.427,61.989L24.545,42.354C24.371,41.653 24.009,41.013 23.498,40.502C22.987,39.991 22.347,39.629 21.646,39.455L2.011,34.573C0.83,34.279 0,33.218 0,32C0,30.782 0.83,29.721 2.011,29.427L21.646,24.545C22.347,24.371 22.987,24.009 23.498,23.498C24.009,22.987 24.371,22.347 24.545,21.646L29.427,2.011Z" @@ -73,14 +74,15 @@ export default defineComponent({ const width = ref(0); const height = ref(0); const colors = ['#FF1493', '#00FFFF', '#FFE202', '#FFE202', '#FFE202']; + let stop = false; + let ro: ResizeObserver | undefined; onMounted(() => { - const ro = new ResizeObserver((entries, observer) => { + ro = new ResizeObserver((entries, observer) => { width.value = el.value?.offsetWidth + 64; height.value = el.value?.offsetHeight + 64; }); ro.observe(el.value); - let stop = false; const add = () => { if (stop) return; const x = (Math.random() * (width.value - 64)); @@ -104,10 +106,11 @@ export default defineComponent({ }, 500 + (Math.random() * 500)); }; add(); - onUnmounted(() => { - ro.disconnect(); - stop = true; - }); + }); + + onUnmounted(() => { + if (ro) ro.disconnect(); + stop = true; }); return { |