diff --git a/openlibrarian_root/almanac/templates/almanac/user_friends.html b/openlibrarian_root/almanac/templates/almanac/user_friends.html index 851d7ed..93e898c 100644 --- a/openlibrarian_root/almanac/templates/almanac/user_friends.html +++ b/openlibrarian_root/almanac/templates/almanac/user_friends.html @@ -13,8 +13,12 @@
{% csrf_token %} -
-
+
+
+ + +
+

- +

@@ -93,7 +97,6 @@

Back {% if session.nsec %} - {% endif %}

@@ -102,8 +105,13 @@

-
+ +
+
+ + + {% endblock content %} \ No newline at end of file diff --git a/openlibrarian_root/almanac/templates/almanac/user_profile.html b/openlibrarian_root/almanac/templates/almanac/user_profile.html index 8affca5..1b10d0c 100644 --- a/openlibrarian_root/almanac/templates/almanac/user_profile.html +++ b/openlibrarian_root/almanac/templates/almanac/user_profile.html @@ -112,7 +112,7 @@ -
+
diff --git a/openlibrarian_root/almanac/views.py b/openlibrarian_root/almanac/views.py index 52495ff..9cae2dd 100644 --- a/openlibrarian_root/almanac/views.py +++ b/openlibrarian_root/almanac/views.py @@ -3,7 +3,7 @@ from utils.Session import async_logged_in, async_get_session_info, async_set_session_info, cache_get, cache_set, cache_key, cache_delete from utils.Profile import edit_profile_info, edit_relay_list, fetch_profile_info from utils.Login import check_nsec -from utils.Connections import fetch_social_list +from utils.Connections import fetch_social_list, add_follow from nostr_sdk import Keys # Users Settings View @@ -173,9 +173,17 @@ async def user_friends(request): friends = connctions['friends'] muted = connctions['muted'] + notification = None + # POST requests if request.method == 'POST': - if request.POST.get('refresh'): + if request.POST.get('refresh') or request.POST.get('follow_user'): + + # Attempt to add new follow + if request.POST.get('follow_user'): + notification = await add_follow(session['relays'], npub=session['npub'], nsec=session['nsec'], follow_id=request.POST.get('follow_user')) + + # Fetch lists again friends = await fetch_social_list(relays=session['relays'], npub=session['npub'], list_type="follow") muted = await fetch_social_list(relays=session['relays'], npub=session['npub'], list_type="mute") @@ -183,4 +191,4 @@ async def user_friends(request): await cache_delete(await cache_key(key_str,session)) await cache_set(await cache_key(key_str,session), {'friends': friends, 'muted': muted}, 1800) - return render(request, 'almanac/user_friends.html', {'session': session, 'friends': friends, 'muted': muted}) \ No newline at end of file + return render(request, 'almanac/user_friends.html', {'session': session, 'friends': friends, 'muted': muted, 'notification': notification}) \ No newline at end of file diff --git a/openlibrarian_root/library_card/templates/library_card/card.html b/openlibrarian_root/library_card/templates/library_card/card.html index 39148f9..a02c560 100644 --- a/openlibrarian_root/library_card/templates/library_card/card.html +++ b/openlibrarian_root/library_card/templates/library_card/card.html @@ -103,7 +103,7 @@
Explore my Profile or Build your Own!
-
+
diff --git a/openlibrarian_root/static/js/add-toast.js b/openlibrarian_root/static/js/add-toast.js index 591d603..05e81ab 100644 --- a/openlibrarian_root/static/js/add-toast.js +++ b/openlibrarian_root/static/js/add-toast.js @@ -5,7 +5,7 @@ if (notification && notification !== "None") { const toastContainer = document.getElementById('toastContainer'); const toast = document.createElement('div'); toast.classList.add('toast', 'align-items-center', 'text-white', 'bg-success', 'border-0'); - if (notification === "Book already in library.") { + if (notification === "Book already in library." || notification === "Already Following.") { toast.classList.remove('bg-success'); toast.classList.add('bg-danger'); } diff --git a/openlibrarian_root/static/js/copy-toast.js b/openlibrarian_root/static/js/copy-toast.js index 39b9099..5c422f9 100644 --- a/openlibrarian_root/static/js/copy-toast.js +++ b/openlibrarian_root/static/js/copy-toast.js @@ -12,7 +12,7 @@ copyButtons.forEach(button => { document.execCommand('copy'); document.body.removeChild(textarea); - const toastContainer = document.getElementById('toastContainer'); + const toastContainer = document.getElementById('copytoastContainer'); const toast = document.createElement('div'); toast.classList.add('toast', 'align-items-center', 'text-white', 'bg-success', 'border-0'); toast.setAttribute('role', 'alert'); diff --git a/openlibrarian_root/utils/Connections.py b/openlibrarian_root/utils/Connections.py index 73ec04e..bf003a8 100644 --- a/openlibrarian_root/utils/Connections.py +++ b/openlibrarian_root/utils/Connections.py @@ -1,4 +1,4 @@ -from nostr_sdk import Client, Filter, Kind, KindEnum, Metadata, PublicKey, TagKind, SingleLetterTag, Alphabet, Keys, NostrSigner, EventBuilder, Contact +from nostr_sdk import Client, Filter, Kind, KindEnum, Metadata, PublicKey, Keys, NostrSigner, EventBuilder, get_nip05_profile, Tag from utils.Network import nostr_get, nostr_post from utils.Login import check_npub, check_nsec @@ -25,7 +25,8 @@ async def fetch_social_list(relays: dict, npub: str = None, list_type: str = "fo follow_list = [] if eventlist: for tag in eventlist[0].tags(): - follow_list.append(PublicKey.from_hex(tag.content())) + if tag.as_vec()[0] == "p": + follow_list.append(PublicKey.from_hex(tag.content())) # Get following and related profile data f = Filter().kind(Kind(0)).authors(follow_list) @@ -101,5 +102,48 @@ async def clone_follow(relays: dict, npub: str = None, nsec: str = None): await nostr_post(client=client, relays_dict=relays, eventbuilder=mute_builder, connect=False, disconnect=True) +async def add_follow(relays: dict, npub: str = None, nsec: str = None, follow_id: str = None): + if npub in [None, ""] or check_npub(npub) is False: + raise Exception("Invalid or missing npub") + if nsec in [None, ""] or check_nsec(nsec) is False: + raise Exception("Invalid or missing nsec") + if follow_id in [None, ""]: + raise Exception("Missing follow value") + + # Check if follow is a valid pubkey or nip05 and get key + if check_npub(follow_id) is True: + follow_key = PublicKey.parse(follow_id) + else: + try: + profile = await get_nip05_profile(follow_id,None) + follow_key = profile.public_key() + except: + return "Invalid npub or nip05" + # Keys + keys = Keys.parse(nsec) + + # Signer + signer = NostrSigner.keys(keys) + client = Client(signer) + + # Request profile Metadata + follow_filter = Filter().kind(Kind.from_enum(KindEnum.CONTACT_LIST())).author(keys.public_key()).limit(1) + + # Get following list and extract p tags + follow_events = await nostr_get(client=client, filters=[follow_filter], relays_dict=relays, wait=10, connect=True, disconnect=True) + + for follow in follow_events: + if Tag.public_key(follow_key) not in follow.tags(): + follow.tags().append(Tag.public_key(follow_key)) + + # Build follow event + follow_builder = EventBuilder(kind=Kind.from_enum(KindEnum.CONTACT_LIST()), tags=follow.tags(), content="") + + # Post events + await nostr_post(client=client, relays_dict=relays, eventbuilder=follow_builder, connect=True, disconnect=True) + return "Added Friend." + + else: + return "Already Following."