a demonstration replicated social networking web app built with anproto
wiredove.net/
social
ed25519
protocols
1import { h } from 'h'
2import { apds } from 'apds'
3
4const cache = new Map()
5setInterval(() => { cache.clear() }, 60000)
6
7export const nameDiv = async () => {
8 const name = await apds.get('name')
9
10 const namer = h('input', {
11 placeholder: name || 'Name yourself'
12 })
13
14 const namerDiv = h('div', [
15 namer,
16 h('button', {onclick: async () => {
17 if (namer.value) {
18 namer.placeholder = namer.value
19 await apds.put('name', namer.value)
20 namer.value = ''
21 }
22 }}, ['Save'])
23 ])
24
25 return namerDiv
26}
27
28export const nameSpan = async () => {
29 const pubkey = await apds.pubkey()
30 const span = h('a', {href: '#' + pubkey, classList: 'avatarlink'}, [await apds.get('name') || pubkey.substring(0, 10)])
31 return span
32}
33
34export const imageSpan = async () => {
35 const avatarImg = await apds.visual(await apds.pubkey())
36
37 const existingImage = await apds.get('image')
38
39 if (existingImage) {
40 if (cache.get(existingImage)) {
41 avatarImg.src = cache.get(existingImage)
42 } else {
43 const src = await apds.get(existingImage)
44 avatarImg.src = src
45 cache.set(existingImage, src)
46 }
47 }
48
49 avatarImg.classList = 'avatar_small'
50
51 return avatarImg
52}
53
54export const avatarSpan = async () => {
55 const avatarImg = await apds.visual(await apds.pubkey())
56
57 const existingImage = await apds.get('image')
58
59 if (existingImage) {
60 if (cache.get(existingImage)) {
61 avatarImg.src = cache.get(existingImage)
62 } else {
63 const src = await apds.get(existingImage)
64 avatarImg.src = src
65 cache.set(existingImage, src)
66 }
67 }
68
69 avatarImg.classList = 'avatar'
70
71 avatarImg.onclick = () => {uploader.click()}
72
73 const uploader = h('input', { type: 'file', style: 'display: none;'})
74
75 uploader.addEventListener('change', (e) => {
76 const file = e.target.files[0]
77 const reader = new FileReader()
78
79 reader.onload = (e) => {
80 const canvas = document.createElement("canvas")
81 const ctx = canvas.getContext("2d")
82 const img = new Image()
83
84 img.onload = async () => {
85 const size = 256
86 if (img.width > size || img.height > size) {
87 const width = img.width
88 const height = img.height
89 let cropWidth
90 let cropHeight
91
92 if (width > height) {
93 cropWidth = size
94 cropHeight = cropWidth * (height / width)
95 } else {
96 cropHeight = size
97 cropWidth = cropHeight * (width / height)
98 }
99
100 canvas.width = cropWidth
101 canvas.height = cropHeight
102 ctx.drawImage(img, 0, 0, width, height, 0, 0, cropWidth, cropHeight)
103 const croppedImage = canvas.toDataURL()
104 avatarImg.src = croppedImage
105 const hash = await apds.make(croppedImage)
106 await apds.put('image', hash)
107 cache.set(hash, croppedImage)
108 } else {
109 const croppedImage = canvas.toDataURL()
110 avatarImg.src = img.src
111 const hash = await apds.make(img.src)
112 await apds.put('image', hash)
113 cache.set(hash, img.src)
114 }
115 }
116 img.src = e.target.result
117 }
118 reader.readAsDataURL(file)
119 })
120
121 const span = h('span', [
122 avatarImg,
123 uploader
124 ])
125
126 return span
127}
128
129export const profile = async () => {
130 const div = h('div')
131
132 div.appendChild(await avatarSpan())
133
134 div.appendChild(h('div', {classList: 'pubkey' }, [await apds.pubkey()]))
135
136 div.appendChild(await nameDiv())
137 return div
138}