diff --git a/src/css/style.css b/src/css/style.css index 3dfe0412..ea88a9e8 100644 --- a/src/css/style.css +++ b/src/css/style.css @@ -1864,6 +1864,13 @@ container-link { gap: 4px; } +@media (hover: hover) { + .small-post .repost-label a:hover, + .reply-to-author a:hover { + text-decoration: underline; + } +} + .small-post .repost-label .icon { height: 17px; width: 17px; @@ -3307,6 +3314,22 @@ image-carousel { font-weight: 600; } +.notification-others-button { + background: none; + border: none; + padding: 0; + margin: 0; + font: inherit; + font-weight: 600; + cursor: pointer; +} + +@media (hover: hover) { + .notification-others-button:hover { + text-decoration: underline; + } +} + .notification-time { color: var(--text-color-muted); font-weight: normal; @@ -10103,6 +10126,12 @@ p.setting-item-desc { } } +.bottom-sheet.profile-list-modal .profile-list-modal-body { + max-height: 60vh; + overflow-y: auto; + margin: 0 -24px; +} + .bottom-sheet.welcome-modal { width: 90%; max-width: 410px; diff --git a/src/js/api.js b/src/js/api.js index 23f1fadd..a8a6478b 100644 --- a/src/js/api.js +++ b/src/js/api.js @@ -632,11 +632,14 @@ export class Api { } async sendInteractions(interactions, feedProxyUrl) { + // Without an explicit feed generator to proxy to (e.g. a manual + // moderation action with no feed context), fall back to the main + // AppView, which implements sendInteractions; the PDS itself doesn't. await this.request(`app.bsky.feed.sendInteractions`, { method: "POST", body: { interactions }, headers: { - "atproto-proxy": feedProxyUrl, + "atproto-proxy": feedProxyUrl || this.bskyAppViewServiceDid, }, parseJson: false, // third-party feed might not return JSON }); diff --git a/src/js/dataHelpers.js b/src/js/dataHelpers.js index 5651b90f..99a235e8 100644 --- a/src/js/dataHelpers.js +++ b/src/js/dataHelpers.js @@ -51,6 +51,17 @@ export function valueForPinnedItem(item) { return item.type === "timeline" ? FOLLOWING_FEED_URI : item.data.uri; } +// The atproto-proxy value that routes app.bsky.feed.sendInteractions (and +// post-seen tracking) to a specific feed generator's own service, so +// algorithmic feeds actually receive the signal instead of it silently +// going nowhere useful. +export function getFeedGeneratorProxyUrl(feedGenerator) { + if (!feedGenerator?.did) { + return null; + } + return `${feedGenerator.did}#bsky_fg`; +} + export function getRKey(record) { return record.uri.split("/").pop(); } diff --git a/src/js/dataLayer/mutations.js b/src/js/dataLayer/mutations.js index dabd3e88..42941a74 100644 --- a/src/js/dataLayer/mutations.js +++ b/src/js/dataLayer/mutations.js @@ -333,7 +333,7 @@ export class Mutations { const showLessInteraction = { item: postURI, event: "app.bsky.feed.defs#requestLess", - feedContext, + ...(feedContext ? { feedContext } : {}), }; this.dataStore.$showLessInteractions.set([ ...this.dataStore.$showLessInteractions.get(), @@ -351,7 +351,7 @@ export class Mutations { const showMoreInteraction = { item: postURI, event: "app.bsky.feed.defs#requestMore", - feedContext, + ...(feedContext ? { feedContext } : {}), }; // Note, we don't really need to store this interaction because we don't use it in the UI (yet). // But, let's do it anyway for consistency. diff --git a/src/js/modals/profileList.modal.js b/src/js/modals/profileList.modal.js new file mode 100644 index 00000000..da14167a --- /dev/null +++ b/src/js/modals/profileList.modal.js @@ -0,0 +1,52 @@ +import { html } from "/js/lib/lit-html.js"; +import { Modal } from "/js/modals/modal.js"; +import { profileFeedTemplate } from "/js/templates/profileFeed.template.js"; + +class ProfileListModal extends Modal { + get className() { + return "bottom-sheet text-modal profile-list-modal"; + } + + get attributes() { + return { "data-testid": "profile-list-modal" }; + } + + render({ + props: { + title, + profiles, + isAuthenticated, + currentUserDid, + profileInteractionHandler, + }, + }) { + return html` +