Can join a family
This commit is contained in:
		@@ -2,6 +2,14 @@ import { APIClient } from "./ApiClient";
 | 
			
		||||
 | 
			
		||||
export interface Family {}
 | 
			
		||||
 | 
			
		||||
export enum JoinFamilyResult {
 | 
			
		||||
  TooManyRequests,
 | 
			
		||||
  InvalidCode,
 | 
			
		||||
  AlreadyMember,
 | 
			
		||||
  Error,
 | 
			
		||||
  Success,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class FamilyApi {
 | 
			
		||||
  /**
 | 
			
		||||
   * Create a new family
 | 
			
		||||
@@ -13,4 +21,29 @@ export class FamilyApi {
 | 
			
		||||
      jsonData: { name: name },
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Join an existing family
 | 
			
		||||
   */
 | 
			
		||||
  static async JoinFamily(code: string): Promise<JoinFamilyResult> {
 | 
			
		||||
    const res = await APIClient.exec({
 | 
			
		||||
      method: "POST",
 | 
			
		||||
      uri: "/family/join",
 | 
			
		||||
      allowFail: true,
 | 
			
		||||
      jsonData: { code: code },
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (res.status >= 200 && res.status < 300) return JoinFamilyResult.Success;
 | 
			
		||||
 | 
			
		||||
    switch (res.status) {
 | 
			
		||||
      case 429:
 | 
			
		||||
        return JoinFamilyResult.TooManyRequests;
 | 
			
		||||
      case 404:
 | 
			
		||||
        return JoinFamilyResult.InvalidCode;
 | 
			
		||||
      case 409:
 | 
			
		||||
        return JoinFamilyResult.AlreadyMember;
 | 
			
		||||
      default:
 | 
			
		||||
        return JoinFamilyResult.Error;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ interface Constraints {
 | 
			
		||||
  user_name_len: LenConstraint;
 | 
			
		||||
  password_len: LenConstraint;
 | 
			
		||||
  family_name_len: LenConstraint;
 | 
			
		||||
  invitation_code_len: LenConstraint;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface OIDCProvider {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ export function CreateFamilyDialog(p: {
 | 
			
		||||
  return (
 | 
			
		||||
    <TextInputDialog
 | 
			
		||||
      open={p.open}
 | 
			
		||||
      title="Creation d'une famille"
 | 
			
		||||
      title="Création d'une famille"
 | 
			
		||||
      text="Veuillez définir le nom que vous souhaitez donner à la famille créée :"
 | 
			
		||||
      label="Nom de la famille"
 | 
			
		||||
      minLen={ServerApi.Config.constraints.family_name_len.min}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										81
									
								
								geneit_app/src/dialogs/JoinFamilyDialog.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								geneit_app/src/dialogs/JoinFamilyDialog.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
			
		||||
import React from "react";
 | 
			
		||||
import { TextInputDialog } from "./TextInputDialog";
 | 
			
		||||
import { ServerApi } from "../api/ServerApi";
 | 
			
		||||
import { FamilyApi, JoinFamilyResult } from "../api/FamilyApi";
 | 
			
		||||
import { useAlert } from "../widgets/AlertDialogProvider";
 | 
			
		||||
 | 
			
		||||
export function JoinFamilyDialog(p: {
 | 
			
		||||
  open: boolean;
 | 
			
		||||
  onClose: () => void;
 | 
			
		||||
  onJoined: () => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  const [code, setCode] = React.useState("");
 | 
			
		||||
  const [joining, setJoining] = React.useState(false);
 | 
			
		||||
  const [error, setError] = React.useState<string>();
 | 
			
		||||
 | 
			
		||||
  const alert = useAlert();
 | 
			
		||||
 | 
			
		||||
  const cancel = () => {
 | 
			
		||||
    setCode("");
 | 
			
		||||
    setJoining(false);
 | 
			
		||||
    setError(undefined);
 | 
			
		||||
    p.onClose();
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const joinFamily = async () => {
 | 
			
		||||
    setJoining(true);
 | 
			
		||||
    setError(undefined);
 | 
			
		||||
    try {
 | 
			
		||||
      const res = await FamilyApi.JoinFamily(code);
 | 
			
		||||
 | 
			
		||||
      switch (res) {
 | 
			
		||||
        case JoinFamilyResult.Success:
 | 
			
		||||
          setCode("");
 | 
			
		||||
          p.onJoined();
 | 
			
		||||
          await alert.alert("La famille a été rejointe avec succès !");
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case JoinFamilyResult.TooManyRequests:
 | 
			
		||||
          setError("Trop de tentatives, veuillez réessayer ultérieurement...");
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case JoinFamilyResult.InvalidCode:
 | 
			
		||||
          setError("Le code spécifié est invalide !");
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case JoinFamilyResult.AlreadyMember:
 | 
			
		||||
          setError("Vous êtes déjà membre de cette famille !");
 | 
			
		||||
          break;
 | 
			
		||||
 | 
			
		||||
        case JoinFamilyResult.Error:
 | 
			
		||||
          setError("Erreur lors de la tentative de jonction à la famille !");
 | 
			
		||||
          break;
 | 
			
		||||
      }
 | 
			
		||||
    } catch (e) {
 | 
			
		||||
      console.error(e);
 | 
			
		||||
      setError("Echec de l'appel au serveur !");
 | 
			
		||||
    }
 | 
			
		||||
    setJoining(false);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <TextInputDialog
 | 
			
		||||
      open={p.open}
 | 
			
		||||
      title="Rejoindre une famille"
 | 
			
		||||
      text="Veuillez spécifier le code d'invitation de la famille :"
 | 
			
		||||
      label="Code de la famille"
 | 
			
		||||
      minLen={ServerApi.Config.constraints.invitation_code_len.min}
 | 
			
		||||
      maxLen={ServerApi.Config.constraints.invitation_code_len.max}
 | 
			
		||||
      onClose={cancel}
 | 
			
		||||
      onCancel={cancel}
 | 
			
		||||
      submitButton="Rejoindre la famille"
 | 
			
		||||
      value={code}
 | 
			
		||||
      onValueChange={setCode}
 | 
			
		||||
      isChecking={joining}
 | 
			
		||||
      checkingMessage="Jointure en cours..."
 | 
			
		||||
      errorIsBlocking={false}
 | 
			
		||||
      error={error}
 | 
			
		||||
      onSubmit={joinFamily}
 | 
			
		||||
    />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@@ -3,6 +3,7 @@ import { AsyncWidget } from "../widgets/AsyncWidget";
 | 
			
		||||
import { Family } from "../api/FamilyApi";
 | 
			
		||||
import { Button, Typography } from "@mui/material";
 | 
			
		||||
import { CreateFamilyDialog } from "../dialogs/CreateFamilyDialog";
 | 
			
		||||
import { JoinFamilyDialog } from "../dialogs/JoinFamilyDialog";
 | 
			
		||||
 | 
			
		||||
export function FamiliesListRoute(): React.ReactElement {
 | 
			
		||||
  const loadKey = React.useRef(1);
 | 
			
		||||
@@ -10,6 +11,7 @@ export function FamiliesListRoute(): React.ReactElement {
 | 
			
		||||
  const [families, setFamilies] = React.useState<Family[] | null>(null);
 | 
			
		||||
 | 
			
		||||
  const [createFamily, setCreateFamily] = React.useState(false);
 | 
			
		||||
  const [joinFamily, setJoinFamily] = React.useState(false);
 | 
			
		||||
 | 
			
		||||
  const load = async () => {
 | 
			
		||||
    // TODO : implement
 | 
			
		||||
@@ -24,6 +26,10 @@ export function FamiliesListRoute(): React.ReactElement {
 | 
			
		||||
    setCreateFamily(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onRequestJoinFamily = async () => {
 | 
			
		||||
    setJoinFamily(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <AsyncWidget
 | 
			
		||||
      loadKey={loadKey.current}
 | 
			
		||||
@@ -32,7 +38,10 @@ export function FamiliesListRoute(): React.ReactElement {
 | 
			
		||||
      build={() => (
 | 
			
		||||
        <>
 | 
			
		||||
          {families!!.length === 0 ? (
 | 
			
		||||
            <NoFamilyWidget onRequestCreateFamily={onRequestCreateFamily} />
 | 
			
		||||
            <NoFamilyWidget
 | 
			
		||||
              onRequestCreateFamily={onRequestCreateFamily}
 | 
			
		||||
              onRequestJoinFamily={onRequestJoinFamily}
 | 
			
		||||
            />
 | 
			
		||||
          ) : (
 | 
			
		||||
            <HasFamilysWidget onReload={reload} families={families!} />
 | 
			
		||||
          )}
 | 
			
		||||
@@ -46,6 +55,16 @@ export function FamiliesListRoute(): React.ReactElement {
 | 
			
		||||
              reload();
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
 | 
			
		||||
          {/** Join family dialog anchor */}
 | 
			
		||||
          <JoinFamilyDialog
 | 
			
		||||
            open={joinFamily}
 | 
			
		||||
            onClose={() => setJoinFamily(false)}
 | 
			
		||||
            onJoined={() => {
 | 
			
		||||
              setJoinFamily(false);
 | 
			
		||||
              reload();
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
        </>
 | 
			
		||||
      )}
 | 
			
		||||
    />
 | 
			
		||||
@@ -54,6 +73,7 @@ export function FamiliesListRoute(): React.ReactElement {
 | 
			
		||||
 | 
			
		||||
function NoFamilyWidget(p: {
 | 
			
		||||
  onRequestCreateFamily: () => void;
 | 
			
		||||
  onRequestJoinFamily: () => void;
 | 
			
		||||
}): React.ReactElement {
 | 
			
		||||
  return (
 | 
			
		||||
    <div style={{ flex: 1, display: "flex", alignItems: "center" }}>
 | 
			
		||||
@@ -74,7 +94,10 @@ function NoFamilyWidget(p: {
 | 
			
		||||
          onClick={p.onRequestCreateFamily}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
        <NoFamilyButton label="Rejoindre une famille" onClick={() => {}} />
 | 
			
		||||
        <NoFamilyButton
 | 
			
		||||
          label="Rejoindre une famille"
 | 
			
		||||
          onClick={p.onRequestJoinFamily}
 | 
			
		||||
        />
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -23,6 +23,7 @@ pub struct StaticConstraints {
 | 
			
		||||
    pub user_name_len: SizeConstraint,
 | 
			
		||||
    pub password_len: SizeConstraint,
 | 
			
		||||
    pub family_name_len: SizeConstraint,
 | 
			
		||||
    pub invitation_code_len: SizeConstraint,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for StaticConstraints {
 | 
			
		||||
@@ -32,6 +33,10 @@ impl Default for StaticConstraints {
 | 
			
		||||
            user_name_len: SizeConstraint::new(3, 30),
 | 
			
		||||
            password_len: SizeConstraint::new(8, 255),
 | 
			
		||||
            family_name_len: SizeConstraint::new(3, 30),
 | 
			
		||||
            invitation_code_len: SizeConstraint::new(
 | 
			
		||||
                FAMILY_INVITATION_CODE_LEN,
 | 
			
		||||
                FAMILY_INVITATION_CODE_LEN,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user