mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-05-13 22:42:38 -05:00
Allow user to leave group Closes #749
This commit is contained in:
parent
436b44d2d8
commit
fc835eae55
|
|
@ -9,39 +9,47 @@ import { GroupMembers } from "./GroupMembers";
|
|||
|
||||
export function GroupCard({
|
||||
group,
|
||||
canTakeAction = false,
|
||||
type,
|
||||
action,
|
||||
showAction,
|
||||
ranked,
|
||||
lookingForMatch,
|
||||
isOwnGroup = false,
|
||||
}: {
|
||||
group: LookingLoaderDataGroup;
|
||||
canTakeAction?: boolean;
|
||||
type?: "LIKES_GIVEN" | "NEUTRAL" | "LIKES_RECEIVED";
|
||||
action: Exclude<LookingActionSchema["_action"], "UNEXPIRE">;
|
||||
showAction: boolean;
|
||||
ranked?: boolean;
|
||||
lookingForMatch: boolean;
|
||||
isOwnGroup?: boolean;
|
||||
}) {
|
||||
const fetcher = useFetcher();
|
||||
|
||||
const buttonText = () => {
|
||||
if (isOwnGroup) return "Stop looking";
|
||||
if (type === "LIKES_GIVEN") return "Undo";
|
||||
if (type === "NEUTRAL") return "Let's play?";
|
||||
|
||||
return lookingForMatch ? "Match up" : "Group up";
|
||||
};
|
||||
const buttonValue = (): LookingActionSchema["_action"] => {
|
||||
if (isOwnGroup) return "LOOK_AGAIN";
|
||||
if (type === "LIKES_GIVEN") return "UNLIKE";
|
||||
if (type === "NEUTRAL") return "LIKE";
|
||||
|
||||
return lookingForMatch ? "MATCH_UP" : "UNITE_GROUPS";
|
||||
switch (action) {
|
||||
case "LEAVE_GROUP":
|
||||
return "Leave group";
|
||||
case "LIKE":
|
||||
return "Let's play?";
|
||||
case "UNLIKE":
|
||||
return "Undo";
|
||||
case "UNITE_GROUPS":
|
||||
return "Group up";
|
||||
case "MATCH_UP":
|
||||
return "Match up";
|
||||
case "LOOK_AGAIN":
|
||||
return "Stop looking";
|
||||
default:
|
||||
throw new Error(`Invalid group action type: ${action}`);
|
||||
}
|
||||
};
|
||||
const buttonVariant = (): ButtonProps["variant"] => {
|
||||
if (isOwnGroup) return "minimal-destructive";
|
||||
|
||||
return type === "LIKES_GIVEN" ? "destructive" : undefined;
|
||||
switch (action) {
|
||||
case "LEAVE_GROUP":
|
||||
case "LOOK_AGAIN":
|
||||
return "minimal-destructive";
|
||||
case "UNLIKE":
|
||||
return "destructive";
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -60,14 +68,14 @@ export function GroupCard({
|
|||
</div>
|
||||
)}
|
||||
<input type="hidden" name="targetGroupId" value={group.id} />
|
||||
{type === "LIKES_RECEIVED" && (
|
||||
{action === "UNITE_GROUPS" && (
|
||||
<input
|
||||
type="hidden"
|
||||
name="targetGroupSize"
|
||||
value={group.members?.length ?? -1}
|
||||
/>
|
||||
)}
|
||||
{canTakeAction && (
|
||||
{showAction && (
|
||||
<Button
|
||||
className={
|
||||
isOwnGroup
|
||||
|
|
@ -76,7 +84,7 @@ export function GroupCard({
|
|||
}
|
||||
type="submit"
|
||||
name="_action"
|
||||
value={buttonValue()}
|
||||
value={action}
|
||||
tiny
|
||||
variant={buttonVariant()}
|
||||
loading={fetcher.state !== "idle"}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,28 @@ export function setInactive(id: string) {
|
|||
});
|
||||
}
|
||||
|
||||
export function leaveGroup({
|
||||
groupId,
|
||||
memberId,
|
||||
}: {
|
||||
groupId: string;
|
||||
memberId: string;
|
||||
}) {
|
||||
// delete many so we don't throw in case
|
||||
// the group was just integrated into another
|
||||
// group
|
||||
return db.lfgGroupMember.deleteMany({
|
||||
where: {
|
||||
groupId,
|
||||
memberId,
|
||||
// no escaping group if match has been formed
|
||||
group: {
|
||||
matchId: null,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function unexpire(groupId: string) {
|
||||
return db.lfgGroup.update({
|
||||
where: {
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ const lookingActionSchema = z.union([
|
|||
z.object({
|
||||
_action: z.literal("LOOK_AGAIN"),
|
||||
}),
|
||||
z.object({
|
||||
_action: z.literal("LEAVE_GROUP"),
|
||||
}),
|
||||
z.object({
|
||||
_action: z.literal("UNEXPIRE"),
|
||||
}),
|
||||
|
|
@ -84,10 +87,12 @@ export const action: ActionFunction = async ({ request, context }) => {
|
|||
const ownGroup = await LFGGroup.findActiveByMember(user);
|
||||
validate(ownGroup, "No active group");
|
||||
validate(ownGroup.looking, "Group is not looking");
|
||||
validate(isGroupAdmin({ group: ownGroup, user }), "Not group admin");
|
||||
|
||||
const validateIsGroupAdmin = () =>
|
||||
validate(isGroupAdmin({ group: ownGroup, user }), "Not group admin");
|
||||
switch (data._action) {
|
||||
case "UNITE_GROUPS": {
|
||||
validateIsGroupAdmin();
|
||||
validate(
|
||||
canUniteWithGroup({
|
||||
ownGroupType: ownGroup.type,
|
||||
|
|
@ -132,6 +137,7 @@ export const action: ActionFunction = async ({ request, context }) => {
|
|||
break;
|
||||
}
|
||||
case "MATCH_UP": {
|
||||
validateIsGroupAdmin();
|
||||
const groupToMatchUpWith = await LFGGroup.findById(data.targetGroupId);
|
||||
validate(groupToMatchUpWith, "Invalid targetGroupId");
|
||||
|
||||
|
|
@ -142,6 +148,7 @@ export const action: ActionFunction = async ({ request, context }) => {
|
|||
break;
|
||||
}
|
||||
case "UNLIKE": {
|
||||
validateIsGroupAdmin();
|
||||
await LFGGroup.unlike({
|
||||
likerId: ownGroup.id,
|
||||
targetId: data.targetGroupId,
|
||||
|
|
@ -149,6 +156,7 @@ export const action: ActionFunction = async ({ request, context }) => {
|
|||
break;
|
||||
}
|
||||
case "LIKE": {
|
||||
validateIsGroupAdmin();
|
||||
await LFGGroup.like({
|
||||
likerId: ownGroup.id,
|
||||
targetId: data.targetGroupId,
|
||||
|
|
@ -156,10 +164,16 @@ export const action: ActionFunction = async ({ request, context }) => {
|
|||
break;
|
||||
}
|
||||
case "LOOK_AGAIN": {
|
||||
validateIsGroupAdmin();
|
||||
await LFGGroup.setInactive(ownGroup.id);
|
||||
return redirect("/play");
|
||||
}
|
||||
case "LEAVE_GROUP": {
|
||||
await LFGGroup.leaveGroup({ memberId: user.id, groupId: ownGroup.id });
|
||||
return redirect("/play");
|
||||
}
|
||||
case "UNEXPIRE": {
|
||||
validateIsGroupAdmin();
|
||||
await LFGGroup.unexpire(ownGroup.id);
|
||||
break;
|
||||
}
|
||||
|
|
@ -288,12 +302,23 @@ export default function LookingPage() {
|
|||
const lookingForMatch = data.ownGroup.members?.length === LFG_GROUP_FULL_SIZE;
|
||||
|
||||
const columns = [
|
||||
{ type: "LIKES_GIVEN", groups: data.likedGroups, title: "Liked" },
|
||||
{ type: "NEUTRAL", groups: data.neutralGroups, title: "Neutral" },
|
||||
{
|
||||
type: "LIKES_GIVEN",
|
||||
groups: data.likedGroups,
|
||||
title: "Liked",
|
||||
action: "UNLIKE",
|
||||
},
|
||||
{
|
||||
type: "NEUTRAL",
|
||||
groups: data.neutralGroups,
|
||||
title: "Neutral",
|
||||
action: "LIKE",
|
||||
},
|
||||
{
|
||||
type: "LIKES_RECEIVED",
|
||||
groups: data.likerGroups,
|
||||
title: lookingForMatch ? "Match up" : "Group up",
|
||||
action: lookingForMatch ? "MATCH_UP" : "UNITE_GROUPS",
|
||||
},
|
||||
] as const;
|
||||
|
||||
|
|
@ -312,10 +337,11 @@ export default function LookingPage() {
|
|||
<GroupCard
|
||||
group={data.ownGroup}
|
||||
ranked={data.ownGroup.ranked}
|
||||
lookingForMatch={false}
|
||||
isOwnGroup
|
||||
// we can stop looking even if the group expired
|
||||
canTakeAction={data.isCaptain}
|
||||
action={data.isCaptain ? "LOOK_AGAIN" : "LEAVE_GROUP"}
|
||||
// we can stop looking or leave team
|
||||
// even if team is expired
|
||||
showAction
|
||||
/>
|
||||
<LookingInfoText lastUpdated={lastUpdated} />
|
||||
<hr className="play-looking__divider" />
|
||||
|
|
@ -335,14 +361,13 @@ export default function LookingPage() {
|
|||
<GroupCard
|
||||
key={group.id}
|
||||
group={group}
|
||||
canTakeAction={canTakeAction}
|
||||
type={column.type}
|
||||
action={column.action}
|
||||
showAction={canTakeAction}
|
||||
ranked={
|
||||
column.type === "LIKES_RECEIVED" && !lookingForMatch
|
||||
? data.ownGroup.ranked
|
||||
: group.ranked
|
||||
}
|
||||
lookingForMatch={lookingForMatch}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
@ -360,14 +385,13 @@ export default function LookingPage() {
|
|||
<GroupCard
|
||||
key={group.id}
|
||||
group={group}
|
||||
canTakeAction={canTakeAction}
|
||||
type={column.type}
|
||||
action={column.action}
|
||||
showAction={canTakeAction}
|
||||
ranked={
|
||||
column.type === "LIKES_RECEIVED" && !lookingForMatch
|
||||
? data.ownGroup.ranked
|
||||
: group.ranked
|
||||
}
|
||||
lookingForMatch={lookingForMatch}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user