enhanced overall Chat experience

This commit is contained in:
PxlLoewe
2025-06-09 22:38:31 -07:00
parent ea78b41510
commit b4b7b4def2
7 changed files with 119 additions and 98 deletions

View File

@@ -85,18 +85,18 @@ export const Chat = () => {
Keine Chatpartner gefunden
</option>
)}
{filteredDispatcher?.length ||
(filteredAircrafts?.length && (
{(filteredDispatcher?.length || filteredAircrafts?.length) && (
<option disabled value="default">
Chatpartner auswählen
</option>
))}
)}
{filteredDispatcher?.map((dispatcher) => (
<option key={dispatcher.userId} value={dispatcher.userId}>
{dispatcher.zone} - {asPublicUser(dispatcher.publicUser).fullName}
</option>
))}
{filteredAircrafts?.map((aircraft) => (
<option key={aircraft.userId} value={aircraft.userId}>
{aircraft.Station.bosCallsignShort} -{" "}
@@ -111,7 +111,8 @@ export const Chat = () => {
const dispatcherUser = dispatcher?.find((d) => d.userId === addTabValue);
const user = aircraftUser || dispatcherUser;
if (!user) return;
addChat(addTabValue, asPublicUser(user.publicUser).fullName);
let role = "Station" in user ? user.Station.bosCallsignShort : user.zone;
addChat(addTabValue, `${asPublicUser(user.publicUser).fullName} (${role})`);
setSelectedChat(addTabValue);
}}
>
@@ -124,21 +125,13 @@ export const Chat = () => {
if (!chat) return null;
return (
<Fragment key={userId}>
<input
type="radio"
className="tab"
aria-label={`<${chat.name}>`}
checked={selectedChat === userId}
onClick={() => {
setChatNotification(userId, false);
}}
onChange={(e) => {
if (e.target.checked) {
// Handle tab change
setSelectedChat(userId);
}
}}
/>
<a
className={cn("indicator tab", selectedChat === userId && "tab-active")}
onClick={() => setSelectedChat(userId)}
>
{chat.name}
{chat.notification && <span className="indicator-item status status-info" />}
</a>
<div className="tab-content bg-base-100 border-base-300 p-6 overflow-y-auto">
{chat.messages.map((chatMessage) => {
const isSender = chatMessage.senderId === session.data?.user.id;
@@ -162,6 +155,12 @@ export const Chat = () => {
);
})}
</div>
{!selectedChat && (
<div role="alert" className="alert alert-info alert-outline">
<span>Wähle einen Nutzer aus und drücke auf + um einen Chat zu starten</span>
</div>
)}
{selectedChat && (
<div className="join">
<div className="w-full">
<label className="input join-item w-full">
@@ -220,6 +219,7 @@ export const Chat = () => {
)}
</button>
</div>
)}
</div>
</div>
)}

View File

@@ -372,6 +372,7 @@ export const MissionLayer = () => {
getMissionsAPI({
OR: [{ state: "draft" }, { state: "running" }],
}),
refetchInterval: 10_000,
});
const filteredMissions = useMemo(() => {

View File

@@ -46,7 +46,7 @@ export const useAudioStore = create<TalkState>((set, get) => ({
micDeviceId: null,
speakingParticipants: [],
micVolume: 1,
state: "disconnected",
state: "disconnected" as const,
remoteParticipants: 0,
connectionQuality: ConnectionQuality.Unknown,
room: null,
@@ -77,16 +77,23 @@ export const useAudioStore = create<TalkState>((set, get) => ({
set({ micDeviceId, micVolume });
},
toggleTalking: () => {
const { room, isTalking, micDeviceId, micVolume, speakingParticipants } = get();
const { room, isTalking, micDeviceId, micVolume, speakingParticipants, transmitBlocked } =
get();
if (!room) return;
if (speakingParticipants.length > 0 && !isTalking) {
if (speakingParticipants.length > 0 && !isTalking && !transmitBlocked) {
// Wenn andere sprechen, nicht reden
set({
message: "Rufgruppe besetzt",
transmitBlocked: true,
});
return;
} else if (!isTalking && transmitBlocked) {
set({
message: null,
transmitBlocked: false,
});
return;
}
// Todo: use micVolume
room.localParticipant.setMicrophoneEnabled(!isTalking, {

View File

@@ -30,21 +30,30 @@ export const useLeftMenuStore = create<ChatStore>((set, get) => ({
chatOpen: false,
selectedChat: null,
setChatOpen: (open: boolean) => set({ chatOpen: open }),
setSelectedChat: (chatId: string | null) => set({ selectedChat: chatId }),
setSelectedChat: (chatId: string | null) => {
const { setChatNotification } = get();
set({ selectedChat: chatId });
if (chatId) {
setChatNotification(chatId, false); // Set notification to false when chat is selected
}
},
setOwnId: (id: string) => set({ ownId: id }),
chats: {},
sendMessage: (userId: string, message: string) => {
return new Promise((resolve, reject) => {
if(dispatchSocket.connected){
dispatchSocket.emit("send-message", { userId, message }, ({ error }: { error?: string }) => {
if (dispatchSocket.connected) {
dispatchSocket.emit(
"send-message",
{ userId, message },
({ error }: { error?: string }) => {
if (error) {
reject(error);
} else {
resolve();
}
});
} else if(pilotSocket.connected){
},
);
} else if (pilotSocket.connected) {
pilotSocket.emit("send-message", { userId, message }, ({ error }: { error?: string }) => {
if (error) {
reject(error);
@@ -66,7 +75,6 @@ export const useLeftMenuStore = create<ChatStore>((set, get) => ({
setChatNotification: (userId, notification) => {
const chat = get().chats[userId];
if (!chat) return;
console.log("setChatNotification", userId, notification);
set((state) => {
return {
chats: {
@@ -95,7 +103,7 @@ export const useLeftMenuStore = create<ChatStore>((set, get) => ({
[userId]: {
...user,
name: isSender ? message.receiverName : message.senderName,
notification: !isSender && (state.selectedChat !== userId || !state.chatOpen),
notification: state.selectedChat !== userId || !state.chatOpen,
messages: [...user.messages, message], // Neuen Zustand erzeugen
},
},

View File

@@ -16,7 +16,7 @@ export default async function RootLayout({
}>) {
const session = await getServerSession();
if (!session || !session.user) {
if (!session || !session.user.firstname) {
redirect("/login");
}
if (!session.user.emailVerified) {

View File

@@ -53,7 +53,6 @@ export const Register = () => {
passwordConfirm: "",
},
});
console.log("Register form", form.formState.errors);
return (
<form
className="card-body"
@@ -67,6 +66,10 @@ export const Register = () => {
firstname: form.getValues("firstname"),
lastname: form.getValues("lastname"),
});
if ("error" in user) {
toast.error(user.error);
return;
}
await sendVerificationLink(user.id);
await signIn("credentials", {
callbackUrl: "/",

View File

@@ -27,7 +27,9 @@ export const register = async ({ password, ...user }: Omit<Prisma.UserCreateInpu
},
});
if (existingUser) {
throw new Error("Ein Nutzer mit dieser E-Mail-Adresse existiert bereits.");
return {
error: "Ein Nutzer mit dieser E-Mail-Adresse existiert bereits.",
};
}
const newUser = prisma.user.create({