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

View File

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

View File

@ -1,10 +1,12 @@
import { useTeamsStore } from "@/stores/teams"; import { useTeamsStore } from "@/stores/teams";
import { useInvitesStore } from "@/stores/teams/invites";
import { computed } from "vue"; import { computed } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
export function useTeamDetails() { export function useTeamDetails() {
const route = useRoute(); const route = useRoute();
const teamsStore = useTeamsStore(); const teamsStore = useTeamsStore();
const invitesStore = useInvitesStore();
const teamId = computed(() => Number(route.params.id)); const teamId = computed(() => Number(route.params.id));
@ -13,7 +15,7 @@ export function useTeamDetails() {
}); });
const invites = computed(() => { const invites = computed(() => {
return teamsStore.teamInvites[teamId.value]; return invitesStore.teamInvites[teamId.value];
}); });
const teamMembers = computed(() => { 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 { defineStore } from "pinia";
import { computed, reactive, ref, type Reactive, type Ref } from "vue"; import { reactive, type Reactive } from "vue";
import { useClientStore } from "./client"; import { useClientStore } from "./client";
import { useAuthStore } from "./auth"; import { useAuthStore } from "./auth";
import moment from "moment"; import { type TeamSchema, type RoleSchema, type ViewTeamMembersResponse } from "@/client";
export type TeamMap = { [id: number]: TeamSchema }; export type TeamMap = { [id: number]: TeamSchema };
export const useTeamsStore = defineStore("teams", () => { export const useTeamsStore = defineStore("teams", () => {
const authStore = useAuthStore(); const authStore = useAuthStore();
const clientStore = useClientStore(); const clientStore = useClientStore();
const client = clientStore.client; const client = clientStore.client;
const teams: Reactive<{ [id: number]: TeamSchema }> = reactive({}); const teams: Reactive<{ [id: number]: TeamSchema }> = reactive({});
const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({}); const teamMembers: Reactive<{ [id: number]: ViewTeamMembersResponse[] }> = reactive({});
const teamInvites: Reactive<{ [id: number]: TeamInviteSchema[] }> = reactive({ });
const teamIntegrations = reactive<{ [id: number]: TeamIntegrationSchema[] }>({ });
async function fetchTeams() { async function fetchTeams() {
return clientStore.call( const response = await clientStore.call(
fetchTeams.name, fetchTeams.name,
() => client.default.getTeams(), () => client.default.getTeams()
(response) => { );
response.teams.forEach((team) => { response.teams.forEach((team) => {
teams[team.id] = team; teams[team.id] = team;
}); });
return response; return response;
} }
)
}
async function fetchTeam(id: number) { async function fetchTeam(id: number) {
return clientStore.call( const response = await clientStore.call(
fetchTeam.name, fetchTeam.name,
() => client.default.getTeam(id.toString()), () => client.default.getTeam(id.toString())
(response) => { );
teams[response.team.id] = response.team; teams[response.team.id] = response.team;
return response; return response;
} }
);
}
async function fetchTeamMembers(id: number) { async function fetchTeamMembers(id: number) {
return clientStore.call( const response = await clientStore.call(
fetchTeam.name, fetchTeamMembers.name,
() => client.default.getTeamMembers(id.toString()), () => client.default.getTeamMembers(id.toString())
(response) => { );
response = response teamMembers[id] = response.map((member): ViewTeamMembersResponse => {
.map((member): ViewTeamMembersResponse => { member.roles = member.roles.sort((a, b) => (a.isMain === b.isMain ? 0 : a.isMain ? -1 : 1));
// 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; return member;
}); });
console.log(response); console.log(teamMembers[id]);
teamMembers[id] = response; return teamMembers[id];
return response;
}
);
} }
async function createTeam(teamName: string, tz: string, minuteOffset: number) { 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[]) { async function updateRoles(teamId: number, playerId: string, roles: RoleSchema[]) {
return await client.default return await client.default.editMemberRoles(teamId.toString(), playerId, { roles });
.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;
});
} }
async function leaveTeam(teamId: number) { async function leaveTeam(teamId: number) {
return client.default return client.default.removePlayerFromTeam(teamId.toString(), authStore.steamId);
.removePlayerFromTeam(teamId.toString(), authStore.steamId);
} }
return { return {
teams, teams,
teamInvites,
teamMembers, teamMembers,
fetchTeams, fetchTeams,
fetchTeam, fetchTeam,
fetchTeamMembers, fetchTeamMembers,
createTeam, createTeam,
updateRoles, updateRoles,
getInvites,
createInvite,
consumeInvite,
revokeInvite,
leaveTeam, 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 IntegrationDetails from "@/components/IntegrationDetails.vue";
import { useTeamDetails } from "@/composables/team-details"; import { useTeamDetails } from "@/composables/team-details";
import { useTeamsStore } from "@/stores/teams"; import { useTeamsStore } from "@/stores/teams";
import { useIntegrationsStore } from "@/stores/teams/integrations";
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
const teamsStore = useTeamsStore(); const teamsStore = useTeamsStore();
const { const integrationsStore = useIntegrationsStore();
teamId, const { teamId } = useTeamDetails();
} = useTeamDetails();
const integrations = computed(() => teamsStore.teamIntegrations[teamId.value]); const integrations = computed(() => integrationsStore.teamIntegrations[teamId.value]);
function createIntegration() { function createIntegration() {
teamsStore.createIntegration(teamId.value, "discord"); integrationsStore.createIntegration(teamId.value, "discord");
} }
onMounted(() => { onMounted(() => {
teamsStore.fetchTeam(teamId.value) teamsStore.fetchTeam(teamId.value)
.then(() => teamsStore.getIntegrations(teamId.value)); .then(() => integrationsStore.getIntegrations(teamId.value));
}); });
</script> </script>

View File

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