mirror of
https://github.com/lukevella/rallly.git
synced 2025-07-27 13:17:51 +02:00
Adjust permissions for unclaimed participants
This commit is contained in:
parent
5c991d7011
commit
ccc2896b2d
3 changed files with 31 additions and 24 deletions
|
@ -13,7 +13,6 @@ import {
|
||||||
CreateCommentPayload,
|
CreateCommentPayload,
|
||||||
} from "../../api-client/create-comment";
|
} from "../../api-client/create-comment";
|
||||||
import { requiredString } from "../../utils/form-validation";
|
import { requiredString } from "../../utils/form-validation";
|
||||||
import Badge from "../badge";
|
|
||||||
import Button from "../button";
|
import Button from "../button";
|
||||||
import CompactButton from "../compact-button";
|
import CompactButton from "../compact-button";
|
||||||
import Dropdown, { DropdownItem } from "../dropdown";
|
import Dropdown, { DropdownItem } from "../dropdown";
|
||||||
|
@ -24,7 +23,7 @@ import TruncatedLinkify from "../poll/truncated-linkify";
|
||||||
import UserAvatar from "../poll/user-avatar";
|
import UserAvatar from "../poll/user-avatar";
|
||||||
import { usePoll } from "../poll-context";
|
import { usePoll } from "../poll-context";
|
||||||
import { usePreferences } from "../preferences/use-preferences";
|
import { usePreferences } from "../preferences/use-preferences";
|
||||||
import { useSession } from "../session";
|
import { isUnclaimed, useSession } from "../session";
|
||||||
|
|
||||||
export interface DiscussionProps {
|
export interface DiscussionProps {
|
||||||
pollId: string;
|
pollId: string;
|
||||||
|
@ -125,7 +124,9 @@ const Discussion: React.VoidFunctionComponent<DiscussionProps> = ({
|
||||||
<AnimatePresence initial={false}>
|
<AnimatePresence initial={false}>
|
||||||
{comments.map((comment) => {
|
{comments.map((comment) => {
|
||||||
const canDelete =
|
const canDelete =
|
||||||
poll.role === "admin" || session.ownsObject(comment);
|
poll.role === "admin" ||
|
||||||
|
session.ownsObject(comment) ||
|
||||||
|
isUnclaimed(comment);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|
|
@ -15,11 +15,10 @@ import Trash from "@/components/icons/trash.svg";
|
||||||
import { usePoll } from "@/components/poll-context";
|
import { usePoll } from "@/components/poll-context";
|
||||||
|
|
||||||
import { requiredString } from "../../utils/form-validation";
|
import { requiredString } from "../../utils/form-validation";
|
||||||
import Badge from "../badge";
|
|
||||||
import Button from "../button";
|
import Button from "../button";
|
||||||
import { styleMenuItem } from "../menu-styles";
|
import { styleMenuItem } from "../menu-styles";
|
||||||
import NameInput from "../name-input";
|
import NameInput from "../name-input";
|
||||||
import { useSession } from "../session";
|
import { isUnclaimed, useSession } from "../session";
|
||||||
import TimeZonePicker from "../time-zone-picker";
|
import TimeZonePicker from "../time-zone-picker";
|
||||||
import PollOptions from "./mobile-poll/poll-options";
|
import PollOptions from "./mobile-poll/poll-options";
|
||||||
import TimeSlotOptions from "./mobile-poll/time-slot-options";
|
import TimeSlotOptions from "./mobile-poll/time-slot-options";
|
||||||
|
@ -66,7 +65,7 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
? participantById[selectedParticipantId]
|
? participantById[selectedParticipantId]
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const [editable, setEditable] = React.useState(false);
|
const [isEditing, setIsEditing] = React.useState(false);
|
||||||
|
|
||||||
const [shouldShowSaveButton, setShouldShowSaveButton] = React.useState(false);
|
const [shouldShowSaveButton, setShouldShowSaveButton] = React.useState(false);
|
||||||
const formRef = React.useRef<HTMLFormElement>(null);
|
const formRef = React.useRef<HTMLFormElement>(null);
|
||||||
|
@ -129,7 +128,7 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
{
|
{
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
resolve(data);
|
resolve(data);
|
||||||
setEditable(false);
|
setIsEditing(false);
|
||||||
},
|
},
|
||||||
onError: reject,
|
onError: reject,
|
||||||
},
|
},
|
||||||
|
@ -139,7 +138,7 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
onSuccess: (newParticipant) => {
|
onSuccess: (newParticipant) => {
|
||||||
setSelectedParticipantId(newParticipant.id);
|
setSelectedParticipantId(newParticipant.id);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
setEditable(false);
|
setIsEditing(false);
|
||||||
},
|
},
|
||||||
onError: reject,
|
onError: reject,
|
||||||
});
|
});
|
||||||
|
@ -152,13 +151,13 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
<Listbox
|
<Listbox
|
||||||
value={selectedParticipantId}
|
value={selectedParticipantId}
|
||||||
onChange={setSelectedParticipantId}
|
onChange={setSelectedParticipantId}
|
||||||
disabled={editable}
|
disabled={isEditing}
|
||||||
>
|
>
|
||||||
<div className="menu min-w-0 grow">
|
<div className="menu min-w-0 grow">
|
||||||
<Listbox.Button
|
<Listbox.Button
|
||||||
as={Button}
|
as={Button}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
disabled={!editable}
|
disabled={!isEditing}
|
||||||
data-testid="participant-selector"
|
data-testid="participant-selector"
|
||||||
>
|
>
|
||||||
<div className="min-w-0 grow text-left">
|
<div className="min-w-0 grow text-left">
|
||||||
|
@ -206,14 +205,16 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
</Listbox.Options>
|
</Listbox.Options>
|
||||||
</div>
|
</div>
|
||||||
</Listbox>
|
</Listbox>
|
||||||
{!poll.closed && !editable ? (
|
{!poll.closed && !isEditing ? (
|
||||||
selectedParticipant &&
|
selectedParticipant &&
|
||||||
(role === "admin" || session.ownsObject(selectedParticipant)) ? (
|
(role === "admin" ||
|
||||||
|
session.ownsObject(selectedParticipant) ||
|
||||||
|
isUnclaimed(selectedParticipant)) ? (
|
||||||
<div className="flex space-x-3">
|
<div className="flex space-x-3">
|
||||||
<Button
|
<Button
|
||||||
icon={<Pencil />}
|
icon={<Pencil />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditable(true);
|
setIsEditing(true);
|
||||||
reset({
|
reset({
|
||||||
name: selectedParticipant.name,
|
name: selectedParticipant.name,
|
||||||
votes: selectedParticipant.votes.map(
|
votes: selectedParticipant.votes.map(
|
||||||
|
@ -241,17 +242,17 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
icon={<PlusCircle />}
|
icon={<PlusCircle />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
reset({ name: "", votes: [] });
|
reset({ name: "", votes: [] });
|
||||||
setEditable(true);
|
setIsEditing(true);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
New
|
New
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
) : null}
|
) : null}
|
||||||
{editable ? (
|
{isEditing ? (
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setEditable(false);
|
setIsEditing(false);
|
||||||
reset();
|
reset();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -273,7 +274,7 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
<PollOptions
|
<PollOptions
|
||||||
selectedParticipantId={selectedParticipantId}
|
selectedParticipantId={selectedParticipantId}
|
||||||
options={pollContext.options}
|
options={pollContext.options}
|
||||||
editable={editable}
|
editable={isEditing}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case "timeSlot":
|
case "timeSlot":
|
||||||
|
@ -281,13 +282,13 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
<TimeSlotOptions
|
<TimeSlotOptions
|
||||||
selectedParticipantId={selectedParticipantId}
|
selectedParticipantId={selectedParticipantId}
|
||||||
options={pollContext.options}
|
options={pollContext.options}
|
||||||
editable={editable}
|
editable={isEditing}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})()}
|
})()}
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{shouldShowSaveButton && editable ? (
|
{shouldShowSaveButton && isEditing ? (
|
||||||
<motion.button
|
<motion.button
|
||||||
type="button"
|
type="button"
|
||||||
variants={{
|
variants={{
|
||||||
|
@ -309,7 +310,7 @@ const MobilePoll: React.VoidFunctionComponent<PollProps> = ({ pollId }) => {
|
||||||
) : null}
|
) : null}
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{editable ? (
|
{isEditing ? (
|
||||||
<motion.div
|
<motion.div
|
||||||
variants={{
|
variants={{
|
||||||
hidden: { opacity: 0, y: -100, height: 0 },
|
hidden: { opacity: 0, y: -100, height: 0 },
|
||||||
|
|
|
@ -11,14 +11,16 @@ export type SessionProps = {
|
||||||
user: UserSessionData | null;
|
user: UserSessionData | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type ParticipantOrComment = {
|
||||||
|
userId: string | null;
|
||||||
|
guestId: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
type SessionContextValue = {
|
type SessionContextValue = {
|
||||||
logout: () => Promise<void>;
|
logout: () => Promise<void>;
|
||||||
user: (UserSessionData & { shortName: string }) | null;
|
user: (UserSessionData & { shortName: string }) | null;
|
||||||
refresh: () => void;
|
refresh: () => void;
|
||||||
ownsObject: (obj: {
|
ownsObject: (obj: ParticipantOrComment) => boolean;
|
||||||
userId: string | null;
|
|
||||||
guestId: string | null;
|
|
||||||
}) => boolean;
|
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -101,3 +103,6 @@ export const withSession = <P extends SessionProps>(
|
||||||
ComposedComponent.displayName = component.displayName;
|
ComposedComponent.displayName = component.displayName;
|
||||||
return ComposedComponent;
|
return ComposedComponent;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isUnclaimed = (obj: ParticipantOrComment) =>
|
||||||
|
!obj.guestId && !obj.userId;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue