@myrjfa/state 1.1.0 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/lib/actions/actions.d.ts +56 -7
- package/dist/lib/actions/actions.d.ts.map +1 -1
- package/dist/lib/actions/actions.js +50 -10
- package/dist/lib/actions/auth.d.ts +12 -2
- package/dist/lib/actions/auth.d.ts.map +1 -1
- package/dist/lib/actions/chat.d.ts +80 -11
- package/dist/lib/actions/chat.d.ts.map +1 -1
- package/dist/lib/actions/chat.js +81 -20
- package/dist/lib/actions/fetcher.d.ts +0 -3
- package/dist/lib/actions/fetcher.d.ts.map +1 -1
- package/dist/lib/actions/fetcher.js +44 -6
- package/dist/lib/{severActions.d.ts → actions/serverActions.d.ts} +1 -1
- package/dist/lib/actions/serverActions.d.ts.map +1 -0
- package/dist/lib/actions/{severActions.js → serverActions.js} +3 -0
- package/dist/lib/actions/user.d.ts +23 -0
- package/dist/lib/actions/user.d.ts.map +1 -0
- package/dist/lib/actions/user.js +55 -0
- package/dist/lib/authSessionManager.d.ts.map +1 -1
- package/dist/lib/authSessionManager.js +14 -7
- package/dist/lib/context/ChatContext.d.ts +7 -0
- package/dist/lib/context/ChatContext.d.ts.map +1 -1
- package/dist/lib/context/ChatContext.js +121 -11
- package/dist/lib/models/blog.d.ts +3 -2
- package/dist/lib/models/blog.d.ts.map +1 -1
- package/dist/lib/models/chat.d.ts +32 -7
- package/dist/lib/models/chat.d.ts.map +1 -1
- package/dist/lib/models/notifications.d.ts +94 -0
- package/dist/lib/models/notifications.d.ts.map +1 -0
- package/dist/lib/models/notifications.js +60 -0
- package/dist/lib/models/opportunities/freelance.d.ts +48 -20
- package/dist/lib/models/opportunities/freelance.d.ts.map +1 -1
- package/dist/lib/models/opportunities/internship.d.ts +48 -20
- package/dist/lib/models/opportunities/internship.d.ts.map +1 -1
- package/dist/lib/models/opportunities/job.d.ts +58 -30
- package/dist/lib/models/opportunities/job.d.ts.map +1 -1
- package/dist/lib/models/opportunities/opportunity.d.ts +61 -33
- package/dist/lib/models/opportunities/opportunity.d.ts.map +1 -1
- package/dist/lib/models/opportunities/opportunity.js +1 -0
- package/dist/lib/models/opportunities/volunteerJob.d.ts +48 -20
- package/dist/lib/models/opportunities/volunteerJob.d.ts.map +1 -1
- package/dist/lib/models/portfolio.d.ts +2 -2
- package/dist/lib/models/props.d.ts +21 -4
- package/dist/lib/models/props.d.ts.map +1 -1
- package/dist/lib/models/user.d.ts +19 -4
- package/dist/lib/models/user.d.ts.map +1 -1
- package/dist/lib/models/user.js +5 -0
- package/dist/lib/userAtom.d.ts +24 -4
- package/dist/lib/userAtom.d.ts.map +1 -1
- package/dist/lib/userAtom.js +13 -11
- package/dist/lib/utils/fileCompression.d.ts +16 -0
- package/dist/lib/utils/fileCompression.d.ts.map +1 -0
- package/dist/lib/utils/fileCompression.js +56 -0
- package/dist/lib/utils/socialMediaUrl.d.ts +25 -0
- package/dist/lib/utils/socialMediaUrl.d.ts.map +1 -0
- package/dist/lib/utils/socialMediaUrl.js +97 -0
- package/dist/lib/utils.js +4 -4
- package/package.json +3 -1
- package/dist/lib/actions/severActions.d.ts +0 -3
- package/dist/lib/actions/severActions.d.ts.map +0 -1
- package/dist/lib/actions.d.ts +0 -141
- package/dist/lib/actions.d.ts.map +0 -1
- package/dist/lib/actions.js +0 -307
- package/dist/lib/auth.d.ts +0 -150
- package/dist/lib/auth.d.ts.map +0 -1
- package/dist/lib/auth.js +0 -125
- package/dist/lib/fetcher.d.ts +0 -9
- package/dist/lib/fetcher.d.ts.map +0 -1
- package/dist/lib/fetcher.js +0 -84
- package/dist/lib/models/notfications.d.ts +0 -26
- package/dist/lib/models/notfications.d.ts.map +0 -1
- package/dist/lib/models/notfications.js +0 -46
- package/dist/lib/models/volunteerJob.d.ts +0 -398
- package/dist/lib/models/volunteerJob.d.ts.map +0 -1
- package/dist/lib/models/volunteerJob.js +0 -152
- package/dist/lib/severActions.d.ts.map +0 -1
- package/dist/lib/severActions.js +0 -19
- package/dist/lib/socket.d.ts +0 -7
- package/dist/lib/socket.d.ts.map +0 -1
- package/dist/lib/socket.js +0 -22
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/fetcher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/fetcher.ts"],"names":[],"mappings":"AA6IA,wBAAsB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,WAAW,GAAE,OAAc,cAEpE;AAED,wBAAsB,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,cAEnD;AAED,wBAAsB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,cAElD;AAED,wBAAsB,KAAK,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,cAEpD;AAED,wBAAsB,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,GAAG,cAEnD"}
|
|
@@ -1,6 +1,33 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { getCookieHeader, getRole } from "./serverActions";
|
|
2
|
+
// Custom API error class — do NOT import from next/dist internals
|
|
3
|
+
class ApiError extends Error {
|
|
4
|
+
constructor(statusCode, message) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.statusCode = statusCode;
|
|
7
|
+
this.name = 'ApiError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
3
10
|
const baseURL = process.env.NEXT_PUBLIC_API_URL;
|
|
11
|
+
/**
|
|
12
|
+
* #2 — Stable device fingerprint stored in localStorage.
|
|
13
|
+
* Survives browser sessions (unlike cookies which can expire/be cleared).
|
|
14
|
+
* Generated once per browser using crypto.randomUUID().
|
|
15
|
+
*/
|
|
16
|
+
function getOrCreateDeviceId() {
|
|
17
|
+
if (typeof window === 'undefined')
|
|
18
|
+
return null; // SSR guard
|
|
19
|
+
try {
|
|
20
|
+
let deviceId = localStorage.getItem('_trippeaze_device_id');
|
|
21
|
+
if (!deviceId) {
|
|
22
|
+
deviceId = crypto.randomUUID();
|
|
23
|
+
localStorage.setItem('_trippeaze_device_id', deviceId);
|
|
24
|
+
}
|
|
25
|
+
return deviceId;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
4
31
|
async function customFetch(input, method, body, options = {}) {
|
|
5
32
|
const { includeAuth = false, retry = true } = options;
|
|
6
33
|
const url = `${baseURL}${input}`;
|
|
@@ -8,12 +35,20 @@ async function customFetch(input, method, body, options = {}) {
|
|
|
8
35
|
let headers = isFormData
|
|
9
36
|
? { Accept: 'application/json' }
|
|
10
37
|
: { 'Content-Type': 'application/json', Accept: 'application/json' };
|
|
38
|
+
// #2: Attach stable device ID so backend can correctly identify the same browser
|
|
39
|
+
const deviceId = getOrCreateDeviceId();
|
|
40
|
+
if (deviceId) {
|
|
41
|
+
headers['X-Device-Id'] = deviceId;
|
|
42
|
+
}
|
|
11
43
|
if (includeAuth) {
|
|
12
44
|
try {
|
|
13
45
|
const cookieHeader = await getCookieHeader();
|
|
14
46
|
headers = { ...headers, Cookie: cookieHeader };
|
|
15
47
|
}
|
|
16
48
|
catch (error) {
|
|
49
|
+
if (error && typeof error === 'object' && ('$$typeof' in error || error.digest === 'DYNAMIC_SERVER_USAGE')) {
|
|
50
|
+
throw error; // Re-throw Next.js PPR bail-out
|
|
51
|
+
}
|
|
17
52
|
// If we can't get cookies (CSR context), credentials: 'include' will handle it
|
|
18
53
|
console.warn('Could not get cookies for auth header:', error);
|
|
19
54
|
}
|
|
@@ -45,8 +80,8 @@ async function customFetch(input, method, body, options = {}) {
|
|
|
45
80
|
}
|
|
46
81
|
throw new ApiError(errorBody.statusCode, errorBody.error);
|
|
47
82
|
}
|
|
48
|
-
// Token refresh function
|
|
49
|
-
|
|
83
|
+
// Token refresh function — internal only, not exported
|
|
84
|
+
async function refreshToken(input, method, body) {
|
|
50
85
|
const role = await getRole();
|
|
51
86
|
try {
|
|
52
87
|
const roleForEndpoint = role == "admin" ? "user" : role;
|
|
@@ -63,10 +98,13 @@ export async function refreshToken(input, method, body) {
|
|
|
63
98
|
}
|
|
64
99
|
}
|
|
65
100
|
catch (error) {
|
|
66
|
-
|
|
101
|
+
if (error && typeof error === 'object' && ('$$typeof' in error || error.digest === 'DYNAMIC_SERVER_USAGE')) {
|
|
102
|
+
throw error; // Re-throw Next.js PPR bail-out
|
|
103
|
+
}
|
|
104
|
+
throw new ApiError(error.statusCode ?? 500, error.error || error.message || 'Unknown error');
|
|
67
105
|
}
|
|
68
106
|
}
|
|
69
|
-
// ✅
|
|
107
|
+
// ✅ Server-safe exported functions
|
|
70
108
|
export async function get(url, includeAuth = true) {
|
|
71
109
|
return customFetch(url, 'GET', undefined, { includeAuth });
|
|
72
110
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serverActions.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/serverActions.ts"],"names":[],"mappings":"AAKA,wBAAsB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAKvD;AAED,wBAAsB,OAAO,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWtD"}
|
|
@@ -13,6 +13,9 @@ export async function getRole() {
|
|
|
13
13
|
return (role && validRoles.includes(role)) ? role : null;
|
|
14
14
|
}
|
|
15
15
|
catch (err) {
|
|
16
|
+
if (err && typeof err === 'object' && ('$$typeof' in err || err.digest === 'DYNAMIC_SERVER_USAGE')) {
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
16
19
|
console.error('Invalid token', err);
|
|
17
20
|
return null;
|
|
18
21
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare const userApi: {
|
|
2
|
+
blockUser: (userId: string, role: string) => Promise<{
|
|
3
|
+
error: string;
|
|
4
|
+
data: any;
|
|
5
|
+
} | {
|
|
6
|
+
error: unknown;
|
|
7
|
+
data: null;
|
|
8
|
+
}>;
|
|
9
|
+
unblockUser: (userId: string, role: string) => Promise<{
|
|
10
|
+
error: string;
|
|
11
|
+
data: any;
|
|
12
|
+
} | {
|
|
13
|
+
error: unknown;
|
|
14
|
+
data: null;
|
|
15
|
+
}>;
|
|
16
|
+
getBlockedUsers: () => Promise<any[]>;
|
|
17
|
+
getBlockerUsers: () => Promise<any[]>;
|
|
18
|
+
checkIfBlocked: (userId: string, role: string) => Promise<{
|
|
19
|
+
isBlocked: boolean;
|
|
20
|
+
hasBlocked: boolean;
|
|
21
|
+
}>;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=user.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user.d.ts","sourceRoot":"","sources":["../../../src/lib/actions/user.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,OAAO;wBAEU,MAAM,QAAQ,MAAM;eAED,MAAM;cAAQ,GAAG;;;;;0BAQlC,MAAM,QAAQ,MAAM;eAEH,MAAM;cAAQ,GAAG;;;;;;;6BA4B/B,MAAM,QAAQ,MAAM;mBAEoB,OAAO;oBAAc,OAAO;;CAOtG,CAAC"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { get, post } from "./fetcher";
|
|
2
|
+
const BASE_PATH = "/users";
|
|
3
|
+
export const userApi = {
|
|
4
|
+
// Block Management
|
|
5
|
+
blockUser: async (userId, role) => {
|
|
6
|
+
try {
|
|
7
|
+
const response = await post(`${BASE_PATH}/block/${role}/${userId}`, {});
|
|
8
|
+
return response;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
console.error(error);
|
|
12
|
+
return { error: error, data: null };
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
unblockUser: async (userId, role) => {
|
|
16
|
+
try {
|
|
17
|
+
const response = await post(`${BASE_PATH}/unblock/${role}/${userId}`, {});
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
console.error(error);
|
|
22
|
+
return { error: error, data: null };
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
getBlockedUsers: async () => {
|
|
26
|
+
try {
|
|
27
|
+
const response = await get(`${BASE_PATH}/blocked`);
|
|
28
|
+
return response.data;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error(error);
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
getBlockerUsers: async () => {
|
|
36
|
+
try {
|
|
37
|
+
const response = await get(`${BASE_PATH}/blockers`);
|
|
38
|
+
return response.data;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error(error);
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
checkIfBlocked: async (userId, role) => {
|
|
46
|
+
try {
|
|
47
|
+
const response = await get(`${BASE_PATH}/blocked/${role}/${userId}`);
|
|
48
|
+
return response.data;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
console.error(error);
|
|
52
|
+
return { isBlocked: false, hasBlocked: false };
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"authSessionManager.d.ts","sourceRoot":"","sources":["../../src/lib/authSessionManager.tsx"],"names":[],"mappings":"AAQA,MAAM,CAAC,OAAO,UAAU,kBAAkB,
|
|
1
|
+
{"version":3,"file":"authSessionManager.d.ts","sourceRoot":"","sources":["../../src/lib/authSessionManager.tsx"],"names":[],"mappings":"AAQA,MAAM,CAAC,OAAO,UAAU,kBAAkB,SAqCzC"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useAtom } from 'jotai'; // Import useSetAtom
|
|
3
3
|
import { useEffect } from 'react';
|
|
4
4
|
import { useQuery } from '@tanstack/react-query';
|
|
5
|
-
import { initializeAuthSessionAtom
|
|
5
|
+
import { initializeAuthSessionAtom } from './userAtom'; // Import the atom itself
|
|
6
6
|
import { validateSession } from './actions/auth';
|
|
7
7
|
export default function AuthSessionManager() {
|
|
8
8
|
const [, initializeAuthSession] = useAtom(initializeAuthSessionAtom);
|
|
@@ -24,11 +24,18 @@ export default function AuthSessionManager() {
|
|
|
24
24
|
},
|
|
25
25
|
staleTime: 15 * 60 * 1000,
|
|
26
26
|
});
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
27
|
+
// NOTE: We intentionally do NOT reset auth state on isError here.
|
|
28
|
+
// A failed background session ping could be due to:
|
|
29
|
+
// - Backend temporarily down
|
|
30
|
+
// - Network issue
|
|
31
|
+
// - Wrong role endpoint (hosts vs users)
|
|
32
|
+
// The fetcher's refreshToken logic already handles 401s transparently.
|
|
33
|
+
// Aggressively resetting here was causing false logouts after browser restart.
|
|
34
|
+
// useEffect(() => {
|
|
35
|
+
// if (isError) {
|
|
36
|
+
// console.log("Session validation failed. Logging out.");
|
|
37
|
+
// resetAuthState();
|
|
38
|
+
// }
|
|
39
|
+
// }, [isError, resetAuthState]);
|
|
33
40
|
return null;
|
|
34
41
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { Socket } from "socket.io-client";
|
|
3
3
|
import type { Conversation, Message } from "../models/chat";
|
|
4
|
+
import type { Notif } from "../models/notifications";
|
|
4
5
|
interface ChatContextType {
|
|
5
6
|
socket: Socket | null;
|
|
6
7
|
isConnected: boolean;
|
|
@@ -13,9 +14,15 @@ interface ChatContextType {
|
|
|
13
14
|
onlineUsers: Set<string>;
|
|
14
15
|
refreshConversations: () => Promise<void>;
|
|
15
16
|
sendMessage: (conversationId: string, text: string, files?: File[], replyTo?: string) => Promise<void>;
|
|
17
|
+
createPoll: (conversationId: string, question: string, options: string[], allowMultiple: boolean) => void;
|
|
18
|
+
votePoll: (pollId: string, optionIndex: number) => void;
|
|
16
19
|
editMessage: (messageId: string, text: string) => void;
|
|
17
20
|
toggleMessageReaction: (messageId: string, reaction?: string) => void;
|
|
18
21
|
deleteMessage: (messageId: string) => void;
|
|
22
|
+
markRead: (conversationId: string, unreadIds: string[]) => void;
|
|
23
|
+
unreadNotificationCount: number;
|
|
24
|
+
notifications: Notif[];
|
|
25
|
+
clearNotifications: () => void;
|
|
19
26
|
}
|
|
20
27
|
export declare function useChatContext(): ChatContextType;
|
|
21
28
|
export declare function ChatProvider({ children }: {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../../src/lib/context/ChatContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8E,MAAM,OAAO,CAAC;AACnG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatContext.d.ts","sourceRoot":"","sources":["../../../src/lib/context/ChatContext.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA8E,MAAM,OAAO,CAAC;AACnG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAMrD,UAAU,eAAe;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,kBAAkB,EAAE,YAAY,GAAG,IAAI,CAAC;IACxC,qBAAqB,EAAE,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,KAAK,IAAI,CAAC;IAC3D,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC7D,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACnC,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACzB,oBAAoB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,WAAW,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvG,UAAU,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,aAAa,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1G,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,WAAW,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACvD,qBAAqB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACtE,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,QAAQ,EAAE,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAChE,uBAAuB,EAAE,MAAM,CAAC;IAChC,aAAa,EAAE,KAAK,EAAE,CAAC;IACvB,kBAAkB,EAAE,MAAM,IAAI,CAAC;CAClC;AAyBD,wBAAgB,cAAc,oBAE7B;AAED,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,2CAgWvE"}
|
|
@@ -17,9 +17,15 @@ const ChatContext = createContext({
|
|
|
17
17
|
onlineUsers: new Set(),
|
|
18
18
|
refreshConversations: async () => { },
|
|
19
19
|
sendMessage: async () => { },
|
|
20
|
+
createPoll: () => { },
|
|
21
|
+
votePoll: () => { },
|
|
20
22
|
editMessage: () => { },
|
|
21
23
|
toggleMessageReaction: () => { },
|
|
22
24
|
deleteMessage: () => { },
|
|
25
|
+
markRead: () => { },
|
|
26
|
+
unreadNotificationCount: 0,
|
|
27
|
+
notifications: [],
|
|
28
|
+
clearNotifications: () => { },
|
|
23
29
|
});
|
|
24
30
|
export function useChatContext() {
|
|
25
31
|
return useContext(ChatContext);
|
|
@@ -28,14 +34,18 @@ export function ChatProvider({ children }) {
|
|
|
28
34
|
const user = useAtomValue(userAtom);
|
|
29
35
|
const [isConnected, setIsConnected] = useState(false);
|
|
30
36
|
const [conversations, setConversations] = useState([]);
|
|
31
|
-
const [activeConversation,
|
|
37
|
+
const [activeConversation, _setActiveConversation] = useState(null);
|
|
32
38
|
const activeConversationRef = useRef(null);
|
|
33
39
|
const [messages, setMessages] = useState([]);
|
|
34
40
|
const [typingUsers, setTypingUsers] = useState(new Map());
|
|
35
41
|
const [onlineUsers, setOnlineUsers] = useState(new Set());
|
|
42
|
+
const [unreadNotificationCount, setUnreadNotificationCount] = useState(0);
|
|
43
|
+
const [notifications, setNotifications] = useState([]);
|
|
36
44
|
const socketRef = useRef(null);
|
|
37
|
-
|
|
38
|
-
|
|
45
|
+
const setActiveConversation = useCallback((conv) => {
|
|
46
|
+
_setActiveConversation(conv);
|
|
47
|
+
activeConversationRef.current = conv;
|
|
48
|
+
}, []);
|
|
39
49
|
useEffect(() => {
|
|
40
50
|
activeConversationRef.current = activeConversation;
|
|
41
51
|
}, [activeConversation]);
|
|
@@ -49,6 +59,32 @@ export function ChatProvider({ children }) {
|
|
|
49
59
|
}
|
|
50
60
|
}, []);
|
|
51
61
|
const sendMessage = useCallback(async (conversationId, text, files, replyTo) => {
|
|
62
|
+
let actualId = conversationId;
|
|
63
|
+
// If it's a draft conversation, create it first
|
|
64
|
+
if (conversationId.startsWith("draft_")) {
|
|
65
|
+
const otherUserId = conversationId.replace("draft_", "");
|
|
66
|
+
try {
|
|
67
|
+
const res = await chatApi.createConversation({
|
|
68
|
+
type: "individual",
|
|
69
|
+
otherUserId
|
|
70
|
+
});
|
|
71
|
+
if (res._id) {
|
|
72
|
+
actualId = res._id;
|
|
73
|
+
// Update active conversation in state to point to the real one
|
|
74
|
+
setActiveConversation(res);
|
|
75
|
+
// Refresh to include in list
|
|
76
|
+
await refreshConversations();
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.error("Failed to create conversation during lazy send");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
console.error("Failed to create conversation during lazy send:", err);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
52
88
|
if (!socketRef.current)
|
|
53
89
|
return;
|
|
54
90
|
let media = [];
|
|
@@ -65,7 +101,7 @@ export function ChatProvider({ children }) {
|
|
|
65
101
|
return;
|
|
66
102
|
}
|
|
67
103
|
media = response.data.files.map((f) => ({
|
|
68
|
-
type: f.mimetype.startsWith("image") ? "image" : "file",
|
|
104
|
+
type: f.mimetype.startsWith("audio") ? "audio" : (f.mimetype.startsWith("image") ? "image" : "file"),
|
|
69
105
|
url: f.location,
|
|
70
106
|
name: f.originalname
|
|
71
107
|
}));
|
|
@@ -75,12 +111,12 @@ export function ChatProvider({ children }) {
|
|
|
75
111
|
}
|
|
76
112
|
}
|
|
77
113
|
socketRef.current.emit("message:send", {
|
|
78
|
-
conversationId,
|
|
114
|
+
conversationId: actualId,
|
|
79
115
|
content: { text, media },
|
|
80
116
|
type: media.length > 0 ? "media" : "text",
|
|
81
117
|
replyTo,
|
|
82
118
|
});
|
|
83
|
-
}, []);
|
|
119
|
+
}, [refreshConversations, setActiveConversation]);
|
|
84
120
|
const editMessage = useCallback((messageId, text) => {
|
|
85
121
|
if (!socketRef.current)
|
|
86
122
|
return;
|
|
@@ -96,6 +132,25 @@ export function ChatProvider({ children }) {
|
|
|
96
132
|
return;
|
|
97
133
|
socketRef.current.emit("message:delete", { messageId });
|
|
98
134
|
}, []);
|
|
135
|
+
const createPoll = useCallback((conversationId, question, options, allowMultiple) => {
|
|
136
|
+
if (!socketRef.current)
|
|
137
|
+
return;
|
|
138
|
+
socketRef.current.emit("poll:create", { conversationId, question, options, allowMultiple });
|
|
139
|
+
}, []);
|
|
140
|
+
const votePoll = useCallback((pollId, optionIndex) => {
|
|
141
|
+
if (!socketRef.current)
|
|
142
|
+
return;
|
|
143
|
+
socketRef.current.emit("poll:vote", { pollId, optionIndex });
|
|
144
|
+
}, []);
|
|
145
|
+
const clearNotifications = useCallback(() => {
|
|
146
|
+
setNotifications([]);
|
|
147
|
+
setUnreadNotificationCount(0);
|
|
148
|
+
}, []);
|
|
149
|
+
const markRead = useCallback((conversationId, unreadIds) => {
|
|
150
|
+
if (!socketRef.current)
|
|
151
|
+
return;
|
|
152
|
+
socketRef.current.emit("message:read", { conversationId, unreadIds });
|
|
153
|
+
}, []);
|
|
99
154
|
useEffect(() => {
|
|
100
155
|
if (!user) {
|
|
101
156
|
setIsConnected(false);
|
|
@@ -124,10 +179,8 @@ export function ChatProvider({ children }) {
|
|
|
124
179
|
socket.on("message:new", ({ conversationId, message }) => {
|
|
125
180
|
setMessages((prev) => {
|
|
126
181
|
const currentActiveId = activeConversationRef.current?._id;
|
|
127
|
-
// If
|
|
128
|
-
|
|
129
|
-
if ((prev.length > 0 && prev[0]?.conversationId === conversationId) ||
|
|
130
|
-
(prev.length === 0 && currentActiveId === conversationId)) {
|
|
182
|
+
// If this message belongs to the active conversation, add it
|
|
183
|
+
if (currentActiveId === conversationId) {
|
|
131
184
|
// Deduplicate
|
|
132
185
|
if (prev.some((m) => m._id === message._id))
|
|
133
186
|
return prev;
|
|
@@ -154,6 +207,51 @@ export function ChatProvider({ children }) {
|
|
|
154
207
|
socket.on("message:updated", ({ message }) => {
|
|
155
208
|
setMessages((prev) => prev.map((m) => (m._id === message._id ? message : m)));
|
|
156
209
|
});
|
|
210
|
+
socket.on("message:readed", ({ conversationId, userId, unreadIds }) => {
|
|
211
|
+
setMessages((prev) => prev.map((m) => {
|
|
212
|
+
if (m.conversationId === conversationId && unreadIds.includes(m._id)) {
|
|
213
|
+
return {
|
|
214
|
+
...m,
|
|
215
|
+
isRead: true,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return m;
|
|
219
|
+
}));
|
|
220
|
+
setConversations((prev) => {
|
|
221
|
+
return prev.map((c) => {
|
|
222
|
+
if (c._id === conversationId) {
|
|
223
|
+
return {
|
|
224
|
+
...c,
|
|
225
|
+
participants: c.participants.map((p) => {
|
|
226
|
+
if (p.userId === userId) {
|
|
227
|
+
return {
|
|
228
|
+
...p,
|
|
229
|
+
unreadCounts: {
|
|
230
|
+
messages: 0,
|
|
231
|
+
mentions: 0,
|
|
232
|
+
milestones: 0,
|
|
233
|
+
expenses: 0,
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return p;
|
|
238
|
+
}),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
return c;
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
// // Poll created/updated (we treat them as messages that might trigger UI updates)
|
|
246
|
+
// socket.on("poll:created", ({ pollMessage }: { pollMessage: Message }) => {
|
|
247
|
+
// setMessages((prev) => [...prev, pollMessage]);
|
|
248
|
+
// });
|
|
249
|
+
socket.on("poll:updated", ({ pollMessage }) => {
|
|
250
|
+
// Re-fetch messages or update a specific one if referenceId matches
|
|
251
|
+
setMessages(prev => prev.map(m => (m.type === "poll" && m._id === pollMessage._id)
|
|
252
|
+
? pollMessage // Store poll detail in message temporarily for UI
|
|
253
|
+
: m));
|
|
254
|
+
});
|
|
157
255
|
// Message deleted
|
|
158
256
|
socket.on("message:deleted", ({ messageId }) => {
|
|
159
257
|
setMessages((prev) => prev.filter((m) => m._id !== messageId));
|
|
@@ -188,6 +286,11 @@ export function ChatProvider({ children }) {
|
|
|
188
286
|
return next;
|
|
189
287
|
});
|
|
190
288
|
});
|
|
289
|
+
// Real-time notifications
|
|
290
|
+
socket.on("notification:new", (notification) => {
|
|
291
|
+
setNotifications((prev) => [notification, ...prev].slice(0, 50));
|
|
292
|
+
setUnreadNotificationCount((prev) => prev + 1);
|
|
293
|
+
});
|
|
191
294
|
return () => {
|
|
192
295
|
socket.off("connect", onConnect);
|
|
193
296
|
socket.off("disconnect", onDisconnect);
|
|
@@ -198,6 +301,7 @@ export function ChatProvider({ children }) {
|
|
|
198
301
|
socket.off("typing:stop");
|
|
199
302
|
socket.off("presence:online");
|
|
200
303
|
socket.off("presence:offline");
|
|
304
|
+
socket.off("notification:new");
|
|
201
305
|
// Disconnect on unmount? Maybe not if shared across pages.
|
|
202
306
|
// But if specific to chat...
|
|
203
307
|
// For shared state lib, we might want to keep it open?
|
|
@@ -223,6 +327,12 @@ export function ChatProvider({ children }) {
|
|
|
223
327
|
sendMessage,
|
|
224
328
|
editMessage,
|
|
225
329
|
toggleMessageReaction,
|
|
226
|
-
deleteMessage
|
|
330
|
+
deleteMessage,
|
|
331
|
+
markRead,
|
|
332
|
+
createPoll,
|
|
333
|
+
votePoll,
|
|
334
|
+
unreadNotificationCount,
|
|
335
|
+
notifications,
|
|
336
|
+
clearNotifications,
|
|
227
337
|
}, children: children }));
|
|
228
338
|
}
|
|
@@ -19,7 +19,7 @@ export declare const BlogSchema: z.ZodObject<{
|
|
|
19
19
|
authorImage: z.ZodString;
|
|
20
20
|
authorRole: z.ZodEnum<["host", "admin", "user"]>;
|
|
21
21
|
}, "strict", z.ZodTypeAny, {
|
|
22
|
-
status: "
|
|
22
|
+
status: "pending" | "draft" | "posted" | "archived" | "denied";
|
|
23
23
|
title: string;
|
|
24
24
|
createdAt: Date;
|
|
25
25
|
updatedAt: Date;
|
|
@@ -36,7 +36,7 @@ export declare const BlogSchema: z.ZodObject<{
|
|
|
36
36
|
authorRole: "host" | "user" | "admin";
|
|
37
37
|
hashtags?: string | undefined;
|
|
38
38
|
}, {
|
|
39
|
-
status: "
|
|
39
|
+
status: "pending" | "draft" | "posted" | "archived" | "denied";
|
|
40
40
|
title: string;
|
|
41
41
|
createdAt: Date;
|
|
42
42
|
updatedAt: Date;
|
|
@@ -58,6 +58,7 @@ export type BlogMetaData = {
|
|
|
58
58
|
image: string;
|
|
59
59
|
title: string;
|
|
60
60
|
overview: string;
|
|
61
|
+
hashtags: string;
|
|
61
62
|
};
|
|
62
63
|
export declare const hashTagOptions: {
|
|
63
64
|
title: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../../../src/lib/models/blog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,gBAAgB,iEAA+D,CAAC;AAC7F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAG1D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBZ,CAAC;AAEZ,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,cAAc;;GAyB1B,CAAA"}
|
|
1
|
+
{"version":3,"file":"blog.d.ts","sourceRoot":"","sources":["../../../src/lib/models/blog.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,gBAAgB,iEAA+D,CAAC;AAC7F,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAG1D,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBZ,CAAC;AAEZ,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C,MAAM,MAAM,YAAY,GAAG;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,eAAO,MAAM,cAAc;;GAyB1B,CAAA"}
|
|
@@ -7,6 +7,12 @@ export interface Participant {
|
|
|
7
7
|
joinedAt: string;
|
|
8
8
|
lastReadMessageId?: string;
|
|
9
9
|
notifications: "all" | "mentions" | "none";
|
|
10
|
+
unreadCounts: {
|
|
11
|
+
messages: number;
|
|
12
|
+
milestones: number;
|
|
13
|
+
expenses: number;
|
|
14
|
+
mentions: number;
|
|
15
|
+
};
|
|
10
16
|
}
|
|
11
17
|
export interface ConversationFeatures {
|
|
12
18
|
milestones: boolean;
|
|
@@ -29,13 +35,14 @@ export interface Conversation {
|
|
|
29
35
|
senderId: string;
|
|
30
36
|
sentAt: string;
|
|
31
37
|
};
|
|
38
|
+
currency?: string;
|
|
32
39
|
createdBy: string;
|
|
33
40
|
createdAt: string;
|
|
34
41
|
updatedAt: string;
|
|
35
42
|
}
|
|
36
43
|
export interface MediaItem {
|
|
37
44
|
url: string;
|
|
38
|
-
type: "image" | "video" | "document";
|
|
45
|
+
type: "image" | "video" | "document" | "audio";
|
|
39
46
|
name: string;
|
|
40
47
|
size: number;
|
|
41
48
|
}
|
|
@@ -43,7 +50,7 @@ export interface MessageContent {
|
|
|
43
50
|
text?: string;
|
|
44
51
|
media?: MediaItem[];
|
|
45
52
|
referenceId?: string;
|
|
46
|
-
referenceType?: "milestone" | "expense";
|
|
53
|
+
referenceType?: "milestone" | "expense" | "poll";
|
|
47
54
|
}
|
|
48
55
|
export interface Reaction {
|
|
49
56
|
userId: string;
|
|
@@ -58,7 +65,7 @@ export interface Message {
|
|
|
58
65
|
_id: string;
|
|
59
66
|
conversationId: string;
|
|
60
67
|
senderId: string;
|
|
61
|
-
type: "text" | "image" | "file" | "media" | "milestone" | "expense" | "system" | "audio" | "location";
|
|
68
|
+
type: "text" | "image" | "file" | "media" | "milestone" | "expense" | "poll" | "system" | "audio" | "location";
|
|
62
69
|
content: MessageContent;
|
|
63
70
|
replyTo?: Message;
|
|
64
71
|
reactions: Reaction[];
|
|
@@ -83,11 +90,11 @@ export interface Milestone {
|
|
|
83
90
|
checks: MilestoneCheck[];
|
|
84
91
|
isCompleted: boolean;
|
|
85
92
|
completedAt?: string;
|
|
93
|
+
dueDate?: string;
|
|
86
94
|
createdBy: string;
|
|
87
95
|
createdAt: string;
|
|
88
96
|
updatedAt: string;
|
|
89
97
|
}
|
|
90
|
-
export type ExpenseCategory = "food" | "transport" | "accommodation" | "activity" | "shopping" | "other";
|
|
91
98
|
export type SplitType = "equal" | "exact" | "percentage" | "shares";
|
|
92
99
|
export interface ExpensePayer {
|
|
93
100
|
userId: string;
|
|
@@ -98,8 +105,6 @@ export interface ExpenseSplit {
|
|
|
98
105
|
amount: number;
|
|
99
106
|
shares?: number;
|
|
100
107
|
percentage?: number;
|
|
101
|
-
isPaid: boolean;
|
|
102
|
-
paidAt?: string;
|
|
103
108
|
}
|
|
104
109
|
export interface Expense {
|
|
105
110
|
_id: string;
|
|
@@ -108,7 +113,7 @@ export interface Expense {
|
|
|
108
113
|
description?: string;
|
|
109
114
|
amount: number;
|
|
110
115
|
currency: string;
|
|
111
|
-
category:
|
|
116
|
+
category: string;
|
|
112
117
|
paidBy: ExpensePayer[];
|
|
113
118
|
splitType: SplitType;
|
|
114
119
|
splits: ExpenseSplit[];
|
|
@@ -119,4 +124,24 @@ export interface Expense {
|
|
|
119
124
|
createdAt: string;
|
|
120
125
|
updatedAt: string;
|
|
121
126
|
}
|
|
127
|
+
export interface PollOption {
|
|
128
|
+
_id: string;
|
|
129
|
+
text: string;
|
|
130
|
+
}
|
|
131
|
+
export interface PollVote {
|
|
132
|
+
userId: string;
|
|
133
|
+
optionIndex: number;
|
|
134
|
+
votedAt: string;
|
|
135
|
+
}
|
|
136
|
+
export interface Poll {
|
|
137
|
+
_id: string;
|
|
138
|
+
conversationId: string;
|
|
139
|
+
question: string;
|
|
140
|
+
options: PollOption[];
|
|
141
|
+
allowMultiple: boolean;
|
|
142
|
+
votes: PollVote[];
|
|
143
|
+
createdBy: string;
|
|
144
|
+
createdAt: string;
|
|
145
|
+
updatedAt: string;
|
|
146
|
+
}
|
|
122
147
|
//# sourceMappingURL=chat.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/lib/models/chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC,MAAM,WAAW,WAAW;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC7B,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,KAAK,GAAG,UAAU,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/lib/models/chat.ts"],"names":[],"mappings":"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"}
|