diff --git a/src/components/SwipeRefresh.vue b/src/components/SwipeRefresh.vue
index 9221c1a6..5ceb57e4 100644
--- a/src/components/SwipeRefresh.vue
+++ b/src/components/SwipeRefresh.vue
@@ -1,6 +1,6 @@
@@ -18,6 +18,11 @@ export default defineComponent({
type: Boolean,
default: true,
},
+
+ loading: {
+ type: Boolean,
+ default: false,
+ },
},
emits: {
@@ -30,15 +35,50 @@ export default defineComponent({
end: 0,
updateFrame: 0,
progress: 0,
+ animate: false,
+ firstcycle: 0,
}),
+ mounted() {
+ this.animate = this.loading; // start if needed
+ },
+
+ watch: {
+ loading() {
+ if (this.loading) {
+ if (!this.animate) {
+ // Let the animation run for at least half cycle
+ this.firstcycle = window.setTimeout(() => {
+ this.firstcycle = 0;
+ this.animate = this.loading;
+ }, 750);
+ }
+ this.animate = this.loading;
+ } else {
+ if (!this.firstcycle) {
+ console.log('stop');
+ this.animate = this.loading;
+ }
+ }
+ },
+ },
+
computed: {
+ show() {
+ return (this.on && this.progress) || this.animate;
+ },
+
gradient() {
- const start = 50 - this.progress / 2;
- const end = 50 + this.progress / 2;
- const out = 'transparent';
- const progress = 'var(--color-primary)';
- return `linear-gradient(to right, ${out} ${start}%, ${progress} ${start}%, ${progress} ${end}%, ${out} ${end}%)`;
+ if (this.animate) {
+ // CSS animation below
+ return undefined;
+ }
+
+ // Pull down progress
+ const p = this.progress;
+ const outer = 'transparent';
+ const inner = 'var(--color-primary)';
+ return `radial-gradient(circle at center, ${inner} 0, ${inner} ${p}%, ${outer} ${p}%, ${outer} 100%)`;
},
},
@@ -89,9 +129,49 @@ export default defineComponent({
top: 0;
width: 100%;
height: 3px;
+ pointer-events: none;
html.native & {
top: 2px;
}
+
+ &.animate {
+ $progress-inside: radial-gradient(
+ circle at center,
+ transparent 0%,
+ transparent 1%,
+ var(--color-primary) 1%,
+ var(--color-primary) 100%
+ );
+ $progress-outside: radial-gradient(
+ circle at center,
+ var(--color-primary) 0%,
+ var(--color-primary) 1%,
+ transparent 1%,
+ transparent 100%
+ );
+
+ animation: swipe-loading 1.5s ease infinite;
+ background-position: center;
+
+ @keyframes swipe-loading {
+ 0% {
+ background-image: $progress-inside;
+ background-size: 100% 100%;
+ }
+ 49.99% {
+ background-image: $progress-inside;
+ background-size: 12000% 12000%;
+ }
+ 50% {
+ background-image: $progress-outside;
+ background-size: 100% 100%;
+ }
+ 100% {
+ background-image: $progress-outside;
+ background-size: 12000% 12000%;
+ }
+ }
+ }
}
diff --git a/src/components/Timeline.vue b/src/components/Timeline.vue
index a9788b19..691d5f84 100644
--- a/src/components/Timeline.vue
+++ b/src/components/Timeline.vue
@@ -1,8 +1,11 @@
-
-
-
-
+
@@ -371,18 +374,28 @@ export default defineComponent({
},
/**
- * Fetch and re-process days (can be awaited).
+ * Fetch and re-process days (can be awaited if sync).
* Do not pass this function as a callback directly.
*/
async softRefreshInternal(sync: boolean) {
this.refs.selectionManager.clear();
this.fetchDayQueue = []; // reset queue
+ // Fetch days and reset loading
+ this.updateLoading(1);
+ const doFetch = async () => {
+ try {
+ await this.fetchDays(true);
+ } finally {
+ this.updateLoading(-1);
+ }
+ };
+
// Fetch days
if (sync) {
- await this.fetchDays(true);
+ doFetch();
} else {
- utils.setRenewingTimeout(this, '_softRefreshInternalTimer', () => this.fetchDays(true), 30);
+ utils.setRenewingTimeout(this, '_softRefreshInternalTimer', doFetch, 30);
}
},