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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,8 @@ fizzy search "bug" --sort newest # Sort by created_at desc

```bash
fizzy notification list
fizzy notification tray # Unread notifications (up to 100)
fizzy notification tray --include-read # Include read notifications
fizzy notification read NOTIFICATION_ID
fizzy notification unread NOTIFICATION_ID
fizzy notification read-all
Expand Down
46 changes: 46 additions & 0 deletions e2e/tests/notification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,52 @@ func TestNotificationReadUnread(t *testing.T) {
})
}

func TestNotificationTray(t *testing.T) {
h := harness.New(t)

t.Run("returns notification tray", func(t *testing.T) {
result := h.Run("notification", "tray")

if result.ExitCode != harness.ExitSuccess {
t.Errorf("expected exit code %d, got %d\nstderr: %s", harness.ExitSuccess, result.ExitCode, result.Stderr)
}

if result.Response == nil {
t.Fatalf("expected JSON response, got nil\nstdout: %s", result.Stdout)
}

if !result.Response.Success {
t.Error("expected success=true")
}

arr := result.GetDataArray()
if arr == nil {
t.Error("expected data to be an array")
}
})

t.Run("supports --include-read flag", func(t *testing.T) {
result := h.Run("notification", "tray", "--include-read")

if result.ExitCode != harness.ExitSuccess {
t.Errorf("expected exit code %d, got %d\nstderr: %s", harness.ExitSuccess, result.ExitCode, result.Stderr)
}

if result.Response == nil {
t.Fatalf("expected JSON response, got nil\nstdout: %s", result.Stdout)
}

if !result.Response.Success {
t.Error("expected success=true")
}

arr := result.GetDataArray()
if arr == nil {
t.Error("expected data to be an array")
}
})
}

func TestNotificationReadAll(t *testing.T) {
h := harness.New(t)

Expand Down
53 changes: 53 additions & 0 deletions internal/commands/notification.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,55 @@ var notificationReadAllCmd = &cobra.Command{
},
}

// Notification tray flags
var notificationTrayIncludeRead bool

var notificationTrayCmd = &cobra.Command{
Use: "tray",
Short: "Show notification tray",
Long: "Shows your notification tray (up to 100 unread notifications). Use --include-read to also include read notifications.",
Run: func(cmd *cobra.Command, args []string) {
if err := requireAuthAndAccount(); err != nil {
exitWithError(err)
}

client := getClient()
path := "/notifications/tray.json"
if notificationTrayIncludeRead {
path += "?include_read=true"
}

resp, err := client.Get(path)
if err != nil {
exitWithError(err)
}

// Build summary
count := 0
unreadCount := 0
if arr, ok := resp.Data.([]interface{}); ok {
count = len(arr)
for _, item := range arr {
if notif, ok := item.(map[string]interface{}); ok {
if read, ok := notif["read"].(bool); ok && !read {
unreadCount++
}
}
}
}
summary := fmt.Sprintf("%d notifications (%d unread)", count, unreadCount)

// Build breadcrumbs
breadcrumbs := []response.Breadcrumb{
breadcrumb("read", "fizzy notification read <id>", "Mark as read"),
breadcrumb("read-all", "fizzy notification read-all", "Mark all as read"),
breadcrumb("list", "fizzy notification list", "List all notifications"),
}

printSuccessWithBreadcrumbs(resp.Data, summary, breadcrumbs)
},
}

func init() {
rootCmd.AddCommand(notificationCmd)

Expand All @@ -168,6 +217,10 @@ func init() {
notificationListCmd.Flags().BoolVar(&notificationListAll, "all", false, "Fetch all pages")
notificationCmd.AddCommand(notificationListCmd)

// Tray
notificationTrayCmd.Flags().BoolVar(&notificationTrayIncludeRead, "include-read", false, "Include read notifications")
notificationCmd.AddCommand(notificationTrayCmd)

// Read/Unread
notificationCmd.AddCommand(notificationReadCmd)
notificationCmd.AddCommand(notificationUnreadCmd)
Expand Down
56 changes: 56 additions & 0 deletions internal/commands/notification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,62 @@ func TestNotificationUnread(t *testing.T) {
})
}

func TestNotificationTray(t *testing.T) {
t.Run("returns notification tray", func(t *testing.T) {
mock := NewMockClient()
mock.GetResponse = &client.APIResponse{
StatusCode: 200,
Data: []interface{}{
map[string]interface{}{"id": "1", "read": false},
map[string]interface{}{"id": "2", "read": false},
},
}

result := SetTestMode(mock)
SetTestConfig("token", "account", "https://api.example.com")
defer ResetTestMode()

RunTestCommand(func() {
notificationTrayCmd.Run(notificationTrayCmd, []string{})
})

if result.ExitCode != 0 {
t.Errorf("expected exit code 0, got %d", result.ExitCode)
}
if mock.GetCalls[0].Path != "/notifications/tray.json" {
t.Errorf("expected path '/notifications/tray.json', got '%s'", mock.GetCalls[0].Path)
}
})

t.Run("includes read notifications with flag", func(t *testing.T) {
mock := NewMockClient()
mock.GetResponse = &client.APIResponse{
StatusCode: 200,
Data: []interface{}{
map[string]interface{}{"id": "1", "read": false},
map[string]interface{}{"id": "2", "read": true},
},
}

result := SetTestMode(mock)
SetTestConfig("token", "account", "https://api.example.com")
defer ResetTestMode()

notificationTrayIncludeRead = true
RunTestCommand(func() {
notificationTrayCmd.Run(notificationTrayCmd, []string{})
})
notificationTrayIncludeRead = false

if result.ExitCode != 0 {
t.Errorf("expected exit code 0, got %d", result.ExitCode)
}
if mock.GetCalls[0].Path != "/notifications/tray.json?include_read=true" {
t.Errorf("expected path '/notifications/tray.json?include_read=true', got '%s'", mock.GetCalls[0].Path)
}
})
}

func TestNotificationReadAll(t *testing.T) {
t.Run("marks all notifications as read", func(t *testing.T) {
mock := NewMockClient()
Expand Down
2 changes: 2 additions & 0 deletions skills/fizzy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,8 @@ fizzy pin list # List your pinned cards (up to 1

```bash
fizzy notification list [--page N] [--all]
fizzy notification tray # Unread notifications (up to 100)
fizzy notification tray --include-read # Include read notifications
fizzy notification read NOTIFICATION_ID
fizzy notification read-all
fizzy notification unread NOTIFICATION_ID
Expand Down
Loading