decentralized and customizable links page on top of atproto

edit links (wip)

Changed files
+89 -15
src
templates
+69 -14
src/main.py
··· 62 62 if not pds: 63 63 return "did not found", 404 64 64 pro = load_profile(pds, profile.did, reload=True) 65 - # links = load_links(pds, profile.did, reload=True) 65 + links = load_links(pds, profile.did, reload=True) or [{}] 66 66 67 - return render_template("editor.html", profile=pro) 67 + return render_template( 68 + "editor.html", 69 + handle=profile.handle, 70 + profile=pro, 71 + links=links, 72 + ) 68 73 69 74 70 75 @app.post("/editor/profile") 71 - def post_editor(): 72 - display_name = request.form.get("displayName") 73 - description = request.form.get("description") 74 - if not display_name or not description: 75 - return redirect("/editor", 303) 76 - 76 + def post_editor_profile(): 77 77 session = request.cookies.get("session") 78 78 if session is None or not session: 79 79 return redirect("/", 303) 80 80 client = Client() 81 81 profile = client.login(session_string=session) 82 82 83 - data_model = ComAtprotoRepoCreateRecord.Data( 84 - collection=f"{SCHEMA}.actor.profile", 83 + display_name = request.form.get("displayName") 84 + description = request.form.get("description") or "" 85 + if not display_name: 86 + return redirect("/editor", 303) 87 + 88 + put_record( 89 + client=client, 85 90 repo=profile.did, 91 + collection=f"{SCHEMA}.actor.profile", 86 92 rkey="self", 87 93 record={ 88 94 "$type": f"{SCHEMA}.actor.profile", ··· 90 96 "description": description, 91 97 }, 92 98 ) 93 - _ = client.invoke_procedure( 94 - "com.atproto.repo.putRecord", 95 - data=data_model, 96 - input_encoding="application/json", 99 + 100 + return redirect("/editor", 303) 101 + 102 + 103 + @app.post("/editor/links") 104 + def post_editor_links(): 105 + session = request.cookies.get("session") 106 + if session is None or not session: 107 + return redirect("/", 303) 108 + client = Client() 109 + profile = client.login(session_string=session) 110 + 111 + links: list[dict[str, str]] = [] 112 + for i in range(0, 100): 113 + url = request.form.get(f"link{i}-url") 114 + title = request.form.get(f"link{i}-title") 115 + detail = request.form.get(f"link{i}-detail") 116 + color = request.form.get(f"link{i}-color") 117 + if not url or not title or not color: 118 + break 119 + link: dict[str, str] = { 120 + "url": url, 121 + "title": title, 122 + "color": color, 123 + } 124 + if detail: 125 + link["detail"] = detail 126 + links.append(link) 127 + 128 + put_record( 129 + client=client, 130 + repo=profile.did, 131 + collection=f"{SCHEMA}.actor.links", 132 + rkey="self", 133 + record={ 134 + "$type": f"{SCHEMA}.actor.links", 135 + "links": links, 136 + }, 97 137 ) 138 + 98 139 return redirect("/editor", 303) 99 140 100 141 ··· 174 215 f"{pds}/xrpc/com.atproto.repo.getRecord?repo={repo}&collection={collection}&rkey={record}" 175 216 ) 176 217 return response 218 + 219 + 220 + def put_record(client: Client, repo: str, collection: str, rkey: str, record): 221 + data_model = ComAtprotoRepoCreateRecord.Data( 222 + collection=collection, 223 + repo=repo, 224 + rkey=rkey, 225 + record=record, 226 + ) 227 + _ = client.invoke_procedure( 228 + "com.atproto.repo.putRecord", 229 + data=data_model, 230 + input_encoding="application/json", 231 + ) 177 232 178 233 179 234 def http_get(url: str) -> str | None:
+20 -1
src/templates/editor.html
··· 6 6 <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" /> 7 7 </head> 8 8 <body> 9 + <p> 10 + <a href="/{{ handle }}">see profile</a> 11 + </p> 12 + 9 13 profile 10 14 <form action="/editor/profile" method="post"> 11 15 <input type="text" name="displayName" value="{{ profile.0 }}" required /> ··· 13 17 <button type="submit">save!</button> 14 18 </form> 15 19 16 - <a href="/auth/logout">logout</a> 20 + links 21 + <form action="/editor/links" method="post"> 22 + {% for link in links %} 23 + <div> 24 + <input type="text" name="link{{ loop.index0 }}-url" value="{{ link.url }}" required /> 25 + <input type="text" name="link{{ loop.index0 }}-title" value="{{ link.title }}" required /> 26 + <input type="text" name="link{{ loop.index0 }}-detail" value="{{ link.detail }}" /> 27 + <input type="color" name="link{{ loop.index0 }}-color" value="{{ link.color }}" required /> 28 + </div> 29 + {% endfor %} 30 + <button type="submit">save!</button> 31 + </form> 32 + 33 + <p> 34 + <a href="/auth/logout">logout</a> 35 + </p> 17 36 </body> 18 37 </html>