refactor: Split teams store into separate stores

Refactor the codebase to move invites and integrations logic into
separate stores. This change improves the separation of concerns and
maintainability by isolating the invites and integrations logic from
the teams store.
master
John Montagu, the 4th Earl of Sandvich 2024-11-19 01:06:44 -08:00
parent 6f504802d7
commit 436359594b
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
8 changed files with 135 additions and 173 deletions

View File

@ -1,10 +1,10 @@
<script setup lang="ts">
import { type TeamInviteSchema } from "../client";
import { useTeamsStore } from "../stores/teams";
import { useInvitesStore } from "../stores/teams/invites";
import { computed, type PropType } from "vue";
import moment from "moment";
const teamsStore = useTeamsStore();
const invitesStore = useInvitesStore();
const createdAt = computed(() => moment(props.invite.createdAt).format("L LT"));
@ -26,7 +26,7 @@ function copyLink() {
}
function revokeInvite() {
teamsStore.revokeInvite(props.invite.teamId, props.invite.key);
invitesStore.revokeInvite(props.invite.teamId, props.invite.key);
}
</script>

View File

@ -1,9 +1,8 @@
<script setup lang="ts">
import type { PlayerTeamRole } from "../player";
import { computed, type PropType, ref, watch } from "vue";
import { useTeamsStore } from "../stores/teams";
import { useRosterStore } from "../stores/roster";
import { type ViewTeamMembersResponse, type TeamSchema, RoleSchema } from "@/client";
import { type ViewTeamMembersResponse, type TeamSchema, type RoleSchema } from "@/client";
import SvgIcon from "@jamescoyle/vue-icon";
import { mdiCrown } from "@mdi/js";
import RoleTag from "../components/RoleTag.vue";
@ -24,32 +23,12 @@ const teamsStore = useTeamsStore();
const rosterStore = useRosterStore();
//const roles = computed({
// get: () => ({
// "PocketScout": "",
// "FlankScout": "",
// "PocketSoldier": "",
// "Roamer": "",
// "Demoman": "",
// "Medic": "",
// }),
//});
const isEditing = ref(false);
// this is the roles of the player we are editing
const roles = ref<(RoleSchema | undefined)[]>([]);
const updatedRoles = ref<RoleSchema[]>([]);
//const rolesMap = reactive({
// "Role.PocketScout": undefined,
// "Role.FlankScout": undefined,
// "Role.PocketSoldier": undefined,
// "Role.Roamer": undefined,
// "Role.Demoman": undefined,
// "Role.Medic": undefined,
//});
const possibleRoles = [
"PocketScout",
"FlankScout",

View File

@ -1,10 +1,12 @@
import { useTeamsStore } from "@/stores/teams";
import { useInvitesStore } from "@/stores/teams/invites";
import { computed } from "vue";
import { useRoute } from "vue-router";
export function useTeamDetails() {
const route = useRoute();
const teamsStore = useTeamsStore();
const invitesStore = useInvitesStore();
const teamId = computed(() => Number(route.params.id));
@ -13,7 +15,7 @@ export function useTeamDetails() {
});
const invites = computed(() => {
return teamsStore.teamInvites[teamId.value];
return invitesStore.teamInvites[teamId.value];
});
const teamMembers = computed(() => {

View File

@ -1,70 +1,50 @@
import Cacheable from "@/cacheable";
import { AvailabilitfClient, type TeamInviteSchema, type RoleSchema, type TeamSchema, type ViewTeamMembersResponse, type ViewTeamResponse, type ViewTeamsResponse, type TeamIntegrationSchema, type AbstractTeamIntegrationSchema } from "@/client";
import { defineStore } from "pinia";
import { computed, reactive, ref, type Reactive, type Ref } from "vue";
import { reactive, type Reactive } from "vue";
import { useClientStore } from "./client";
import { useAuthStore } from "./auth";
import moment from "moment";
import { type TeamSchema, type RoleSchema, type ViewTeamMembersResponse } from "@/client";
export type TeamMap = { [id: number]: TeamSchema };
export const useTeamsStore = defineStore("teams", () => {
const authStore = useAuthStore();
const clientStore = useClientStore();
const client = clientStore.client;
const teams: Reactive<{ [id: number]: TeamSchema }> = reactive({ });
const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({ });
const teamInvites: Reactive<{ [id: number]: TeamInviteSchema[] }> = reactive({ });
const teamIntegrations = reactive<{ [id: number]: TeamIntegrationSchema[] }>({ });
const teams: Reactive<{ [id: number]: TeamSchema }> = reactive({});
const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({});
async function fetchTeams() {
return clientStore.call(
const response = await clientStore.call(
fetchTeams.name,
() => client.default.getTeams(),
(response) => {
response.teams.forEach((team) => {
teams[team.id] = team;
});
return response;
}
)
() => client.default.getTeams()
);
response.teams.forEach((team) => {
teams[team.id] = team;
});
return response;
}
async function fetchTeam(id: number) {
return clientStore.call(
const response = await clientStore.call(
fetchTeam.name,
() => client.default.getTeam(id.toString()),
(response) => {
teams[response.team.id] = response.team;
return response;
}
() => client.default.getTeam(id.toString())
);
teams[response.team.id] = response.team;
return response;
}
async function fetchTeamMembers(id: number) {
return clientStore.call(
fetchTeam.name,
() => client.default.getTeamMembers(id.toString()),
(response) => {
response = response
.map((member): ViewTeamMembersResponse => {
// TODO: snake_case to camelCase
member.roles = member.roles
.sort((a, b) => {
if (a.isMain == b.isMain) {
return 0;
}
return a.isMain ? -1 : 1;
});
return member;
});
console.log(response);
teamMembers[id] = response;
return response;
}
const response = await clientStore.call(
fetchTeamMembers.name,
() => client.default.getTeamMembers(id.toString())
);
teamMembers[id] = response.map((member): ViewTeamMembersResponse => {
member.roles = member.roles.sort((a, b) => (a.isMain === b.isMain ? 0 : a.isMain ? -1 : 1));
return member;
});
console.log(teamMembers[id]);
return teamMembers[id];
}
async function createTeam(teamName: string, tz: string, minuteOffset: number) {
@ -76,114 +56,21 @@ export const useTeamsStore = defineStore("teams", () => {
}
async function updateRoles(teamId: number, playerId: string, roles: RoleSchema[]) {
return await client.default
.editMemberRoles(teamId.toString(), playerId, {
roles,
});
}
async function getInvites(teamId: number) {
return clientStore.call(
getInvites.name,
() => client.default.getInvites(teamId.toString()),
(response) => {
teamInvites[teamId] = response;
return response;
}
);
}
async function createInvite(teamId: number) {
return client.default.createInvite(teamId.toString())
.then((response) => {
teamInvites[teamId].push(response);
return response;
})
}
async function consumeInvite(teamId: number, key: string) {
return client.default.consumeInvite(teamId.toString(), key)
.then((response) => {
teamInvites[teamId] = teamInvites[teamId]
.filter((invite) => invite.key != key);
return response;
});
}
async function revokeInvite(teamId: number, key: string) {
return client.default.revokeInvite(teamId.toString(), key)
.then((response) => {
teamInvites[teamId] = teamInvites[teamId]
.filter((invite) => invite.key != key);
return response;
});
}
async function getIntegrations(teamId: number) {
return client.default.getIntegrations(teamId.toString())
.then((response) => {
teamIntegrations[teamId] = response;
return response;
});
}
async function createIntegration(teamId: number, integrationType: string) {
return client.default
.createIntegration(teamId.toString(), integrationType)
.then((response) => {
teamIntegrations[teamId].push(response);
return response;
});
}
async function deleteIntegration(teamId: number, integrationId: number) {
return client.default
.deleteIntegration(teamId.toString(), integrationId.toString())
.then((response) => {
teamIntegrations[teamId] = teamIntegrations[teamId]
.filter((integration) => integration.id != integrationId);
return response;
});
}
async function updateIntegration(
teamId: number,
integration: AbstractTeamIntegrationSchema,
) {
return client.default
.updateIntegration(teamId.toString(), integration.id.toString(), integration)
.then((response) => {
const index = teamIntegrations[teamId]
.findIndex((x) => x.id == integration.id);
teamIntegrations[teamId][index] = response;
return response;
});
return await client.default.editMemberRoles(teamId.toString(), playerId, { roles });
}
async function leaveTeam(teamId: number) {
return client.default
.removePlayerFromTeam(teamId.toString(), authStore.steamId);
return client.default.removePlayerFromTeam(teamId.toString(), authStore.steamId);
}
return {
teams,
teamInvites,
teamMembers,
fetchTeams,
fetchTeam,
fetchTeamMembers,
createTeam,
updateRoles,
getInvites,
createInvite,
consumeInvite,
revokeInvite,
leaveTeam,
// TODO: move to separate store
teamIntegrations,
getIntegrations,
createIntegration,
deleteIntegration,
updateIntegration,
};
});

View File

@ -0,0 +1,44 @@
import { defineStore } from "pinia";
import { reactive, type Reactive } from "vue";
import { useClientStore } from "../client";
import { type TeamIntegrationSchema, type AbstractTeamIntegrationSchema } from "@/client";
export const useIntegrationsStore = defineStore("integrations", () => {
const clientStore = useClientStore();
const client = clientStore.client;
const teamIntegrations = reactive<{ [id: number]: TeamIntegrationSchema[] }>({});
async function getIntegrations(teamId: number) {
const response = await client.default.getIntegrations(teamId.toString());
teamIntegrations[teamId] = response;
return response;
}
async function createIntegration(teamId: number, integrationType: string) {
const response = await client.default.createIntegration(teamId.toString(), integrationType);
teamIntegrations[teamId].push(response);
return response;
}
async function deleteIntegration(teamId: number, integrationId: number) {
const response = await client.default.deleteIntegration(teamId.toString(), integrationId.toString());
teamIntegrations[teamId] = teamIntegrations[teamId].filter((integration) => integration.id != integrationId);
return response;
}
async function updateIntegration(teamId: number, integration: AbstractTeamIntegrationSchema) {
const response = await client.default.updateIntegration(teamId.toString(), integration.id.toString(), integration);
const index = teamIntegrations[teamId].findIndex((x) => x.id == integration.id);
teamIntegrations[teamId][index] = response;
return response;
}
return {
teamIntegrations,
getIntegrations,
createIntegration,
deleteIntegration,
updateIntegration,
};
});

View File

@ -0,0 +1,48 @@
import { defineStore } from "pinia";
import { reactive, type Reactive } from "vue";
import { useClientStore } from "../client";
import { type TeamInviteSchema } from "@/client";
export const useInvitesStore = defineStore("invites", () => {
const clientStore = useClientStore();
const client = clientStore.client;
const teamInvites: Reactive<{ [id: number]: TeamInviteSchema[] }> = reactive({});
async function getInvites(teamId: number) {
return clientStore.call(
getInvites.name,
() => client.default.getInvites(teamId.toString()),
(response) => {
teamInvites[teamId] = response;
return response;
}
);
}
async function createInvite(teamId: number) {
const response = await client.default.createInvite(teamId.toString());
teamInvites[teamId].push(response);
return response;
}
async function consumeInvite(teamId: number, key: string) {
const response = await client.default.consumeInvite(teamId.toString(), key);
teamInvites[teamId] = teamInvites[teamId].filter((invite) => invite.key != key);
return response;
}
async function revokeInvite(teamId: number, key: string) {
const response = await client.default.revokeInvite(teamId.toString(), key);
teamInvites[teamId] = teamInvites[teamId].filter((invite) => invite.key != key);
return response;
}
return {
teamInvites,
getInvites,
createInvite,
consumeInvite,
revokeInvite,
};
});

View File

@ -2,22 +2,22 @@
import IntegrationDetails from "@/components/IntegrationDetails.vue";
import { useTeamDetails } from "@/composables/team-details";
import { useTeamsStore } from "@/stores/teams";
import { useIntegrationsStore } from "@/stores/teams/integrations";
import { computed, onMounted, ref } from "vue";
const teamsStore = useTeamsStore();
const {
teamId,
} = useTeamDetails();
const integrationsStore = useIntegrationsStore();
const { teamId } = useTeamDetails();
const integrations = computed(() => teamsStore.teamIntegrations[teamId.value]);
const integrations = computed(() => integrationsStore.teamIntegrations[teamId.value]);
function createIntegration() {
teamsStore.createIntegration(teamId.value, "discord");
integrationsStore.createIntegration(teamId.value, "discord");
}
onMounted(() => {
teamsStore.fetchTeam(teamId.value)
.then(() => teamsStore.getIntegrations(teamId.value));
.then(() => integrationsStore.getIntegrations(teamId.value));
});
</script>

View File

@ -3,8 +3,10 @@ import { useTeamDetails } from "@/composables/team-details";
import { useTeamsStore } from "@/stores/teams";
import { onMounted } from "vue";
import InviteEntry from "@/components/InviteEntry.vue";
import { useInvitesStore } from "@/stores/teams/invites";
const teamsStore = useTeamsStore();
const invitesStore = useInvitesStore();
const {
team,
@ -13,12 +15,12 @@ const {
} = useTeamDetails();
function createInvite() {
teamsStore.createInvite(team.value.id);
invitesStore.createInvite(team.value.id);
}
onMounted(() => {
teamsStore.fetchTeam(teamId.value)
.then(() => teamsStore.getInvites(teamId.value));
.then(() => invitesStore.getInvites(teamId.value));
});
</script>