Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 3 additions & 105 deletions mobile/constants/i18n/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { initReactI18next } from 'react-i18next';

import ar from '../../locales/ar.json';
import en from '../../locales/en.json';
import es from '../../locales/es.json';
import fr from '../../locales/fr.json';
import sw from '../../locales/sw.json';

export type SupportedLanguage = 'ar' | 'en' | 'es' | 'fr' | 'sw';
export const LANGUAGE_STORAGE_KEY = 'esustellar_app_language';

/** Languages written right-to-left. */
const RTL_LANGUAGES: ReadonlySet<SupportedLanguage> = new Set(['ar']);
const RTL_LANGUAGES: ReadonlySet<SupportedLanguage> = new Set<SupportedLanguage>(['ar']);

export const languageOptions = [
{ label: 'Ψ§Ω„ΨΉΨ±Ψ¨ΩŠΨ©', value: 'ar' as SupportedLanguage },
Expand All @@ -33,110 +34,7 @@ const applyRTL = (lang: SupportedLanguage): void => {
const resources = {
ar: { translation: ar },
en: { translation: en },
es: {
translation: {
tabs: {
home: 'Inicio',
groups: 'Grupos',
notifications: 'Notificaciones',
profile: 'Perfil',
},
home: {
goodMorning: 'Buenos dΓ­as',
goodAfternoon: 'Buenas tardes',
goodEvening: 'Buenas noches',
defaultUser: 'Usuario de EsuStellar',
totalBalance: 'Saldo total',
quickActions: 'Accesos rΓ‘pidos',
balanceValue: 'β€” XLM',
notifications: 'Notificaciones',
},
onboarding: {
skip: 'Omitir',
next: 'Siguiente',
getStarted: 'Comenzar',
stayInformed: 'Mantente informado',
notificationBody:
'Recibe recordatorios de fechas de vencimiento, pagos y actualizaciones del grupo para no perderte ningΓΊn momento importante.',
allowNotifications: 'Permitir notificaciones',
skipForNow: 'Omitir por ahora',
slides: {
welcome: {
eyebrow: 'Bienvenido',
title: 'Ahorra con personas de confianza',
description:
'Lleva tu cΓ­rculo de ahorro comunitario a la cadena de bloques sin perder la experiencia familiar de grupo.',
},
transparent: {
eyebrow: 'Transparente',
title: 'Sigue cada contribuciΓ³n claramente',
description:
'Mantente al tanto de los pagos, fechas de vencimiento y el progreso del grupo con actualizaciones simples en un solo lugar.',
},
secure: {
eyebrow: 'Seguro',
title: 'Comienza con confianza',
description:
'Conecta tu billetera Stellar, gestiona tus opciones de seguridad y recibe recordatorios ΓΊtiles cuando importa.',
},
},
},
settings: {
title: 'Ajustes de seguridad',
walletAddress: 'DirecciΓ³n de la billetera',
copyWalletAddress: 'Copiar direcciΓ³n de la billetera',
copyToast: 'DirecciΓ³n copiada al portapapeles',
copyFailed: 'No se pudo copiar la direcciΓ³n',
biometricAuthentication: 'AutenticaciΓ³n biomΓ©trica',
supported: 'Compatible',
enableBiometrics: 'Activar biometrΓ­a',
useToSignIn: 'Usa {{supportedLabel}} para iniciar sesiΓ³n',
checkingDeviceCapabilities: 'Comprobando capacidades del dispositivo…',
biometricsNotSupported: 'La biometrΓ­a no es compatible con este dispositivo',
setUpPinFallback:
'Configura un PIN como respaldo cuando la biometrΓ­a no estΓ© disponible.',
noBiometricsEnrolled: 'No hay biometrΓ­a registrada',
setupBiometricsHelper:
'Configura huella o reconocimiento facial en los ajustes del dispositivo y vuelve aquΓ­ para activarlo.',
about: 'Acerca de',
version: 'VersiΓ³n',
build: 'CompilaciΓ³n',
pinFallback: 'PIN de respaldo',
pinDescription:
'Configura un PIN de 4-6 dΓ­gitos como respaldo cuando la biometrΓ­a no estΓ© disponible.',
setUpPin: 'Configurar PIN',
enterPin: 'Ingresa tu PIN',
confirmYourPin: 'Confirma tu PIN',
pinDigits: '4-6 dΓ­gitos',
reenterPin: 'Vuelve a ingresar el PIN',
cancel: 'Cancelar',
next: 'Siguiente',
confirm: 'Confirmar',
pinSet: 'El PIN estΓ‘ configurado',
pinFallbackInfo: 'Se usa como respaldo cuando la biometrΓ­a falla',
change: 'Cambiar',
remove: 'Eliminar',
pinMustDigits: 'El PIN debe tener 4-6 dΓ­gitos',
pinsDoNotMatch: 'Los PIN no coinciden',
biometricsEnabled: 'AutenticaciΓ³n biomΓ©trica activada',
biometricsDisabled: 'AutenticaciΓ³n biomΓ©trica desactivada',
biometricFailed: 'La verificaciΓ³n biomΓ©trica fallΓ³',
failedToSavePin: 'No se pudo guardar el PIN',
pinRemoved: 'PIN eliminado',
language: 'Idioma',
languageLabel: 'Elige el idioma de la aplicaciΓ³n',
languageChangeSuccess: 'Idioma actualizado.',
},
profile: {
editProfile: 'Editar perfil',
settings: 'Ajustes',
disconnectWallet: 'Desconectar billetera',
},
lock: {
tapToUnlock: 'Toca para desbloquear',
},
},
},
es: { translation: es },
fr: { translation: fr },
sw: { translation: sw },
};
Expand Down
95 changes: 95 additions & 0 deletions mobile/locales/es.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
"tabs": {
"home": "Inicio",
"groups": "Grupos",
"notifications": "Notificaciones",
"profile": "Perfil"
},
"home": {
"goodMorning": "Buenos dΓ­as",
"goodAfternoon": "Buenas tardes",
"goodEvening": "Buenas noches",
"defaultUser": "Usuario de EsuStellar",
"totalBalance": "Saldo total",
"quickActions": "Accesos rΓ‘pidos",
"balanceValue": "β€” XLM",
"notifications": "Notificaciones"
},
"onboarding": {
"skip": "Omitir",
"next": "Siguiente",
"getStarted": "Comenzar",
"stayInformed": "Mantente informado",
"notificationBody": "Recibe recordatorios de fechas de vencimiento, pagos y actualizaciones del grupo para no perderte ningΓΊn momento importante.",
"allowNotifications": "Permitir notificaciones",
"skipForNow": "Omitir por ahora",
"slides": {
"welcome": {
"eyebrow": "Bienvenido",
"title": "Ahorra con personas de confianza",
"description": "Lleva tu cΓ­rculo de ahorro comunitario a la cadena de bloques sin perder la experiencia familiar de grupo."
},
"transparent": {
"eyebrow": "Transparente",
"title": "Sigue cada contribuciΓ³n claramente",
"description": "Mantente al tanto de los pagos, fechas de vencimiento y el progreso del grupo con actualizaciones simples en un solo lugar."
},
"secure": {
"eyebrow": "Seguro",
"title": "Comienza con confianza",
"description": "Conecta tu billetera Stellar, gestiona tus opciones de seguridad y recibe recordatorios ΓΊtiles cuando importa."
}
}
},
"settings": {
"title": "Ajustes de seguridad",
"walletAddress": "DirecciΓ³n de la billetera",
"copyWalletAddress": "Copiar direcciΓ³n de la billetera",
"copyToast": "DirecciΓ³n copiada al portapapeles",
"copyFailed": "No se pudo copiar la direcciΓ³n",
"biometricAuthentication": "AutenticaciΓ³n biomΓ©trica",
"supported": "Compatible",
"enableBiometrics": "Activar biometrΓ­a",
"useToSignIn": "Usa {{supportedLabel}} para iniciar sesiΓ³n",
"checkingDeviceCapabilities": "Comprobando capacidades del dispositivo…",
"biometricsNotSupported": "La biometrΓ­a no es compatible con este dispositivo",
"setUpPinFallback": "Configura un PIN como respaldo cuando la biometrΓ­a no estΓ© disponible.",
"noBiometricsEnrolled": "No hay biometrΓ­a registrada",
"setupBiometricsHelper": "Configura huella o reconocimiento facial en los ajustes del dispositivo y vuelve aquΓ­ para activarlo.",
"about": "Acerca de",
"version": "VersiΓ³n",
"build": "CompilaciΓ³n",
"pinFallback": "PIN de respaldo",
"pinDescription": "Configura un PIN de 4-6 dΓ­gitos como respaldo cuando la biometrΓ­a no estΓ© disponible.",
"setUpPin": "Configurar PIN",
"enterPin": "Ingresa tu PIN",
"confirmYourPin": "Confirma tu PIN",
"pinDigits": "4-6 dΓ­gitos",
"reenterPin": "Vuelve a ingresar el PIN",
"cancel": "Cancelar",
"next": "Siguiente",
"confirm": "Confirmar",
"pinSet": "El PIN estΓ‘ configurado",
"pinFallbackInfo": "Se usa como respaldo cuando la biometrΓ­a falla",
"change": "Cambiar",
"remove": "Eliminar",
"pinMustDigits": "El PIN debe tener 4-6 dΓ­gitos",
"pinsDoNotMatch": "Los PIN no coinciden",
"biometricsEnabled": "AutenticaciΓ³n biomΓ©trica activada",
"biometricsDisabled": "AutenticaciΓ³n biomΓ©trica desactivada",
"biometricFailed": "La verificaciΓ³n biomΓ©trica fallΓ³",
"failedToSavePin": "No se pudo guardar el PIN",
"pinRemoved": "PIN eliminado",
"language": "Idioma",
"languageLabel": "Elige el idioma de la aplicaciΓ³n",
"languageChangeSuccess": "Idioma actualizado."
},
"profile": {
"editProfile": "Editar perfil",
"settings": "Ajustes",
"disconnectWallet": "Desconectar billetera"
},
"lock": {
"tapToUnlock": "Toca para desbloquear"
}
}
61 changes: 59 additions & 2 deletions mobile/services/api/transactionsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,13 @@ class TransactionsApiService {
}

/**
* Get all transactions for a user
* Get all transactions for a user (paginated)
*/
async getUserTransactions(userAddress: string): Promise<ApiResponse<Transaction[]>> {
async getUserTransactions(
userAddress: string,
cursor?: string,
limit: number = 20
): Promise<ApiResponse<Transaction[]>> {
try {
console.log(`Fetching transactions for user: ${userAddress}`);

Expand Down Expand Up @@ -68,6 +72,59 @@ class TransactionsApiService {
};
}
}

/**
* Get paginated transactions with cursor
*/
async getPaginatedTransactions(
userAddress: string,
cursor?: string,
limit: number = 20
): Promise<ApiResponse<{
transactions: Transaction[];
hasMore: boolean;
nextCursor?: string;
totalCount: number;
}>> {
try {
console.log(`Fetching paginated transactions for user: ${userAddress}`);

await new Promise(resolve => setTimeout(resolve, 800));

const totalMockEntities = 50;
const allMockTx: Transaction[] = Array.from({ length: totalMockEntities }, (_, i) => ({
id: `tx_${i + 1}`,
groupId: `group_${(i % 3) + 1}`,
userAddress,
amount: (i + 1) * 100,
type: i % 2 === 0 ? 'contribution' : 'payout',
status: 'confirmed',
createdAt: new Date(Date.now() - i * 3600000).toISOString(),
txHash: `0xhash${i}${Date.now().toString(16)}`,
}));

const pageIndex = cursor ? parseInt(cursor, 10) : 0;
const startIndex = pageIndex * limit;
const endIndex = Math.min(startIndex + limit, allMockTx.length);
const pageTx = allMockTx.slice(startIndex, endIndex);

return {
success: true,
data: {
transactions: pageTx,
hasMore: endIndex < allMockTx.length,
nextCursor: endIndex < allMockTx.length ? String(pageIndex + 1) : undefined,
totalCount: allMockTx.length,
},
};
} catch (error) {
console.error('Failed to fetch paginated transactions:', error);
return {
success: false,
error: 'Failed to fetch paginated transactions',
};
}
}
}

export const transactionsApi = new TransactionsApiService();
Expand Down
1 change: 1 addition & 0 deletions mobile/services/queryClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const queryKeys = {
},
transactions: {
all: ['transactions'] as const,
user: (address: string) => ['transactions', 'user', address] as const,
group: (groupId: string) => ['transactions', groupId] as const,
},
notifications: {
Expand Down
20 changes: 16 additions & 4 deletions mobile/services/sync/backgroundSync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,23 @@ class BackgroundSyncService {
* Sync transactions data
*/
private async syncTransactions(userAddress: string) {
const result = await transactionsApi.getUserTransactions(userAddress);
if (result.success && result.data) {
queryClient.setQueryData(['transactions', 'user', userAddress], result);
console.log('[SyncService] Transactions synced successfully');
const result = await transactionSyncService.syncTransactions(userAddress);

if (!result.success) {
console.error('[SyncService] Transaction sync failed:', result.error);
return;
}

const existingData = queryClient.getQueryData<{ data?: Transaction[] }>(
queryKeys.transactions.user(userAddress)
);

queryClient.setQueryData(
queryKeys.transactions.user(userAddress),
{ ...existingData, data: existingData?.data || [] }
);

console.log('[SyncService] Transactions synced successfully');
}

/**
Expand Down
Loading