@@ -509,28 +731,33 @@ const ChatPage: React.FC = () => {
{/* Messages */}
- {groupedMessages.length > 0 ? (
+ {channelMessages && channelMessages.length > 0 ? (
- {groupedMessages.map((message) => {
+ {channelMessages.map((message) => {
const user = users?.find((u) => u.id === message.userId);
- const replyToMessage = messages.find(
- (m) => m.id === message.replyTo?.id,
+ const replyToMessage = channelMessages?.find(
+ (m) => m.id === message.replyToId,
);
- const replyToUser = replyToMessage?.user;
+ const replyToUser = replyToMessage
+ ? users?.find((u) => u.id === replyToMessage.userId)
+ : undefined;
if (!user) return null;
+
return (
);
})}
@@ -548,20 +775,41 @@ const ChatPage: React.FC = () => {
{/* Message Input */}
-
- setReplyingTo(null)}
- replyingToUser={
- replyingTo
- ? users?.find((u) => u.id === replyingTo.userId) || null
- : null
- }
- />
-
+ {currentUser && (
+
+ setReplyingTo(null)}
+ replyingToUser={
+ replyingTo
+ ? users?.find((u) => u.id === replyingTo.userId) || null
+ : null
+ }
+ />
+
+ )}
+
+ {/* Edit Message Modal */}
+ {editingMessage && (
+
setEditingMessage(null)}
+ message={editingMessage}
+ channelId={channelId!}
+ />
+ )}
+
+ {/* Pinned Messages Modal */}
+ setShowPinnedMessages(false)}
+ channelId={channelId!}
+ channelName={currentChannel?.name || "channel"}
+ canManagePins={canPinMessages ? canPinMessages : false}
+ />
);
};
diff --git a/concord-client/src/pages/LoginPage.tsx b/concord-client/src/pages/LoginPage.tsx
index 2f47326..f9e5450 100644
--- a/concord-client/src/pages/LoginPage.tsx
+++ b/concord-client/src/pages/LoginPage.tsx
@@ -10,13 +10,17 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
+import { Alert, AlertDescription } from "@/components/ui/alert";
import { useAuthStore } from "@/stores/authStore";
+import { useLogin } from "@/hooks/useAuth";
const LoginPage: React.FC = () => {
- const { isAuthenticated, setAuth } = useAuthStore();
+ const { isAuthenticated } = useAuthStore();
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
- const [isLoading, setIsLoading] = useState(false);
+
+ // Use the real login hook
+ const { mutate: login, isPending, error } = useLogin();
// Redirect if already authenticated
if (isAuthenticated) {
@@ -25,35 +29,12 @@ const LoginPage: React.FC = () => {
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault();
- setIsLoading(true);
- try {
- // TODO: Replace with actual login API call
- setTimeout(() => {
- setAuth(
- {
- id: "1",
- username,
- nickname: username,
- bio: "Test user",
- picture: "",
- banner: "",
- hashPassword: "",
- admin: false,
- status: "online",
- createdAt: new Date().toISOString(),
- updatedAt: new Date().toISOString(),
- roles: [],
- },
- "fake-token",
- "fake-refresh-token",
- );
- setIsLoading(false);
- }, 1000);
- } catch (error) {
- console.error("Login failed:", error);
- setIsLoading(false);
+ if (!username.trim() || !password.trim()) {
+ return;
}
+
+ login({ username: username.trim(), password });
};
return (
@@ -69,6 +50,16 @@ const LoginPage: React.FC = () => {
diff --git a/concord-client/src/pages/SettingsPage.tsx b/concord-client/src/pages/SettingsPage.tsx
index 881a5c9..f338f58 100644
--- a/concord-client/src/pages/SettingsPage.tsx
+++ b/concord-client/src/pages/SettingsPage.tsx
@@ -7,10 +7,10 @@ import {
Mic,
Settings,
ChevronRight,
- Eye,
Moon,
Sun,
Monitor,
+ Lock,
} from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -25,6 +25,7 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
+import { Alert, AlertDescription } from "@/components/ui/alert";
import { ThemeSelector } from "@/components/theme-selector";
import { useTheme } from "@/components/theme-provider";
import { useAuthStore } from "@/stores/authStore";
@@ -44,6 +45,12 @@ const SETTINGS_SECTIONS: SettingsSection[] = [
icon: User,
description: "Profile, privacy, and account settings",
},
+ {
+ id: "security",
+ title: "Security",
+ icon: Lock,
+ description: "Password and security settings",
+ },
{
id: "appearance",
title: "Appearance",
@@ -58,6 +65,389 @@ const SETTINGS_SECTIONS: SettingsSection[] = [
},
];
+const SecuritySettings: React.FC = () => {
+ const { user } = useAuthStore();
+ const [currentPassword, setCurrentPassword] = useState("");
+ const [newPassword, setNewPassword] = useState("");
+ const [confirmPassword, setConfirmPassword] = useState("");
+ const [isChangingPassword, setIsChangingPassword] = useState(false);
+ const [passwordError, setPasswordError] = useState("");
+ const [passwordSuccess, setPasswordSuccess] = useState("");
+ const [twoFactorEnabled, setTwoFactorEnabled] = useState(false);
+
+ const handlePasswordChange = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setPasswordError("");
+ setPasswordSuccess("");
+
+ if (!currentPassword || !newPassword || !confirmPassword) {
+ setPasswordError("All password fields are required");
+ return;
+ }
+
+ if (newPassword !== confirmPassword) {
+ setPasswordError("New passwords do not match");
+ return;
+ }
+
+ if (newPassword.length < 8) {
+ setPasswordError("New password must be at least 8 characters long");
+ return;
+ }
+
+ setIsChangingPassword(true);
+
+ try {
+ // TODO: Implement actual password change API call
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
+
+ console.log("Changing password for user:", user?.id);
+ // const result = await authClient.changePassword({
+ // userId: user.id,
+ // currentPassword,
+ // newPassword,
+ // token: authStore.token
+ // });
+
+ setPasswordSuccess("Password changed successfully");
+ setCurrentPassword("");
+ setNewPassword("");
+ setConfirmPassword("");
+ } catch (error) {
+ setPasswordError(
+ "Failed to change password. Please check your current password.",
+ );
+ } finally {
+ setIsChangingPassword(false);
+ }
+ };
+
+ return (
+
+
+
+
+
+ Change Password
+
+
+ Update your password to keep your account secure.
+
+
+
+ {passwordError && (
+
+ {passwordError}
+
+ )}
+
+ {passwordSuccess && (
+
+ {passwordSuccess}
+
+ )}
+
+
+
+
+
+
+
+
+
+ Security Options
+
+
+ Additional security features for your account.
+
+
+
+
+
+
+ Two-Factor Authentication
+
+
+ Add an extra layer of security to your account
+
+
+
+
+
+
+
+
+
Active Sessions
+
+ Manage devices that are currently logged into your account
+
+
+ View Active Sessions
+
+
+
+
+
+
+
Account Backup
+
+ Download a copy of your account data
+
+
+ Request Data Export
+
+
+
+
+
+ );
+};
+
+const AccountSettings: React.FC = () => {
+ const { user, updateUser } = useAuthStore();
+ const [username, setUsername] = useState(user?.username || "");
+ const [nickname, setNickname] = useState(user?.nickname || "");
+ const [bio, setBio] = useState(user?.bio || "");
+ const [isChanged, setIsChanged] = useState(false);
+ const [isSaving, setIsSaving] = useState(false);
+ const [saveError, setSaveError] = useState("");
+ const [saveSuccess, setSaveSuccess] = useState("");
+
+ const handleSave = async () => {
+ setSaveError("");
+ setSaveSuccess("");
+ setIsSaving(true);
+
+ try {
+ // TODO: Implement actual profile update API call
+ await new Promise((resolve) => setTimeout(resolve, 1000)); // Simulate API call
+
+ console.log("Updating profile:", { username, nickname, bio });
+ // const updatedUser = await userClient.updateProfile({
+ // userId: user.id,
+ // username: username.trim(),
+ // nickname: nickname.trim() || null,
+ // bio: bio.trim() || null,
+ // token: authStore.token
+ // });
+
+ // Update local state
+ updateUser({
+ username: username.trim(),
+ nickname: nickname.trim() || null,
+ bio: bio.trim() || null,
+ });
+
+ setSaveSuccess("Profile updated successfully");
+ setIsChanged(false);
+ } catch (error) {
+ setSaveError("Failed to update profile. Please try again.");
+ } finally {
+ setIsSaving(false);
+ }
+ };
+
+ const handleChange = () => {
+ setIsChanged(true);
+ setSaveError("");
+ setSaveSuccess("");
+ };
+
+ return (
+
+
+
+
+
+ Profile
+
+
+ Update your profile information and display settings.
+
+
+
+ {saveError && (
+
+ {saveError}
+
+ )}
+
+ {saveSuccess && (
+
+ {saveSuccess}
+
+ )}
+
+
+
+
+
+ {isSaving ? "Saving..." : "Save Changes"}
+
+ {isChanged && (
+ {
+ setUsername(user?.username || "");
+ setNickname(user?.nickname || "");
+ setBio(user?.bio || "");
+ setIsChanged(false);
+ setSaveError("");
+ setSaveSuccess("");
+ }}
+ >
+ Reset
+
+ )}
+
+
+
+
+
+
+
+
+ Privacy
+
+
+ Control who can contact you and see your information.
+
+
+
+
+
+
+ Allow Direct Messages
+
+
+ Let other users send you direct messages
+
+
+
+
+
+
+
+
+
+
+ Show Online Status
+
+
+ Display when you're online to other users
+
+
+
+
+
+
+
+ );
+};
+
+// Appearance Settings Component (keeping existing implementation)
const AppearanceSettings: React.FC = () => {
const {
currentLightTheme,
@@ -67,12 +457,6 @@ const AppearanceSettings: React.FC = () => {
setMode,
setTheme,
} = useTheme();
- const [compactMode, setCompactMode] = useState(false);
- const [showTimestamps, setShowTimestamps] = useState(true);
- const [animationsEnabled, setAnimationsEnabled] = useState(true);
- const [reduceMotion, setReduceMotion] = useState(false);
- const [highContrast, setHighContrast] = useState(false);
-
const lightThemes = getThemesForMode("light");
const darkThemes = getThemesForMode("dark");
@@ -245,344 +629,13 @@ const AppearanceSettings: React.FC = () => {