feat: loading spinner on views navigation

pull/1373/head
Ramires Viana 2021-04-16 12:47:50 +00:00
parent b92152693f
commit 976eb5583d
13 changed files with 185 additions and 77 deletions

View File

@ -77,7 +77,7 @@
opacity: 0;
}
.spinner {
#loading .spinner {
width: 70px;
text-align: center;
position: fixed;
@ -87,7 +87,7 @@
transform: translate(-50%, -50%);
}
.spinner > div {
#loading .spinner > div {
width: 18px;
height: 18px;
background-color: #333;
@ -97,12 +97,12 @@
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
.spinner .bounce1 {
#loading .spinner .bounce1 {
-webkit-animation-delay: -0.32s;
animation-delay: -0.32s;
}
.spinner .bounce2 {
#loading .spinner .bounce2 {
-webkit-animation-delay: -0.16s;
animation-delay: -0.16s;
}

View File

@ -16,7 +16,7 @@ body {
#loading {
background: var(--background);
}
#loading .spinner div, #previewer .loading .spinner div {
#loading .spinner div, main .spinner div {
background: var(--icon);
}

View File

@ -17,6 +17,48 @@
color: var(--blue);
}
main .spinner {
display: block;
text-align: center;
line-height: 0;
padding: 1em 0;
}
main .spinner > div {
width: .8em;
height: .8em;
margin: 0 .1em;
font-size: 1em;
background-color: rgba(0, 0, 0, 0.3);
border-radius: 100%;
display: inline-block;
animation: sk-bouncedelay 1.4s infinite ease-in-out both;
}
main .spinner .bounce1 {
animation-delay: -0.32s;
}
main .spinner .bounce2 {
animation-delay: -0.16s;
}
.delayed {
animation: delayed linear 100ms;
}
@keyframes delayed {
0% {
opacity: 0;
}
99% {
opacity: 0;
}
100% {
opacity: 1;
}
}
/* * * * * * * * * * * * * * * *
* ACTION *
* * * * * * * * * * * * * * * */
@ -204,6 +246,20 @@
right: 0.5em;
}
#previewer .spinner {
text-align: center;
position: fixed;
top: calc(50% + 1.85em);
left: 50%;
transform: translate(-50%, -50%);
}
#previewer .spinner > div {
width: 18px;
height: 18px;
background-color: white;
}
/* EDITOR */
#editor-container {
@ -217,11 +273,6 @@
overflow: hidden;
}
#previewer .loading {
height: 100%;
width: 100%;
}
#editor-container #editor {
height: calc(100vh - 8.4em);
}
@ -283,7 +334,6 @@
@keyframes spin {
100% {
-webkit-transform: rotate(-360deg);
transform: rotate(-360deg);
}
}

View File

@ -7,7 +7,12 @@
<errors v-if="error" :errorCode="errorCode" />
<component v-else-if="currentView" :is="currentView"></component>
<div v-else>
<h2 class="message">
<h2 class="message delayed">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
<span>{{ $t("files.loading") }}</span>
</h2>
</div>

View File

@ -34,6 +34,17 @@
</div>
</div>
<div v-if="loading">
<h2 class="message delayed">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
<span>{{ $t("files.loading") }}</span>
</h2>
</div>
<router-view></router-view>
</div>
</template>
@ -49,7 +60,7 @@ export default {
HeaderBar,
},
computed: {
...mapState(["user"]),
...mapState(["user", "loading"]),
},
};
</script>

View File

@ -19,7 +19,50 @@
<breadcrumbs :base="'/share/' + hash" />
<div v-if="!loading">
<div v-if="loading">
<h2 class="message delayed">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
<span>{{ $t("files.loading") }}</span>
</h2>
</div>
<div v-else-if="error">
<div v-if="error.message === '401'">
<div class="card floating" id="password">
<div v-if="attemptedPasswordLogin" class="share__wrong__password">
{{ $t("login.wrongCredentials") }}
</div>
<div class="card-title">
<h2>{{ $t("login.password") }}</h2>
</div>
<div class="card-content">
<input
v-focus
type="password"
:placeholder="$t('login.password')"
v-model="password"
@keyup.enter="fetchData"
/>
</div>
<div class="card-action">
<button
class="button button--flat"
@click="fetchData"
:aria-label="$t('buttons.submit')"
:title="$t('buttons.submit')"
>
{{ $t("buttons.submit") }}
</button>
</div>
</div>
</div>
<errors v-else :errorCode="errorCode" />
</div>
<div v-else>
<div class="share">
<div class="share__box share__box__info">
<div class="share__box__header">
@ -106,39 +149,6 @@
</div>
</div>
</div>
<div v-if="error">
<div v-if="error.message === '401'">
<div class="card floating" id="password">
<div v-if="attemptedPasswordLogin" class="share__wrong__password">
{{ $t("login.wrongCredentials") }}
</div>
<div class="card-title">
<h2>{{ $t("login.password") }}</h2>
</div>
<div class="card-content">
<input
v-focus
type="password"
:placeholder="$t('login.password')"
v-model="password"
@keyup.enter="fetchData"
/>
</div>
<div class="card-action">
<button
class="button button--flat"
@click="fetchData"
:aria-label="$t('buttons.submit')"
:title="$t('buttons.submit')"
>
{{ $t("buttons.submit") }}
</button>
</div>
</div>
</div>
<errors v-else :errorCode="errorCode" />
</div>
</div>
</template>
@ -256,9 +266,10 @@ export default {
this.token = file.token || "";
this.updateRequest(file);
this.setLoading(false);
} catch (e) {
this.error = e;
} finally {
this.setLoading(false);
}
},
keyEvent(event) {

View File

@ -114,8 +114,13 @@
/>
</div>
<div v-if="$store.state.loading">
<h2 class="message">
<div v-if="loading">
<h2 class="message delayed">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
<span>{{ $t("files.loading") }}</span>
</h2>
</div>
@ -284,7 +289,15 @@ export default {
};
},
computed: {
...mapState(["req", "selected", "user", "show", "multiple", "selected"]),
...mapState([
"req",
"selected",
"user",
"show",
"multiple",
"selected",
"loading",
]),
...mapGetters(["selectedCount"]),
nameSorted() {
return this.req.sorting.by === "name";
@ -799,17 +812,13 @@ export default {
viewMode: this.user.viewMode === "mosaic" ? "list" : "mosaic",
};
try {
await users.update(data, ["viewMode"]);
users.update(data, ["viewMode"]).catch(this.$showError);
// Await ensures correct value for setItemWeight()
await this.$store.commit("updateUser", data);
// Await ensures correct value for setItemWeight()
await this.$store.commit("updateUser", data);
this.setItemWeight();
this.fillWindow();
} catch (e) {
this.$showError(e);
}
this.setItemWeight();
this.fillWindow();
},
upload: function () {
if (

View File

@ -46,15 +46,14 @@
</template>
</header-bar>
<div class="loading" v-if="loading">
<div class="loading delayed" v-if="loading">
<div class="spinner">
<div class="bounce1"></div>
<div class="bounce2"></div>
<div class="bounce3"></div>
</div>
</div>
<template v-if="!loading">
<template v-else>
<div class="preview">
<ExtendedImage v-if="req.type == 'image'" :src="raw"></ExtendedImage>
<audio

View File

@ -1,5 +1,5 @@
<template>
<div class="row" v-if="settings !== null">
<div class="row" v-if="!loading">
<div class="column">
<form class="card" @submit.prevent="save">
<div class="card-title">
@ -170,7 +170,7 @@
</template>
<script>
import { mapState } from "vuex";
import { mapState, mapMutations } from "vuex";
import { settings as api } from "@/api";
import UserForm from "@/components/settings/UserForm";
import Rules from "@/components/settings/Rules";
@ -191,11 +191,13 @@ export default {
};
},
computed: {
...mapState(["user"]),
...mapState(["user", "loading"]),
isExecEnabled: () => enableExec,
},
async created() {
try {
this.setLoading(true);
const original = await api.get();
let settings = { ...original, commands: [] };
@ -210,11 +212,14 @@ export default {
this.originalSettings = original;
this.settings = settings;
this.setLoading(false);
} catch (e) {
this.$showError(e);
}
},
methods: {
...mapMutations(["setLoading"]),
capitalize(name, where = "_") {
if (where === "caps") where = /(?=[A-Z])/;
let splitted = name.split(where);

View File

@ -103,12 +103,13 @@ export default {
},
},
created() {
this.setLoading(false);
this.locale = this.user.locale;
this.hideDotfiles = this.user.hideDotfiles;
this.singleClick = this.user.singleClick;
},
methods: {
...mapMutations(["updateUser"]),
...mapMutations(["updateUser", "setLoading"]),
async updatePassword(event) {
event.preventDefault();

View File

@ -1,5 +1,5 @@
<template>
<div class="row">
<div class="row" v-if="!loading">
<div class="column">
<div class="card">
<div class="card-title">
@ -62,11 +62,11 @@ import { share as api, users } from "@/api";
import moment from "moment";
import { baseURL } from "@/utils/constants";
import Clipboard from "clipboard";
import { mapState } from "vuex";
import { mapState, mapMutations } from "vuex";
export default {
name: "shares",
computed: mapState(["user"]),
computed: mapState(["user", "loading"]),
data: function () {
return {
links: [],
@ -74,6 +74,8 @@ export default {
};
},
async created() {
this.setLoading(true);
try {
let links = await api.list();
if (this.user.perm.admin) {
@ -86,6 +88,8 @@ export default {
: "";
}
this.links = links;
this.setLoading(false);
} catch (e) {
this.$showError(e);
}
@ -100,6 +104,7 @@ export default {
this.clip.destroy();
},
methods: {
...mapMutations(["setLoading"]),
deleteLink: async function (event, link) {
event.preventDefault();

View File

@ -1,7 +1,7 @@
<template>
<div class="row">
<div class="row" v-if="!loading">
<div class="column">
<form v-if="loaded" @submit="save" class="card">
<form @submit="save" class="card">
<div class="card-title">
<h2 v-if="user.id === 0">{{ $t("settings.newUser") }}</h2>
<h2 v-else>{{ $t("settings.user") }} {{ user.username }}</h2>
@ -55,7 +55,7 @@
</template>
<script>
import { mapMutations } from "vuex";
import { mapState, mapMutations } from "vuex";
import { users as api, settings } from "@/api";
import UserForm from "@/components/settings/UserForm";
import deepClone from "lodash.clonedeep";
@ -69,7 +69,6 @@ export default {
return {
originalUser: null,
user: {},
loaded: false,
};
},
created() {
@ -79,6 +78,7 @@ export default {
isNew() {
return this.$route.path === "/settings/users/new";
},
...mapState(["loading"]),
},
watch: {
$route: "fetchData",
@ -88,8 +88,10 @@ export default {
},
},
methods: {
...mapMutations(["closeHovers", "showHover", "setUser"]),
...mapMutations(["closeHovers", "showHover", "setUser", "setLoading"]),
async fetchData() {
this.setLoading(true);
try {
if (this.isNew) {
let { defaults } = await settings.get();
@ -106,7 +108,7 @@ export default {
this.user = { ...(await api.get(id)) };
}
this.loaded = true;
this.setLoading(false);
} catch (e) {
this.$router.push({ path: "/settings/users/new" });
}

View File

@ -1,5 +1,5 @@
<template>
<div class="row">
<div class="row" v-if="!loading">
<div class="column">
<div class="card">
<div class="card-title">
@ -41,6 +41,7 @@
</template>
<script>
import { mapState, mapMutations } from "vuex";
import { users as api } from "@/api";
export default {
@ -51,11 +52,20 @@ export default {
};
},
async created() {
this.setLoading(true);
try {
this.users = await api.getAll();
this.setLoading(false);
} catch (e) {
this.$showError(e);
}
},
computed: {
...mapState(["loading"]),
},
methods: {
...mapMutations(["setLoading"]),
},
};
</script>