196 lines
5.5 KiB
TypeScript
196 lines
5.5 KiB
TypeScript
/**
|
|
* Request Service - Platform-aware money request management
|
|
* Uses Firebase abstraction layer for cross-platform support
|
|
*/
|
|
import { doc, collection, FieldValue } from '../firebase';
|
|
import { withGlobalLoading } from '../stores/uiStore';
|
|
|
|
export interface MoneyRequest {
|
|
id?: string;
|
|
requestorName: string;
|
|
requesteeName: string;
|
|
requestorPhoneNumber: string;
|
|
requesteePhoneNumber: string;
|
|
requestorType: 'saved' | 'contact';
|
|
amount: number;
|
|
note?: string;
|
|
notificationMethod: 'SMS' | 'WhatsApp';
|
|
status: 'pending' | 'accepted' | 'declined' | 'expired';
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
}
|
|
|
|
export class RequestService {
|
|
private static snapshotExists(docSnap: any): boolean {
|
|
const existsValue = docSnap?.exists;
|
|
if (typeof existsValue === 'function') {
|
|
try {
|
|
return !!existsValue.call(docSnap);
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
return !!existsValue;
|
|
}
|
|
|
|
/**
|
|
* Find user UID by phone number
|
|
*/
|
|
static async findUserByPhoneNumber(phoneNumber: string): Promise<string | null> {
|
|
try {
|
|
const usersCollection = collection('users');
|
|
const querySnapshot = await usersCollection
|
|
.where('phoneNumber', '==', phoneNumber)
|
|
.get();
|
|
|
|
if (querySnapshot.empty) {
|
|
return null;
|
|
}
|
|
|
|
return querySnapshot.docs[0].id;
|
|
} catch (error) {
|
|
console.error('Error finding user by phone number:', error);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get user details by phone number
|
|
*/
|
|
static async getUserByPhoneNumber(phoneNumber: string): Promise<{ success: boolean; user?: { uid: string; displayName: string; phoneNumber: string }; error?: string }> {
|
|
try {
|
|
const usersCollection = collection('users');
|
|
const querySnapshot = await usersCollection
|
|
.where('phoneNumber', '==', phoneNumber)
|
|
.get();
|
|
|
|
if (querySnapshot.empty) {
|
|
return { success: false, error: 'User not found' };
|
|
}
|
|
|
|
const userDoc = querySnapshot.docs[0];
|
|
const userData = userDoc.data();
|
|
|
|
return {
|
|
success: true,
|
|
user: {
|
|
uid: userDoc.id,
|
|
displayName: userData.displayName || userData.name || 'Unknown',
|
|
phoneNumber: userData.phoneNumber
|
|
}
|
|
};
|
|
} catch (error) {
|
|
console.error('Error getting user by phone number:', error);
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Failed to get user'
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get request by ID
|
|
*/
|
|
static async getRequestById(requestId: string): Promise<{ success: boolean; request?: MoneyRequest; error?: string }> {
|
|
try {
|
|
const requestRef = doc('requests', requestId);
|
|
const requestSnapshot = await requestRef.get();
|
|
|
|
if (!RequestService.snapshotExists(requestSnapshot)) {
|
|
return { success: false, error: 'Request not found' };
|
|
}
|
|
|
|
const requestData = requestSnapshot.data() as any;
|
|
const request: MoneyRequest = {
|
|
id: requestSnapshot.id,
|
|
requestorName: requestData.requestorName,
|
|
requesteeName: requestData.requesteeName,
|
|
requestorPhoneNumber: requestData.requestorPhoneNumber,
|
|
requesteePhoneNumber: requestData.requesteePhoneNumber,
|
|
requestorType: requestData.requestorType,
|
|
amount: requestData.amount,
|
|
note: requestData.note,
|
|
notificationMethod: requestData.notificationMethod || 'SMS',
|
|
status: requestData.status,
|
|
createdAt: requestData.createdAt?.toDate?.() || new Date(),
|
|
updatedAt: requestData.updatedAt?.toDate?.() || new Date(),
|
|
};
|
|
|
|
return {
|
|
success: true,
|
|
request
|
|
};
|
|
} catch (error) {
|
|
console.error('Error getting request by ID:', error);
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Failed to get request'
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update request status
|
|
*/
|
|
static async updateRequestStatus(
|
|
requestId: string,
|
|
status: 'pending' | 'accepted' | 'declined' | 'expired'
|
|
): Promise<{ success: boolean; error?: string }> {
|
|
return withGlobalLoading(async () => {
|
|
try {
|
|
const requestRef = doc('requests', requestId);
|
|
await requestRef.update({
|
|
status,
|
|
updatedAt: FieldValue.serverTimestamp(),
|
|
});
|
|
|
|
return {
|
|
success: true,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error updating request status:', error);
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Failed to update request status',
|
|
};
|
|
}
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a new money request
|
|
*/
|
|
static async createRequest(
|
|
uid: string,
|
|
requestData: Omit<MoneyRequest, 'id' | 'createdAt' | 'updatedAt'>
|
|
): Promise<{ success: boolean; requestId?: string; error?: string }> {
|
|
return withGlobalLoading(async () => {
|
|
try {
|
|
// Generate a unique request ID
|
|
const requestId = `request_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
|
|
const request = {
|
|
...requestData,
|
|
uid,
|
|
createdAt: FieldValue.serverTimestamp(),
|
|
updatedAt: FieldValue.serverTimestamp(),
|
|
};
|
|
|
|
const requestRef = doc('requests', requestId);
|
|
await requestRef.set(request);
|
|
|
|
return {
|
|
success: true,
|
|
requestId,
|
|
};
|
|
} catch (error) {
|
|
console.error('Error creating money request:', error);
|
|
return {
|
|
success: false,
|
|
error: error instanceof Error ? error.message : 'Failed to create request',
|
|
};
|
|
}
|
|
});
|
|
}
|
|
}
|