bluesky viewer in the terminal
at main 4.9 kB view raw
1package main 2 3import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/stormlightlabs/skypanel/cli/internal/registry" 9 "github.com/stormlightlabs/skypanel/cli/internal/setup" 10 "github.com/stormlightlabs/skypanel/cli/internal/store" 11 "github.com/stormlightlabs/skypanel/cli/internal/ui" 12 "github.com/urfave/cli/v3" 13) 14 15// ListPostsAction lists the authenticated user's own posts 16func ListPostsAction(ctx context.Context, cmd *cli.Command) error { 17 if err := setup.EnsurePersistenceReady(ctx); err != nil { 18 return fmt.Errorf("persistence layer not ready: %w", err) 19 } 20 21 reg := registry.Get() 22 23 service, err := reg.GetService() 24 if err != nil { 25 return fmt.Errorf("failed to get service: %w", err) 26 } 27 28 if !service.Authenticated() { 29 return fmt.Errorf("not authenticated: run 'skycli login' first") 30 } 31 32 sessionRepo, err := reg.GetSessionRepo() 33 if err != nil { 34 return fmt.Errorf("failed to get session repository: %w", err) 35 } 36 37 did, err := sessionRepo.GetDid(ctx) 38 if err != nil { 39 return fmt.Errorf("failed to get user DID: %w", err) 40 } 41 42 limit := cmd.Int("limit") 43 asJSON := cmd.Bool("json") 44 45 logger.Debug("Fetching user's posts", "did", did, "limit", limit) 46 47 response, err := service.GetAuthorFeed(ctx, did, limit, "") 48 if err != nil { 49 return fmt.Errorf("failed to fetch user posts: %w", err) 50 } 51 52 if asJSON { 53 return ui.DisplayJSON(response) 54 } 55 56 ui.Titleln("Your Posts") 57 ui.DisplayFeed(response.Feed, response.Cursor) 58 return nil 59} 60 61// ListFeedsAction lists user's feeds (from local cache or refetch from API) 62func ListFeedsAction(ctx context.Context, cmd *cli.Command) error { 63 if err := setup.EnsurePersistenceReady(ctx); err != nil { 64 return fmt.Errorf("persistence layer not ready: %w", err) 65 } 66 67 reg := registry.Get() 68 69 refetch := cmd.Bool("refetch") 70 asJSON := cmd.Bool("json") 71 72 if refetch { 73 service, err := reg.GetService() 74 if err != nil { 75 return fmt.Errorf("failed to get service: %w", err) 76 } 77 78 if !service.Authenticated() { 79 return fmt.Errorf("not authenticated: run 'skycli login' first") 80 } 81 82 sessionRepo, err := reg.GetSessionRepo() 83 if err != nil { 84 return fmt.Errorf("failed to get session repository: %w", err) 85 } 86 87 handle, err := sessionRepo.GetHandle(ctx) 88 if err != nil { 89 return fmt.Errorf("failed to get user handle: %w", err) 90 } 91 92 logger.Debug("Refetching feeds from API", "handle", handle) 93 94 // TODO: Implement GetUserFeeds in BlueskyService 95 // For now, fall back to local feeds 96 ui.Warningln("API refetch not yet implemented, showing local feeds") 97 } 98 99 feedRepo, err := reg.GetFeedRepo() 100 if err != nil { 101 return fmt.Errorf("failed to get feed repository: %w", err) 102 } 103 104 feeds, err := feedRepo.List(ctx) 105 if err != nil { 106 logger.Error("Failed to list feeds", "error", err) 107 return err 108 } 109 110 if len(feeds) == 0 { 111 ui.Infoln("No feeds found.") 112 return nil 113 } 114 115 if asJSON { 116 return ui.DisplayJSON(feeds) 117 } 118 119 ui.Titleln("Your Feeds") 120 fmt.Println() 121 122 for i, model := range feeds { 123 if feed, ok := model.(*store.FeedModel); ok { 124 ui.Subtitleln("[%d] %s", i+1, feed.Name) 125 ui.Infoln(" ID: %s", feed.ID()) 126 ui.Infoln(" Source: %s", feed.Source) 127 ui.Infoln(" Local: %t", feed.IsLocal) 128 ui.Infoln(" Created: %s", feed.CreatedAt().Format(time.RFC3339)) 129 fmt.Println() 130 } 131 } 132 133 ui.Successln("Total: %d feed(s)", len(feeds)) 134 return nil 135} 136 137// ListCommand returns the list command with subcommands for posts and feeds 138func ListCommand() *cli.Command { 139 commonFlags := []cli.Flag{ 140 &cli.BoolFlag{ 141 Name: "refetch", 142 Aliases: []string{"r"}, 143 Usage: "Refetch from API instead of using local cache", 144 }, 145 &cli.BoolFlag{ 146 Name: "json", 147 Aliases: []string{"j"}, 148 Usage: "Output raw JSON response", 149 }, 150 } 151 152 return &cli.Command{ 153 Name: "list", 154 Usage: "List user's posts or feeds", 155 Commands: []*cli.Command{ 156 { 157 Name: "posts", 158 Usage: "List authenticated user's own posts", 159 ArgsUsage: " ", 160 Flags: []cli.Flag{ 161 &cli.IntFlag{ 162 Name: "limit", 163 Aliases: []string{"l"}, 164 Usage: "Maximum number of posts to list", 165 Value: 25, 166 }, 167 &cli.BoolFlag{ 168 Name: "json", 169 Aliases: []string{"j"}, 170 Usage: "Output raw JSON response", 171 }, 172 }, 173 Action: ListPostsAction, 174 }, 175 { 176 Name: "feeds", 177 Usage: "List user's feeds (local cache or refetch with -r)", 178 ArgsUsage: " ", 179 Flags: commonFlags, 180 Action: ListFeedsAction, 181 }, 182 }, 183 // Default action when no subcommand is provided 184 Action: func(ctx context.Context, cmd *cli.Command) error { 185 // Default to posts 186 return ListPostsAction(ctx, cmd) 187 }, 188 Flags: []cli.Flag{ 189 &cli.IntFlag{ 190 Name: "limit", 191 Aliases: []string{"l"}, 192 Usage: "Maximum number of posts to list", 193 Value: 25, 194 }, 195 &cli.BoolFlag{ 196 Name: "json", 197 Aliases: []string{"j"}, 198 Usage: "Output raw JSON response", 199 }, 200 }, 201 } 202}