From dfe92f6885192c20a392441fc68135240109ab60 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 2 Aug 2025 00:32:52 +0200 Subject: [PATCH 1/3] feat: add end at --- app2/src/lib/dashboard/database.types.ts | 74 ++++++++++++++++++- .../dashboard/stores/achievements.svelte.ts | 38 ++++++++-- 2 files changed, 104 insertions(+), 8 deletions(-) diff --git a/app2/src/lib/dashboard/database.types.ts b/app2/src/lib/dashboard/database.types.ts index ba7a9bfd27..dc0db2ac6a 100644 --- a/app2/src/lib/dashboard/database.types.ts +++ b/app2/src/lib/dashboard/database.types.ts @@ -7,6 +7,8 @@ export type Json = | Json[] export type Database = { + // Allows to automatically instanciate createClient with right options + // instead of createClient(URL, KEY) __InternalSupabase: { PostgrestVersion: "12.2.3 (519615d)" } @@ -38,6 +40,7 @@ export type Database = { category: number | null created_at: string description: string | null + end_at: string | null id: number image: string | null meta: Json | null @@ -55,6 +58,7 @@ export type Database = { category?: number | null created_at?: string description?: string | null + end_at?: string | null id?: number image?: string | null meta?: Json | null @@ -72,6 +76,7 @@ export type Database = { category?: number | null created_at?: string description?: string | null + end_at?: string | null id?: number image?: string | null meta?: Json | null @@ -1085,6 +1090,24 @@ export type Database = { } Relationships: [] } + raccoons_nft_staging: { + Row: { + count: number + ethereum_address: string + initia_address: string + } + Insert: { + count: number + ethereum_address: string + initia_address: string + } + Update: { + count?: number + ethereum_address?: string + initia_address?: string + } + Relationships: [] + } resend_audiences: { Row: { created_at: string @@ -1211,6 +1234,27 @@ export type Database = { } Relationships: [] } + smart_followers: { + Row: { + smart_follower_count: number + twitter_id: string + updated_at: string | null + user_id: string + } + Insert: { + smart_follower_count?: number + twitter_id: string + updated_at?: string | null + user_id: string + } + Update: { + smart_follower_count?: number + twitter_id?: string + updated_at?: string | null + user_id?: string + } + Relationships: [] + } snag_updated_users: { Row: { id: number @@ -1243,6 +1287,7 @@ export type Database = { display_name: string | null ip_addresses: string[] | null pfp: string | null + smart_followers: number | null twitter_id: string | null updated_at: string | null user_id: string @@ -1255,6 +1300,7 @@ export type Database = { display_name?: string | null ip_addresses?: string[] | null pfp?: string | null + smart_followers?: number | null twitter_id?: string | null updated_at?: string | null user_id: string @@ -1267,6 +1313,7 @@ export type Database = { display_name?: string | null ip_addresses?: string[] | null pfp?: string | null + smart_followers?: number | null twitter_id?: string | null updated_at?: string | null user_id?: string @@ -1285,6 +1332,7 @@ export type Database = { pfp: string | null processed_at: string | null run_id: string + smart_followers: number | null twitter_id: string | null user_id: string wallet_data: string[] | null @@ -1299,6 +1347,7 @@ export type Database = { pfp?: string | null processed_at?: string | null run_id: string + smart_followers?: number | null twitter_id?: string | null user_id: string wallet_data?: string[] | null @@ -1313,6 +1362,7 @@ export type Database = { pfp?: string | null processed_at?: string | null run_id?: string + smart_followers?: number | null twitter_id?: string | null user_id?: string wallet_data?: string[] | null @@ -2098,6 +2148,14 @@ export type Database = { } Relationships: [] } + name_clusters: { + Row: { + base_name: string | null + names: string[] | null + similar_users: number | null + } + Relationships: [] + } pg_stat_monitor: { Row: { application_name: string | null @@ -2177,6 +2235,14 @@ export type Database = { } Relationships: [] } + user_ip: { + Row: { + ip_addresses: unknown[] | null + ip_count: number | null + user_id: string | null + } + Relationships: [] + } user_levels: { Row: { current_xp: number | null @@ -2353,8 +2419,8 @@ export type Database = { get_unearned_achievements: { Args: { achievement_type: number } | { p_user_id: string } Returns: { - user_id: string - achievement_id: number + id: number + type: string meta: Json }[] } @@ -2453,6 +2519,10 @@ export type Database = { } Returns: undefined } + migrate_raccoons_nfts: { + Args: Record + Returns: undefined + } pg_stat_monitor_internal: { Args: { showtext: boolean } Returns: Record[] diff --git a/app2/src/lib/dashboard/stores/achievements.svelte.ts b/app2/src/lib/dashboard/stores/achievements.svelte.ts index ab914736b1..9c58f95f6b 100644 --- a/app2/src/lib/dashboard/stores/achievements.svelte.ts +++ b/app2/src/lib/dashboard/stores/achievements.svelte.ts @@ -12,6 +12,7 @@ import { errorStore } from "../stores/errors.svelte" export type Achievement = QueryAchievement & { category?: { title: string } | null subcategory?: { title: string } | null + end_at?: string | null reward_achievements?: { rewards: { created_at: string @@ -34,27 +35,30 @@ const POLL_INTERVAL = 5 * 60_000 export class AchievementsStore { /** Achievements the current user has earned */ achieved = $state>>(Option.none()) - /** All achievements that can be earned */ + /** All achievements that can be earned (filtered to exclude expired ones) */ available = $state>>(Option.none()) /** Polling fiber */ private pollFiber: Fiber.Fiber | null = null - /** Achievements organized by chain */ + /** Achievements organized by chain (also filtered to exclude expired achievements) */ achievementByChain = $derived( Option.flatMap(this.available, (achievements) => { + // Filter out expired achievements first + const activeAchievements = this.filterActiveAchievements(achievements) + const achievementsMap = new Map( - achievements.map((achievement) => [achievement.id, achievement]), + activeAchievements.map((achievement) => [achievement.id, achievement]), ) // Helper function to find the first achievement in a chain const findChainStart = (achievement: Achievement): Achievement => { - const previous = achievements.find((a) => a.next === achievement.id) + const previous = activeAchievements.find((a) => a.next === achievement.id) return previous ? findChainStart(previous) : achievement } return Option.some( - achievements.reduce>>((chains, achievement) => { + activeAchievements.reduce>>((chains, achievement) => { // Skip if achievement is already in any chain if (chains.some((chain) => chain.some((a) => a.id === achievement.id))) { return chains @@ -113,6 +117,24 @@ export class AchievementsStore { ) } + /** + * Filters achievements to exclude those that have passed their end_at date + * @private + */ + private filterActiveAchievements(achievements: Array): Array { + const now = new Date() + return achievements.filter((achievement) => { + // If no end_at date is set, the achievement is always active + if (!achievement.end_at) { + return true + } + + // Parse the end_at date and check if it's in the future + const endDate = new Date(achievement.end_at) + return endDate > now + }) + } + /** * Loads all available achievements */ @@ -121,7 +143,11 @@ export class AchievementsStore { pipe( getAvailableAchievements(), Effect.tap((result) => { - this.available = result + // Apply filtering to the result before setting it + this.available = Option.map( + result, + (achievements) => this.filterActiveAchievements(achievements), + ) return Effect.void }), Effect.catchAll((error) => { From 03fe916de6473b3c8661b33752451a479332e1a0 Mon Sep 17 00:00:00 2001 From: Lukas Date: Sat, 2 Aug 2025 00:43:58 +0200 Subject: [PATCH 2/3] chore: remove comment --- app2/src/lib/dashboard/database.types.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/app2/src/lib/dashboard/database.types.ts b/app2/src/lib/dashboard/database.types.ts index dc0db2ac6a..1a6873018e 100644 --- a/app2/src/lib/dashboard/database.types.ts +++ b/app2/src/lib/dashboard/database.types.ts @@ -7,8 +7,6 @@ export type Json = | Json[] export type Database = { - // Allows to automatically instanciate createClient with right options - // instead of createClient(URL, KEY) __InternalSupabase: { PostgrestVersion: "12.2.3 (519615d)" } From 37e0ab469e6048a617b433b41e6618d3872da3f9 Mon Sep 17 00:00:00 2001 From: Lukas Date: Tue, 12 Aug 2025 11:31:37 +0200 Subject: [PATCH 3/3] feat: show expired instead --- .../dashboard/components/Achievement.svelte | 45 ++++++++++-- .../components/AchievementStats.svelte | 14 +++- .../components/AchievementTabs.svelte | 72 +++++-------------- .../dashboard/stores/achievements.svelte.ts | 45 +++++------- 4 files changed, 86 insertions(+), 90 deletions(-) diff --git a/app2/src/lib/dashboard/components/Achievement.svelte b/app2/src/lib/dashboard/components/Achievement.svelte index 4f3d272b6f..9b585160cc 100644 --- a/app2/src/lib/dashboard/components/Achievement.svelte +++ b/app2/src/lib/dashboard/components/Achievement.svelte @@ -13,10 +13,17 @@ type Props = { isCurrent?: boolean isNext?: boolean isCompleted?: boolean + isExpired?: boolean } -const { achievement, userAchievements, isCurrent = false, isNext = false, isCompleted = false } = - $props() +const { + achievement, + userAchievements, + isCurrent = false, + isNext = false, + isCompleted = false, + isExpired = false, +} = $props() let showXp = $state(false) @@ -40,19 +47,36 @@ function getStatusColor() { if (completed) { return "text-accent" } + if (isExpired && !completed) { + return "text-red-400" + } if (isNext) { return "text-zinc-400" } return "text-accent" } + +function getContainerClasses() { + let baseClasses = + "flex flex-col gap-3 p-3 rounded-lg transition-all duration-300 cursor-pointer relative w-full text-left" + + if (isExpired && !completed) { + baseClasses += " opacity-60 grayscale" + } + + if (isCurrent) { + baseClasses += " bg-zinc-800/50" + } else { + baseClasses += " hover:bg-zinc-800/30" + } + + return baseClasses +}