| Comment: | add follow notices |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
00a6815988cf730166006380c81ff264 |
| User & Date: | lexi on 2021-01-10 11:17:29 |
| Other Links: | manifest | tags |
|
2021-01-10
| ||
| 14:26 | get some user admin shit working, general cleanups check-in: e1ff4f301e user: lexi tags: trunk | |
| 11:17 | add follow notices check-in: 00a6815988 user: lexi tags: trunk | |
| 08:19 | begin replacing inefficient memory management with a pool-based solution; fix memory leaks check-in: 7c8769bf96 user: lexi tags: trunk | |
Modified backend/pgsql.t from [794844a9c8] to [b388ba7244].
240 240 delete from parsav_rights where 241 241 actor = $1::bigint and 242 242 key = $2::text 243 243 ]] 244 244 }; 245 245 246 246 actor_rel_create = { 247 - params = {uint16,uint64, uint64}, cmd = true, sql = [[ 248 - insert into parsav_rels (kind,relator,relatee) 249 - values($1::smallint, $2::bigint, $3::bigint) 247 + params = {uint16,uint64, uint64, int64}, cmd = true, sql = [[ 248 + insert into parsav_rels (kind,relator,relatee,since) 249 + values($1::smallint, $2::bigint, $3::bigint, $4::bigint) 250 250 on conflict do nothing 251 251 ]]; 252 252 }; 253 253 254 254 actor_rel_destroy = { 255 255 params = {uint16,uint64, uint64}, cmd = true, sql = [[ 256 256 delete from parsav_rels where ................................................................................ 1499 1499 end]; 1500 1500 1501 1501 actor_rel_create = [terra( 1502 1502 src: &lib.store.source, 1503 1503 kind: uint16, 1504 1504 relator: uint64, 1505 1505 relatee: uint64 1506 - ): {} queries.actor_rel_create.exec(src,kind,relator,relatee) end]; 1506 + ): {} queries.actor_rel_create.exec(src,kind,relator,relatee,lib.osclock.time(nil)) end]; 1507 1507 1508 1508 actor_rel_destroy = [terra( 1509 1509 src: &lib.store.source, 1510 1510 kind: uint16, 1511 1511 relator: uint64, 1512 1512 relatee: uint64 1513 1513 ): {} queries.actor_rel_destroy.exec(src,kind,relator,relatee) end];
Modified backend/schema/pgsql-views.sql from [c997d2f5ad] to [0fea8fa8b6].
53 53 ($1).time, 54 54 ($1).actor, 55 55 ($1).subject, 56 56 null::bigint, 57 57 ($1).body 58 58 )::pg_temp.parsavpg_intern_notice as notice 59 59 from (values 60 - ('rt', <notice:rt> ), 61 - ('like', <notice:like> ), 62 - ('react', <notice:react>) 60 + ('rt', <notice:rt> ), 61 + ('like', <notice:like> ), 62 + ('react', <notice:react> ), 63 + ('follow',<notice:follow>) 63 64 ) as kmap(kstr,kind) where kmap.kstr = ($1).kind 64 65 $$ language sql; 65 66 66 67 create type pg_temp.parsavpg_intern_actor as ( 67 68 id bigint, 68 69 nym text, 69 70 handle text, ................................................................................ 208 209 null::text 209 210 )::pg_temp.parsavpg_intern_notice as notice, 210 211 par.author as rcpt 211 212 from parsav_posts as p 212 213 inner join parsav_posts as par on p.parent = par.id 213 214 left join ntimes as nt on nt.uid = p.author 214 215 where p.discovered >= coalesce(nt.when,0) 215 - ), allnotices as (select * from acts union select * from replies) 216 + ), follows as ( 217 + select row( 218 + <notice:follow>::smallint, 219 + r.since, 220 + r.relator, 221 + r.relatee, 222 + null::bigint, 223 + null::text 224 + )::pg_temp.parsavpg_intern_notice as notice, 225 + r.relatee as rcpt 226 + from parsav_rels as r 227 + left join ntimes as nt on nt.uid = r.relatee 228 + where 229 + r.since >= coalesce(nt.when,0) and 230 + r.kind = <rel:follow> 231 + ), allnotices as (table acts union table replies union table follows) 216 232 217 233 table allnotices order by (notice).when desc 218 234 ); 219 235
Modified backend/schema/pgsql.sql from [bc736c4c1d] to [daac991db5].
85 85 create index on parsav_posts (parent); 86 86 87 87 create table parsav_rels ( 88 88 relator bigint references parsav_actors(id) 89 89 on delete cascade, -- e.g. follower 90 90 relatee bigint references parsav_actors(id) 91 91 on delete cascade, -- e.g. followed 92 - kind smallint, -- e.g. follow, block, mute 92 + kind smallint not null, -- e.g. follow, block, mute 93 + since bigint not null, 93 94 94 95 primary key (relator, relatee, kind) 95 96 ); 96 97 comment on table parsav_rels is 97 98 'all relationships, positive and negative, between local users and other users; kind is a version-specific integer mapping to a type-of-relationship enum in store.t'; 98 99 99 100 create table parsav_acts (
Modified config.lua from [6cff716428] to [a1f76576fd].
58 58 {'heart.webp', 'image/webp'}; 59 59 {'retweet.webp', 'image/webp'}; 60 60 {'padlock.svg', 'image/svg+xml'}; 61 61 {'warn.svg', 'image/svg+xml'}; 62 62 {'query.webp', 'image/webp'}; 63 63 {'reply.webp', 'image/webp'}; 64 64 {'file.webp', 'image/webp'}; 65 + {'follow.webp', 'image/webp'}; 65 66 -- keep in mind before you add anything to this list: these are not 66 67 -- just files parsav can access, they are files that are *kept in 67 68 -- memory* for fast access the entire time parsav is running, and 68 69 -- which need to be loaded into memory before the program can even 69 70 -- start. it's imperative to keep these as small and few in number 70 71 -- as is realistically possible. 71 72 };
Modified http.t from [4c9f723184] to [5f40c98144].
59 59 end 60 60 m.codestr = terra(code: uint16) 61 61 var [resptext] var [resplen] 62 62 switch code do [respbranches] end 63 63 return resptext, resplen 64 64 end 65 65 66 -terra m.hier(uri: lib.mem.ptr(int8)): lib.mem.ptr(lib.mem.ref(int8)) 66 +terra m.hier(pool: &lib.mem.pool, uri: lib.mem.ptr(int8)): lib.mem.ptr(lib.mem.ref(int8)) 67 67 if uri.ct == 0 then return [lib.mem.ptr(lib.mem.ref(int8))] { ptr = nil, ct = 0 } end 68 68 var sz = 1 69 69 var start = 0 if uri.ptr[0] == @'/' then start = 1 end 70 70 for i = start, uri.ct do if uri.ptr[i] == @'/' then sz = sz + 1 end end 71 - var lst = lib.mem.heapa([lib.mem.ref(int8)], sz) 71 + var lst = pool:alloc([lib.mem.ref(int8)], sz) 72 72 if sz == 0 then 73 73 lst.ptr[0].ptr = uri.ptr 74 74 lst.ptr[0].ct = uri.ct 75 75 return lst 76 76 end 77 77 78 78 var idx: intptr = 0
Modified makefile from [8559ac7b2c] to [d43e9d6c44].
1 1 dl = git 2 2 dbg-flags = $(if $(dbg),-g) 3 3 4 -images = static/default-avatar.webp static/query.webp static/heart.webp static/retweet.webp static/reply.webp static/file.webp 4 +images = static/default-avatar.webp static/query.webp static/heart.webp static/retweet.webp static/reply.webp static/file.webp static/follow.webp 5 5 #$(addsuffix .webp, $(basename $(wildcard static/*.svg))) 6 6 styles = $(addsuffix .css, $(basename $(wildcard static/*.scss))) 7 7 8 8 parsav parsavd: parsav.t config.lua pkgdata.lua $(images) $(styles) 9 9 terra $(dbg-flags) $< 10 10 parsav.o parsavd.o: parsav.t config.lua pkgdata.lua $(images) $(styles) 11 11 env parsav_link=no terra $(dbg-flags) $<
Modified mem.t from [7e2b478eaf] to [05a21ff4d9].
207 207 self.sz = sz 208 208 self.debris = [&m.pool](b) 209 209 self.debris.storage = nil 210 210 return self 211 211 end 212 212 213 213 terra m.pool:free(): {} 214 -lib.io.fmt('DRAINING POOL %p\n',self.storage) 215 214 if self.storage == nil then return end 216 215 if self.debris.storage ~= nil then self.debris:free() end 217 216 m.heapf(self.debris) -- storage + debris field allocated in one block 218 217 self.storage = nil 219 218 self.cursor = nil 220 219 self.sz = 0 221 220 self.debris = nil ................................................................................ 224 223 terra m.pool:clear() 225 224 if self.debris.storage ~= nil then self.debris:free() end 226 225 self.cursor = self.storage 227 226 return self 228 227 end 229 228 230 229 terra m.pool:alloc_bytes(sz: intptr): &opaque 231 - var space = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage)) 232 -lib.io.fmt('%p / %p @ allocating %llu bytes in %llu of space\n',self.storage,self.cursor,sz,space) 230 + var space: intptr = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage)) 233 231 if space < sz then 234 -lib.dbg('reserving more space') 235 - self:cue(space + sz + 256) end 232 + self:cue(self.sz + sz + 256) end 236 233 var ptr = self.cursor 237 234 self.cursor = [&opaque]([&uint8](self.cursor) + sz) 238 235 return ptr 239 236 end 240 237 241 238 terra m.pool:realloc_bytes(oldptr: &opaque, oldsz: intptr, newsz: intptr): &opaque 242 - var space = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage)) 239 + var space: intptr = self.sz - ([&uint8](self.cursor) - [&uint8](self.storage)) 243 240 var cur = [&uint8](self.cursor) 244 241 if cur - [&uint8](oldptr) == oldsz and newsz - oldsz < space then 245 242 lib.dbg('moving pool cursor') 246 243 cur = cur + (newsz - oldsz) 247 244 self.cursor = [&opaque](cur) 248 245 return oldptr 249 246 else
Modified parsav.md from [1e18c01a8c] to [af14d66fc5].
135 135 * ldap for auth (and maybe actors?) 136 136 * cdb (for static content, maybe? does this make sense?) 137 137 * mariadb/mysql 138 138 * the various nosql horrors, e.g. redis, mongo, and so on 139 139 140 140 parsav urgently needs an internationalization framework as well. right now everything is just hardcoded in english. yuck. 141 141 142 -parsav could be significantly improved by adjusting its memory management strategy. instead of allocating everything with lib.mem.heapa (which currently maps to malloc on all platforms), we should allocate a static buffer for the server overlord object which can simply be cleared and re-used for each http request, and enlarged with `realloc` when necessary. the entire region could be `mlock`ed for better performance, and it would no longer be necessary to track and free memory, as the entire buffer would simply be discarded after use (similar to PHP's original memory management strategy). this would remove possibly the largest source of latency in the codebase, as `parsav` is regrettably quite heavy on malloc, performing numerous allocations for each page rendered. 142 +parsav could be significantly improved by adjusting its memory management strategy. instead of allocating everything with lib.mem.heapa (which currently maps to malloc on all platforms), we should allocate a static buffer for the server overlord object which can simply be cleared and re-used for each http request, and enlarged with `realloc` when necessary. the entire region could be `mlock`ed for better performance, and it would no longer be necessary to track and free memory, as the entire buffer would simply be discarded after use (similar to PHP's original memory management strategy). this would remove possibly the largest source of latency in the codebase, as `parsav` is regrettably quite heavy on malloc, performing numerous allocations for each page rendered. **update:** this is now in progress
Modified render/notices.t from [745afb2f25] to [1e81acf0af].
30 30 else pflink:lpush('/@') end 31 31 pflink:push(who(0).xid,0) 32 32 var n = data.view.notice { 33 33 avatar = cs(who(0).avatar); 34 34 nym = lib.render.nym(who.ptr,0,nil,true); 35 35 pflink = pstr{ptr = pflink.buf; ct = pflink.sz}; 36 36 } 37 - var notweet = true 38 - var what = co.srv:post_fetch(notes(i).what) defer what:free() 37 + var notweet, nopost = true, false 39 38 switch notes(i).kind do 40 39 case lib.store.noticetype.rt then 41 40 n.kind = P'rt' 42 41 n.act = P'retweeted your post' 43 42 end 44 43 case lib.store.noticetype.like then 45 44 n.kind = P'like' 46 45 n.act = P'likes your post' 47 46 end 48 47 case lib.store.noticetype.reply then 49 48 n.kind = P'reply' 50 49 n.act = P'replied to your post' 51 50 notweet = false 51 + end 52 + case lib.store.noticetype.follow then 53 + n.kind = P'follow' 54 + n.act = P'followed you!' 55 + nopost = true 52 56 end 53 57 else goto skip end 54 - do var idbuf: int8[lib.math.shorthand.maxlen] 55 - var idlen = lib.math.shorthand.gen(notes(i).what, idbuf) 58 + if not nopost then 59 + var what = co.srv:post_fetch(notes(i).what) defer what:free() 56 60 var b = lib.smackdown.html(&co.srv.pool, pstr {ptr=what(0).body,ct=0},true) --defer b:free() 57 - body:lpush(' <a class="quote" href="/post/'):push(&idbuf[0],idlen):lpush('">'):ppush(b):lpush('</a>') 61 + body:lpush(' <a class="quote" href="/post/'):shpush(notes(i).what):lpush('">'):ppush(b):lpush('</a>') 58 62 end 59 63 if not notweet then 60 64 var reply = co.srv:post_fetch(notes(i).reply) 61 65 lib.render.tweet(co,reply.ptr,&body) 62 66 reply:free() 63 67 end 64 68 n.ref = pstr {ptr = body.buf, ct = body.sz} 65 - 66 69 n:append(&pg) 70 + 67 71 ::skip:: n.nym:free() 68 72 pflink:reset() 69 73 body:reset() 70 74 end 71 75 --pflink:free() 72 76 pg:lpush('<form method="post"><button name="act" value="clear">clear all notices</button></form>') 73 77 co:livepage([lib.srv.convo.page] {
Modified render/profile.t from [a033243372] to [cdb26ba3d8].
66 66 end 67 67 68 68 -- this is really more what epithets are for, i think 69 69 if actor.rights.rank > 0 and stafftxt:ref() then 70 70 comments:lpush('<li>'):ppush(stafftxt):lpush('</li>') 71 71 end 72 72 73 - if co.who:outranks(actor) then 74 - comments:lpush('<li style="--co:50">underling</li>') 75 - elseif actor:outranks(co.who) then 76 - comments:lpush('<li style="--co:-50">outranks you</li>') 73 + if co.who.rights.rank ~= 0 then 74 + if co.who:outranks(actor) then 75 + comments:lpush('<li style="--co:50">underling</li>') 76 + elseif actor:outranks(co.who) then 77 + comments:lpush('<li style="--co:-50">outranks you</li>') 78 + end 77 79 end 78 80 79 81 if relationship.recip.follow() then 80 82 comments:lpush('<li style="--co:30">follows you</li>') 81 83 end 82 84 end 83 85
Modified route.t from [a232000d86] to [47b3eeec8d].
11 11 12 12 terra http.actor_profile(co: &lib.srv.convo, actor: &lib.store.actor, meth: method.t) 13 13 var rel: lib.store.relationship 14 14 if co.aid ~= 0 then 15 15 rel = co.srv:actor_rel_calc(co.who.id, actor.id) 16 16 if meth == method.post then 17 17 var act = co:ppostv('act') 18 - if act:cmp(lib.str.plit 'follow') and not rel.rel.follow() then 19 - if rel.recip.block() then 18 + if rel.recip.block() then 19 + if act:cmp(lib.str.plit 'follow') or act:cmp(lib.str.plit 'subscribe') then 20 20 co:complain(403,'blocked','you cannot follow a user you are blocked by') return 21 21 end 22 - (rel.rel.follow << true) 23 - co.srv:actor_rel_create([lib.store.relation.idvmap.follow], co.who.id, actor.id) 24 - elseif act:cmp(lib.str.plit 'unfollow') and rel.rel.follow() then 25 - (rel.rel.follow << false) 26 - co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], co.who.id, actor.id) 22 + end 23 + if act:cmp(lib.str.plit 'block') and not rel.rel.block() then 24 + (rel.rel.block << true) ; (rel.recip.follow << false) 25 + co.srv:actor_rel_create([lib.store.relation.idvmap.block], co.who.id, actor.id) 26 + co.srv:actor_rel_destroy([lib.store.relation.idvmap.follow], actor.id, co.who.id) 27 + else 28 + [(function() 29 + local tests = quote co:complain(400,'bad request','the action you have attempted on this user is not meaningful') return end 30 + for i,v in ipairs(lib.store.relation.members) do 31 + tests = quote 32 + if [v ~= 'block'] and act:cmp(lib.str.plit([v])) and not rel.rel.[v]() then -- rely on dead code elimination :/ 33 + (rel.rel.[v] << true) 34 + co.srv:actor_rel_create([lib.store.relation.idvmap[v]], co.who.id, actor.id) 35 + elseif act:cmp(lib.str.plit(['un'..v])) and rel.rel.[v]() then 36 + (rel.rel.[v] << false) 37 + co.srv:actor_rel_destroy([lib.store.relation.idvmap[v]], co.who.id, actor.id) 38 + else [tests] end 39 + end 40 + end 41 + return tests 42 + end)()] 27 43 end 28 44 end 29 45 else 30 46 rel.rel:clear() 31 47 rel.recip:clear() 32 48 end 33 49 ................................................................................ 42 58 if handle.ct == 0 then 43 59 handle.ct = uri.ct - 2 44 60 uri:advance(uri.ct) 45 61 elseif handle.ct + 2 < uri.ct then uri:advance(handle.ct + 2) end 46 62 47 63 lib.dbg('looking up user by xid "', {handle.ptr,handle.ct} ,'", path: ', {uri.ptr,uri.ct}) 48 64 49 - var path = lib.http.hier(uri) defer path:free() 65 + var path = lib.http.hier(&co.srv.pool, uri) --defer path:free() 50 66 for i=0,path.ct do 51 67 lib.dbg('got path component ', {path.ptr[i].ptr, path.ptr[i].ct}) 52 68 end 53 69 54 70 var actor = co.srv:actor_fetch_xid(handle) 55 71 if actor.ptr == nil then 56 72 co:complain(404,'no such user','no such user known to this server') ................................................................................ 318 334 credsec_for_uid(co: &lib.srv.convo, uid: uint64) 319 335 var act = co:ppostv('act') 320 336 lib.dbg('showing credentials') 321 337 if act:cmp(lib.str.plit 'invalidate') then 322 338 lib.dbg('setting user\'s cookie validation time to now') 323 339 co.who.source:auth_sigtime_user_alter(uid, lib.osclock.time(nil)) 324 340 -- the current session has been invalidated as well, so we need to immediately install a new authentication cookie with the same aid so the user doesn't need to log back in all over again 325 - co:installkey('/conf/sec',co.aid) 341 + co:installkey('?',co.aid) 326 342 return 327 343 elseif act:cmp(lib.str.plit 'newcred') then 328 344 var cmt = co:ppostv('comment') 329 345 var pw = co:ppostv('newpw') 330 346 var aid: uint64 = 0 331 347 if pw:ref() then 332 348 var cpw = co:ppostv('rptpw') ................................................................................ 692 708 end 693 709 elseif uri:cmp(lib.str.plit '/logout') then 694 710 if co.aid == 0 695 711 then goto notfound 696 712 else co:reroute_cookie('/','auth=; Path=/') 697 713 end 698 714 else -- hierarchical routes 699 - var path = lib.http.hier(uri) defer path:free() 715 + var path = lib.http.hier(&co.srv.pool, uri) --defer path:free() 700 716 if path.ct > 1 and path(0):cmp(lib.str.lit('user')) then 701 717 http.actor_profile_uid(co, path, meth) 702 718 elseif path.ct > 1 and path(0):cmp(lib.str.lit('post')) then 703 719 http.tweet_page(co, path, meth) 704 720 elseif path(0):cmp(lib.str.lit('tl')) then 705 721 http.timeline(co, path) 706 722 elseif path(0):cmp(lib.str.lit('media')) then
Added static/follow.svg version [61b9b2bee5].
1 +<?xml version="1.0" encoding="UTF-8" standalone="no"?> 2 +<!-- Created with Inkscape (http://www.inkscape.org/) --> 3 + 4 +<svg 5 + xmlns:dc="http://purl.org/dc/elements/1.1/" 6 + xmlns:cc="http://creativecommons.org/ns#" 7 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 8 + xmlns:svg="http://www.w3.org/2000/svg" 9 + xmlns="http://www.w3.org/2000/svg" 10 + xmlns:xlink="http://www.w3.org/1999/xlink" 11 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 12 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 13 + width="20" 14 + height="20" 15 + viewBox="0 0 5.2916664 5.2916665" 16 + version="1.1" 17 + id="svg8" 18 + inkscape:version="0.92.4 (5da689c313, 2019-01-14)" 19 + sodipodi:docname="follow.svg"> 20 + <defs 21 + id="defs2"> 22 + <linearGradient 23 + inkscape:collect="always" 24 + id="linearGradient1054"> 25 + <stop 26 + style="stop-color:#ff73e0;stop-opacity:1" 27 + offset="0" 28 + id="stop1050" /> 29 + <stop 30 + style="stop-color:#ff00cb;stop-opacity:0.98431373" 31 + offset="1" 32 + id="stop1052" /> 33 + </linearGradient> 34 + <linearGradient 35 + id="linearGradient1034" 36 + inkscape:collect="always"> 37 + <stop 38 + id="stop1030" 39 + offset="0" 40 + style="stop-color:#ff55dd;stop-opacity:0.50869566" /> 41 + <stop 42 + id="stop1032" 43 + offset="1" 44 + style="stop-color:#ff55dd;stop-opacity:0.02173913" /> 45 + </linearGradient> 46 + <linearGradient 47 + id="linearGradient1019" 48 + inkscape:collect="always"> 49 + <stop 50 + id="stop1013" 51 + offset="0" 52 + style="stop-color:#680054;stop-opacity:0.01304348" /> 53 + <stop 54 + style="stop-color:#ff40d9;stop-opacity:0.23913044" 55 + offset="0.30000001" 56 + id="stop1015" /> 57 + <stop 58 + id="stop1017" 59 + offset="1" 60 + style="stop-color:#d400aa;stop-opacity:0;" /> 61 + </linearGradient> 62 + <linearGradient 63 + id="linearGradient1005" 64 + inkscape:collect="always"> 65 + <stop 66 + id="stop1001" 67 + offset="0" 68 + style="stop-color:#fff6fd;stop-opacity:1" /> 69 + <stop 70 + id="stop1003" 71 + offset="1" 72 + style="stop-color:#ff3cd7;stop-opacity:0.98260868" /> 73 + </linearGradient> 74 + <linearGradient 75 + inkscape:collect="always" 76 + id="linearGradient979"> 77 + <stop 78 + style="stop-color:#680054;stop-opacity:1" 79 + offset="0" 80 + id="stop975" /> 81 + <stop 82 + id="stop983" 83 + offset="0.30000001" 84 + style="stop-color:#d400aa;stop-opacity:0.77254902;" /> 85 + <stop 86 + style="stop-color:#d400aa;stop-opacity:0;" 87 + offset="1" 88 + id="stop977" /> 89 + </linearGradient> 90 + <linearGradient 91 + inkscape:collect="always" 92 + id="linearGradient971"> 93 + <stop 94 + style="stop-color:#ff55dd;stop-opacity:1;" 95 + offset="0" 96 + id="stop967" /> 97 + <stop 98 + style="stop-color:#ff55dd;stop-opacity:0.97826087" 99 + offset="1" 100 + id="stop969" /> 101 + </linearGradient> 102 + <linearGradient 103 + inkscape:collect="always" 104 + id="linearGradient963"> 105 + <stop 106 + style="stop-color:#c839ac;stop-opacity:0.00392157" 107 + offset="0" 108 + id="stop959" /> 109 + <stop 110 + style="stop-color:#852572;stop-opacity:1" 111 + offset="1" 112 + id="stop961" /> 113 + </linearGradient> 114 + <linearGradient 115 + inkscape:collect="always" 116 + id="linearGradient943"> 117 + <stop 118 + style="stop-color:#f4d7ee;stop-opacity:1;" 119 + offset="0" 120 + id="stop939" /> 121 + <stop 122 + style="stop-color:#f4d7ee;stop-opacity:0;" 123 + offset="1" 124 + id="stop941" /> 125 + </linearGradient> 126 + <linearGradient 127 + inkscape:collect="always" 128 + id="linearGradient954"> 129 + <stop 130 + style="stop-color:#ffffff;stop-opacity:1;" 131 + offset="0" 132 + id="stop950" /> 133 + <stop 134 + style="stop-color:#ffffff;stop-opacity:0;" 135 + offset="1" 136 + id="stop952" /> 137 + </linearGradient> 138 + <linearGradient 139 + inkscape:collect="always" 140 + id="linearGradient938"> 141 + <stop 142 + style="stop-color:#d9fff6;stop-opacity:1;" 143 + offset="0" 144 + id="stop934" /> 145 + <stop 146 + style="stop-color:#d9fff6;stop-opacity:0;" 147 + offset="1" 148 + id="stop936" /> 149 + </linearGradient> 150 + <linearGradient 151 + inkscape:collect="always" 152 + id="linearGradient1403"> 153 + <stop 154 + style="stop-color:#ccaaff;stop-opacity:1;" 155 + offset="0" 156 + id="stop1399" /> 157 + <stop 158 + style="stop-color:#ccaaff;stop-opacity:0;" 159 + offset="1" 160 + id="stop1401" /> 161 + </linearGradient> 162 + <linearGradient 163 + id="linearGradient1395" 164 + inkscape:collect="always"> 165 + <stop 166 + id="stop1391" 167 + offset="0" 168 + style="stop-color:#ff1616;stop-opacity:1" /> 169 + <stop 170 + id="stop1393" 171 + offset="1" 172 + style="stop-color:#ff1d1d;stop-opacity:0" /> 173 + </linearGradient> 174 + <linearGradient 175 + inkscape:collect="always" 176 + id="linearGradient1383"> 177 + <stop 178 + style="stop-color:#980000;stop-opacity:1;" 179 + offset="0" 180 + id="stop1379" /> 181 + <stop 182 + style="stop-color:#980000;stop-opacity:0;" 183 + offset="1" 184 + id="stop1381" /> 185 + </linearGradient> 186 + <linearGradient 187 + inkscape:collect="always" 188 + id="linearGradient832"> 189 + <stop 190 + style="stop-color:#ffcfcf;stop-opacity:1;" 191 + offset="0" 192 + id="stop828" /> 193 + <stop 194 + style="stop-color:#ffcfcf;stop-opacity:0;" 195 + offset="1" 196 + id="stop830" /> 197 + </linearGradient> 198 + <radialGradient 199 + inkscape:collect="always" 200 + xlink:href="#linearGradient832" 201 + id="radialGradient834" 202 + cx="3.2286437" 203 + cy="286.62921" 204 + fx="3.2286437" 205 + fy="286.62921" 206 + r="1.0866126" 207 + gradientTransform="matrix(1.8608797,0.8147617,-0.38242057,0.87343168,106.71446,33.692223)" 208 + gradientUnits="userSpaceOnUse" /> 209 + <radialGradient 210 + inkscape:collect="always" 211 + xlink:href="#linearGradient1383" 212 + id="radialGradient1385" 213 + cx="4.1787109" 214 + cy="286.89261" 215 + fx="4.1787109" 216 + fy="286.89261" 217 + r="1.2260786" 218 + gradientTransform="matrix(1.7016464,0,0,1.6348586,-2.9319775,-182.10895)" 219 + gradientUnits="userSpaceOnUse" /> 220 + <radialGradient 221 + inkscape:collect="always" 222 + xlink:href="#linearGradient1395" 223 + id="radialGradient1389" 224 + gradientUnits="userSpaceOnUse" 225 + gradientTransform="matrix(0.66230313,-1.6430738,1.0154487,0.40931507,-290.06307,177.39489)" 226 + cx="4.02069" 227 + cy="287.79269" 228 + fx="4.02069" 229 + fy="287.79269" 230 + r="1.0866126" /> 231 + <linearGradient 232 + inkscape:collect="always" 233 + xlink:href="#linearGradient1403" 234 + id="linearGradient1405" 235 + x1="8.3939333" 236 + y1="288.1091" 237 + x2="7.0158253" 238 + y2="287.32819" 239 + gradientUnits="userSpaceOnUse" /> 240 + <linearGradient 241 + inkscape:collect="always" 242 + xlink:href="#linearGradient938" 243 + id="linearGradient940" 244 + x1="7.609839" 245 + y1="288.73215" 246 + x2="7.609839" 247 + y2="283.78305" 248 + gradientUnits="userSpaceOnUse" /> 249 + <linearGradient 250 + inkscape:collect="always" 251 + xlink:href="#linearGradient954" 252 + id="linearGradient956" 253 + x1="3.0150654" 254 + y1="285.94464" 255 + x2="3.0150654" 256 + y2="282.40109" 257 + gradientUnits="userSpaceOnUse" /> 258 + <linearGradient 259 + inkscape:collect="always" 260 + xlink:href="#linearGradient954" 261 + id="linearGradient1138" 262 + gradientUnits="userSpaceOnUse" 263 + x1="3.0150654" 264 + y1="285.94464" 265 + x2="3.0150654" 266 + y2="284.62277" /> 267 + <radialGradient 268 + inkscape:collect="always" 269 + xlink:href="#linearGradient1005" 270 + id="radialGradient945" 271 + cx="6.1517248" 272 + cy="285.09021" 273 + fx="6.1517248" 274 + fy="285.09021" 275 + r="1.3844374" 276 + gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821073,-417.49152)" 277 + gradientUnits="userSpaceOnUse" /> 278 + <radialGradient 279 + inkscape:collect="always" 280 + xlink:href="#linearGradient943" 281 + id="radialGradient953" 282 + gradientUnits="userSpaceOnUse" 283 + gradientTransform="matrix(2.4674713,0,0,2.4674669,-9.027479,-418.36044)" 284 + cx="6.1517248" 285 + cy="285.09021" 286 + fx="6.1517248" 287 + fy="285.09021" 288 + r="1.3844374" /> 289 + <radialGradient 290 + inkscape:collect="always" 291 + xlink:href="#linearGradient963" 292 + id="radialGradient965" 293 + cx="6.1523438" 294 + cy="285.08984" 295 + fx="6.1523438" 296 + fy="285.08984" 297 + r="1.6679688" 298 + gradientTransform="matrix(1,0,0,0.99999775,0,6.4095141e-4)" 299 + gradientUnits="userSpaceOnUse" /> 300 + <radialGradient 301 + inkscape:collect="always" 302 + xlink:href="#linearGradient971" 303 + id="radialGradient973" 304 + cx="4.6300988" 305 + cy="285.0715" 306 + fx="4.6300988" 307 + fy="285.0715" 308 + r="0.88396439" 309 + gradientTransform="matrix(3.5121044,0,0,4.5073949,-11.771195,-1000.0836)" 310 + gradientUnits="userSpaceOnUse" /> 311 + <linearGradient 312 + inkscape:collect="always" 313 + xlink:href="#linearGradient979" 314 + id="linearGradient981" 315 + x1="6.3269596" 316 + y1="286.08289" 317 + x2="6.3263793" 318 + y2="288.44873" 319 + gradientUnits="userSpaceOnUse" /> 320 + <linearGradient 321 + inkscape:collect="always" 322 + xlink:href="#linearGradient1019" 323 + id="linearGradient1009" 324 + gradientUnits="userSpaceOnUse" 325 + x1="6.3269596" 326 + y1="286.08289" 327 + x2="6.3263793" 328 + y2="288.44873" /> 329 + <radialGradient 330 + inkscape:collect="always" 331 + xlink:href="#linearGradient1034" 332 + id="radialGradient1028" 333 + cx="7.8964839" 334 + cy="10.825195" 335 + fx="7.8964839" 336 + fy="10.825195" 337 + r="6.1388507" 338 + gradientTransform="matrix(1.5553588,0,0,2.1211746,-4.385382,-12.136934)" 339 + gradientUnits="userSpaceOnUse" /> 340 + <radialGradient 341 + inkscape:collect="always" 342 + xlink:href="#linearGradient1034" 343 + id="radialGradient1038" 344 + gradientUnits="userSpaceOnUse" 345 + gradientTransform="matrix(-1.5553588,0,0,-1.3840186,20.178349,25.807467)" 346 + cx="7.8964839" 347 + cy="10.825195" 348 + fx="7.8964839" 349 + fy="10.825195" 350 + r="6.1388507" /> 351 + <radialGradient 352 + inkscape:collect="always" 353 + xlink:href="#linearGradient1054" 354 + id="radialGradient1048" 355 + gradientUnits="userSpaceOnUse" 356 + gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821075,-417.49152)" 357 + cx="6.1517248" 358 + cy="285.09021" 359 + fx="6.1517248" 360 + fy="285.09021" 361 + r="1.3844374" /> 362 + <filter 363 + inkscape:collect="always" 364 + style="color-interpolation-filters:sRGB" 365 + id="filter1132" 366 + x="-0.27479975" 367 + width="1.5495995" 368 + y="-0.27480025" 369 + height="1.5496005"> 370 + <feGaussianBlur 371 + inkscape:collect="always" 372 + stdDeviation="0.3170359" 373 + id="feGaussianBlur1134" /> 374 + </filter> 375 + <radialGradient 376 + inkscape:collect="always" 377 + xlink:href="#linearGradient1054" 378 + id="radialGradient1140" 379 + gradientUnits="userSpaceOnUse" 380 + gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821075,-417.49152)" 381 + cx="6.1517248" 382 + cy="285.09021" 383 + fx="6.1517248" 384 + fy="285.09021" 385 + r="1.3844374" /> 386 + <radialGradient 387 + inkscape:collect="always" 388 + xlink:href="#linearGradient1005" 389 + id="radialGradient1142" 390 + gradientUnits="userSpaceOnUse" 391 + gradientTransform="matrix(2.4674713,0,0,2.4674669,-5.8821073,-417.49152)" 392 + cx="6.1517248" 393 + cy="285.09021" 394 + fx="6.1517248" 395 + fy="285.09021" 396 + r="1.3844374" /> 397 + <radialGradient 398 + inkscape:collect="always" 399 + xlink:href="#linearGradient1034" 400 + id="radialGradient1149" 401 + gradientUnits="userSpaceOnUse" 402 + gradientTransform="matrix(-1.5553588,0,0,-1.3840186,20.178349,25.807467)" 403 + cx="7.8964839" 404 + cy="10.825195" 405 + fx="7.8964839" 406 + fy="10.825195" 407 + r="6.1388507" /> 408 + <linearGradient 409 + inkscape:collect="always" 410 + xlink:href="#linearGradient979" 411 + id="linearGradient1151" 412 + gradientUnits="userSpaceOnUse" 413 + x1="6.3269596" 414 + y1="286.08289" 415 + x2="6.3263793" 416 + y2="288.44873" /> 417 + <radialGradient 418 + inkscape:collect="always" 419 + xlink:href="#linearGradient971" 420 + id="radialGradient1153" 421 + gradientUnits="userSpaceOnUse" 422 + gradientTransform="matrix(3.5121044,0,0,4.5073949,-11.771195,-1000.0836)" 423 + cx="4.6300988" 424 + cy="285.0715" 425 + fx="4.6300988" 426 + fy="285.0715" 427 + r="0.88396439" /> 428 + </defs> 429 + <sodipodi:namedview 430 + id="base" 431 + pagecolor="#181818" 432 + bordercolor="#666666" 433 + borderopacity="1.0" 434 + inkscape:pageopacity="0" 435 + inkscape:pageshadow="2" 436 + inkscape:zoom="11.2" 437 + inkscape:cx="30.205871" 438 + inkscape:cy="-5.2770461" 439 + inkscape:document-units="mm" 440 + inkscape:current-layer="layer1" 441 + showgrid="false" 442 + units="px" 443 + inkscape:window-width="1920" 444 + inkscape:window-height="1042" 445 + inkscape:window-x="0" 446 + inkscape:window-y="38" 447 + inkscape:window-maximized="0" 448 + showguides="true" 449 + fit-margin-top="0" 450 + fit-margin-left="0" 451 + fit-margin-right="0" 452 + fit-margin-bottom="0" /> 453 + <metadata 454 + id="metadata5"> 455 + <rdf:RDF> 456 + <cc:Work 457 + rdf:about=""> 458 + <dc:format>image/svg+xml</dc:format> 459 + <dc:type 460 + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> 461 + <dc:title></dc:title> 462 + </cc:Work> 463 + </rdf:RDF> 464 + </metadata> 465 + <g 466 + inkscape:label="Layer 1" 467 + inkscape:groupmode="layer" 468 + id="layer1" 469 + transform="translate(-2.6134661,-283.36966)"> 470 + <g 471 + id="g1147" 472 + transform="matrix(0.9120087,0,0,0.9120087,0.33514277,25.128587)" 473 + style="stroke-width:1.09648085"> 474 + <path 475 + transform="matrix(0.26458333,0,0,0.26458333,2.6134661,283.36966)" 476 + d="m 7.9257812,0.8359375 a 1.6178892,1.6178892 0 0 0 -0.2207031,0.009766 C 5.1410214,1.1501128 3.6301435,2.94586 3.1875,4.4550781 2.798777,5.7804512 2.9557893,6.822698 3.0019531,7.09375 c 0.045127,0.9218304 0.3822636,1.5885831 0.7910157,2.2910156 0.3127802,0.5375068 0.7335894,1.0229114 1.15625,1.4980464 0.028572,0.111047 0.050755,0.187185 0.082031,0.326172 0.0073,0.03244 0.00535,0.02259 0.011719,0.05664 -0.3761238,0.02572 -0.8216256,0.06774 -1.2910157,0.138672 -0.4857631,0.07341 -0.9830994,0.168253 -1.4765625,0.332031 -0.493463,0.163778 -1.0998654,0.233591 -1.6191406,1.203125 -0.23350107,0.435868 -0.24245693,0.654459 -0.2890625,0.923828 -0.0466056,0.269369 -0.0772406,0.547444 -0.10351562,0.84961 -0.0525501,0.604332 -0.0820372,1.293158 -0.0996094,1.955078 -0.0351443,1.323838 -0.0214844,2.544922 -0.0214844,2.544922 A 1.6178892,1.6178892 0 0 0 1.7597656,20.814453 H 14.033203 a 1.6178892,1.6178892 0 0 0 1.617188,-1.601562 c 0,0 0.01366,-1.221084 -0.02149,-2.544922 -0.01757,-0.661919 -0.04706,-1.350747 -0.09961,-1.955078 -0.02628,-0.302166 -0.05691,-0.580241 -0.103516,-0.84961 -0.04661,-0.269368 -0.05556,-0.487961 -0.289062,-0.923828 C 14.61759,11.970465 14.010855,11.900125 13.517578,11.736328 13.024302,11.572531 12.526554,11.477735 12.041016,11.404297 11.571088,11.33322 11.126348,11.28939 10.75,11.263672 c 0.0028,-0.01394 8.95e-4,-0.0098 0.0039,-0.02344 0.02862,-0.129185 0.05214,-0.210465 0.08008,-0.320312 0.433111,-0.472266 0.867515,-0.9510681 1.1875,-1.498047 0.424707,-0.7259885 0.791976,-1.4348115 0.777344,-2.4375 l -0.02734,0.3183594 c 0,0 0.276624,-1.338406 -0.166015,-2.8476563 C 12.162829,2.9458278 10.651988,1.1499497 8.0878906,0.84570312 A 1.6178892,1.6178892 0 0 0 7.9257812,0.8359375 Z M 10.701172,11.511719 c -0.0029,-0.0036 0.007,0.0064 0.0039,0.002 -0.04632,-0.06737 -0.0293,-0.353973 -0.0293,0.134766 0,0.04526 0.01972,-0.102408 0.02539,-0.136719 z m -5.6093751,0.002 c 0.00534,0.02911 0.025391,0.180144 0.025391,0.134766 0,-0.486237 0.016732,-0.199942 -0.029297,-0.132813 -0.00293,0.0043 0.00675,-0.0055 0.00391,-0.002 z" 477 + id="path1021" 478 + style="fill:url(#radialGradient1149);fill-opacity:1;stroke:none;stroke-width:1.09648073px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 479 + inkscape:original="M 7.8964844 2.453125 C 3.8572424 2.9326715 4.6113281 7.0078125 4.6113281 7.0078125 C 4.6485531 7.8381593 5.5094605 9.2817797 6.4453125 10.195312 C 6.4986525 10.392157 6.734375 11.271085 6.734375 11.648438 C 6.734375 12.069175 6.1908578 12.91084 5.8691406 12.861328 C 5.5474216 12.811888 2.4532451 13.010035 2.0820312 13.703125 C 1.7108136 14.396064 1.7597656 19.197266 1.7597656 19.197266 L 14.033203 19.197266 C 14.033203 19.197266 14.082152 14.396064 13.710938 13.703125 C 13.339721 13.010224 10.247498 12.811816 9.9257812 12.861328 C 9.6040608 12.910798 9.0585937 12.069174 9.0585938 11.648438 C 9.0585938 11.282797 9.2742792 10.466705 9.3378906 10.230469 C 10.300978 9.3271083 11.194239 7.8711535 11.181641 7.0078125 C 11.181641 7.0078125 11.935727 2.9324069 7.8964844 2.453125 z " 480 + inkscape:radius="1.6177274" 481 + sodipodi:type="inkscape:offset" /> 482 + <path 483 + sodipodi:nodetypes="cccsccscccc" 484 + inkscape:connector-curvature="0" 485 + d="m 3.0792354,288.44873 c 0,0 -0.013096,-1.27028 0.085122,-1.45362 0.098217,-0.18338 0.9166976,-0.23571 1.0018191,-0.22263 0.085121,0.0131 0.2291744,-0.20953 0.2291744,-0.32085 0,-0.11131 -0.085123,-0.41905 -0.085123,-0.41905 h 0.7851589 c 0,0 -0.085122,0.30774 -0.085122,0.41905 0,0.11132 0.1440525,0.33394 0.2291744,0.32085 0.085121,-0.0131 0.9036015,0.0393 1.0018191,0.22263 0.098217,0.18334 0.085121,1.45362 0.085121,1.45362 z" 486 + style="fill:url(#linearGradient1151);fill-opacity:1;stroke:none;stroke-width:0.29011053px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" 487 + id="path893" /> 488 + <path 489 + sodipodi:nodetypes="cczcc" 490 + inkscape:connector-curvature="0" 491 + d="m 4.7028071,284.01884 c -1.0687161,0.12688 -0.8692216,1.20492 -0.8692216,1.20492 0.015116,0.33718 0.5431676,1.06402 0.8692218,1.06402 0.3260542,0 0.8742883,-0.71682 0.8692217,-1.06402 0,0 0.1994946,-1.07811 -0.8692216,-1.20492" 492 + style="fill:url(#radialGradient1153);fill-opacity:1;stroke:none;stroke-width:0.18091933;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" 493 + id="path888" /> 494 + </g> 495 + <g 496 + id="g1138" 497 + transform="matrix(0.83362971,0,0,0.83362961,-1.263316,47.635689)" 498 + style="stroke-width:1.1995734"> 499 + <path 500 + id="path1046" 501 + style="fill:url(#radialGradient1140);fill-opacity:1;stroke:none;stroke-width:0.19792962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter1132)" 502 + d="m 9.0817743,287.34356 h 0.43065 v -1.16911 H 10.681534 V 285.7438 H 9.5124243 v -1.16911 h -0.43065 v 1.16911 h -1.169115 v 0.43065 h 1.169115 z" 503 + inkscape:connector-curvature="0" 504 + sodipodi:nodetypes="ccccccccccccc" /> 505 + <path 506 + sodipodi:nodetypes="ccccccccccccc" 507 + inkscape:connector-curvature="0" 508 + d="m 9.0817743,287.34356 h 0.43065 v -1.16911 H 10.681534 V 285.7438 H 9.5124243 v -1.16911 h -0.43065 v 1.16911 h -1.169115 v 0.43065 h 1.169115 z" 509 + style="fill:url(#radialGradient1142);fill-opacity:1;stroke:none;stroke-width:0.19792962;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" 510 + id="path932" /> 511 + </g> 512 + </g> 513 +</svg>
Modified static/style.scss from [ba9256aed1] to [08446e37b3].
585 585 } 586 586 &:empty { 587 587 transition: 0.3s; 588 588 opacity: 0.0001; // qutebrowser won't show hints if opacity=0 :( 589 589 &:hover, &:focus { opacity: 0.6 !important; } 590 590 } 591 591 } 592 - > .like { background-image: url(/s/heart.webp); } 593 - > .rt { background-image: url(/s/retweet.webp); } 592 + > .like { background-image: url(/s/heart.webp); } 593 + > .rt { background-image: url(/s/retweet.webp); } 594 594 } 595 595 596 596 // used for keyboard navigation 597 597 &.live-selected { 598 598 //margin-left: 0.4in; margin-right: -0.4in; 599 599 box-shadow: 0 0 0 1px tone(15%), 0 0 1in tone(5%, -0.5); 600 600 transform: scale(1.05) translateX(0.1in); ................................................................................ 988 988 body.notices { 989 989 form { text-align: center; } 990 990 div.notice { 991 991 padding: 0.15in; 992 992 background: linear-gradient(to bottom, tone(10%, -0.9), transparent); 993 993 border: 1px solid tone(-60%); 994 994 & + div.notice { border-top: none; } 995 - &.rt, &.like, &.reply { &::before { 995 + &.rt, &.like, &.reply, &.follow { &::before { 996 996 display: inline-block; 997 997 width: 1em; height: 1em; 998 998 margin-right: 1ex; 999 999 background-size: contain; 1000 1000 vertical-align: bottom; 1001 1001 content: ""; // 🙄 1002 1002 }} 1003 - &.rt::before { background-image: url(/s/retweet.webp); } 1004 - &.like::before { background-image: url(/s/heart.webp); } 1005 - &.reply::before { background-image: url(/s/reply.webp); } 1003 + &.rt::before { background-image: url(/s/retweet.webp); } 1004 + &.like::before { background-image: url(/s/heart.webp); } 1005 + &.reply::before { background-image: url(/s/reply.webp); } 1006 + &.follow::before { background-image: url(/s/follow.webp); } 1006 1007 > .action { 1007 1008 display: inline-block; 1008 1009 color: tone(5%); 1009 1010 > .id { 1010 1011 display: inline-block; 1011 1012 > img { 1012 1013 width: 1em; height: 1em;
Modified store.t from [adf1545306] to [8b07464dc2].
2 2 local m = { 3 3 timepoint = lib.osclock.time_t; 4 4 scope = lib.enum { 5 5 'public', 'private', 'local'; 6 6 'personal', 'direct', 'circle'; 7 7 }; 8 8 noticetype = lib.enum { 9 - 'none', 'mention', 'reply', 'like', 'rt', 'react' 9 + 'none', 'mention', 'reply', 'like', 'rt', 'react', 'follow' 10 10 }; 11 11 12 12 relation = lib.set { 13 13 'follow', 14 14 'subscribe', -- get a notification for every post 15 15 'mute', -- posts will be completely hidden at all times 16 16 'block', -- no interactions will be permitted, but posts will remain visible