1import { getRKey, parseUri, hasValidHandle } from "/js/dataHelpers.js"; 2import { isNil } from "/js/utils.js"; 3 4function encodePathSegment(segment) { 5 return encodeURIComponent(segment).replace(/%3A/g, ":").replace(/%40/g, "@"); 6} 7 8function profileIdentifier(profile) { 9 return hasValidHandle(profile) ? profile.handle : profile.did; 10} 11 12export function linkToHashtag(hashtag) { 13 return `/hashtag/${encodePathSegment(hashtag)}`; 14} 15 16export function linkToProfile(profile) { 17 return `/profile/${encodePathSegment(profileIdentifier(profile))}`; 18} 19 20export function linkToProfileByDid(did) { 21 return `/profile/${encodePathSegment(did)}`; 22} 23 24export function linkToLabeler(labeler) { 25 return linkToProfile(labeler.creator); 26} 27 28export function linkToPost(post) { 29 return `/profile/${encodePathSegment(profileIdentifier(post.author))}/post/${encodePathSegment(getRKey(post))}`; 30} 31 32export function linkToPostFromUri(postUri) { 33 const { repo, rkey } = parseUri(postUri); 34 return `/profile/${encodePathSegment(repo)}/post/${encodePathSegment(rkey)}`; 35} 36 37export function linkToPostLikes(post) { 38 return `/profile/${encodePathSegment(profileIdentifier(post.author))}/post/${encodePathSegment(getRKey(post))}/likes`; 39} 40 41export function linkToPostQuotes(post) { 42 return `/profile/${encodePathSegment(profileIdentifier(post.author))}/post/${encodePathSegment(getRKey(post))}/quotes`; 43} 44 45export function linkToPostReposts(post) { 46 return `/profile/${encodePathSegment(profileIdentifier(post.author))}/post/${encodePathSegment(getRKey(post))}/reposts`; 47} 48 49export function linkToProfileFollowers(profile) { 50 return `/profile/${encodePathSegment(profileIdentifier(profile))}/followers`; 51} 52 53export function linkToProfileFollowing(profile) { 54 return `/profile/${encodePathSegment(profileIdentifier(profile))}/following`; 55} 56 57export function linkToProfileKnownFollowers(profile) { 58 return `/profile/${encodePathSegment(profileIdentifier(profile))}/known-followers`; 59} 60 61export function linkToFeed(feedGenerator) { 62 return `/profile/${encodePathSegment(profileIdentifier(feedGenerator.creator))}/feed/${encodePathSegment( 63 getRKey(feedGenerator), 64 )}`; 65} 66 67export function linkToList(list) { 68 return `/profile/${encodePathSegment(profileIdentifier(list.creator))}/lists/${encodePathSegment( 69 getRKey(list), 70 )}`; 71} 72 73export function linkToSearchPostsByProfile(profile) { 74 const searchString = `from:@${profile.handle} `; 75 const query = new URLSearchParams(); 76 query.set("q", searchString); 77 query.set("tab", "top"); 78 return `/search?${query.toString()}`; 79} 80 81function getPermalinkOrigin() { 82 return "https://bsky.app"; 83} 84 85export function getPermalinkForPost(post) { 86 return getPermalinkOrigin() + linkToPost(post); 87} 88 89export function getPermalinkForProfile(profile) { 90 return getPermalinkOrigin() + linkToProfile(profile); 91} 92 93export function getPermalinkForConvo(convoId) { 94 return `${getPermalinkOrigin()}/messages/${encodePathSegment(convoId)}`; 95} 96 97export function linkToGroupChatDetails(convoId) { 98 return `/messages/${encodePathSegment(convoId)}/settings`; 99} 100 101export function linkToPluginSettings(pluginId) { 102 return `/plugin/${encodePathSegment(pluginId)}/settings`; 103} 104 105export function linkToCommunityPlugin(pluginId) { 106 return `/plugins/community/${encodePathSegment(pluginId)}`; 107} 108 109export function getPermalinkForCommunityPlugin(pluginId) { 110 return "https://impro.social" + linkToCommunityPlugin(pluginId); 111} 112 113export function linkToLogin({ query = {} } = {}) { 114 const params = new URLSearchParams(); 115 for (const [key, value] of Object.entries(query)) { 116 if (isNil(value)) continue; 117 params.set(key, String(value)); 118 } 119 const { pathname, search, hash } = window.location; 120 if (pathname !== "/login" && pathname !== "/") { 121 params.set("returnTo", pathname + search + hash); 122 } 123 const queryString = params.toString(); 124 return queryString ? "/login?" + queryString : "/login"; 125} 126 127export function validateReturnToParam(raw) { 128 if (typeof raw !== "string" || raw.length === 0) { 129 return null; 130 } 131 if (!raw.startsWith("/")) { 132 return null; 133 } 134 if (raw.startsWith("//") || raw.startsWith("/\\")) { 135 return null; 136 } 137 return raw; 138}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.