tiny whitewind client
1<?php
2require_once 'vendor/autoload.php';
3use League\CommonMark\Environment\Environment;
4use League\CommonMark\Exception\CommonMarkException;
5use League\CommonMark\Extension\Autolink\AutolinkExtension;
6use League\CommonMark\Extension\CommonMark\CommonMarkCoreExtension;
7use League\CommonMark\MarkdownConverter;
8
9$config = [
10 'autolink' => [
11 'allowed_protocols' => ['https'],
12 'default_protocol' => 'https',
13 ],
14];
15$environment = new Environment($config);
16$environment->addExtension(new CommonMarkCoreExtension());
17$environment->addExtension(new AutolinkExtension());
18$converter = new MarkdownConverter($environment);
19
20function getSlingshot(string $user): mixed
21{
22 return $_SESSION["slingshot"][$user] ??=
23 json_decode(file_get_contents("https://slingshot.microcosm.blue/xrpc/com.bad-example.identity.resolveMiniDoc?identifier=$user"));
24}
25
26session_start();
27
28// short circuit if no other user is specified
29$user = $_GET["user"] ?? "benharri.org";
30$format = $_GET["format"] ?? "html";
31
32$id = getSlingshot($user);
33$user_url = "?user=$id->handle";
34$at_uri = "at://$id->handle/com.whtwnd.blog.entry";
35$at_permauri = "at://$id->did/com.whtwnd.blog.entry";
36
37if (isset($_GET["p"])) {
38 // get single post record
39 $query = http_build_query([
40 "repo" => $id->did,
41 "collection" => "com.whtwnd.blog.entry",
42 "rkey" => $_GET["p"]
43 ]);
44 $post = json_decode(file_get_contents("$id->pds/xrpc/com.atproto.repo.getRecord?$query"), true);
45
46 if ($format === "markdown") {
47 // print raw markdown and we're done
48 header("Content-Type: text/markdown; variant=GFM");
49 echo '# ' . $post["value"]["title"] . PHP_EOL . PHP_EOL . $post["value"]["content"];
50 die();
51 }
52
53 $post_url = "$user_url&p={$_GET["p"]}";
54 $at_uri .= "/{$_GET["p"]}";
55
56} else {
57 // get all posts
58 // TODO: pagination
59 $q = http_build_query([
60 "repo" => $id->did,
61 "collection" => "com.whtwnd.blog.entry",
62 "limit" => 100
63 ]);
64 $posts = json_decode(file_get_contents("$id->pds/xrpc/com.atproto.repo.listRecords?$q"), true)["records"];
65}
66
67?>
68<!doctype html>
69<html lang="en">
70<head>
71 <meta charset="utf-8">
72 <meta http-equiv="x-ua-compatible" content="ie=edge">
73 <meta name="viewport" content="width=device-width, initial-scale=1">
74 <link rel="stylesheet" href="gruvbox.css">
75 <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='48' height='48' viewBox='0 0 16 16'><text x='0' y='14'>🖋️</text></svg>">
76<?php if (isset($post_url)) { ?>
77 <link rel="alternate" type="text/markdown; variant=GFM" href="https://bhh.sh/pub/atblog/<?=$post_url?>&format=markdown">
78<?php } ?>
79 <link rel="alternate" href="<?=$at_permauri?>">
80 <title>atblog: <?=$post["value"]["title"] ?? $id->handle ?></title>
81</head>
82
83<body>
84<main>
85 <form method="GET">
86 <input name="user" placeholder="jump to user">
87 </form>
88 <p><a href="https://pdsls.dev/<?=$at_permauri?>"><?=$at_uri?></a></p>
89
90<?php if (isset($post) && isset($post_url)) { ?>
91 <p>
92 <a href="<?=$user_url?>">back to post list</a>
93 <a href="<?=$post_url?>&format=markdown">raw markdown</a>
94 </p>
95
96 <h1><?=$post["value"]["title"]?></h1>
97 <hr>
98 <?php try {
99 echo $converter->convert($post["value"]["content"])->getContent();
100 } catch (CommonMarkException $e) {
101 echo "Failed to parse markdown {$e->getMessage()}";
102 } ?>
103
104<?php } else if (isset($posts)) { ?>
105 <ul>
106<?php foreach ($posts as $post) { ?>
107 <li><?=substr($post["value"]["createdAt"], 0, 10)?> <a href="<?=$user_url?>&p=<?=explode('/', $post["uri"])[4]?>"><?=$post["value"]["title"]?></a></li>
108<?php } ?>
109 </ul>
110<?php } ?>
111</main>
112</body>
113</html>