Skip to content

Commit

Permalink
Merge pull request #201 from SpaceTurtle-Dao/development
Browse files Browse the repository at this point in the history
Development
  • Loading branch information
ALLiDoizCode authored Feb 9, 2025
2 parents ecb35c9 + 21cda8f commit 61b16b8
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 35 deletions.
93 changes: 87 additions & 6 deletions src/lib/ao/relay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,16 +146,23 @@ export const fetchFollowList = async (
return followList;
};

// Returns Profile in Key value format that can be used in UsersProfileMapStore
export const fetchProfilesForUsersProfileMap = async (): Promise<
Map<string, Profile>
> => {
let profiles = await fetchProfiles([]);
// Modified fetchProfilesForUsersProfileMap with pagination
export const fetchProfilesForUsersProfileMap = async (
page: number = 0,
limit: number = 10
): Promise<Map<string, Profile>> => {
// Calculate start and end indices for pagination
const startIndex = page * limit;

let allProfiles = await fetchProfiles([]);

// Paginate the profiles array
const paginatedProfiles = allProfiles.slice(startIndex, startIndex + limit);

try {
const map = new Map<string, Profile>();

profiles.forEach((profile) => {
paginatedProfiles.forEach((profile) => {
const duplicate = map.get(profile.address);

if (duplicate) {
Expand All @@ -180,3 +187,77 @@ export const fetchProfilesForUsersProfileMap = async (): Promise<
throw e;
}
};

// that stores all profiles and returns paginated results from cache
export class ProfileCache {
private static instance: ProfileCache;
private profiles: Profile[] = [];
private lastFetch: number = 0;
private CACHE_DURATION = 10 * 60 * 1000; // 5 minutes

private constructor() {}

static getInstance(): ProfileCache {
if (!ProfileCache.instance) {
ProfileCache.instance = new ProfileCache();
}
return ProfileCache.instance;
}

private async refreshCache(): Promise<void> {
const now = Date.now();
if (now - this.lastFetch > this.CACHE_DURATION || this.profiles.length === 0) {
this.profiles = await fetchProfiles([]);
this.lastFetch = now;
}
}

async getPaginatedProfiles(page: number, limit: number): Promise<Map<string, Profile>> {
await this.refreshCache();

const startIndex = page * limit;
const paginatedProfiles = this.profiles.slice(startIndex, startIndex + limit);

const map = new Map<string, Profile>();

paginatedProfiles.forEach((profile) => {
const duplicate = map.get(profile.address);

if (duplicate) {
if (
duplicate.updated_at !== undefined &&
profile.updated_at !== undefined
) {
if (profile.updated_at > duplicate.updated_at) {
map.set(profile.address, profile);
}
} else if (profile.updated_at) {
map.set(profile.address, profile);
}
} else {
map.set(profile.address, profile);
}
});

return map;
}

hasMoreProfiles(page: number, limit: number): boolean {
return (page + 1) * limit < this.profiles.length;
}
}

// Modified version of fetchProfilesForUsersProfileMap that uses the cache
export const fetchPaginatedProfilesForUsersProfileMap = async (
page: number = 0,
limit: number = 10
): Promise<Map<string, Profile>> => {
const cache = ProfileCache.getInstance();
return await cache.getPaginatedProfiles(page, limit);
};

// Helper function to check if more profiles are available
export const hasMoreProfiles = (page: number, limit: number): boolean => {
const cache = ProfileCache.getInstance();
return cache.hasMoreProfiles(page, limit);
};
56 changes: 53 additions & 3 deletions src/lib/components/UserList/UserList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,54 @@
import { usersProfile } from "$lib/stores/users-profile.store";
import { currentUser } from "$lib/stores/current-user.store";
import type { Profile } from "$lib/models/Profile";
import { onMount } from "svelte";
let profiles;
const ITEMS_PER_PAGE = 10;
let currentPage = 0;
let profiles: Profile[] = [];
let containerRef: HTMLDivElement;
let loading = false;
let hasMore = true;
usersProfile.subscribe((value) => {
profiles = value.values();
profiles = Array.from(value.values());
});
$: usersProfile.isLoading.subscribe((value) => {
loading = value;
});
$: usersProfile.hasMore.subscribe((value) => {
hasMore = value;
});
onMount(() => {
loadInitialProfiles();
});
async function loadInitialProfiles() {
await usersProfile.fetchProfiles(currentPage, ITEMS_PER_PAGE);
}
async function loadMoreProfiles() {
if (loading || !hasMore) return;
currentPage++;
await usersProfile.fetchProfiles(currentPage, ITEMS_PER_PAGE);
}
function handleScroll(event: Event) {
const target = event.target as HTMLDivElement;
const threshold = 100; // pixels from bottom to trigger load
if (
target.scrollHeight - (target.scrollTop + target.clientHeight) < threshold &&
!loading &&
hasMore
) {
loadMoreProfiles();
}
}
</script>

{#if $usersProfile.size > 0}
Expand All @@ -24,13 +66,21 @@
</Card.Header>
<Card.Content class="w-full">
<div
bind:this={containerRef}
on:scroll={handleScroll}
class="grid gap-6 lg:gap-8 max-h-[60vh] lg:max-h-[80vh] overflow-y-auto scrollable-element pr-2 lg:pr-3"
>
{#each $usersProfile.values() as profile}
{#each profiles as profile}
{#if profile.address !== $currentUser.address}
<ProfileCard {profile} />
{/if}
{/each}

{#if loading}
<div class="flex justify-center p-4">
<div class="animate-spin h-6 w-6 border-2 border-primary rounded-full border-t-transparent"></div>
</div>
{/if}
</div>
</Card.Content>
</Card.Root>
Expand Down
1 change: 0 additions & 1 deletion src/lib/components/posts/CreatePost.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@
_tags.push(urlTag);
_tags.push(mTag);
}
let contentTag: Tag = {
name: "Content",
value: _content,
Expand Down
7 changes: 4 additions & 3 deletions src/lib/components/posts/Post.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,10 @@
}
await Promise.all(promises);
} catch (error) {
console.error("Error loading event data:", error);
loadError = "Failed to load post data";
}
catch (error) {
// console.error("Error loading event data:", error);
// loadError = "Failed to load post data";
} finally {
// isLoading = false;
}
Expand Down
7 changes: 3 additions & 4 deletions src/lib/components/views/signup/SignUp.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import { onMount } from "svelte";
import { z } from "zod";
import type { Profile } from "$lib/models/Profile";
import { user } from "$lib/stores/profile.store";
import { Input } from "$lib/components/ui/input";
import { Label } from "$lib/components/ui/label";
import { Button } from "$lib/components/ui/button";
Expand Down Expand Up @@ -57,9 +56,9 @@
let _relay: string | undefined;
let userInfo: Profile;
// currentUser.subscribe((value) => {
// userInfo = value;
// });
currentUser.subscribe((value) => {
userInfo = value;
});
function handleFileChange(event: Event, type: "picture" | "banner") {
const target = event.target as HTMLInputElement;
Expand Down
50 changes: 32 additions & 18 deletions src/lib/stores/users-profile.store.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,59 @@
// users-profile.store.ts
import { fetchProfile, fetchProfilesForUsersProfileMap } from "$lib/ao/relay";
import type { Profile } from "$lib/models/Profile";
import { get, writable, type Readable } from "svelte/store";
import { addressStore } from "./address.store";

export interface UsersProfileMapStore extends Readable<Map<string, Profile>> {
fetchProfiles: () => Promise<void>;
interface PaginatedProfileStore extends Readable<Map<string, Profile>> {
fetchProfiles: (page: number, limit: number) => Promise<void>;
get: (address: string) => Promise<Profile | undefined>;
hasMore: Readable<boolean>;
isLoading: Readable<boolean>;
}

const initUsersProfileMapStore = (): UsersProfileMapStore => {
const initUsersProfileMapStore = (): PaginatedProfileStore => {
const { subscribe, set, update } = writable<Map<string, Profile>>(
new Map<string, Profile>()
);
const { subscribe: subscribeToHasMore, set: setHasMore } =
writable<boolean>(true);
const { subscribe: subscribeToLoading, set: setLoading } =
writable<boolean>(false);

return {
subscribe,
fetchProfiles: async () => {
hasMore: { subscribe: subscribeToHasMore },
isLoading: { subscribe: subscribeToLoading },
fetchProfiles: async (page: number, limit: number) => {
try {
console.log("**Fetching all profiles**");
fetchProfilesForUsersProfileMap().then((profiles) => {
set(profiles);
console.log("profile-map", profiles);
}).catch(console.log);
setLoading(true);
console.log(`Fetching profiles - page ${page}, limit ${limit}`);

// Modify your fetchProfilesForUsersProfileMap to accept pagination params
const profiles = await fetchProfilesForUsersProfileMap(page, limit);

update((existingProfiles) => {
// Merge new profiles with existing ones
profiles.forEach((profile, address) => {
existingProfiles.set(address, profile);
});
return existingProfiles;
});

// Update hasMore based on whether we received fewer items than requested
setHasMore(profiles.size === limit);
} catch (error) {
console.error("Failed to fetch Users Profiles", error);
} finally {
setLoading(false);
}
},
get: async (address: string): Promise<Profile | undefined> => {
try {
let profile = get(usersProfile).get(address);

let profile = get({ subscribe }).get(address);
if (profile) return profile;

/*fetchProfile(address).then((profile) => {
}).catch()*/

const fetchedProfile = await fetchProfile(address);

update((map) => map.set(fetchedProfile.address, fetchedProfile));

return fetchedProfile;
} catch (error) {
console.error("UsersProfileMapStore.get ", error);
Expand Down

0 comments on commit 61b16b8

Please sign in to comment.