Skip to content

Commit 68be165

Browse files
committed
Major UI/UX improvements and system cleanup
- Fix dark mode background color issue in PWA and regular browser modes - Improve paragraph number styling: same size as body text, better spacing - Normal mode: add space after paragraph numbers for better readability - Dense mode: reduce space after and add space before paragraph numbers - Remove deprecated donation/support components and scripts - Clean up legacy migration scripts and unused node_modules - Add new token allocation modal and pie chart components - Implement global token increment context and subscription warnings - Enhance mobile navigation and sidebar layouts - Improve error handling and logging systems - Update subscription management UI with better state indicators - Streamline payment and pledge bar implementations
1 parent 90bb655 commit 68be165

10,184 files changed

Lines changed: 3116 additions & 1155625 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/api/home-dashboard/route.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,34 +70,34 @@ export async function GET(request: NextRequest) {
7070
async function getRecentPagesOptimized(limitCount: number, userId?: string | null): Promise<any[]> {
7171
try {
7272
let pagesQuery;
73-
73+
7474
if (userId) {
75-
// For logged-in users, show their pages + public pages (exclude deleted)
75+
// For logged-in users, get recent pages and filter deleted ones in code
76+
// This avoids the composite index requirement
7677
pagesQuery = query(
7778
collection(db, 'pages'),
78-
where('deleted', '!=', true),
7979
orderBy('lastModified', 'desc'),
80-
limit(limitCount * 2) // Get more to filter later
80+
limit(limitCount * 3) // Get more to account for filtering deleted pages
8181
);
8282
} else {
83-
// For anonymous users, only public pages (exclude deleted)
83+
// For anonymous users, only public pages
8484
pagesQuery = query(
8585
collection(db, 'pages'),
8686
where('isPublic', '==', true),
87-
where('deleted', '!=', true),
8887
orderBy('lastModified', 'desc'),
89-
limit(limitCount)
88+
limit(limitCount * 2) // Get more to account for filtering deleted pages
9089
);
9190
}
92-
91+
9392
const snapshot = await getDocs(pagesQuery);
9493
const pages = snapshot.docs.map(doc => ({
9594
id: doc.id,
9695
...doc.data()
9796
}));
98-
99-
// Filter and limit results
97+
98+
// Filter out deleted pages in application code to avoid composite index requirement
10099
const filteredPages = pages
100+
.filter(page => page.deleted !== true) // Filter deleted pages in code
101101
.filter(page => {
102102
if (!userId) return page.isPublic;
103103
return page.isPublic || page.userId === userId;

app/api/log-console-error/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export async function POST(request: NextRequest) {
229229
}
230230

231231
// Add user agent for debugging browser-specific issues
232-
if (userAgent && (level === 'error' || message.includes('Firebase'))) {
232+
if (userAgent && (level === 'error' || (message && message.includes('Firebase')))) {
233233
const browserInfo = userAgent.match(/(Chrome|Firefox|Safari|Edge)\/[\d.]+/)?.[0] || 'Unknown'
234234
logMessage += `\n🌐 Browser: ${browserInfo}`
235235
}

app/api/tokens/allocations/route.ts

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,22 @@ export async function GET(request: NextRequest) {
2727
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
2828
}
2929

30+
// Get pagination parameters
31+
const { searchParams } = new URL(request.url);
32+
const limit = parseInt(searchParams.get('limit') || '20');
33+
const offset = parseInt(searchParams.get('offset') || '0');
34+
3035
// Get token balance first to check if user has any allocations
3136
console.log(`Fetching token balance for user ${userId}`);
3237

3338
const balance = await ServerTokenService.getUserTokenBalance(userId);
39+
console.log('🎯 Token Allocations API: User balance', {
40+
userId,
41+
balance,
42+
hasBalance: !!balance,
43+
allocatedTokens: balance?.allocatedTokens
44+
});
45+
3446
if (!balance) {
3547
return NextResponse.json({
3648
success: true,
@@ -44,18 +56,12 @@ export async function GET(request: NextRequest) {
4456
});
4557
}
4658

47-
// If user has allocated tokens, try to get the detailed allocations
48-
if (balance.allocatedTokens === 0) {
49-
return NextResponse.json({
50-
success: true,
51-
allocations: [],
52-
summary: {
53-
totalAllocations: 0,
54-
totalTokensAllocated: 0,
55-
balance: balance
56-
}
57-
});
58-
}
59+
// Always try to get detailed allocations regardless of balance.allocatedTokens
60+
// because the balance.allocatedTokens field might be stale
61+
console.log('🎯 Token Allocations API: Checking allocated tokens', {
62+
allocatedTokens: balance.allocatedTokens,
63+
note: 'Will fetch actual allocations to get real count'
64+
});
5965

6066
// Try to get detailed allocations using ServerTokenService
6167
console.log(`User has ${balance.allocatedTokens} allocated tokens, fetching detailed allocations`);
@@ -68,6 +74,20 @@ export async function GET(request: NextRequest) {
6874
// Filter for page allocations only
6975
pageAllocations = allocations.filter(allocation => allocation.resourceType === 'page');
7076
console.log(`Found ${pageAllocations.length} page allocations for user ${userId}`);
77+
78+
// If no page allocations found, return empty result
79+
if (pageAllocations.length === 0) {
80+
console.log('🎯 Token Allocations API: No page allocations found');
81+
return NextResponse.json({
82+
success: true,
83+
allocations: [],
84+
summary: {
85+
totalAllocations: 0,
86+
totalTokensAllocated: 0,
87+
balance: balance
88+
}
89+
});
90+
}
7191
} catch (error) {
7292
console.error('Error fetching detailed allocations:', error);
7393
// If we can't get detailed allocations, return summary only
@@ -169,11 +189,23 @@ export async function GET(request: NextRequest) {
169189
// Sort by tokens (highest first)
170190
enhancedAllocations.sort((a, b) => b.tokens - a.tokens);
171191

192+
// Apply pagination
193+
const totalAllocations = enhancedAllocations.length;
194+
const paginatedAllocations = enhancedAllocations.slice(offset, offset + limit);
195+
const hasMore = offset + limit < totalAllocations;
196+
172197
return NextResponse.json({
173198
success: true,
174-
allocations: enhancedAllocations,
199+
allocations: paginatedAllocations,
200+
pagination: {
201+
offset,
202+
limit,
203+
total: totalAllocations,
204+
hasMore,
205+
returned: paginatedAllocations.length
206+
},
175207
summary: {
176-
totalAllocations: enhancedAllocations.length,
208+
totalAllocations: totalAllocations,
177209
totalTokensAllocated: enhancedAllocations.reduce((sum, allocation) => sum + allocation.tokens, 0),
178210
balance: balance
179211
}

app/api/trending/route.js

Lines changed: 84 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ export async function GET(request) {
5555
db = firestore;
5656

5757
try {
58+
// Add simple in-memory cache for trending data (5 minute TTL)
59+
const cacheKey = `trending_${limitCount}`;
60+
const cacheExpiry = 5 * 60 * 1000; // 5 minutes
61+
62+
// Check if we have cached data (in a real app, use Redis or similar)
63+
if (global.trendingCache && global.trendingCache[cacheKey]) {
64+
const cached = global.trendingCache[cacheKey];
65+
if (Date.now() - cached.timestamp < cacheExpiry) {
66+
console.log('Returning cached trending data');
67+
return NextResponse.json({ trendingPages: cached.data }, { headers });
68+
}
69+
}
5870

5971
// Get current date and time
6072
const now = new Date();
@@ -67,10 +79,12 @@ export async function GET(request) {
6779
yesterday.setDate(yesterday.getDate() - 1);
6880
const yesterdayStr = yesterday.toISOString().split('T')[0];
6981

70-
// Get page views for today and yesterday
82+
// Get page views for today and yesterday with optimized queries
7183
const pageViewsRef = db.collection("pageViews");
72-
const todayQuery = pageViewsRef.where("date", "==", todayStr);
73-
const yesterdayQuery = pageViewsRef.where("date", "==", yesterdayStr);
84+
85+
// Limit the initial query to reduce data transfer
86+
const todayQuery = pageViewsRef.where("date", "==", todayStr).limit(100);
87+
const yesterdayQuery = pageViewsRef.where("date", "==", yesterdayStr).limit(100);
7488

7589
const [todaySnapshot, yesterdaySnapshot] = await Promise.all([
7690
todayQuery.get(),
@@ -223,56 +237,85 @@ export async function GET(request) {
223237
});
224238
}
225239

226-
// Fetch page titles and user info for the trending pages
227-
const pagesWithTitlesPromises = trendingPages.map(async (page) => {
228-
try {
229-
const pageDoc = await db.collection("pages").doc(page.id).get();
230-
if (pageDoc.exists) {
231-
const pageData = pageDoc.data();
240+
// Optimize: Batch fetch page data and user data separately
241+
const pageIds = trendingPages.map(page => page.id);
232242

233-
// Only include public, non-deleted pages
234-
if (pageData.isPublic === false || pageData.deleted === true) {
235-
return null;
236-
}
243+
// Batch fetch all page documents at once
244+
const pageDocsPromise = db.getAll(...pageIds.map(id => db.collection("pages").doc(id)));
245+
246+
// Execute the batch fetch
247+
const pageDocs = await pageDocsPromise;
237248

238-
// Get username if userId exists
239-
let username = "Anonymous";
249+
// Extract user IDs and prepare page data
250+
const pageDataMap = new Map();
251+
const userIds = new Set();
252+
253+
pageDocs.forEach((doc, index) => {
254+
if (doc.exists) {
255+
const pageData = doc.data();
256+
const pageId = pageIds[index];
257+
258+
// Only include public, non-deleted pages
259+
if (pageData.isPublic !== false && pageData.deleted !== true) {
260+
pageDataMap.set(pageId, pageData);
240261
if (pageData.userId) {
241-
try {
242-
const userDoc = await db.collection("users").doc(pageData.userId).get();
243-
if (userDoc.exists) {
244-
const userData = userDoc.data();
245-
username = userData.username || userData.displayName || "Anonymous";
246-
}
247-
} catch (usernameError) {
248-
// Handle permission denied errors gracefully - this is expected for private user data
249-
if (usernameError?.code === 'permission-denied') {
250-
console.log(`Permission denied getting username for user ${pageData.userId} - this is expected for private user data`);
251-
} else {
252-
console.error(`Error getting username for user ${pageData.userId}:`, usernameError);
253-
}
254-
}
262+
userIds.add(pageData.userId);
255263
}
256-
257-
return {
258-
...page,
259-
title: pageData.title || 'Untitled',
260-
userId: pageData.userId,
261-
username
262-
};
263264
}
264-
return { ...page, title: 'Untitled' };
265-
} catch (err) {
266-
console.error(`Error fetching page data for ${page.id}:`, err);
267-
return { ...page, title: 'Untitled' };
268265
}
269266
});
270267

271-
const pagesWithTitles = await Promise.all(pagesWithTitlesPromises);
268+
// Batch fetch user data for all unique user IDs
269+
const userDataMap = new Map();
270+
if (userIds.size > 0) {
271+
try {
272+
const userDocs = await db.getAll(...Array.from(userIds).map(id => db.collection("users").doc(id)));
273+
userDocs.forEach((doc, index) => {
274+
if (doc.exists) {
275+
const userData = doc.data();
276+
const userId = Array.from(userIds)[index];
277+
userDataMap.set(userId, userData);
278+
}
279+
});
280+
} catch (userError) {
281+
console.warn('Error batch fetching user data:', userError.message);
282+
// Continue without user data if there's an error
283+
}
284+
}
285+
286+
// Combine the data efficiently
287+
const pagesWithTitles = trendingPages.map(page => {
288+
const pageData = pageDataMap.get(page.id);
289+
if (!pageData) {
290+
return null; // Page was private or deleted
291+
}
292+
293+
let username = "Anonymous";
294+
if (pageData.userId && userDataMap.has(pageData.userId)) {
295+
const userData = userDataMap.get(pageData.userId);
296+
username = userData.username || userData.displayName || "Anonymous";
297+
}
298+
299+
return {
300+
...page,
301+
title: pageData.title || 'Untitled',
302+
userId: pageData.userId,
303+
username
304+
};
305+
}).filter(page => page !== null);
272306

273307
// Filter out null entries (private pages)
274308
const publicPages = pagesWithTitles.filter(page => page !== null);
275309

310+
// Cache the results
311+
if (!global.trendingCache) {
312+
global.trendingCache = {};
313+
}
314+
global.trendingCache[cacheKey] = {
315+
data: publicPages,
316+
timestamp: Date.now()
317+
};
318+
276319
return NextResponse.json({ trendingPages: publicPages }, { headers });
277320
} catch (error) {
278321
console.error("Error getting trending pages:", error);

0 commit comments

Comments
 (0)