···79798080let lastTimestamp = 0n;
81818282+// Will return null if the rkey is not a TID.
8383+export function getTimestampFromRkey(rkey) {
8484+ const noDashes = rkey.replaceAll("-", "");
8585+ if (noDashes.length !== 13) {
8686+ return null;
8787+ }
8888+ let value = 0;
8989+ for (const c of noDashes.slice(0, 11)) {
9090+ value = value * 32 + TID_ALPHABET.indexOf(c);
9191+ }
9292+ return value;
9393+}
9494+8295export function generateTid() {
8396 const nowMicroseconds = BigInt(Date.now()) * 1000n;
8497 const timestamp =
+25-3
src/js/views/notifications.view.js
···1616 getImagesFromPost,
1717 getVideoFromPost,
1818 isUnavailablePost,
1919+ parseUri,
1920} from "/js/dataHelpers.js";
2121+import { getTimestampFromRkey } from "/js/atproto.js";
2022import { notificationsIconTemplate } from "/js/templates/icons/notificationsIcon.template.js";
2123import { tabBarTemplate } from "/js/templates/tabBar.template.js";
2224import { NOTIFICATIONS_PAGE_SIZE } from "/js/config.js";
···114116 "repost-via-repost",
115117 ];
116118119119+ // Check if you're following the author of the notification,
120120+ // and the notification was created after you followed them.
121121+ function isFollowBackNotification(notification) {
122122+ if (notification.reason !== "follow") return false;
123123+ const viewerFollowing = notification.author?.viewer?.following;
124124+ if (!viewerFollowing) return false;
125125+ const { rkey: followingRkey } = parseUri(viewerFollowing);
126126+ const followingTimestamp = getTimestampFromRkey(followingRkey);
127127+ if (followingTimestamp === null) return false;
128128+ const followedTimestamp =
129129+ new Date(notification.record?.createdAt).getTime() * 1000;
130130+ return followedTimestamp > followingTimestamp;
131131+ }
132132+117133 function groupNotificationsForBatch(notifications) {
118134 const notificationGroups = [];
119135120136 notifications.forEach((notification) => {
121121- const type = notification.reason;
137137+ const reason = notification.reason;
122138 const subject = notification.reasonSubject;
139139+140140+ const isFollowBackNotif = isFollowBackNotification(notification);
141141+ const type = isFollowBackNotif ? "follow-back" : reason;
123142124143 const existingGroup = notificationGroups.find(
125144 (group) => group.type === type && group.subject === subject,
···207226 ${notificationAvatarsTemplate({ notifications })}
208227 <div class="notification-text">
209228 ${notificationProfileNamesTemplate({ notificationGroup })}
210210- followed you <span class="notification-time">· ${timeAgo}</span>
229229+ ${notificationGroup.type === "follow-back"
230230+ ? "followed you back"
231231+ : "followed you"}
232232+ <span class="notification-time">· ${timeAgo}</span>
211233 </div>
212234 </div>
213235 </div>
···357379358380 function notificationGroupTemplate({ notificationGroup, currentUser }) {
359381 const { type } = notificationGroup;
360360- if (type === "follow") {
382382+ if (type === "follow" || type === "follow-back") {
361383 return followNotificationTemplate({ notificationGroup });
362384 }
363385 if (type === "like") {
+14
tests/e2e/factories.js
···11+const TID_ALPHABET = "234567abcdefghijklmnopqrstuvwxyz";
22+33+export function createTid(dateString) {
44+ let tid = BigInt(new Date(dateString).getTime()) * 1000n;
55+ tid = tid << 10n;
66+ let result = "";
77+ for (let i = 0; i < 13; i++) {
88+ const remainder = tid % 32n;
99+ result = TID_ALPHABET[Number(remainder)] + result;
1010+ tid = tid / 32n;
1111+ }
1212+ return result;
1313+}
1414+115export function createConvo({
216 id,
317 otherMember,