enhanced overall Chat experience
This commit is contained in:
@@ -85,18 +85,18 @@ export const Chat = () => {
|
|||||||
Keine Chatpartner gefunden
|
Keine Chatpartner gefunden
|
||||||
</option>
|
</option>
|
||||||
)}
|
)}
|
||||||
{filteredDispatcher?.length ||
|
{(filteredDispatcher?.length || filteredAircrafts?.length) && (
|
||||||
(filteredAircrafts?.length && (
|
|
||||||
<option disabled value="default">
|
<option disabled value="default">
|
||||||
Chatpartner auswählen
|
Chatpartner auswählen
|
||||||
</option>
|
</option>
|
||||||
))}
|
)}
|
||||||
|
|
||||||
{filteredDispatcher?.map((dispatcher) => (
|
{filteredDispatcher?.map((dispatcher) => (
|
||||||
<option key={dispatcher.userId} value={dispatcher.userId}>
|
<option key={dispatcher.userId} value={dispatcher.userId}>
|
||||||
{dispatcher.zone} - {asPublicUser(dispatcher.publicUser).fullName}
|
{dispatcher.zone} - {asPublicUser(dispatcher.publicUser).fullName}
|
||||||
</option>
|
</option>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
{filteredAircrafts?.map((aircraft) => (
|
{filteredAircrafts?.map((aircraft) => (
|
||||||
<option key={aircraft.userId} value={aircraft.userId}>
|
<option key={aircraft.userId} value={aircraft.userId}>
|
||||||
{aircraft.Station.bosCallsignShort} -{" "}
|
{aircraft.Station.bosCallsignShort} -{" "}
|
||||||
@@ -111,7 +111,8 @@ export const Chat = () => {
|
|||||||
const dispatcherUser = dispatcher?.find((d) => d.userId === addTabValue);
|
const dispatcherUser = dispatcher?.find((d) => d.userId === addTabValue);
|
||||||
const user = aircraftUser || dispatcherUser;
|
const user = aircraftUser || dispatcherUser;
|
||||||
if (!user) return;
|
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);
|
setSelectedChat(addTabValue);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -124,21 +125,13 @@ export const Chat = () => {
|
|||||||
if (!chat) return null;
|
if (!chat) return null;
|
||||||
return (
|
return (
|
||||||
<Fragment key={userId}>
|
<Fragment key={userId}>
|
||||||
<input
|
<a
|
||||||
type="radio"
|
className={cn("indicator tab", selectedChat === userId && "tab-active")}
|
||||||
className="tab"
|
onClick={() => setSelectedChat(userId)}
|
||||||
aria-label={`<${chat.name}>`}
|
>
|
||||||
checked={selectedChat === userId}
|
{chat.name}
|
||||||
onClick={() => {
|
{chat.notification && <span className="indicator-item status status-info" />}
|
||||||
setChatNotification(userId, false);
|
</a>
|
||||||
}}
|
|
||||||
onChange={(e) => {
|
|
||||||
if (e.target.checked) {
|
|
||||||
// Handle tab change
|
|
||||||
setSelectedChat(userId);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="tab-content bg-base-100 border-base-300 p-6 overflow-y-auto">
|
<div className="tab-content bg-base-100 border-base-300 p-6 overflow-y-auto">
|
||||||
{chat.messages.map((chatMessage) => {
|
{chat.messages.map((chatMessage) => {
|
||||||
const isSender = chatMessage.senderId === session.data?.user.id;
|
const isSender = chatMessage.senderId === session.data?.user.id;
|
||||||
@@ -162,6 +155,12 @@ export const Chat = () => {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</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="join">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<label className="input join-item w-full">
|
<label className="input join-item w-full">
|
||||||
@@ -220,6 +219,7 @@ export const Chat = () => {
|
|||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -372,6 +372,7 @@ export const MissionLayer = () => {
|
|||||||
getMissionsAPI({
|
getMissionsAPI({
|
||||||
OR: [{ state: "draft" }, { state: "running" }],
|
OR: [{ state: "draft" }, { state: "running" }],
|
||||||
}),
|
}),
|
||||||
|
refetchInterval: 10_000,
|
||||||
});
|
});
|
||||||
|
|
||||||
const filteredMissions = useMemo(() => {
|
const filteredMissions = useMemo(() => {
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
|||||||
micDeviceId: null,
|
micDeviceId: null,
|
||||||
speakingParticipants: [],
|
speakingParticipants: [],
|
||||||
micVolume: 1,
|
micVolume: 1,
|
||||||
state: "disconnected",
|
state: "disconnected" as const,
|
||||||
remoteParticipants: 0,
|
remoteParticipants: 0,
|
||||||
connectionQuality: ConnectionQuality.Unknown,
|
connectionQuality: ConnectionQuality.Unknown,
|
||||||
room: null,
|
room: null,
|
||||||
@@ -77,16 +77,23 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
|||||||
set({ micDeviceId, micVolume });
|
set({ micDeviceId, micVolume });
|
||||||
},
|
},
|
||||||
toggleTalking: () => {
|
toggleTalking: () => {
|
||||||
const { room, isTalking, micDeviceId, micVolume, speakingParticipants } = get();
|
const { room, isTalking, micDeviceId, micVolume, speakingParticipants, transmitBlocked } =
|
||||||
|
get();
|
||||||
if (!room) return;
|
if (!room) return;
|
||||||
|
|
||||||
if (speakingParticipants.length > 0 && !isTalking) {
|
if (speakingParticipants.length > 0 && !isTalking && !transmitBlocked) {
|
||||||
// Wenn andere sprechen, nicht reden
|
// Wenn andere sprechen, nicht reden
|
||||||
set({
|
set({
|
||||||
message: "Rufgruppe besetzt",
|
message: "Rufgruppe besetzt",
|
||||||
transmitBlocked: true,
|
transmitBlocked: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
} else if (!isTalking && transmitBlocked) {
|
||||||
|
set({
|
||||||
|
message: null,
|
||||||
|
transmitBlocked: false,
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// Todo: use micVolume
|
// Todo: use micVolume
|
||||||
room.localParticipant.setMicrophoneEnabled(!isTalking, {
|
room.localParticipant.setMicrophoneEnabled(!isTalking, {
|
||||||
|
|||||||
@@ -30,21 +30,30 @@ export const useLeftMenuStore = create<ChatStore>((set, get) => ({
|
|||||||
chatOpen: false,
|
chatOpen: false,
|
||||||
selectedChat: null,
|
selectedChat: null,
|
||||||
setChatOpen: (open: boolean) => set({ chatOpen: open }),
|
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 }),
|
setOwnId: (id: string) => set({ ownId: id }),
|
||||||
chats: {},
|
chats: {},
|
||||||
sendMessage: (userId: string, message: string) => {
|
sendMessage: (userId: string, message: string) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
if(dispatchSocket.connected){
|
if (dispatchSocket.connected) {
|
||||||
|
dispatchSocket.emit(
|
||||||
dispatchSocket.emit("send-message", { userId, message }, ({ error }: { error?: string }) => {
|
"send-message",
|
||||||
|
{ userId, message },
|
||||||
|
({ error }: { error?: string }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
resolve();
|
resolve();
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
} else if(pilotSocket.connected){
|
);
|
||||||
|
} else if (pilotSocket.connected) {
|
||||||
pilotSocket.emit("send-message", { userId, message }, ({ error }: { error?: string }) => {
|
pilotSocket.emit("send-message", { userId, message }, ({ error }: { error?: string }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@@ -66,7 +75,6 @@ export const useLeftMenuStore = create<ChatStore>((set, get) => ({
|
|||||||
setChatNotification: (userId, notification) => {
|
setChatNotification: (userId, notification) => {
|
||||||
const chat = get().chats[userId];
|
const chat = get().chats[userId];
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
console.log("setChatNotification", userId, notification);
|
|
||||||
set((state) => {
|
set((state) => {
|
||||||
return {
|
return {
|
||||||
chats: {
|
chats: {
|
||||||
@@ -95,7 +103,7 @@ export const useLeftMenuStore = create<ChatStore>((set, get) => ({
|
|||||||
[userId]: {
|
[userId]: {
|
||||||
...user,
|
...user,
|
||||||
name: isSender ? message.receiverName : message.senderName,
|
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
|
messages: [...user.messages, message], // Neuen Zustand erzeugen
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export default async function RootLayout({
|
|||||||
}>) {
|
}>) {
|
||||||
const session = await getServerSession();
|
const session = await getServerSession();
|
||||||
|
|
||||||
if (!session || !session.user) {
|
if (!session || !session.user.firstname) {
|
||||||
redirect("/login");
|
redirect("/login");
|
||||||
}
|
}
|
||||||
if (!session.user.emailVerified) {
|
if (!session.user.emailVerified) {
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ export const Register = () => {
|
|||||||
passwordConfirm: "",
|
passwordConfirm: "",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log("Register form", form.formState.errors);
|
|
||||||
return (
|
return (
|
||||||
<form
|
<form
|
||||||
className="card-body"
|
className="card-body"
|
||||||
@@ -67,6 +66,10 @@ export const Register = () => {
|
|||||||
firstname: form.getValues("firstname"),
|
firstname: form.getValues("firstname"),
|
||||||
lastname: form.getValues("lastname"),
|
lastname: form.getValues("lastname"),
|
||||||
});
|
});
|
||||||
|
if ("error" in user) {
|
||||||
|
toast.error(user.error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await sendVerificationLink(user.id);
|
await sendVerificationLink(user.id);
|
||||||
await signIn("credentials", {
|
await signIn("credentials", {
|
||||||
callbackUrl: "/",
|
callbackUrl: "/",
|
||||||
|
|||||||
@@ -27,7 +27,9 @@ export const register = async ({ password, ...user }: Omit<Prisma.UserCreateInpu
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
if (existingUser) {
|
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({
|
const newUser = prisma.user.create({
|
||||||
|
|||||||
Reference in New Issue
Block a user