Ignore:
Timestamp:
07/16/2026 08:02:27 PM (8 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
010e073
Parents:
87d2787
git-author:
Robin <roboburr@…> (07/16/2026 07:56:26 PM)
git-committer:
Robin <roboburr@…> (07/16/2026 08:02:27 PM)
Message:

Feature: Connect page merges following + followers into one view

/connect combines who you follow (ap_following) and who follows you
(ap_followers) into a single list keyed by actor_uri, each with a direction
badge (following →, follower ←, mutual ↔) and, for accounts we deliver to, a
light 'last delivery' line. Failing/never-reached followers move into a
collapsed 'Unreachable' section at the bottom with the remove action, keeping
the main list clean. The two Following/Followers tabs collapse into one
'Connect' tab; the old routes redirect to /connect so links keep working.
i18n NL/EN/DE. Verified locally: merge/direction/unreachable logic + render.

Co-Authored-By: Claude Opus 4.8 <noreply@…>

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ActivityPubService.js

    r87d2787 rb109a29  
    563563}
    564564
     565// Merge who-you-follow (ap_following, rich display) with who-follows-you (ap_followers,
     566// delivery health) into ONE connections list, keyed by actor_uri. Each entry gets a
     567// direction (following →, follower ←, mutual ↔) and, for accounts we deliver to, an
     568// `unreachable` flag (never delivered, or last attempt failed after the last success) so
     569// the view can split dead connections into their own section. Powers the Connect page.
     570export function listConnections(slug) {
     571  const byUri = new Map();
     572  for (const f of listFollowing(slug)) {
     573    byUri.set(f.actor_uri, {
     574      actor_uri: f.actor_uri, name: f.name || null, handle: f.handle || null,
     575      icon: f.icon || null, url: f.url || null, auto_boost: f.auto_boost ? 1 : 0,
     576      status: f.status || null, following: true, follower: false,
     577      last_delivery_at: null, last_error_at: null, follower_id: null,
     578    });
     579  }
     580  for (const fo of listFollowers(slug)) {
     581    const e = byUri.get(fo.actor_uri);
     582    if (e) { e.follower = true; e.last_delivery_at = fo.last_delivery_at; e.last_error_at = fo.last_error_at; e.follower_id = fo.id; }
     583    else byUri.set(fo.actor_uri, {
     584      actor_uri: fo.actor_uri, name: null, handle: null, icon: null, url: null,
     585      auto_boost: 0, status: null, following: false, follower: true,
     586      last_delivery_at: fo.last_delivery_at, last_error_at: fo.last_error_at, follower_id: fo.id,
     587    });
     588  }
     589  return [...byUri.values()].map((e) => {
     590    e.direction = (e.following && e.follower) ? 'mutual' : (e.following ? 'following' : 'follower');
     591    e.unreachable = e.follower && (!e.last_delivery_at || (!!e.last_error_at && (!e.last_delivery_at || e.last_error_at > e.last_delivery_at)));
     592    return e;
     593  });
     594}
     595
    565596// ── inbound interactions store (replies / likes / boosts) + our outbound replies ──
    566597let _insI, _delLA, _delReply, _listI, _getI, _insO, _listO, _getO;
     
    24922523  deliverWithRetry, enqueueDelivery, processDeliveryQueue, startDeliveryWorker,
    24932524  getReplyUris, markNotificationsSeen, countUnseenNotifications, hasPlayableAudio,
    2494   linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower,
     2525  linkifyBody, bakePostContent, bakePostContentWithMentions, listFollowers, removeFollower, listConnections,
    24952526};
Note: See TracChangeset for help on using the changeset viewer.