diff options
Diffstat (limited to 'src/client/app')
12 files changed, 93 insertions, 90 deletions
diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index 0dea38a7a1..e2b9089d35 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -1,5 +1,6 @@ import Vue from 'vue'; +import noteSkeleton from './note-skeleton.vue'; import theme from './theme.vue'; import instance from './instance.vue'; import cwButton from './cw-button.vue'; @@ -44,6 +45,7 @@ import uiSelect from './ui/select.vue'; import formButton from './ui/form/button.vue'; import formRadio from './ui/form/radio.vue'; +Vue.component('mk-note-skeleton', noteSkeleton); Vue.component('mk-theme', theme); Vue.component('mk-instance', instance); Vue.component('mk-cw-button', cwButton); diff --git a/src/client/app/common/views/components/note-skeleton.vue b/src/client/app/common/views/components/note-skeleton.vue new file mode 100644 index 0000000000..a2e09e3222 --- /dev/null +++ b/src/client/app/common/views/components/note-skeleton.vue @@ -0,0 +1,52 @@ +<template> +<div> + <vue-content-loading v-if="width" :width="width" :height="100" :primary="primary" :secondary="secondary"> + <circle cx="30" cy="30" r="30" /> + <rect x="75" y="13" rx="4" ry="4" :width="150 + r1" height="15" /> + <rect x="75" y="39" rx="4" ry="4" :width="260 + r2" height="10" /> + <rect x="75" y="59" rx="4" ry="4" :width="230 + r3" height="10" /> + </vue-content-loading> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import VueContentLoading from 'vue-content-loading'; +import * as tinycolor from 'tinycolor2'; + +export default Vue.extend({ + components: { + VueContentLoading, + }, + + data() { + return { + width: 0, + r1: (Math.random() * 100) - 50, + r2: (Math.random() * 100) - 50, + r3: (Math.random() * 100) - 50 + }; + }, + + computed: { + text(): tinycolor.Instance { + const text = tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')); + return text; + }, + + primary(): string { + return '#' + this.text.clone().toHex(); + }, + + secondary(): string { + return '#' + this.text.clone().darken(20).toHex(); + } + }, + + mounted() { + let width = this.$el.clientWidth; + if (width < 400) width = 400; + this.width = width; + } +}); +</script> diff --git a/src/client/app/desktop/views/components/ellipsis-icon.vue b/src/client/app/desktop/views/components/ellipsis-icon.vue deleted file mode 100644 index 4a5a0f23dc..0000000000 --- a/src/client/app/desktop/views/components/ellipsis-icon.vue +++ /dev/null @@ -1,37 +0,0 @@ -<template> -<div class="mk-ellipsis-icon"> - <div></div><div></div><div></div> -</div> -</template> - -<style lang="stylus" scoped> -.mk-ellipsis-icon - width 70px - margin 0 auto - text-align center - - > div - display inline-block - width 18px - height 18px - background-color rgba(#000, 0.3) - border-radius 100% - animation bounce 1.4s infinite ease-in-out both - - &:nth-child(1) - animation-delay 0s - - &:nth-child(2) - margin 0 6px - animation-delay 0.16s - - &:nth-child(3) - animation-delay 0.32s - - @keyframes bounce - 0%, 80%, 100% - transform scale(0) - 40% - transform scale(1) - -</style> diff --git a/src/client/app/desktop/views/components/index.ts b/src/client/app/desktop/views/components/index.ts index 7b7a38afa2..38b1547448 100644 --- a/src/client/app/desktop/views/components/index.ts +++ b/src/client/app/desktop/views/components/index.ts @@ -9,7 +9,6 @@ import subNoteContent from './sub-note-content.vue'; import window from './window.vue'; import noteFormWindow from './post-form-window.vue'; import renoteFormWindow from './renote-form-window.vue'; -import ellipsisIcon from './ellipsis-icon.vue'; import mediaImage from './media-image.vue'; import mediaImageDialog from './media-image-dialog.vue'; import mediaVideo from './media-video.vue'; @@ -39,7 +38,6 @@ Vue.component('mk-sub-note-content', subNoteContent); Vue.component('mk-window', window); Vue.component('mk-post-form-window', noteFormWindow); Vue.component('mk-renote-form-window', renoteFormWindow); -Vue.component('mk-ellipsis-icon', ellipsisIcon); Vue.component('mk-media-image', mediaImage); Vue.component('mk-media-image-dialog', mediaImageDialog); Vue.component('mk-media-video', mediaVideo); diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue index 84b13ed84e..b43720d19b 100644 --- a/src/client/app/desktop/views/components/notes.vue +++ b/src/client/app/desktop/views/components/notes.vue @@ -9,6 +9,12 @@ <button @click="resolveInitPromise">%i18n:@retry%</button> </div> + <div class="skeleton" v-if="fetching"> + <template v-for="i in 10"> + <mk-note-skeleton :key="i"/> + </template> + </div> + <!-- トランジションを有効にするとなぜかメモリリークする --> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes"> <template v-for="(note, i) in _notes"> @@ -226,6 +232,10 @@ export default Vue.extend({ > * transition transform .3s ease, opacity .3s ease + > .skeleton + padding 32px + opacity 0.3 + > .notes > .date display block diff --git a/src/client/app/desktop/views/components/notifications.vue b/src/client/app/desktop/views/components/notifications.vue index a572f43adc..361ca5f1c8 100644 --- a/src/client/app/desktop/views/components/notifications.vue +++ b/src/client/app/desktop/views/components/notifications.vue @@ -1,5 +1,11 @@ <template> <div class="mk-notifications"> + <div class="skeleton" v-if="fetching"> + <template v-for="i in 10"> + <mk-note-skeleton :key="i"/> + </template> + </div> + <div class="notifications" v-if="notifications.length != 0"> <!-- トランジションを有効にするとなぜかメモリリークする --> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition" tag="div"> @@ -102,7 +108,6 @@ <template v-if="fetchingMoreNotifications">%fa:spinner .pulse .fw%</template>{{ fetchingMoreNotifications ? '%i18n:common.loading%' : '%i18n:@more%' }} </button> <p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p> - <p class="loading" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> </div> </template> @@ -202,6 +207,10 @@ export default Vue.extend({ > * transition transform .3s ease, opacity .3s ease + > .skeleton + padding 16px + opacity 0.3 + > .notifications > div > .notification @@ -319,13 +328,4 @@ export default Vue.extend({ text-align center color #aaa - > .loading - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - </style> diff --git a/src/client/app/desktop/views/components/timeline.core.vue b/src/client/app/desktop/views/components/timeline.core.vue index 2c17e936eb..f1af7116b2 100644 --- a/src/client/app/desktop/views/components/timeline.core.vue +++ b/src/client/app/desktop/views/components/timeline.core.vue @@ -1,9 +1,6 @@ <template> <div class="mk-timeline-core"> <mk-friends-maker v-if="src == 'home' && alone"/> - <div class="fetching" v-if="fetching"> - <mk-ellipsis-icon/> - </div> <mk-notes ref="timeline" :more="existMore ? more : null"> <p :class="$style.empty" slot="empty"> @@ -170,15 +167,10 @@ export default Vue.extend({ </script> <style lang="stylus" scoped> - - .mk-timeline-core > .mk-friends-maker border-bottom solid 1px #eee - > .fetching - padding 64px 0 - </style> <style lang="stylus" module> diff --git a/src/client/app/desktop/views/pages/search.vue b/src/client/app/desktop/views/pages/search.vue index 0b6c9a032a..f088ba114d 100644 --- a/src/client/app/desktop/views/pages/search.vue +++ b/src/client/app/desktop/views/pages/search.vue @@ -3,9 +3,6 @@ <header :class="$style.header"> <h1>{{ q }}</h1> </header> - <div :class="$style.loading" v-if="fetching"> - <mk-ellipsis-icon/> - </div> <p :class="$style.notAvailable" v-if="!fetching && notAvailable">%i18n:@not-available%</p> <p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:not-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:not-found%'.split('{}')[1] }}</p> <mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/> @@ -119,9 +116,6 @@ export default Vue.extend({ border-radius 6px overflow hidden -.loading - padding 64px 0 - .empty display block margin 0 auto diff --git a/src/client/app/desktop/views/pages/tag.vue b/src/client/app/desktop/views/pages/tag.vue index 04b377e0ab..5305b4ac13 100644 --- a/src/client/app/desktop/views/pages/tag.vue +++ b/src/client/app/desktop/views/pages/tag.vue @@ -3,9 +3,6 @@ <header :class="$style.header"> <h1>#{{ $route.params.tag }}</h1> </header> - <div :class="$style.loading" v-if="fetching"> - <mk-ellipsis-icon/> - </div> <p :class="$style.empty" v-if="!fetching && empty">%fa:search% {{ '%i18n:no-posts-found%'.split('{}')[0] }}{{ q }}{{ '%i18n:no-posts-found%'.split('{}')[1] }}</p> <mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/> </mk-ui> @@ -108,9 +105,6 @@ export default Vue.extend({ border-radius 6px overflow hidden -.loading - padding 64px 0 - .empty display block margin 0 auto diff --git a/src/client/app/desktop/views/pages/user/user.timeline.vue b/src/client/app/desktop/views/pages/user/user.timeline.vue index 608c12b7e2..a8d49c8d64 100644 --- a/src/client/app/desktop/views/pages/user/user.timeline.vue +++ b/src/client/app/desktop/views/pages/user/user.timeline.vue @@ -5,9 +5,6 @@ <span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'">%fa:comments% %i18n:@with-replies%</span> <span :data-active="mode == 'with-media'" @click="mode = 'with-media'">%fa:images% %i18n:@with-media%</span> </header> - <div class="loading" v-if="fetching"> - <mk-ellipsis-icon/> - </div> <mk-notes ref="timeline" :more="existMore ? more : null"> <p class="empty" slot="empty">%fa:R comments%%i18n:@empty%</p> </mk-notes> @@ -152,9 +149,6 @@ export default Vue.extend({ &:hover color var(--desktopTimelineSrcHover) - > .loading - padding 64px 0 - > .empty display block margin 0 auto diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue index 8f0a1ef196..62ab8c442a 100644 --- a/src/client/app/mobile/views/components/notes.vue +++ b/src/client/app/mobile/views/components/notes.vue @@ -4,8 +4,10 @@ <slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot> - <div class="init" v-if="fetching"> - %fa:spinner .pulse%%i18n:common.loading% + <div class="skeleton" v-if="fetching"> + <template v-for="i in 10"> + <mk-note-skeleton :key="i"/> + </template> </div> <div v-if="!fetching && requestInitPromise != null"> @@ -251,13 +253,12 @@ export default Vue.extend({ [data-fa] margin-right 8px - > .init - padding 64px 0 - text-align center - color #999 + > .skeleton + padding 16px + opacity 0.3 - > [data-fa] - margin-right 4px + @media (min-width 500px) + padding 32px > .empty margin 0 auto diff --git a/src/client/app/mobile/views/components/notifications.vue b/src/client/app/mobile/views/components/notifications.vue index d9ea2aea09..fb8a5c1061 100644 --- a/src/client/app/mobile/views/components/notifications.vue +++ b/src/client/app/mobile/views/components/notifications.vue @@ -1,5 +1,11 @@ <template> <div class="mk-notifications"> + <div class="skeleton" v-if="fetching"> + <template v-for="i in 10"> + <mk-note-skeleton :key="i"/> + </template> + </div> + <!-- トランジションを有効にするとなぜかメモリリークする --> <component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications"> <template v-for="(notification, i) in _notifications"> @@ -17,7 +23,6 @@ </button> <p class="empty" v-if="notifications.length == 0 && !fetching">%i18n:@empty%</p> - <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> </div> </template> @@ -179,13 +184,11 @@ export default Vue.extend({ text-align center color #aaa - > .fetching - margin 0 + > .skeleton padding 16px - text-align center - color #aaa + opacity 0.3 - > [data-fa] - margin-right 4px + @media (min-width 500px) + padding 32px </style> |