timeline: revert loading icon move

Signed-off-by: Varun Patil <radialapps@gmail.com>
pull/653/merge
Varun Patil 2023-11-02 12:12:30 -07:00
parent a7e7f80745
commit 90003614b7
3 changed files with 70 additions and 19 deletions

View File

@ -6,7 +6,7 @@
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent, type PropType } from 'vue';
const SWIPE_PX = 250; const SWIPE_PX = 250;
@ -14,37 +14,56 @@ export default defineComponent({
name: 'SwipeRefresh', name: 'SwipeRefresh',
props: { props: {
refresh: {
type: Function as PropType<() => Promise<any>>,
required: true,
},
allowSwipe: { allowSwipe: {
type: Boolean, type: Boolean,
default: true, default: true,
}, },
loading: { state: {
type: Boolean, type: Number,
default: false, default: Math.random(),
}, },
}, },
emits: {
refresh: () => true,
},
data: () => ({ data: () => ({
/** Is active interaction */
on: false, on: false,
/** Start touch Y coordinate */
start: 0, start: 0,
/** End touch Y coordinate */
end: 0, end: 0,
updateFrame: 0, /** Percentage progress to show in swiping */
progress: 0, progress: 0,
/** Next update frame reference */
updateFrame: 0,
// Loading animation state
loading: false,
animate: false, animate: false,
wasSwiped: true, wasSwiped: true,
firstcycle: 0, firstcycle: 0,
}), }),
emits: [],
mounted() { mounted() {
this.animate = this.loading; // start if needed this.animate = this.loading; // start if needed
}, },
beforeDestroy() {
this.reset();
},
watch: { watch: {
state() {
this.reset();
},
loading() { loading() {
this.wasSwiped = this.progress >= 100; this.wasSwiped = this.progress >= 100;
if (!this.wasSwiped) { if (!this.wasSwiped) {
@ -93,6 +112,21 @@ export default defineComponent({
}, },
methods: { methods: {
reset() {
// Clear events
window.cancelAnimationFrame(this.updateFrame);
window.clearTimeout(this.firstcycle);
// Reset state
this.on = false;
this.progress = 0;
this.updateFrame = 0;
this.loading = false;
this.animate = false;
this.wasSwiped = true;
this.firstcycle = 0;
},
/** Start gesture on container (passive) */ /** Start gesture on container (passive) */
touchstart(event: TouchEvent) { touchstart(event: TouchEvent) {
if (!this.allowSwipe) return; if (!this.allowSwipe) return;
@ -104,12 +138,12 @@ export default defineComponent({
/** Execute gesture on container (passive) */ /** Execute gesture on container (passive) */
touchmove(event: TouchEvent) { touchmove(event: TouchEvent) {
if (!this.allowSwipe) return; if (!this.allowSwipe || !this.on) return;
const touch = event.touches[0]; const touch = event.touches[0];
this.end = touch.clientY; this.end = touch.clientY;
// Update progress only once per frame // Update progress only once per frame
this.updateFrame ||= requestAnimationFrame(() => { this.updateFrame ||= window.requestAnimationFrame(async () => {
this.updateFrame = 0; this.updateFrame = 0;
// Compute percentage of swipe // Compute percentage of swipe
@ -118,8 +152,16 @@ export default defineComponent({
// Execute action on threshold // Execute action on threshold
if (this.progress >= 100) { if (this.progress >= 100) {
this.$emit('refresh');
this.on = false; this.on = false;
const state = this.state;
try {
this.loading = true;
await this.refresh();
} finally {
if (this.state === state) {
this.loading = false;
}
}
} }
}); });
}, },
@ -170,7 +212,7 @@ export default defineComponent({
} }
49.99% { 49.99% {
background-image: $progress-outside; background-image: $progress-outside;
background-size: 12000% 12000%; background-size: 11000% 11000%;
} }
50% { 50% {
background-image: $progress-inside; background-image: $progress-inside;
@ -178,7 +220,7 @@ export default defineComponent({
} }
100% { 100% {
background-image: $progress-inside; background-image: $progress-inside;
background-size: 12000% 12000%; background-size: 11000% 11000%;
} }
} }
} }

View File

@ -2,10 +2,13 @@
<SwipeRefresh <SwipeRefresh
class="container no-user-select" class="container no-user-select"
ref="container" ref="container"
:refresh="softRefreshSync"
:allowSwipe="allowSwipe" :allowSwipe="allowSwipe"
@refresh="softRefresh" :state="state"
:loading="loading > 0"
> >
<!-- Loading indicator -->
<XLoadingIcon class="loading-icon centered" v-if="loading" />
<!-- Static top matter --> <!-- Static top matter -->
<TopMatter ref="topmatter" /> <TopMatter ref="topmatter" />
@ -275,7 +278,7 @@ export default defineComponent({
// Do a soft refresh if the query changes // Do a soft refresh if the query changes
else if (JSON.stringify(from.query) !== JSON.stringify(to.query)) { else if (JSON.stringify(from.query) !== JSON.stringify(to.query)) {
await this.softRefreshInternal(true); await this.softRefreshSync();
} }
// Check if viewer is supposed to be open // Check if viewer is supposed to be open
@ -370,14 +373,19 @@ export default defineComponent({
* when changing the configuration * when changing the configuration
*/ */
softRefresh() { softRefresh() {
this.softRefreshInternal(false); this._softRefreshInternal(false);
},
/** Fetch and re-process days (sync can be awaited) */
async softRefreshSync() {
await this._softRefreshInternal(true);
}, },
/** /**
* Fetch and re-process days (can be awaited if sync). * Fetch and re-process days (can be awaited if sync).
* Do not pass this function as a callback directly. * Do not pass this function as a callback directly.
*/ */
async softRefreshInternal(sync: boolean) { async _softRefreshInternal(sync: boolean) {
this.refs.selectionManager.clear(); this.refs.selectionManager.clear();
this.fetchDayQueue = []; // reset queue this.fetchDayQueue = []; // reset queue

View File

@ -31,6 +31,7 @@ export default defineComponent({
.loading-icon { .loading-icon {
z-index: 100000; // above everything z-index: 100000; // above everything
pointer-events: none;
.higher { .higher {
position: relative; position: relative;