Files

93 lines
3.6 KiB
Go
Raw Permalink Normal View History

2026-05-22 20:26:11 -04:00
package usercollections
import (
"context"
"fmt"
"time"
"github.com/jackc/pgx/v5/pgxpool"
)
// ServerVisibleCollection is the per-user view of a personal collection the
// owner has opted into their own library Collections tab. Personal collections
// are private to their owner; this list never crosses user boundaries.
type ServerVisibleCollection struct {
ID string `json:"id"`
CreatorProfileID string `json:"creator_profile_id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
CollectionType string `json:"collection_type"`
ItemCount int `json:"item_count"`
PosterPath string `json:"-"`
PosterURL string `json:"poster_url,omitempty"`
PosterThumbhash string `json:"poster_thumbhash,omitempty"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// serverVisibleListLimit caps how many opt-in collections a single library tab
// will surface so a user with hundreds of published collections can't blow up
// the response.
const serverVisibleListLimit = 500
// ListServerVisibleByLibrary returns the current user's personal collections
// that have opted into their library Collections tab and whose library scope
// matches the requested library. Imported exact collections read library_ids
// from source_config, while legacy rows can fall back to query_definition. A
// collection with no library_ids is treated as library-agnostic and therefore
// visible in every library tab. Other users' collections are never returned.
func ListServerVisibleByLibrary(ctx context.Context, pool *pgxpool.Pool, userID int, profileID string, libraryID int) ([]ServerVisibleCollection, error) {
2026-05-22 20:26:11 -04:00
rows, err := pool.Query(ctx,
`WITH visible_collections AS (
SELECT upc.id, upc.creator_profile_id, upc.name, upc.description, upc.collection_type,
upc.item_count, upc.poster_url, upc.poster_thumbhash, upc.created_at, upc.updated_at,
CASE
WHEN upc.collection_type = 'smart' THEN upc.query_definition
WHEN upc.source_config ? 'library_ids' THEN upc.source_config
ELSE upc.query_definition
END AS scope_config
FROM user_personal_collections upc
WHERE upc.user_id = $1
AND upc.include_in_server_collections = TRUE
AND EXISTS (
SELECT 1 FROM user_personal_collection_profiles vp
WHERE vp.user_id = upc.user_id
AND vp.collection_id = upc.id
AND vp.profile_id = $2
)
)
SELECT id, creator_profile_id, name, description, collection_type, item_count,
2026-05-22 20:26:11 -04:00
poster_url, poster_thumbhash, created_at, updated_at
FROM visible_collections
WHERE TRUE
2026-05-22 20:26:11 -04:00
AND (
NOT (scope_config ? 'library_ids')
OR jsonb_array_length(COALESCE(scope_config->'library_ids', '[]'::jsonb)) = 0
OR scope_config->'library_ids' @> to_jsonb($3::int)
2026-05-22 20:26:11 -04:00
)
ORDER BY name ASC
LIMIT $4`,
userID, profileID, libraryID, serverVisibleListLimit,
2026-05-22 20:26:11 -04:00
)
if err != nil {
return nil, fmt.Errorf("listing server-visible user collections: %w", err)
}
defer rows.Close()
var out []ServerVisibleCollection
for rows.Next() {
var c ServerVisibleCollection
var createdAt, updatedAt time.Time
if err := rows.Scan(
&c.ID, &c.CreatorProfileID, &c.Name, &c.Description, &c.CollectionType,
&c.ItemCount, &c.PosterPath, &c.PosterThumbhash, &createdAt, &updatedAt,
); err != nil {
return nil, fmt.Errorf("scanning server-visible user collection: %w", err)
}
c.CreatedAt = createdAt.UTC().Format(time.RFC3339)
c.UpdatedAt = updatedAt.UTC().Format(time.RFC3339)
out = append(out, c)
}
return out, rows.Err()
}