Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | 901x 1272x 1272x 1272x 1272x 1272x 1272x 1009x 3726x 784x 862x 3726x 3726x 862x 1272x 2942x 1272x 1847x 1272x 1152x 1272x 1272x 3x 10x 2x 2x 2x 2x 3765x 3765x 3765x 7452x 3726x | import type React from "react";
import { useTranslation } from "react-i18next";
import { isNonNullish } from "remeda";
import { LoadablePeer } from "~app/components/app/loadable-peer";
import { useTrpcMutationStates } from "~app/hooks/use-trpc-mutation-state";
import { useTRPC } from "~app/utils/trpc";
import { AvatarGroup } from "~components/avatar";
import { Select } from "~components/select";
import { useActionsHooksContext, useReceiptContext } from "./context";
import { useCanEdit, useIsOwner } from "./hooks";
import type { Item } from "./state";
import { SORT_USERS } from "./utils";
type Props = {
item: Item;
className?: string;
};
export const ReceiptItemPayers: React.FC<Props> = ({ item, className }) => {
const { t } = useTranslation("receipts");
const { participants } = useReceiptContext();
const { addItemPayer, removeItemPayer } = useActionsHooksContext();
const isOwner = useIsOwner();
const canEdit = useCanEdit();
const trpc = useTRPC();
const ownerPeerIds = participants
.filter((participant) => participant.role === "owner")
.map(({ peerId }) => peerId);
const possibleParticipantIds = participants
.map(({ peerId }) => peerId)
.filter((participantId) => !ownerPeerIds.includes(participantId));
const addedParticipantsIds = item.payers.map(({ peerId }) => peerId);
const notAddedParticipantsIds = new Set(
possibleParticipantIds.filter(
(participantId) => !addedParticipantsIds.includes(participantId),
),
);
const addConsumerMutationStates =
useTrpcMutationStates<"receiptItemConsumers.add">(
trpc.receiptItemConsumers.add.mutationKey(),
({ itemId }) => itemId === item.id,
);
const removeConsumerMutationStates =
useTrpcMutationStates<"receiptItemConsumers.remove">(
trpc.receiptItemConsumers.remove.mutationKey(),
({ itemId }) => itemId === item.id,
);
const defaultOwnerOnly = item.payers.length === 0;
return (
<Select
className={className}
label={t("item.payer.label")}
placeholder={t("item.payer.placeholder")}
selectedKeys={
defaultOwnerOnly
? ownerPeerIds
: item.payers.map(({ peerId }) => peerId)
}
disabledKeys={
addConsumerMutationStates.some(({ status }) => status === "pending") ||
removeConsumerMutationStates.some(({ status }) => status === "pending")
? possibleParticipantIds
: undefined
}
onSelectionChange={(nextSelected) => {
for (const id of addedParticipantsIds.filter(
(candidateId) => !nextSelected.includes(candidateId),
)) {
removeItemPayer(item.id, id);
}
for (const id of nextSelected.filter((candidateId) =>
notAddedParticipantsIds.has(candidateId),
)) {
addItemPayer(item.id, id, 1);
}
}}
renderValue={(selectedParticipants) => {
Eif (selectedParticipants.length === 1) {
// oxlint-disable-next-line typescript/no-non-null-assertion
const { peerId } = selectedParticipants[0]!;
return (
<LoadablePeer
key={peerId}
className={canEdit ? "cursor-pointer" : undefined}
avatarProps={{ size: "sm", dimmed: defaultOwnerOnly }}
id={peerId}
foreign={!isOwner}
/>
);
}
return (
<AvatarGroup
className={canEdit ? "cursor-pointer" : undefined}
size="sm"
>
{selectedParticipants
.map((participant) => participant.peerId)
.filter(isNonNullish)
.map((peerId) => (
<LoadablePeer key={peerId} id={peerId} foreign={!isOwner} />
))}
</AvatarGroup>
);
}}
items={participants.toSorted(SORT_USERS)}
getKey={({ peerId }) => peerId}
>
{({ peerId }) => <LoadablePeer id={peerId} foreign={!isOwner} />}
</Select>
);
};
|