diff --git a/feedgenerator.go b/feedgenerator.go index f7aa6f7..2b4fc18 100644 --- a/feedgenerator.go +++ b/feedgenerator.go @@ -12,7 +12,8 @@ import ( type feedStore interface { GetUsersFeed(usersDID string, cursor int64, limit int) ([]store.FeedPost, error) GetSubscriptionsForUser(ctx context.Context, userDID string) ([]store.Subscription, error) - DeleteSubscriptionByIdAndUser(userDID string, id int) error + DeleteSubscriptionBySubRKeyAndUser(userDID, rkey string) error + DeleteFeedPostsForSubscribedPostURIandUserDID(subscribedPostURI, userDID string) error } type FeedGenerator struct { @@ -66,6 +67,10 @@ func (f *FeedGenerator) GetSubscriptionsForUser(ctx context.Context, userDID str return f.store.GetSubscriptionsForUser(ctx, userDID) } -func (f *FeedGenerator) DeleteSubscriptionByIdAndUser(userDID string, id int) error { - return f.store.DeleteSubscriptionByIdAndUser(userDID, id) +func (f *FeedGenerator) DeleteSubscriptionBySubRKeyAndUser(userDID, rkey string) error { + return f.store.DeleteSubscriptionBySubRKeyAndUser(userDID, rkey) +} + +func (f *FeedGenerator) DeleteFeedPostsForSubscribedPostURIandUserDID(subscribedPostURI, userDID string) error { + return f.store.DeleteFeedPostsForSubscribedPostURIandUserDID(subscribedPostURI, userDID) } diff --git a/frontend/base.templ b/frontend/base.templ index 8c2ca1a..8b3b05d 100644 --- a/frontend/base.templ +++ b/frontend/base.templ @@ -4,7 +4,7 @@ templ Base() { - Redirecter + BSFeeder diff --git a/frontend/base_templ.go b/frontend/base_templ.go index 7bdd76e..5565f2b 100644 --- a/frontend/base_templ.go +++ b/frontend/base_templ.go @@ -29,7 +29,7 @@ func Base() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Redirecter") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -41,7 +41,7 @@ func Base() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/frontend/home_templ.go b/frontend/home_templ.go index 09c4d6f..48d8ffd 100644 --- a/frontend/home_templ.go +++ b/frontend/home_templ.go @@ -91,7 +91,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your username is

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -104,7 +104,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your email is

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -117,7 +117,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your username is

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -130,7 +130,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your email is

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -143,7 +143,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your username is

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -156,7 +156,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

Your email is

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 6) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -169,7 +169,7 @@ func Something(name, email string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 7) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/frontend/login_templ.go b/frontend/login_templ.go index 6c7462f..99cffa3 100644 --- a/frontend/login_templ.go +++ b/frontend/login_templ.go @@ -62,7 +62,7 @@ func LoginForm(handle, errorMsg string) templ.Component { templ_7745c5c3_Var2 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if errorMsg != "" { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/frontend/nav.templ b/frontend/nav.templ index 0a35e44..9a30e1e 100644 --- a/frontend/nav.templ +++ b/frontend/nav.templ @@ -14,10 +14,7 @@ templ Nav() { Home
  • - Something -
  • -
  • - Hello + Subcriptions
  • diff --git a/frontend/nav_templ.go b/frontend/nav_templ.go index 63221c2..0037d08 100644 --- a/frontend/nav_templ.go +++ b/frontend/nav_templ.go @@ -35,35 +35,35 @@ func Nav() templ.Component { templ_7745c5c3_Var1 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    ") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if username, ok := ctx.Value(ContextUsernameKey).(string); ok { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } var templ_7745c5c3_Var2 string templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(username) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `frontend/nav.templ`, Line: 27, Col: 53} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `frontend/nav.templ`, Line: 24, Col: 53} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } else { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("Account ") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
    ") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/frontend/subscriptions.templ b/frontend/subscriptions.templ index 89cad49..707b090 100644 --- a/frontend/subscriptions.templ +++ b/frontend/subscriptions.templ @@ -14,25 +14,27 @@ templ Subscriptions(errorMsg string, subscriptions []store.Subscription) { } -
    -

    Subscriptions

    - // LOOP THROUGH THE TODOS - -
    +
    + + + for _, sub := range subscriptions { + + + + + } + +
    + { sub.SubscribedPostURI } + + +
    +
    } diff --git a/frontend/subscriptions_templ.go b/frontend/subscriptions_templ.go index 94ec6db..027b1b3 100644 --- a/frontend/subscriptions_templ.go +++ b/frontend/subscriptions_templ.go @@ -39,7 +39,7 @@ func Subscriptions(errorMsg string, subscriptions []store.Subscription) templ.Co return templ_7745c5c3_Err } if errorMsg != "" { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

    ") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -52,30 +52,30 @@ func Subscriptions(errorMsg string, subscriptions []store.Subscription) templ.Co if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

    ") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("

    Subscriptions

    ") + templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 10) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/frontend_handlers.go b/frontend_handlers.go index fa7d47b..38027a2 100644 --- a/frontend_handlers.go +++ b/frontend_handlers.go @@ -8,7 +8,6 @@ import ( "io" "log/slog" "net/http" - "strconv" "strings" "github.com/willdot/bskyfeedgen/frontend" @@ -70,8 +69,6 @@ func (s *Server) HandleSubscriptions(w http.ResponseWriter, r *http.Request) { handle = did } - slog.Info("sub id", "id", sub.ID) - uri := fmt.Sprintf("https://bsky.app/profile/%s/post/%s", handle, splitStr[4]) sub.SubscribedPostURI = uri subResp = append(subResp, sub) @@ -81,9 +78,9 @@ func (s *Server) HandleSubscriptions(w http.ResponseWriter, r *http.Request) { } func (s *Server) HandleDeleteSubscription(w http.ResponseWriter, r *http.Request) { - sub := r.PathValue("id") + subRKey := r.PathValue("id") - slog.Info("deleting sub", "sub", sub) + slog.Info("deleting sub", "sub", subRKey) didCookie, err := r.Cookie(didCookieName) if err != nil { @@ -99,16 +96,23 @@ func (s *Server) HandleDeleteSubscription(w http.ResponseWriter, r *http.Request usersDid := didCookie.Value - id, err := strconv.Atoi(sub) + // id, err := strconv.Atoi(sub) + // if err != nil { + // slog.Error("failed to convert sub ID to int", "error", err) + // http.Error(w, "invalid ID", http.StatusBadRequest) + // return + // } + + err = s.feeder.DeleteFeedPostsForSubscribedPostURIandUserDID(subRKey, usersDid) if err != nil { - slog.Error("failed to convert sub ID to int", "error", err) - http.Error(w, "invalid ID", http.StatusBadRequest) + slog.Error("delete feed posts for subscription and user", "error", err, "subscription URI", subRKey) + http.Error(w, "failed to delete feed posts for subscription and user", http.StatusInternalServerError) return } - err = s.feeder.DeleteSubscriptionByIdAndUser(usersDid, id) + err = s.feeder.DeleteSubscriptionBySubRKeyAndUser(usersDid, subRKey) if err != nil { - slog.Error("delete subscription for user", "error", err, "subscription URI", sub) + slog.Error("delete subscription for user", "error", err, "subscription RKey", subRKey) http.Error(w, "failed to delete subscription", http.StatusInternalServerError) return } diff --git a/public/styles.css b/public/styles.css index 9485237..92ab110 100644 --- a/public/styles.css +++ b/public/styles.css @@ -554,6 +554,10 @@ video { display: none; } +.absolute { + position: absolute; +} + .relative { position: relative; } @@ -562,10 +566,19 @@ video { position: sticky; } +.inset-x-0 { + left: 0px; + right: 0px; +} + .top-0 { top: 0px; } +.bottom-0 { + bottom: 0px; +} + .mx-auto { margin-left: auto; margin-right: auto; @@ -595,6 +608,14 @@ video { margin-top: 1.5rem; } +.mt-1 { + margin-top: 0.25rem; +} + +.mt-4 { + margin-top: 1rem; +} + .block { display: block; } @@ -611,10 +632,23 @@ video { display: contents; } +.hidden { + display: none; +} + +.size-16 { + width: 4rem; + height: 4rem; +} + .h-screen { height: 100vh; } +.h-2 { + height: 0.5rem; +} + .w-3\/12 { width: 25%; } @@ -627,6 +661,10 @@ video { width: 100%; } +.min-w-full { + min-width: 100%; +} + .max-w-md { max-width: 28rem; } @@ -645,6 +683,10 @@ video { appearance: none; } +.flex-col-reverse { + flex-direction: column-reverse; +} + .items-center { align-items: center; } @@ -665,10 +707,39 @@ video { gap: 1rem; } +.divide-y > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(1px * var(--tw-divide-y-reverse)); +} + +.divide-y-2 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-y-reverse: 0; + border-top-width: calc(2px * calc(1 - var(--tw-divide-y-reverse))); + border-bottom-width: calc(2px * var(--tw-divide-y-reverse)); +} + +.divide-gray-200 > :not([hidden]) ~ :not([hidden]) { + --tw-divide-opacity: 1; + border-color: rgb(229 231 235 / var(--tw-divide-opacity, 1)); +} + .overflow-hidden { overflow: hidden; } +.overflow-x-auto { + overflow-x: auto; +} + +.whitespace-nowrap { + white-space: nowrap; +} + +.text-pretty { + text-wrap: pretty; +} + .rounded { border-radius: 0.25rem; } @@ -708,6 +779,11 @@ video { border-color: rgb(248 113 113 / var(--tw-border-opacity, 1)); } +.border-gray-100 { + --tw-border-opacity: 1; + border-color: rgb(243 244 246 / var(--tw-border-opacity, 1)); +} + .border-t-zinc-200 { --tw-border-opacity: 1; border-top-color: rgb(228 228 231 / var(--tw-border-opacity, 1)); @@ -738,6 +814,30 @@ video { background-color: rgb(255 255 255 / var(--tw-bg-opacity, 1)); } +.bg-gradient-to-r { + background-image: linear-gradient(to right, var(--tw-gradient-stops)); +} + +.from-green-300 { + --tw-gradient-from: #86efac var(--tw-gradient-from-position); + --tw-gradient-to: rgb(134 239 172 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to); +} + +.via-blue-500 { + --tw-gradient-to: rgb(59 130 246 / 0) var(--tw-gradient-to-position); + --tw-gradient-stops: var(--tw-gradient-from), #3b82f6 var(--tw-gradient-via-position), var(--tw-gradient-to); +} + +.to-purple-600 { + --tw-gradient-to: #9333ea var(--tw-gradient-to-position); +} + +.object-cover { + -o-object-fit: cover; + object-fit: cover; +} + .p-2 { padding: 0.5rem; } @@ -826,6 +926,11 @@ video { line-height: 1.25rem; } +.text-xs { + font-size: 0.75rem; + line-height: 1rem; +} + .font-bold { font-weight: 700; } @@ -877,6 +982,11 @@ video { color: rgb(255 255 255 / var(--tw-text-opacity, 1)); } +.text-gray-600 { + --tw-text-opacity: 1; + color: rgb(75 85 99 / var(--tw-text-opacity, 1)); +} + .antialiased { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; @@ -900,6 +1010,12 @@ video { box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); } +.shadow-sm { + --tw-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05); + --tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color); + box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow); +} + .ring-1 { --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); @@ -941,10 +1057,38 @@ video { } @media (min-width: 640px) { + .sm\:block { + display: block; + } + + .sm\:flex { + display: flex; + } + + .sm\:shrink-0 { + flex-shrink: 0; + } + + .sm\:justify-between { + justify-content: space-between; + } + + .sm\:gap-4 { + gap: 1rem; + } + + .sm\:gap-6 { + gap: 1.5rem; + } + .sm\:rounded-xl { border-radius: 0.75rem; } + .sm\:p-6 { + padding: 1.5rem; + } + .sm\:px-8 { padding-left: 2rem; padding-right: 2rem; @@ -954,6 +1098,11 @@ video { padding-top: 3rem; padding-bottom: 3rem; } + + .sm\:text-xl { + font-size: 1.25rem; + line-height: 1.75rem; + } } @media (min-width: 768px) { @@ -981,3 +1130,17 @@ video { text-align: right; } } + +@media (min-width: 1024px) { + .lg\:p-8 { + padding: 2rem; + } +} + +.ltr\:text-left:where([dir="ltr"], [dir="ltr"] *) { + text-align: left; +} + +.rtl\:text-right:where([dir="rtl"], [dir="rtl"] *) { + text-align: right; +} diff --git a/server.go b/server.go index 3477e23..d6ff0ec 100644 --- a/server.go +++ b/server.go @@ -13,7 +13,8 @@ import ( type Feeder interface { GetFeed(ctx context.Context, userDID, feed, cursor string, limit int) (FeedReponse, error) GetSubscriptionsForUser(ctx context.Context, userDID string) ([]store.Subscription, error) - DeleteSubscriptionByIdAndUser(userDID string, id int) error + DeleteSubscriptionBySubRKeyAndUser(userDID, rkey string) error + DeleteFeedPostsForSubscribedPostURIandUserDID(subscribedPostURI, userDID string) error } type Server struct { diff --git a/store/subscription.go b/store/subscription.go index 41943be..66b20df 100644 --- a/store/subscription.go +++ b/store/subscription.go @@ -97,7 +97,7 @@ func (s *Store) DeleteSubscriptionForUser(userDID, postURI string) error { } func (s *Store) GetSubscriptionsForUser(ctx context.Context, userDID string) ([]Subscription, error) { - sql := "SELECT id, subscribedPostURI FROM subscriptions WHERE userDID = ?;" + sql := "SELECT id, subscribedPostURI, subscriptionPostRkey FROM subscriptions WHERE userDID = ?;" rows, err := s.db.Query(sql, userDID) if err != nil { return nil, fmt.Errorf("run query to get subscribed posts for user: %w", err) @@ -107,7 +107,7 @@ func (s *Store) GetSubscriptionsForUser(ctx context.Context, userDID string) ([] var results []Subscription for rows.Next() { var subscription Subscription - if err := rows.Scan(&subscription.ID, &subscription.SubscribedPostURI); err != nil { + if err := rows.Scan(&subscription.ID, &subscription.SubscribedPostURI, &subscription.SubscriptionPostRkey); err != nil { return nil, fmt.Errorf("scan row: %w", err) } @@ -116,11 +116,11 @@ func (s *Store) GetSubscriptionsForUser(ctx context.Context, userDID string) ([] return results, nil } -func (s *Store) DeleteSubscriptionByIdAndUser(userDID string, id int) error { - sql := "DELETE FROM subscriptions WHERE id = ?;" - _, err := s.db.Exec(sql, id) +func (s *Store) DeleteSubscriptionBySubRKeyAndUser(userDID, rkey string) error { + sql := "DELETE FROM subscriptions WHERE subscriptionPostRkey = ?;" + _, err := s.db.Exec(sql, rkey) if err != nil { - return fmt.Errorf("exec delete subscription by id: %w", err) + return fmt.Errorf("exec delete subscription by rkey: %w", err) } return nil }