feat: Add vue-content-loader for loaders
							parent
							
								
									0f7995f0c2
								
							
						
					
					
						commit
						88969111ad
					
				| 
						 | 
					@ -20,6 +20,7 @@
 | 
				
			||||||
        "pinia": "^2.2.4",
 | 
					        "pinia": "^2.2.4",
 | 
				
			||||||
        "radix-vue": "^1.9.10",
 | 
					        "radix-vue": "^1.9.10",
 | 
				
			||||||
        "vue": "^3.5.12",
 | 
					        "vue": "^3.5.12",
 | 
				
			||||||
 | 
					        "vue-content-loader": "^2.0.1",
 | 
				
			||||||
        "vue-router": "^4.4.5",
 | 
					        "vue-router": "^4.4.5",
 | 
				
			||||||
        "vue-select": "^4.0.0-beta.6",
 | 
					        "vue-select": "^4.0.0-beta.6",
 | 
				
			||||||
        "vue3-tooltip": "^2.2.4"
 | 
					        "vue3-tooltip": "^2.2.4"
 | 
				
			||||||
| 
						 | 
					@ -7657,6 +7658,15 @@
 | 
				
			||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "license": "MIT"
 | 
					      "license": "MIT"
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/vue-content-loader": {
 | 
				
			||||||
 | 
					      "version": "2.0.1",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/vue-content-loader/-/vue-content-loader-2.0.1.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-pkof4+q2xmzNEdhqelxtJejeP/vQUJtLle4/v2ueG+HURqM9Q/GIGC8GJ2bVVWeLfTDET51jqimwQdmxJTlu0g==",
 | 
				
			||||||
 | 
					      "license": "MIT",
 | 
				
			||||||
 | 
					      "peerDependencies": {
 | 
				
			||||||
 | 
					        "vue": "^3"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/vue-eslint-parser": {
 | 
					    "node_modules/vue-eslint-parser": {
 | 
				
			||||||
      "version": "9.4.3",
 | 
					      "version": "9.4.3",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,6 +27,7 @@
 | 
				
			||||||
    "pinia": "^2.2.4",
 | 
					    "pinia": "^2.2.4",
 | 
				
			||||||
    "radix-vue": "^1.9.10",
 | 
					    "radix-vue": "^1.9.10",
 | 
				
			||||||
    "vue": "^3.5.12",
 | 
					    "vue": "^3.5.12",
 | 
				
			||||||
 | 
					    "vue-content-loader": "^2.0.1",
 | 
				
			||||||
    "vue-router": "^4.4.5",
 | 
					    "vue-router": "^4.4.5",
 | 
				
			||||||
    "vue-select": "^4.0.0-beta.6",
 | 
					    "vue-select": "^4.0.0-beta.6",
 | 
				
			||||||
    "vue3-tooltip": "^2.2.4"
 | 
					    "vue3-tooltip": "^2.2.4"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,6 +34,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  --surface-0: #ccd0da;
 | 
					  --surface-0: #ccd0da;
 | 
				
			||||||
  --base: #eff1f5;
 | 
					  --base: #eff1f5;
 | 
				
			||||||
 | 
					  --base-extra: #f5f6f7;
 | 
				
			||||||
  --mantle: #e6e9ef;
 | 
					  --mantle: #e6e9ef;
 | 
				
			||||||
  --crust: #dce0e8;
 | 
					  --crust: #dce0e8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,25 @@
 | 
				
			||||||
<script setup lang="ts">
 | 
					<script setup lang="ts">
 | 
				
			||||||
import { onMounted } from "vue";
 | 
					import { onMounted, ref } from "vue";
 | 
				
			||||||
import { useTeamsStore } from "../stores/teams";
 | 
					import { useTeamsStore } from "../stores/teams";
 | 
				
			||||||
import { RouterLink } from "vue-router";
 | 
					import { RouterLink } from "vue-router";
 | 
				
			||||||
import { useAuthStore } from "@/stores/auth";
 | 
					import { useAuthStore } from "@/stores/auth";
 | 
				
			||||||
import InviteKeyDialog from "./InviteKeyDialog.vue";
 | 
					import InviteKeyDialog from "./InviteKeyDialog.vue";
 | 
				
			||||||
 | 
					import { ContentLoader } from "vue-content-loader";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const teams = useTeamsStore();
 | 
					const teams = useTeamsStore();
 | 
				
			||||||
 | 
					const isLoading = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const authStore = useAuthStore();
 | 
					const authStore = useAuthStore();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
  teams.fetchTeams();
 | 
					  isLoading.value = true;
 | 
				
			||||||
 | 
					  authStore.getUser()
 | 
				
			||||||
 | 
					    .then(() => {
 | 
				
			||||||
 | 
					      teams.fetchTeams()
 | 
				
			||||||
 | 
					        .then(() => {
 | 
				
			||||||
 | 
					          isLoading.value = false;
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -34,8 +43,20 @@ onMounted(() => {
 | 
				
			||||||
    <div v-if="!authStore.isLoggedIn">
 | 
					    <div v-if="!authStore.isLoggedIn">
 | 
				
			||||||
      Log in to view your teams.
 | 
					      Log in to view your teams.
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
 | 
					    <div v-else-if="isLoading || true">
 | 
				
			||||||
 | 
					      <ContentLoader :speed="1">
 | 
				
			||||||
 | 
					        <circle cx="10" cy="20" r="8" />
 | 
				
			||||||
 | 
					        <rect x="25" y="15" rx="5" ry="5" width="220" height="10" />
 | 
				
			||||||
 | 
					        <circle cx="10" cy="50" r="8" />
 | 
				
			||||||
 | 
					        <rect x="25" y="45" rx="5" ry="5" width="220" height="10" />
 | 
				
			||||||
 | 
					        <circle cx="10" cy="80" r="8" />
 | 
				
			||||||
 | 
					        <rect x="25" y="75" rx="5" ry="5" width="220" height="10" />
 | 
				
			||||||
 | 
					        <circle cx="10" cy="110" r="8" />
 | 
				
			||||||
 | 
					        <rect x="25" y="105" rx="5" ry="5" width="220" height="10" />
 | 
				
			||||||
 | 
					      </ContentLoader>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
    <div
 | 
					    <div
 | 
				
			||||||
      v-if="teams.teamsWithRole"
 | 
					      v-else-if="teams.teamsWithRole"
 | 
				
			||||||
      v-for="(team, _, i) in teams.teamsWithRole"
 | 
					      v-for="(team, _, i) in teams.teamsWithRole"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <div class="team-item">
 | 
					      <div class="team-item">
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,11 +2,13 @@ import { defineStore } from "pinia";
 | 
				
			||||||
import { ref } from "vue";
 | 
					import { ref } from "vue";
 | 
				
			||||||
import { useClientStore } from "./client";
 | 
					import { useClientStore } from "./client";
 | 
				
			||||||
import { useRouter, type LocationQuery } from "vue-router";
 | 
					import { useRouter, type LocationQuery } from "vue-router";
 | 
				
			||||||
 | 
					import { type PlayerSchema } from "@/client";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const useAuthStore = defineStore("auth", () => {
 | 
					export const useAuthStore = defineStore("auth", () => {
 | 
				
			||||||
  const clientStore = useClientStore();
 | 
					  const clientStore = useClientStore();
 | 
				
			||||||
  const client = clientStore.client;
 | 
					  const client = clientStore.client;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const user = ref<PlayerSchema | null>(null);
 | 
				
			||||||
  const steamId = ref("");
 | 
					  const steamId = ref("");
 | 
				
			||||||
  const username = ref("");
 | 
					  const username = ref("");
 | 
				
			||||||
  const isLoggedIn = ref(false);
 | 
					  const isLoggedIn = ref(false);
 | 
				
			||||||
| 
						 | 
					@ -16,16 +18,27 @@ export const useAuthStore = defineStore("auth", () => {
 | 
				
			||||||
  const router = useRouter();
 | 
					  const router = useRouter();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  async function getUser() {
 | 
					  async function getUser() {
 | 
				
			||||||
    hasCheckedAuth.value = true;
 | 
					    if (hasCheckedAuth.value) {
 | 
				
			||||||
 | 
					      if (!isLoggedIn.value) {
 | 
				
			||||||
 | 
					        throw new Error("Not logged in");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return user.value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return clientStore.call(
 | 
					    return clientStore.call(
 | 
				
			||||||
      getUser.name,
 | 
					      getUser.name,
 | 
				
			||||||
      () => client.default.getUser(),
 | 
					      () => client.default.getUser(),
 | 
				
			||||||
      (response) => {
 | 
					      (response) => {
 | 
				
			||||||
 | 
					        hasCheckedAuth.value = true;
 | 
				
			||||||
        isLoggedIn.value = true;
 | 
					        isLoggedIn.value = true;
 | 
				
			||||||
        steamId.value = response.steamId;
 | 
					        steamId.value = response.steamId;
 | 
				
			||||||
        username.value = response.username;
 | 
					        username.value = response.username;
 | 
				
			||||||
 | 
					        user.value = response;
 | 
				
			||||||
        return response;
 | 
					        return response;
 | 
				
			||||||
      }
 | 
					      },
 | 
				
			||||||
 | 
					      undefined,
 | 
				
			||||||
 | 
					      () => hasCheckedAuth.value = true,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,9 @@ export const useClientStore = defineStore("client", () => {
 | 
				
			||||||
  function call<T>(
 | 
					  function call<T>(
 | 
				
			||||||
    key: string,
 | 
					    key: string,
 | 
				
			||||||
    apiCall: () => CancelablePromise<T>,
 | 
					    apiCall: () => CancelablePromise<T>,
 | 
				
			||||||
    thenOnce?: (result: T) => T
 | 
					    thenOnce?: (result: T) => T,
 | 
				
			||||||
 | 
					    catchOnce?: (error: any) => any,
 | 
				
			||||||
 | 
					    finallyOnce?: () => void,
 | 
				
			||||||
  ): Promise<T> {
 | 
					  ): Promise<T> {
 | 
				
			||||||
    console.log("Fetching call " + key);
 | 
					    console.log("Fetching call " + key);
 | 
				
			||||||
    if (!calls.has(key)) {
 | 
					    if (!calls.has(key)) {
 | 
				
			||||||
| 
						 | 
					@ -26,6 +28,14 @@ export const useClientStore = defineStore("client", () => {
 | 
				
			||||||
        promise.then(thenOnce);
 | 
					        promise.then(thenOnce);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (catchOnce) {
 | 
				
			||||||
 | 
					        promise.catch(catchOnce);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (finallyOnce) {
 | 
				
			||||||
 | 
					        promise.finally(finallyOnce);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return promise;
 | 
					      return promise;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return calls.get(key) as Promise<T>;
 | 
					    return calls.get(key) as Promise<T>;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import EventList from "@/components/EventList.vue";
 | 
				
			||||||
import { useTeamsEventsStore } from "@/stores/teams/events";
 | 
					import { useTeamsEventsStore } from "@/stores/teams/events";
 | 
				
			||||||
import MatchCard from "@/components/MatchCard.vue";
 | 
					import MatchCard from "@/components/MatchCard.vue";
 | 
				
			||||||
import { useMatchesStore } from "@/stores/matches";
 | 
					import { useMatchesStore } from "@/stores/matches";
 | 
				
			||||||
 | 
					import { ContentLoader } from "vue-content-loader";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const route = useRoute();
 | 
					const route = useRoute();
 | 
				
			||||||
const teamsStore = useTeamsStore();
 | 
					const teamsStore = useTeamsStore();
 | 
				
			||||||
| 
						 | 
					@ -28,14 +29,17 @@ const key = computed(() => route.query.key);
 | 
				
			||||||
const teamsEventsStore = useTeamsEventsStore();
 | 
					const teamsEventsStore = useTeamsEventsStore();
 | 
				
			||||||
const events = computed(() => teamsEventsStore.teamEvents[teamId.value]);
 | 
					const events = computed(() => teamsEventsStore.teamEvents[teamId.value]);
 | 
				
			||||||
const matches = computed(() => matchesStore.recentMatches);
 | 
					const matches = computed(() => matchesStore.recentMatches);
 | 
				
			||||||
 | 
					const isLoading = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
onMounted(() => {
 | 
					onMounted(() => {
 | 
				
			||||||
 | 
					  isLoading.value = true;
 | 
				
			||||||
  let doFetchTeam = () => {
 | 
					  let doFetchTeam = () => {
 | 
				
			||||||
    teamsStore.fetchTeam(teamId.value)
 | 
					    teamsStore.fetchTeam(teamId.value)
 | 
				
			||||||
      .then(() => {
 | 
					      .then(() => {
 | 
				
			||||||
        teamsStore.fetchTeamMembers(teamId.value);
 | 
					        teamsStore.fetchTeamMembers(teamId.value);
 | 
				
			||||||
        teamsEventsStore.fetchTeamEvents(teamId.value);
 | 
					        teamsEventsStore.fetchTeamEvents(teamId.value);
 | 
				
			||||||
        matchesStore.fetchRecentMatchesForTeam(teamId.value, 5);
 | 
					        matchesStore.fetchRecentMatchesForTeam(teamId.value, 5);
 | 
				
			||||||
 | 
					        isLoading.value = false;
 | 
				
			||||||
      });
 | 
					      });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +59,14 @@ onMounted(() => {
 | 
				
			||||||
        <div class="left">
 | 
					        <div class="left">
 | 
				
			||||||
          <center class="margin">
 | 
					          <center class="margin">
 | 
				
			||||||
            <h1>
 | 
					            <h1>
 | 
				
			||||||
 | 
					              <template v-if="isLoading || true">
 | 
				
			||||||
 | 
					                <content-loader view-box="0 0 250 10">
 | 
				
			||||||
 | 
					                  <rect x="0" y="0" rx="3" ry="3" width="250" height="10" />
 | 
				
			||||||
 | 
					                </content-loader>
 | 
				
			||||||
 | 
					              </template>
 | 
				
			||||||
 | 
					              <template v-else>
 | 
				
			||||||
                {{ team.teamName }}
 | 
					                {{ team.teamName }}
 | 
				
			||||||
 | 
					              </template>
 | 
				
			||||||
            </h1>
 | 
					            </h1>
 | 
				
			||||||
            <span class="aside">
 | 
					            <span class="aside">
 | 
				
			||||||
              Formed on {{ creationDate }}
 | 
					              Formed on {{ creationDate }}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ import { useTeamDetails } from "@/composables/team-details";
 | 
				
			||||||
import { useTeamsStore } from "@/stores/teams";
 | 
					import { useTeamsStore } from "@/stores/teams";
 | 
				
			||||||
import { useIntegrationsStore } from "@/stores/teams/integrations";
 | 
					import { useIntegrationsStore } from "@/stores/teams/integrations";
 | 
				
			||||||
import { onMounted, ref } from "vue";
 | 
					import { onMounted, ref } from "vue";
 | 
				
			||||||
 | 
					import { ContentLoader } from "vue-content-loader";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const teamsStore = useTeamsStore();
 | 
					const teamsStore = useTeamsStore();
 | 
				
			||||||
const integrationsStore = useIntegrationsStore();
 | 
					const integrationsStore = useIntegrationsStore();
 | 
				
			||||||
| 
						 | 
					@ -32,7 +33,14 @@ onMounted(() => {
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <div class="team-integrations">
 | 
					  <div class="team-integrations">
 | 
				
			||||||
    <div v-if="isLoading">
 | 
					    <div v-if="isLoading">
 | 
				
			||||||
      <LoaderContainer />
 | 
					      <ContentLoader>
 | 
				
			||||||
 | 
					        <rect x="0" y="0" rx="3" ry="3" width="250" height="10" />
 | 
				
			||||||
 | 
					        <rect x="20" y="20" rx="3" ry="3" width="220" height="10" />
 | 
				
			||||||
 | 
					        <rect x="20" y="40" rx="3" ry="3" width="170" height="10" />
 | 
				
			||||||
 | 
					        <rect x="0" y="60" rx="3" ry="3" width="250" height="10" />
 | 
				
			||||||
 | 
					        <rect x="20" y="80" rx="3" ry="3" width="200" height="10" />
 | 
				
			||||||
 | 
					        <rect x="20" y="100" rx="3" ry="3" width="80" height="10" />
 | 
				
			||||||
 | 
					      </ContentLoader>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
    <template v-else>
 | 
					    <template v-else>
 | 
				
			||||||
      <DiscordIntegrationForm v-model="integrationsStore.discordIntegration" />
 | 
					      <DiscordIntegrationForm v-model="integrationsStore.discordIntegration" />
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,12 +20,11 @@ onMounted(() => {
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
  <main>
 | 
					 | 
				
			||||||
  <div class="header">
 | 
					  <div class="header">
 | 
				
			||||||
      <h1>
 | 
					    <h2>
 | 
				
			||||||
      <i class="bi bi-trophy-fill margin"></i>
 | 
					      <i class="bi bi-trophy-fill margin"></i>
 | 
				
			||||||
      Matches
 | 
					      Matches
 | 
				
			||||||
      </h1>
 | 
					    </h2>
 | 
				
			||||||
    <div class="button-group">
 | 
					    <div class="button-group">
 | 
				
			||||||
      <AddMatchDialog />
 | 
					      <AddMatchDialog />
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
| 
						 | 
					@ -54,7 +53,6 @@ onMounted(() => {
 | 
				
			||||||
      </tr>
 | 
					      </tr>
 | 
				
			||||||
    </tbody>
 | 
					    </tbody>
 | 
				
			||||||
  </table>
 | 
					  </table>
 | 
				
			||||||
  </main>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style scoped>
 | 
					<style scoped>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,7 +100,10 @@ class Event(app_db.BaseModel):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ringers_needed_msg = ""
 | 
					        ringers_needed_msg = ""
 | 
				
			||||||
        if ringers_needed > 0:
 | 
					        if ringers_needed > 0:
 | 
				
			||||||
            ringers_needed_msg = f" **({ringers_needed} ringer(s) needed)**"
 | 
					            if ringers_needed == 1:
 | 
				
			||||||
 | 
					                ringers_needed_msg = " **(1 ringer needed)**"
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                ringers_needed_msg = f" **({ringers_needed} ringers needed)**"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        domain = os.environ.get("DOMAIN", "availabili.tf")
 | 
					        domain = os.environ.get("DOMAIN", "availabili.tf")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,7 +114,7 @@ class Event(app_db.BaseModel):
 | 
				
			||||||
            "",
 | 
					            "",
 | 
				
			||||||
            f"<t:{start_timestamp}:f>",
 | 
					            f"<t:{start_timestamp}:f>",
 | 
				
			||||||
            "\n".join(players_info),
 | 
					            "\n".join(players_info),
 | 
				
			||||||
            f"Max bipartite matching size: {matchings}" + ringers_needed_msg,
 | 
					            f"Maximum roles filled: {matchings}" + ringers_needed_msg,
 | 
				
			||||||
            "",
 | 
					            "",
 | 
				
			||||||
            "[Confirm attendance here]" +
 | 
					            "[Confirm attendance here]" +
 | 
				
			||||||
                f"(https://{domain}/team/id/{self.team.id}/events/{self.id})",
 | 
					                f"(https://{domain}/team/id/{self.team.id}/events/{self.id})",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue