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
88 changes: 20 additions & 68 deletions src/components/home/SubscriptionList.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { useCallback } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { View, Text, StyleSheet } from 'react-native';
import { FlashList } from '@shopify/flash-list';
import { colors, spacing, typography, borderRadius, shadows } from '../../utils/constants';
import { SubscriptionCard } from '../subscription/SubscriptionCard';
import { Subscription } from '../../types/subscription';
import { usePerformanceProfiler } from '../../hooks/usePerformanceProfiler';
import { EmptyState } from '../common/EmptyState';

interface SubscriptionListProps {
subscriptions: Subscription[];
Expand Down Expand Up @@ -97,7 +98,7 @@ export const SubscriptionList: React.FC<SubscriptionListProps> = React.memo(
<Text style={styles.sectionTitle} accessibilityRole="header">
Your Subscriptions
</Text>
{hasSubscriptions && (
{hasSubscriptions && activeSubscriptions.length > 0 && (
<View
style={styles.sectionHeaderRight}
accessibilityElementsHidden={true}
Expand All @@ -115,7 +116,23 @@ export const SubscriptionList: React.FC<SubscriptionListProps> = React.memo(
)}
</View>

{hasSubscriptions ? (
{!hasSubscriptions ? (
/* Context 1: Absolute empty state (no tracking items exist) */
<EmptyState
icon="📱"
title="No subscriptions yet"
message="Add your first subscription to start tracking your automated expenses and recurring logs."
actionText="Add Subscription"
onAction={onAddFirstPress}
/>
) : activeSubscriptions.length === 0 ? (
/* Context 2: Active filter empty state (subscriptions exist but filtered out) */
<EmptyState
icon="🔍"
title="No matches found"
message="No subscriptions correspond to your active filter or search query parameters."
/>
) : (
<View style={styles.subscriptionsList}>
<FlashList
data={activeSubscriptions}
Expand All @@ -126,38 +143,6 @@ export const SubscriptionList: React.FC<SubscriptionListProps> = React.memo(
showsVerticalScrollIndicator={false}
/>
</View>
) : (
<View
style={styles.emptyState}
accessible={true}
accessibilityLabel="No subscriptions yet. Add your first subscription to start tracking your spending.">
<Text
style={styles.emptyIcon}
accessibilityElementsHidden={true}
importantForAccessibility="no">
📱
</Text>
<Text
style={styles.emptyText}
accessibilityElementsHidden={true}
importantForAccessibility="no">
No subscriptions yet
</Text>
<Text
style={styles.emptySubtext}
accessibilityElementsHidden={true}
importantForAccessibility="no">
Add your first subscription to start tracking your spending
</Text>
<TouchableOpacity
style={styles.addFirstButton}
onPress={onAddFirstPress}
testID="add-subscription-button"
accessibilityRole="button"
accessibilityLabel="Add your first subscription">
<Text style={styles.addFirstButtonText}>Add Subscription</Text>
</TouchableOpacity>
</View>
)}
</View>
</View>
Expand Down Expand Up @@ -226,37 +211,4 @@ const styles = StyleSheet.create({
subscriptionsList: {
marginBottom: spacing.lg,
},
emptyState: {
alignItems: 'center',
paddingVertical: spacing.xl,
paddingHorizontal: spacing.lg,
},
emptyIcon: {
fontSize: 48,
marginBottom: spacing.md,
},
emptyText: {
...typography.h3,
color: colors.text,
marginBottom: spacing.xs,
textAlign: 'center',
},
emptySubtext: {
...typography.body,
color: colors.textSecondary,
textAlign: 'center',
marginBottom: spacing.lg,
lineHeight: 22,
},
addFirstButton: {
backgroundColor: colors.primary,
paddingVertical: spacing.md,
paddingHorizontal: spacing.lg,
borderRadius: borderRadius.md,
},
addFirstButtonText: {
...typography.body,
color: colors.text,
fontWeight: '600',
},
});
7 changes: 2 additions & 5 deletions src/screens/HomeScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,7 @@ const HomeScreen: React.FC = () => {
const [upcomingSubscriptions, setUpcomingSubscriptions] = useState<Subscription[]>([]);
const [showFilterModal, setShowFilterModal] = useState(false);


// Use the new hook
// Use the filter tracking hook
const { filters, filteredAndSorted, activeFilterCount, hasActiveFilters, clearAllFilters } =
useFilteredSubscriptions(subscriptions);

Expand All @@ -66,7 +65,6 @@ const HomeScreen: React.FC = () => {
if (subscriptions) setUpcomingSubscriptions(getUpcomingSubscriptions(subscriptions));
}, [subscriptions, calculateStats, preferredCurrency, exchangeRates]);


const onRefresh = async () => {
await refresh({
fetcher: refreshSubscriptions,
Expand Down Expand Up @@ -160,7 +158,6 @@ const HomeScreen: React.FC = () => {
currency={preferredCurrency}
/>


{!isOnline && (
<View style={styles.offlineBanner}>
<Text style={styles.offlineText}>
Expand Down Expand Up @@ -304,4 +301,4 @@ function createStyles(colors: ReturnType<typeof useThemeColors>) {
});
}

export default HomeScreen;
export default HomeScreen;
29 changes: 19 additions & 10 deletions src/screens/InvoiceListScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ const InvoiceListScreen: React.FC = () => {
{sortedInvoices.length === 0 ? (
<EmptyState
title="No invoices yet"
message="Invoices are created automatically after successful billing events."
message="Invoices are created automatically after successful billing events occur on tracked plans."
icon="🧾"
actionText="Go to Dashboard"
onAction={() => navigation.navigate('Home')}
/>
) : (
sortedInvoices.map((invoice) => (
Expand All @@ -87,14 +89,16 @@ const InvoiceListScreen: React.FC = () => {
</View>

<View style={styles.detailsRow}>
<Text style={styles.detailLabel}>Total</Text>
<Text style={styles.totalValue}>
{formatCurrency(invoice.total, invoice.currency)}
</Text>
</View>
<View style={styles.detailsRow}>
<Text style={styles.detailLabel}>Due</Text>
<Text style={styles.detailValue}>{formatDate(invoice.dueDate)}</Text>
<View>
<Text style={styles.detailLabel}>Total</Text>
<Text style={styles.totalValue}>
{formatCurrency(invoice.total, invoice.currency)}
</Text>
</View>
<View style={{ alignItems: 'flex-end' }}>
<Text style={styles.detailLabel}>Due</Text>
<Text style={styles.detailValue}>{formatDate(invoice.dueDate)}</Text>
</View>
</View>
</Card>
</TouchableOpacity>
Expand Down Expand Up @@ -130,7 +134,12 @@ function createStyles(colors: ReturnType<typeof useThemeColors>) {
alignItems: 'center',
marginTop: spacing.sm,
},
detailLabel: { ...typography.caption, color: colors.textSecondary, textTransform: 'uppercase' },
detailLabel: {
...typography.caption,
color: colors.textSecondary,
textTransform: 'uppercase',
marginBottom: 2,
},
detailValue: { ...typography.body, color: colors.text },
totalValue: { ...typography.h3, color: colors.accent },
});
Expand Down
1 change: 0 additions & 1 deletion src/store/subscriptionStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,6 @@ export const useSubscriptionStore = create<SubscriptionState>()(
fetchSubscriptions: async () => {
set({ isLoading: true, error: null });
try {
// TODO: Replace with remote sync; local storage remains source-of-truth offline.
await new Promise((resolve) => setTimeout(resolve, 1000));
set({ isLoading: false });
get().calculateStats();
Expand Down